@winspan/claude-forge 9.2.0 → 9.12.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/DEVELOPMENT.md +80 -19
- package/README.md +13 -6
- package/dist/catalogs/agents.json +19 -24
- package/dist/catalogs/skills.json +139 -27
- package/dist/claudemd/templates/swarm-protocol.md +1 -1
- package/dist/cli/commands/agent.d.ts +169 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +690 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/codegraph.d.ts +17 -0
- package/dist/cli/commands/codegraph.d.ts.map +1 -0
- package/dist/cli/commands/codegraph.js +263 -0
- package/dist/cli/commands/codegraph.js.map +1 -0
- package/dist/cli/commands/decisions.d.ts.map +1 -1
- package/dist/cli/commands/decisions.js +46 -9
- package/dist/cli/commands/decisions.js.map +1 -1
- package/dist/cli/commands/executions.d.ts.map +1 -1
- package/dist/cli/commands/executions.js +2 -1
- package/dist/cli/commands/executions.js.map +1 -1
- package/dist/cli/commands/insights-goal-check.d.ts +5 -1
- package/dist/cli/commands/insights-goal-check.d.ts.map +1 -1
- package/dist/cli/commands/insights-goal-check.js +15 -15
- package/dist/cli/commands/insights-goal-check.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +51 -0
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +202 -29
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/loop.d.ts +95 -0
- package/dist/cli/commands/loop.d.ts.map +1 -0
- package/dist/cli/commands/loop.js +408 -0
- package/dist/cli/commands/loop.js.map +1 -0
- package/dist/cli/commands/maintenance.d.ts +33 -0
- package/dist/cli/commands/maintenance.d.ts.map +1 -0
- package/dist/cli/commands/maintenance.js +75 -0
- package/dist/cli/commands/maintenance.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +23 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +82 -0
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/skills.d.ts +131 -0
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +409 -9
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/stats.d.ts.map +1 -1
- package/dist/cli/commands/stats.js +9 -2
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/constants.d.ts +37 -0
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +43 -0
- package/dist/core/constants.js.map +1 -1
- package/dist/core/diagnostics/checks.d.ts.map +1 -1
- package/dist/core/diagnostics/checks.js +2 -1
- package/dist/core/diagnostics/checks.js.map +1 -1
- package/dist/core/diagnostics/daemon-status.d.ts +1 -1
- package/dist/core/diagnostics/daemon-status.d.ts.map +1 -1
- package/dist/core/diagnostics/daemon-status.js +1 -1
- package/dist/core/diagnostics/daemon-status.js.map +1 -1
- package/dist/core/diagnostics/entropy-checks.d.ts +3 -2
- package/dist/core/diagnostics/entropy-checks.d.ts.map +1 -1
- package/dist/core/diagnostics/entropy-checks.js +10 -5
- package/dist/core/diagnostics/entropy-checks.js.map +1 -1
- package/dist/core/diagnostics/heartbeat-reader.d.ts +28 -0
- package/dist/core/diagnostics/heartbeat-reader.d.ts.map +1 -0
- package/dist/core/diagnostics/heartbeat-reader.js +50 -0
- package/dist/core/diagnostics/heartbeat-reader.js.map +1 -0
- package/dist/core/event-fields.d.ts +27 -0
- package/dist/core/event-fields.d.ts.map +1 -1
- package/dist/core/event-fields.js +43 -0
- package/dist/core/event-fields.js.map +1 -1
- package/dist/core/governance/global-inject.d.ts +34 -4
- package/dist/core/governance/global-inject.d.ts.map +1 -1
- package/dist/core/governance/global-inject.js +80 -25
- package/dist/core/governance/global-inject.js.map +1 -1
- package/dist/core/insights/agent-anchor-guard.d.ts +77 -0
- package/dist/core/insights/agent-anchor-guard.d.ts.map +1 -0
- package/dist/core/insights/agent-anchor-guard.js +119 -0
- package/dist/core/insights/agent-anchor-guard.js.map +1 -0
- package/dist/core/insights/agent-distill-context.d.ts +55 -0
- package/dist/core/insights/agent-distill-context.d.ts.map +1 -0
- package/dist/core/insights/agent-distill-context.js +146 -0
- package/dist/core/insights/agent-distill-context.js.map +1 -0
- package/dist/core/insights/agent-distill-spawn.d.ts +56 -0
- package/dist/core/insights/agent-distill-spawn.d.ts.map +1 -0
- package/dist/core/insights/agent-distill-spawn.js +179 -0
- package/dist/core/insights/agent-distill-spawn.js.map +1 -0
- package/dist/core/insights/agent-drift.d.ts +66 -0
- package/dist/core/insights/agent-drift.d.ts.map +1 -0
- package/dist/core/insights/agent-drift.js +109 -0
- package/dist/core/insights/agent-drift.js.map +1 -0
- package/dist/core/insights/agent-evolution-sources.d.ts +21 -0
- package/dist/core/insights/agent-evolution-sources.d.ts.map +1 -0
- package/dist/core/insights/agent-evolution-sources.js +36 -0
- package/dist/core/insights/agent-evolution-sources.js.map +1 -0
- package/dist/core/insights/agent-health.d.ts +142 -0
- package/dist/core/insights/agent-health.d.ts.map +1 -0
- package/dist/core/insights/agent-health.js +296 -0
- package/dist/core/insights/agent-health.js.map +1 -0
- package/dist/core/insights/agent-patch-apply.d.ts +45 -0
- package/dist/core/insights/agent-patch-apply.d.ts.map +1 -0
- package/dist/core/insights/agent-patch-apply.js +165 -0
- package/dist/core/insights/agent-patch-apply.js.map +1 -0
- package/dist/core/insights/agent-suggest.d.ts +128 -0
- package/dist/core/insights/agent-suggest.d.ts.map +1 -0
- package/dist/core/insights/agent-suggest.js +284 -0
- package/dist/core/insights/agent-suggest.js.map +1 -0
- package/dist/core/insights/coverage-tiers.d.ts +46 -0
- package/dist/core/insights/coverage-tiers.d.ts.map +1 -0
- package/dist/core/insights/coverage-tiers.js +95 -0
- package/dist/core/insights/coverage-tiers.js.map +1 -0
- package/dist/{daemon/services → core/insights}/experience-extractor.d.ts +0 -7
- package/dist/core/insights/experience-extractor.d.ts.map +1 -0
- package/dist/{daemon/services → core/insights}/experience-extractor.js +5 -9
- package/dist/core/insights/experience-extractor.js.map +1 -0
- package/dist/{daemon/services → core/insights}/violation-reporter.d.ts +20 -1
- package/dist/core/insights/violation-reporter.d.ts.map +1 -0
- package/dist/{daemon/services → core/insights}/violation-reporter.js +56 -4
- package/dist/core/insights/violation-reporter.js.map +1 -0
- package/dist/core/loop/loop-engine.d.ts +140 -0
- package/dist/core/loop/loop-engine.d.ts.map +1 -0
- package/dist/core/loop/loop-engine.js +266 -0
- package/dist/core/loop/loop-engine.js.map +1 -0
- package/dist/core/queue/index.d.ts.map +1 -1
- package/dist/core/queue/index.js +2 -1
- package/dist/core/queue/index.js.map +1 -1
- package/dist/core/storage/base.d.ts +159 -0
- package/dist/core/storage/base.d.ts.map +1 -1
- package/dist/core/storage/base.js +523 -0
- package/dist/core/storage/base.js.map +1 -1
- package/dist/core/storage/codegraph-types.d.ts +79 -0
- package/dist/core/storage/codegraph-types.d.ts.map +1 -0
- package/dist/core/storage/codegraph-types.js +14 -0
- package/dist/core/storage/codegraph-types.js.map +1 -0
- package/dist/core/storage/codegraph.d.ts +186 -0
- package/dist/core/storage/codegraph.d.ts.map +1 -0
- package/dist/core/storage/codegraph.js +452 -0
- package/dist/core/storage/codegraph.js.map +1 -0
- package/dist/core/storage/decisions.d.ts +30 -5
- package/dist/core/storage/decisions.d.ts.map +1 -1
- package/dist/core/storage/decisions.js +45 -13
- package/dist/core/storage/decisions.js.map +1 -1
- package/dist/core/storage/events.d.ts +127 -0
- package/dist/core/storage/events.d.ts.map +1 -1
- package/dist/core/storage/events.js +318 -3
- package/dist/core/storage/events.js.map +1 -1
- package/dist/core/storage/feedback.d.ts +3 -23
- package/dist/core/storage/feedback.d.ts.map +1 -1
- package/dist/core/storage/feedback.js +37 -38
- package/dist/core/storage/feedback.js.map +1 -1
- package/dist/core/storage/injections.d.ts +40 -0
- package/dist/core/storage/injections.d.ts.map +1 -1
- package/dist/core/storage/injections.js +69 -0
- package/dist/core/storage/injections.js.map +1 -1
- package/dist/core/storage/knowledge.d.ts +226 -0
- package/dist/core/storage/knowledge.d.ts.map +1 -1
- package/dist/core/storage/knowledge.js +391 -4
- package/dist/core/storage/knowledge.js.map +1 -1
- package/dist/core/storage/pipeline-rollup.d.ts +1 -7
- package/dist/core/storage/pipeline-rollup.d.ts.map +1 -1
- package/dist/core/storage/pipeline-rollup.js +18 -57
- package/dist/core/storage/pipeline-rollup.js.map +1 -1
- package/dist/core/storage/routing.d.ts +34 -0
- package/dist/core/storage/routing.d.ts.map +1 -1
- package/dist/core/storage/routing.js +92 -2
- package/dist/core/storage/routing.js.map +1 -1
- package/dist/core/storage/rows.d.ts +5 -25
- package/dist/core/storage/rows.d.ts.map +1 -1
- package/dist/core/storage/schema.sql +92 -27
- package/dist/core/storage/sessions.d.ts.map +1 -1
- package/dist/core/storage/sessions.js +2 -1
- package/dist/core/storage/sessions.js.map +1 -1
- package/dist/core/storage/skills.d.ts +159 -0
- package/dist/core/storage/skills.d.ts.map +1 -1
- package/dist/core/storage/skills.js +350 -4
- package/dist/core/storage/skills.js.map +1 -1
- package/dist/core/storage/sqlite.d.ts +81 -25
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +143 -45
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/storage/tasks.d.ts +270 -0
- package/dist/core/storage/tasks.d.ts.map +1 -1
- package/dist/core/storage/tasks.js +495 -16
- package/dist/core/storage/tasks.js.map +1 -1
- package/dist/core/storage/tool-intercepts.d.ts +1 -1
- package/dist/core/storage/tool-intercepts.js +1 -1
- package/dist/core/types.d.ts +26 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/utils/binary-paths.d.ts +32 -0
- package/dist/core/utils/binary-paths.d.ts.map +1 -1
- package/dist/core/utils/binary-paths.js +52 -0
- package/dist/core/utils/binary-paths.js.map +1 -1
- package/dist/core/utils/claude-cli-resolver.d.ts.map +1 -0
- package/dist/{skills/distill → core/utils}/claude-cli-resolver.js +1 -1
- package/dist/core/utils/claude-cli-resolver.js.map +1 -0
- package/dist/core/utils/claude-cli-spawn.d.ts +1 -1
- package/dist/core/utils/claude-cli-spawn.js +2 -2
- package/dist/core/utils/claude-cli-spawn.js.map +1 -1
- package/dist/core/utils/noise-prompt.d.ts +1 -1
- package/dist/core/utils/noise-prompt.js +1 -1
- package/dist/core/utils/time.d.ts +26 -0
- package/dist/core/utils/time.d.ts.map +1 -1
- package/dist/core/utils/time.js +33 -0
- package/dist/core/utils/time.js.map +1 -1
- package/dist/daemon/config-store.d.ts.map +1 -1
- package/dist/daemon/config-store.js +14 -5
- package/dist/daemon/config-store.js.map +1 -1
- package/dist/daemon/event-parser.d.ts.map +1 -1
- package/dist/daemon/event-parser.js +5 -0
- package/dist/daemon/event-parser.js.map +1 -1
- package/dist/daemon/handlers/post-tool-use.d.ts +24 -16
- package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use.js +76 -116
- package/dist/daemon/handlers/post-tool-use.js.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.d.ts +35 -10
- package/dist/daemon/handlers/pre-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.js +71 -38
- package/dist/daemon/handlers/pre-tool-use.js.map +1 -1
- package/dist/daemon/handlers/stop.d.ts +20 -0
- package/dist/daemon/handlers/stop.d.ts.map +1 -1
- package/dist/daemon/handlers/stop.js +96 -8
- package/dist/daemon/handlers/stop.js.map +1 -1
- package/dist/daemon/handlers/user-prompt.d.ts +16 -1
- package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt.js +97 -56
- package/dist/daemon/handlers/user-prompt.js.map +1 -1
- package/dist/daemon/handlers/violation-content-backfill.d.ts +76 -0
- package/dist/daemon/handlers/violation-content-backfill.d.ts.map +1 -0
- package/dist/daemon/handlers/violation-content-backfill.js +167 -0
- package/dist/daemon/handlers/violation-content-backfill.js.map +1 -0
- package/dist/daemon/index.d.ts +19 -0
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +125 -200
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/rules/defaults.d.ts.map +1 -1
- package/dist/daemon/rules/defaults.js +151 -64
- package/dist/daemon/rules/defaults.js.map +1 -1
- package/dist/daemon/rules/types.d.ts +28 -22
- package/dist/daemon/rules/types.d.ts.map +1 -1
- package/dist/daemon/rules/workflow-defaults.js +9 -9
- package/dist/daemon/rules/workflow-defaults.js.map +1 -1
- package/dist/daemon/services/codegraph-sync.d.ts +94 -0
- package/dist/daemon/services/codegraph-sync.d.ts.map +1 -0
- package/dist/daemon/services/codegraph-sync.js +159 -0
- package/dist/daemon/services/codegraph-sync.js.map +1 -0
- package/dist/daemon/services/decision-hint.d.ts +47 -10
- package/dist/daemon/services/decision-hint.d.ts.map +1 -1
- package/dist/daemon/services/decision-hint.js +99 -24
- package/dist/daemon/services/decision-hint.js.map +1 -1
- package/dist/daemon/services/event-ttl-sweep.d.ts.map +1 -1
- package/dist/daemon/services/event-ttl-sweep.js +3 -2
- package/dist/daemon/services/event-ttl-sweep.js.map +1 -1
- package/dist/daemon/services/feedback-aggregator.d.ts +14 -26
- package/dist/daemon/services/feedback-aggregator.d.ts.map +1 -1
- package/dist/daemon/services/feedback-aggregator.js +23 -63
- package/dist/daemon/services/feedback-aggregator.js.map +1 -1
- package/dist/daemon/services/heartbeat-writer.d.ts +6 -15
- package/dist/daemon/services/heartbeat-writer.d.ts.map +1 -1
- package/dist/daemon/services/heartbeat-writer.js +7 -36
- package/dist/daemon/services/heartbeat-writer.js.map +1 -1
- package/dist/daemon/services/kb-injector.d.ts +1 -1
- package/dist/daemon/services/kb-injector.d.ts.map +1 -1
- package/dist/daemon/services/kb-injector.js +10 -2
- package/dist/daemon/services/kb-injector.js.map +1 -1
- package/dist/daemon/services/kb-rebuild-scheduler.d.ts +95 -0
- package/dist/daemon/services/kb-rebuild-scheduler.d.ts.map +1 -0
- package/dist/daemon/services/kb-rebuild-scheduler.js +149 -0
- package/dist/daemon/services/kb-rebuild-scheduler.js.map +1 -0
- package/dist/daemon/services/loop-hint.d.ts +139 -0
- package/dist/daemon/services/loop-hint.d.ts.map +1 -0
- package/dist/daemon/services/loop-hint.js +272 -0
- package/dist/daemon/services/loop-hint.js.map +1 -0
- package/dist/daemon/services/outcome-classification-service.js +1 -1
- package/dist/daemon/services/outcome-classification-service.js.map +1 -1
- package/dist/daemon/services/task-segmenter.d.ts +11 -0
- package/dist/daemon/services/task-segmenter.d.ts.map +1 -1
- package/dist/daemon/services/task-segmenter.js +48 -2
- package/dist/daemon/services/task-segmenter.js.map +1 -1
- package/dist/daemon/startup/maintenance-schedulers.d.ts +68 -0
- package/dist/daemon/startup/maintenance-schedulers.d.ts.map +1 -0
- package/dist/daemon/startup/maintenance-schedulers.js +294 -0
- package/dist/daemon/startup/maintenance-schedulers.js.map +1 -0
- package/dist/daemon/templates/agents/agent-retro-distiller.md +106 -0
- package/dist/daemon/templates/agents/claudemd-writer.md +1 -0
- package/dist/daemon/templates/agents/coder.md +165 -8
- package/dist/daemon/templates/agents/decision-maker.md +107 -21
- package/dist/daemon/templates/agents/doc-reviewer.md +4 -1
- package/dist/daemon/templates/agents/harness-debug-full.md +85 -3
- package/dist/daemon/templates/agents/knowledge-builder.md +1 -0
- package/dist/daemon/templates/agents/patch-applier.md +1 -0
- package/dist/daemon/templates/agents/planner.md +55 -3
- package/dist/daemon/templates/agents/safety-net-implementer.md +278 -0
- package/dist/daemon/templates/agents/skill-distiller.md +1 -0
- package/dist/daemon/templates/agents/task-boundary-classifier.md +1 -0
- package/dist/daemon/templates/agents/verify-agent.md +128 -5
- package/dist/hooks/stop.sh +7 -1
- package/dist/knowledge/builder.js +36 -7
- package/dist/knowledge/builder.js.map +1 -1
- package/dist/knowledge/constants.d.ts +10 -5
- package/dist/knowledge/constants.d.ts.map +1 -1
- package/dist/knowledge/constants.js +10 -5
- package/dist/knowledge/constants.js.map +1 -1
- package/dist/knowledge/graph/edge-extractor.d.ts +45 -0
- package/dist/knowledge/graph/edge-extractor.d.ts.map +1 -0
- package/dist/knowledge/graph/edge-extractor.js +242 -0
- package/dist/knowledge/graph/edge-extractor.js.map +1 -0
- package/dist/knowledge/graph/impact.d.ts +73 -0
- package/dist/knowledge/graph/impact.d.ts.map +1 -0
- package/dist/knowledge/graph/impact.js +94 -0
- package/dist/knowledge/graph/impact.js.map +1 -0
- package/dist/knowledge/graph/types.d.ts +22 -0
- package/dist/knowledge/graph/types.d.ts.map +1 -0
- package/dist/knowledge/graph/types.js +13 -0
- package/dist/knowledge/graph/types.js.map +1 -0
- package/dist/knowledge/prompt.d.ts +9 -0
- package/dist/knowledge/prompt.d.ts.map +1 -1
- package/dist/knowledge/prompt.js +17 -5
- package/dist/knowledge/prompt.js.map +1 -1
- package/dist/knowledge/query.d.ts +13 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +107 -10
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/repo-map.d.ts +11 -5
- package/dist/knowledge/repo-map.d.ts.map +1 -1
- package/dist/knowledge/repo-map.js +42 -3
- package/dist/knowledge/repo-map.js.map +1 -1
- package/dist/knowledge/validator.d.ts.map +1 -1
- package/dist/knowledge/validator.js +69 -2
- package/dist/knowledge/validator.js.map +1 -1
- package/dist/mcp/server.d.ts +64 -8
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +233 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills/distill/distiller.js +1 -1
- package/dist/skills/distill/distiller.js.map +1 -1
- package/dist/skills/distilled/distilled-api-design.md +4 -0
- package/dist/skills/distilled/distilled-brainstorming.md +79 -0
- package/dist/skills/distilled/distilled-brand-guidelines.md +86 -0
- package/dist/skills/distilled/distilled-canvas-design.md +128 -0
- package/dist/skills/distilled/distilled-claude-api.md +185 -0
- package/dist/skills/distilled/distilled-creator.md +5 -2
- package/dist/skills/distilled/distilled-dispatching-parallel-agents.md +136 -0
- package/dist/skills/distilled/distilled-doc-coauthoring.md +144 -0
- package/dist/skills/distilled/distilled-docx.md +231 -0
- package/dist/skills/distilled/distilled-executing-plans.md +85 -50
- package/dist/skills/distilled/distilled-finishing-a-development-branch.md +213 -0
- package/dist/skills/distilled/distilled-frontend-design.md +118 -0
- package/dist/skills/distilled/distilled-harness-engineering.md +1 -1
- package/dist/skills/distilled/distilled-receiving-code-review.md +185 -0
- package/dist/skills/distilled/distilled-subagent-driven-development.md +124 -0
- package/dist/skills/distilled/distilled-systematic-debugging.md +108 -260
- package/dist/skills/distilled/distilled-test-driven-development.md +432 -0
- package/dist/skills/distilled/distilled-using-superpowers.md +134 -0
- package/dist/skills/distilled/distilled-verification-before-completion.md +88 -78
- package/dist/skills/distilled/distilled-writing-skills.md +175 -0
- package/dist/skills/registry.d.ts +10 -50
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +7 -118
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/tools/pipeline-suggest.js +2 -2
- package/dist/skills/tools/pipeline-suggest.js.map +1 -1
- package/dist/skills/tools/skill-invoke.d.ts +2 -1
- package/dist/skills/tools/skill-invoke.d.ts.map +1 -1
- package/dist/skills/tools/skill-invoke.js +3 -1
- package/dist/skills/tools/skill-invoke.js.map +1 -1
- package/dist/web/analytics/anti-pattern-detector.d.ts.map +1 -1
- package/dist/web/analytics/anti-pattern-detector.js +6 -1
- package/dist/web/analytics/anti-pattern-detector.js.map +1 -1
- package/dist/web/analytics/drift-detector.d.ts +6 -0
- package/dist/web/analytics/drift-detector.d.ts.map +1 -1
- package/dist/web/analytics/drift-detector.js +15 -8
- package/dist/web/analytics/drift-detector.js.map +1 -1
- package/dist/web/analytics/weekly-report.d.ts +13 -0
- package/dist/web/analytics/weekly-report.d.ts.map +1 -1
- package/dist/web/analytics/weekly-report.js +17 -3
- package/dist/web/analytics/weekly-report.js.map +1 -1
- package/dist/web/routes/_helpers.d.ts +31 -0
- package/dist/web/routes/_helpers.d.ts.map +1 -1
- package/dist/web/routes/_helpers.js +33 -0
- package/dist/web/routes/_helpers.js.map +1 -1
- package/dist/web/routes/agent-distill.d.ts +49 -0
- package/dist/web/routes/agent-distill.d.ts.map +1 -0
- package/dist/web/routes/agent-distill.js +526 -0
- package/dist/web/routes/agent-distill.js.map +1 -0
- package/dist/web/routes/config.d.ts +56 -0
- package/dist/web/routes/config.d.ts.map +1 -0
- package/dist/web/routes/config.js +243 -0
- package/dist/web/routes/config.js.map +1 -0
- package/dist/web/routes/decisions.js +1 -1
- package/dist/web/routes/decisions.js.map +1 -1
- package/dist/web/routes/error-handler.d.ts +0 -4
- package/dist/web/routes/error-handler.d.ts.map +1 -1
- package/dist/web/routes/error-handler.js +0 -8
- package/dist/web/routes/error-handler.js.map +1 -1
- package/dist/web/routes/events.d.ts.map +1 -1
- package/dist/web/routes/events.js +2 -1
- package/dist/web/routes/events.js.map +1 -1
- package/dist/web/routes/insights.d.ts.map +1 -1
- package/dist/web/routes/insights.js +0 -0
- package/dist/web/routes/insights.js.map +1 -1
- package/dist/web/routes/knowledge.d.ts +43 -2
- package/dist/web/routes/knowledge.d.ts.map +1 -1
- package/dist/web/routes/knowledge.js +117 -6
- package/dist/web/routes/knowledge.js.map +1 -1
- package/dist/web/routes/pipeline.d.ts +0 -9
- package/dist/web/routes/pipeline.d.ts.map +1 -1
- package/dist/web/routes/pipeline.js +0 -4
- package/dist/web/routes/pipeline.js.map +1 -1
- package/dist/web/routes/rules.d.ts.map +1 -1
- package/dist/web/routes/rules.js +20 -6
- package/dist/web/routes/rules.js.map +1 -1
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +8 -7
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/skill-stats.d.ts.map +1 -1
- package/dist/web/routes/skill-stats.js +153 -16
- package/dist/web/routes/skill-stats.js.map +1 -1
- package/dist/web/routes/skills-distill.js +1 -1
- package/dist/web/routes/skills-distill.js.map +1 -1
- package/dist/web/routes/stats.d.ts.map +1 -1
- package/dist/web/routes/stats.js +2 -1
- package/dist/web/routes/stats.js.map +1 -1
- package/dist/web/routes/task-timeline.d.ts +95 -19
- package/dist/web/routes/task-timeline.d.ts.map +1 -1
- package/dist/web/routes/task-timeline.js +344 -88
- package/dist/web/routes/task-timeline.js.map +1 -1
- package/dist/web/routes/tasks.d.ts.map +1 -1
- package/dist/web/routes/tasks.js +52 -30
- package/dist/web/routes/tasks.js.map +1 -1
- package/dist/web/routes/violations.d.ts +1 -1
- package/dist/web/routes/violations.d.ts.map +1 -1
- package/dist/web/routes/violations.js +3 -2
- package/dist/web/routes/violations.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +20 -0
- package/dist/web/server.js.map +1 -1
- package/dist/web/services/agent-distill-manager.d.ts +122 -0
- package/dist/web/services/agent-distill-manager.d.ts.map +1 -0
- package/dist/web/services/agent-distill-manager.js +397 -0
- package/dist/web/services/agent-distill-manager.js.map +1 -0
- package/dist/web/services/distill-manager.d.ts +47 -0
- package/dist/web/services/distill-manager.d.ts.map +1 -1
- package/dist/web/services/distill-manager.js +103 -0
- package/dist/web/services/distill-manager.js.map +1 -1
- package/dist/web/static/assets/AgentDetailPage-DlUeA1sX.js +2 -0
- package/dist/web/static/assets/AgentDetailPage-DlUeA1sX.js.map +1 -0
- package/dist/web/static/assets/AgentDistillRunPage-Cybo4bii.js +3 -0
- package/dist/web/static/assets/AgentDistillRunPage-Cybo4bii.js.map +1 -0
- package/dist/web/static/assets/AgentsPage-Qd9FExLG.js +2 -0
- package/dist/web/static/assets/AgentsPage-Qd9FExLG.js.map +1 -0
- package/dist/web/static/assets/DaemonHealthPage-DTSVqtrI.js +2 -0
- package/dist/web/static/assets/DaemonHealthPage-DTSVqtrI.js.map +1 -0
- package/dist/web/static/assets/DecisionDetailPage-b4BA8dhc.js +2 -0
- package/dist/web/static/assets/DecisionDetailPage-b4BA8dhc.js.map +1 -0
- package/dist/web/static/assets/DecisionsPage-a3NRo_T7.js +2 -0
- package/dist/web/static/assets/DecisionsPage-a3NRo_T7.js.map +1 -0
- package/dist/web/static/assets/DiagnosticsPage-DIVdiIQG.js +2 -0
- package/dist/web/static/assets/DiagnosticsPage-DIVdiIQG.js.map +1 -0
- package/dist/web/static/assets/DistillDetailPage-U6a3l2iP.js +4 -0
- package/dist/web/static/assets/DistillDetailPage-U6a3l2iP.js.map +1 -0
- package/dist/web/static/assets/DistillPage-O7BHtRN8.js +2 -0
- package/dist/web/static/assets/DistillPage-O7BHtRN8.js.map +1 -0
- package/dist/web/static/assets/DistillRunPage-D1JuRWWr.js +2 -0
- package/dist/web/static/assets/DistillRunPage-D1JuRWWr.js.map +1 -0
- package/dist/web/static/assets/GlobalScopeHint-Q3wTJx3F.js +2 -0
- package/dist/web/static/assets/GlobalScopeHint-Q3wTJx3F.js.map +1 -0
- package/dist/web/static/assets/IssueDetailPage-BDfrtk2C.js +2 -0
- package/dist/web/static/assets/IssueDetailPage-BDfrtk2C.js.map +1 -0
- package/dist/web/static/assets/IssuesPage-SKmhlCrw.js +2 -0
- package/dist/web/static/assets/IssuesPage-SKmhlCrw.js.map +1 -0
- package/dist/web/static/assets/KbDetailPage-Yna86Na8.js +2 -0
- package/dist/web/static/assets/KbDetailPage-Yna86Na8.js.map +1 -0
- package/dist/web/static/assets/KbHitsPage-Cljl7H9p.js +2 -0
- package/dist/web/static/assets/KbHitsPage-Cljl7H9p.js.map +1 -0
- package/dist/web/static/assets/{MarkdownRenderer-DZmTl-8J.js → MarkdownRenderer-DlDQNihj.js} +2 -2
- package/dist/web/static/assets/{MarkdownRenderer-DZmTl-8J.js.map → MarkdownRenderer-DlDQNihj.js.map} +1 -1
- package/dist/web/static/assets/NotFound-LMzbP51V.js +2 -0
- package/dist/web/static/assets/{NotFound-BQPh0vaF.js.map → NotFound-LMzbP51V.js.map} +1 -1
- package/dist/web/static/assets/SettingsPage-DzoK4PKg.js +2 -0
- package/dist/web/static/assets/SettingsPage-DzoK4PKg.js.map +1 -0
- package/dist/web/static/assets/SkillDetailPage-BuBJJ_NX.js +2 -0
- package/dist/web/static/assets/SkillDetailPage-BuBJJ_NX.js.map +1 -0
- package/dist/web/static/assets/SkillsPage-aojkJpBc.js +2 -0
- package/dist/web/static/assets/SkillsPage-aojkJpBc.js.map +1 -0
- package/dist/web/static/assets/TaskDetailPage-1ckxnGhw.js +4 -0
- package/dist/web/static/assets/TaskDetailPage-1ckxnGhw.js.map +1 -0
- package/dist/web/static/assets/TasksHubPage-C2PLh3eg.js +6 -0
- package/dist/web/static/assets/TasksHubPage-C2PLh3eg.js.map +1 -0
- package/dist/web/static/assets/WorkplacePage-DHrp5VxS.js +2 -0
- package/dist/web/static/assets/WorkplacePage-DHrp5VxS.js.map +1 -0
- package/dist/web/static/assets/arco-DV6xCLhr.js +14 -0
- package/dist/web/static/assets/arco-DV6xCLhr.js.map +1 -0
- package/dist/web/static/assets/charts-BSV4cyC4.js +37 -0
- package/dist/web/static/assets/charts-BSV4cyC4.js.map +1 -0
- package/dist/web/static/assets/{index-7bl3kbcx.css → index-B_v_MKlb.css} +1 -1
- package/dist/web/static/assets/index-DileOOE4.js +4 -0
- package/dist/web/static/assets/index-DileOOE4.js.map +1 -0
- package/dist/web/static/assets/markdown-CA7ePUts.js +30 -0
- package/dist/web/static/assets/markdown-CA7ePUts.js.map +1 -0
- package/dist/web/static/assets/{outcome-DUn1NjlC.js → outcome-BKGy9azt.js} +2 -2
- package/dist/web/static/assets/{outcome-DUn1NjlC.js.map → outcome-BKGy9azt.js.map} +1 -1
- package/dist/web/static/assets/{query-S6X1S7K9.js → query-CgCOpYWf.js} +2 -2
- package/dist/web/static/assets/{query-S6X1S7K9.js.map → query-CgCOpYWf.js.map} +1 -1
- package/dist/web/static/assets/{react-router-JVUrkhdd.js → react-router-Cxmg8RuL.js} +3 -3
- package/dist/web/static/assets/{react-router-JVUrkhdd.js.map → react-router-Cxmg8RuL.js.map} +1 -1
- package/dist/web/static/assets/{syntax-highlighter-BkZfCDsz.js → syntax-highlighter-BDYycNja.js} +3 -3
- package/dist/web/static/assets/{syntax-highlighter-BkZfCDsz.js.map → syntax-highlighter-BDYycNja.js.map} +1 -1
- package/dist/web/static/assets/task-title-BhOcemuR.js +2 -0
- package/dist/web/static/assets/task-title-BhOcemuR.js.map +1 -0
- package/dist/web/static/assets/useAgentStats-B-uTgqBd.js +2 -0
- package/dist/web/static/assets/useAgentStats-B-uTgqBd.js.map +1 -0
- package/dist/web/static/assets/useDecisions-D-G2Ft5T.js +2 -0
- package/dist/web/static/assets/useDecisions-D-G2Ft5T.js.map +1 -0
- package/dist/web/static/assets/useDistill-21dZkXlT.js +3 -0
- package/dist/web/static/assets/useDistill-21dZkXlT.js.map +1 -0
- package/dist/web/static/assets/useEffectiveProject-DQiyX54y.js +2 -0
- package/dist/web/static/assets/useEffectiveProject-DQiyX54y.js.map +1 -0
- package/dist/web/static/assets/useIssuesFeed-CFiyQkAL.js +2 -0
- package/dist/web/static/assets/useIssuesFeed-CFiyQkAL.js.map +1 -0
- package/dist/web/static/assets/useKbHits-xKXWgqh9.js +2 -0
- package/dist/web/static/assets/useKbHits-xKXWgqh9.js.map +1 -0
- package/dist/web/static/assets/useSkillStats-B5hbIwdf.js +2 -0
- package/dist/web/static/assets/useSkillStats-B5hbIwdf.js.map +1 -0
- package/dist/web/static/assets/vendor-DS-q4Eyc.js +36 -0
- package/dist/web/static/assets/vendor-DS-q4Eyc.js.map +1 -0
- package/dist/web/static/index.html +6 -6
- package/package.json +5 -3
- package/dist/core/storage/workflow-recommendations.d.ts +0 -124
- package/dist/core/storage/workflow-recommendations.d.ts.map +0 -1
- package/dist/core/storage/workflow-recommendations.js +0 -274
- package/dist/core/storage/workflow-recommendations.js.map +0 -1
- package/dist/daemon/services/experience-extractor.d.ts.map +0 -1
- package/dist/daemon/services/experience-extractor.js.map +0 -1
- package/dist/daemon/services/violation-reporter.d.ts.map +0 -1
- package/dist/daemon/services/violation-reporter.js.map +0 -1
- package/dist/daemon/templates/agents/harness-hotfix.md +0 -99
- package/dist/daemon/templates/agents/hybrid-feature-with-safety.md +0 -104
- package/dist/daemon/templates/agents/refactor-specialist.md +0 -98
- package/dist/skills/distill/claude-cli-resolver.d.ts.map +0 -1
- package/dist/skills/distill/claude-cli-resolver.js.map +0 -1
- package/dist/skills/distilled/distilled-defi-amm-security.md +0 -293
- package/dist/skills/keyword-score.d.ts +0 -29
- package/dist/skills/keyword-score.d.ts.map +0 -1
- package/dist/skills/keyword-score.js +0 -54
- package/dist/skills/keyword-score.js.map +0 -1
- package/dist/web/static/assets/AgentContentPage-DkeRNxok.js +0 -2
- package/dist/web/static/assets/AgentContentPage-DkeRNxok.js.map +0 -1
- package/dist/web/static/assets/AgentDelegationTable-ByBa0x1l.js +0 -2
- package/dist/web/static/assets/AgentDelegationTable-ByBa0x1l.js.map +0 -1
- package/dist/web/static/assets/ContextInsightsPage-oUk7_I8u.js +0 -3
- package/dist/web/static/assets/ContextInsightsPage-oUk7_I8u.js.map +0 -1
- package/dist/web/static/assets/DaemonHealthPage-DG2fyOP7.js +0 -2
- package/dist/web/static/assets/DaemonHealthPage-DG2fyOP7.js.map +0 -1
- package/dist/web/static/assets/DecisionsPage-CMAPEnKb.js +0 -2
- package/dist/web/static/assets/DecisionsPage-CMAPEnKb.js.map +0 -1
- package/dist/web/static/assets/DiagnosticsPage-DQd-Zm4r.js +0 -2
- package/dist/web/static/assets/DiagnosticsPage-DQd-Zm4r.js.map +0 -1
- package/dist/web/static/assets/DriftTab-DqpepOhI.js +0 -2
- package/dist/web/static/assets/DriftTab-DqpepOhI.js.map +0 -1
- package/dist/web/static/assets/HealthHomePage-CN6zNIie.js +0 -3
- package/dist/web/static/assets/HealthHomePage-CN6zNIie.js.map +0 -1
- package/dist/web/static/assets/KbHitRateTable-ByEIWujF.js +0 -2
- package/dist/web/static/assets/KbHitRateTable-ByEIWujF.js.map +0 -1
- package/dist/web/static/assets/NotFound-BQPh0vaF.js +0 -2
- package/dist/web/static/assets/ProjectSwitcher-D3lZMFd3.js +0 -2
- package/dist/web/static/assets/ProjectSwitcher-D3lZMFd3.js.map +0 -1
- package/dist/web/static/assets/SettingsPage-oLJBNzQj.js +0 -2
- package/dist/web/static/assets/SettingsPage-oLJBNzQj.js.map +0 -1
- package/dist/web/static/assets/SkillContentPage-DK5rgfgw.js +0 -2
- package/dist/web/static/assets/SkillContentPage-DK5rgfgw.js.map +0 -1
- package/dist/web/static/assets/SkillStatsTable-DYMzjEUV.js +0 -2
- package/dist/web/static/assets/SkillStatsTable-DYMzjEUV.js.map +0 -1
- package/dist/web/static/assets/SkillsDistillTab-C7qaG8q3.js +0 -2
- package/dist/web/static/assets/SkillsDistillTab-C7qaG8q3.js.map +0 -1
- package/dist/web/static/assets/TasksHubPage-03wsRRsJ.js +0 -6
- package/dist/web/static/assets/TasksHubPage-03wsRRsJ.js.map +0 -1
- package/dist/web/static/assets/ViolationsPage-DSiLr-9O.js +0 -3
- package/dist/web/static/assets/ViolationsPage-DSiLr-9O.js.map +0 -1
- package/dist/web/static/assets/arco-Bhi3a6Qp.js +0 -14
- package/dist/web/static/assets/arco-Bhi3a6Qp.js.map +0 -1
- package/dist/web/static/assets/charts-BuHQWDbQ.js +0 -37
- package/dist/web/static/assets/charts-BuHQWDbQ.js.map +0 -1
- package/dist/web/static/assets/index-BIYnq1Dx.js +0 -4
- package/dist/web/static/assets/index-BIYnq1Dx.js.map +0 -1
- package/dist/web/static/assets/useTabsParam-k8qte_0C.js +0 -2
- package/dist/web/static/assets/useTabsParam-k8qte_0C.js.map +0 -1
- package/dist/web/static/assets/vendor-DWgdB1eY.js +0 -65
- package/dist/web/static/assets/vendor-DWgdB1eY.js.map +0 -1
- /package/dist/{skills/distill → core/utils}/claude-cli-resolver.d.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HealthHomePage-CN6zNIie.js","sources":["../../src/components/health/KpiSparkCard.tsx","../../src/components/health/OutcomeDonutChart.tsx","../../src/components/health/TrendLineCard.tsx","../../src/components/health/KbHeatmapCard.tsx","../../src/components/health/SkillBarCard.tsx","../../src/components/health/CommitCalendarCard.tsx","../../src/components/health/InterceptRuleBarCard.tsx","../../src/pages/HealthHomePage.tsx"],"sourcesContent":["/**\n * KpiSparkCard — v6 Datadog/Grafana 风格 KPI 卡。\n *\n * 视觉契约(Step 1 / health v6):\n * - 卡内 padding 20px,背景 #fff,box-shadow 0 1px 3px rgba(0,0,0,0.05)\n * - 大数字 32px tabular-nums,灰色标签 12px\n * - sparkline mini AreaChart 高 40px,色由 sparkColor 控制(默认主色 #1890ff)\n * - trend pill: \"vs 上周 +N ↑\" — 颜色根据 higherIsBetter + delta 计算\n * - onClick 整卡可点\n *\n * 数据由父组件传入;本组件只负责渲染。\n */\n\nimport { memo } from 'react'\nimport { AreaChart, Area, ResponsiveContainer, Tooltip } from 'recharts'\nimport ArcoTooltip from '@arco-design/web-react/es/Tooltip'\n\ninterface KpiSparkCardProps {\n label: string\n /** 大数字(已格式化,如 \"42\" / \"87.5%\" / \"1.2k\") */\n value: string | number\n /** 7d sparkline 数据(每天一个数字) */\n sparkData: number[]\n /** sparkline 颜色,默认主色 #1890ff(失败 KPI 应传 #f5222d) */\n sparkColor?: string\n /** 本周值(用于 trend pill 计算) */\n thisVal: number\n /** 上周值 */\n prevVal: number\n /** 数字越大越好?决定 trend pill 红/绿 */\n higherIsBetter: boolean\n /** 单位后缀,如 \"%\" */\n suffix?: string\n /** 可选 tooltip 文案:传入后 label 旁边出现一个 (?) 图标且 hover 显示说明。\n * C-5: 用于标注 sparkline 是近似分布而非真实 daily 数据。 */\n tooltip?: string\n onClick?: () => void\n}\n\nconst COLOR_UP_GOOD = '#52c41a'\nconst COLOR_DOWN_BAD = '#f5222d'\nconst COLOR_FLAT = '#86909c'\n\nfunction trendPill(thisVal: number, prevVal: number, higherIsBetter: boolean, suffix = ''): {\n text: string\n color: string\n} {\n const delta = thisVal - prevVal\n if (delta === 0) return { text: `vs 上周 0 →`, color: COLOR_FLAT }\n const arrow = delta > 0 ? '↑' : '↓'\n const worsened = higherIsBetter ? delta < 0 : delta > 0\n const color = worsened ? COLOR_DOWN_BAD : COLOR_UP_GOOD\n const sign = delta > 0 ? '+' : ''\n const txt = Number.isInteger(delta) ? `${delta}` : delta.toFixed(1)\n return { text: `vs 上周 ${sign}${txt}${suffix} ${arrow}`, color }\n}\n\nfunction KpiSparkCardInner({\n label,\n value,\n sparkData,\n sparkColor = '#1890ff',\n thisVal,\n prevVal,\n higherIsBetter,\n suffix = '',\n tooltip,\n onClick,\n}: KpiSparkCardProps) {\n const pill = trendPill(thisVal, prevVal, higherIsBetter, suffix)\n const chartData = sparkData.map((v, i) => ({ i, v }))\n const gradId = `spark-grad-${label.replace(/\\s+/g, '-')}-${sparkColor.replace('#', '')}`\n\n return (\n <div\n onClick={onClick}\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n cursor: onClick ? 'pointer' : 'default',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n transition: 'box-shadow 0.15s',\n }}\n onMouseEnter={(e) => {\n if (onClick) (e.currentTarget as HTMLDivElement).style.boxShadow = '0 2px 8px rgba(0,0,0,0.10)'\n }}\n onMouseLeave={(e) => {\n if (onClick) (e.currentTarget as HTMLDivElement).style.boxShadow = '0 1px 3px rgba(0,0,0,0.05)'\n }}\n >\n <div style={{ fontSize: 12, color: '#86909c', fontWeight: 500, display: 'flex', alignItems: 'center', gap: 4 }}>\n <span>{label}</span>\n {tooltip ? (\n <ArcoTooltip content={tooltip} position=\"top\">\n <span\n aria-label=\"explain\"\n style={{\n cursor: 'help',\n fontSize: 11,\n color: '#86909c',\n border: '1px solid var(--color-border-2)',\n borderRadius: '50%',\n width: 14,\n height: 14,\n lineHeight: '12px',\n textAlign: 'center',\n display: 'inline-block',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n ?\n </span>\n </ArcoTooltip>\n ) : null}\n </div>\n <div\n style={{\n fontSize: 32,\n fontWeight: 600,\n color: '#1d2129',\n lineHeight: 1.1,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {value}\n </div>\n <div style={{ height: 40, marginTop: 2 }}>\n {chartData.length > 0 && (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 2, right: 0, left: 0, bottom: 0 }}>\n <defs>\n <linearGradient id={gradId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={sparkColor} stopOpacity={0.35} />\n <stop offset=\"100%\" stopColor={sparkColor} stopOpacity={0.02} />\n </linearGradient>\n </defs>\n <Tooltip\n cursor={false}\n contentStyle={{ fontSize: 11, padding: '4px 8px', borderRadius: 4 }}\n formatter={(v) => [`${v}${suffix}`, label] as [string, string]}\n labelFormatter={() => ''}\n />\n <Area\n type=\"monotone\"\n dataKey=\"v\"\n stroke={sparkColor}\n strokeWidth={1.5}\n fill={`url(#${gradId})`}\n isAnimationActive={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </div>\n <div\n style={{\n fontSize: 11,\n color: pill.color,\n fontWeight: 500,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {pill.text}\n </div>\n </div>\n )\n}\n\n// Perf: memo so window switch / refetch doesn't remount recharts (d3 scale work).\nexport default memo(KpiSparkCardInner)\n","/**\n * OutcomeDonutChart — v6 任务结果总览 donut。\n *\n * 中心显示 总数 + 成功率 %。配色统一来自 '../../utils/outcome'(Arco palette canonical)。\n */\n\nimport { memo } from 'react'\nimport { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts'\nimport { OUTCOME_COLOR as COLOR, OUTCOME_LABEL as LABEL } from '../../utils/outcome'\n\ninterface OutcomeDonutChartProps {\n success: number\n partial: number\n failed: number\n abandoned: number\n /** 图表高度,默认 240 */\n height?: number\n}\n\nfunction OutcomeDonutChartInner({\n success,\n partial,\n failed,\n abandoned,\n height = 240,\n}: OutcomeDonutChartProps) {\n const total = success + partial + failed + abandoned\n const successRate = total > 0 ? Math.round((success / total) * 1000) / 10 : 0\n const data = [\n { name: LABEL.success, value: success, key: 'success' as const },\n { name: LABEL.partial, value: partial, key: 'partial' as const },\n { name: LABEL.failed, value: failed, key: 'failed' as const },\n { name: LABEL.abandoned, value: abandoned, key: 'abandoned' as const },\n ].filter(d => d.value > 0)\n\n if (total === 0) {\n return (\n <div\n style={{\n height,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#86909c',\n fontSize: 13,\n }}\n >\n 近期无已分类任务数据\n </div>\n )\n }\n\n return (\n <div style={{ position: 'relative', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius=\"60%\"\n outerRadius=\"85%\"\n paddingAngle={2}\n stroke=\"none\"\n isAnimationActive={false}\n >\n {data.map((d) => (\n <Cell key={d.key} fill={COLOR[d.key]} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={{ fontSize: 12, padding: '6px 10px', borderRadius: 4 }}\n formatter={(v, n) => {\n const num = typeof v === 'number' ? v : Number(v) || 0\n const pct = total > 0 ? Math.round((num / total) * 100) : 0\n return [`${num} (${pct}%)`, String(n)] as [string, string]\n }}\n />\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 11 }}\n />\n </PieChart>\n </ResponsiveContainer>\n {/* 中心标签 — absolute positioning over donut hole */}\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n pointerEvents: 'none',\n paddingBottom: 24, // offset for legend\n }}\n >\n <div\n style={{\n fontSize: 28,\n fontWeight: 600,\n color: '#1d2129',\n lineHeight: 1.1,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {total}\n </div>\n <div style={{ fontSize: 11, color: '#86909c', marginTop: 2 }}>\n 总任务 · 成功率\n {' '}\n <span style={{ color: '#52c41a', fontWeight: 600 }}>{successRate}%</span>\n </div>\n </div>\n </div>\n )\n}\n\n// Perf: memo avoids remounting pie + d3 scale when parent re-renders with same props.\nexport default memo(OutcomeDonutChartInner)\n","/**\n * TrendLineCard — v6 编排遵守率趋势 line chart 卡(C4/C8 复用)。\n *\n * 视觉契约(health v6 / Step 2):\n * - 卡片白底 + box-shadow 0 1px 3px rgba(0,0,0,0.05) + radius 8 + padding 20\n * - 标题 14px 加粗 + 副标 12px 灰色\n * - 左上角:当前值大字 32px tabular-nums + \"目标 N%\" 灰色小字\n * - 主线主色 #1890ff,目标 dashed #52c41a 横线\n * - chart 高 240px;缺数据天 value=null → line 自然断开\n * - 警示:连续 ≥3 天低于目标 → 显示红色 \"连续 N 天低于目标\"\n *\n * data 形如 [{ date: '05-19', value: 65 | null }, ...],长度=windowDays\n */\n\nimport { memo } from 'react'\nimport {\n LineChart, Line, ResponsiveContainer, XAxis, YAxis, Tooltip, ReferenceLine,\n CartesianGrid,\n} from 'recharts'\n\ninterface TrendPoint {\n date: string\n value: number | null\n}\ninterface TrendLineCardProps {\n title: string\n subtitle?: string\n /** 7d/30d daily points */\n data: TrendPoint[]\n /** 当前值 (e.g. 0.65 → 65) ; 已经是 0-100 整数 */\n currentValue: number | null\n /** 目标线 0-100 */\n target: number\n /** 高度,默认 240 */\n height?: number\n}\n\nconst PRIMARY = '#1890ff'\nconst SUCCESS = '#52c41a'\nconst FAIL = '#f5222d'\nconst SUBDUE = '#86909c'\n\n/** 计算连续 N 天低于目标(仅看末尾连续段) */\nfunction consecutiveBelow(data: TrendPoint[], target: number): number {\n let n = 0\n for (let i = data.length - 1; i >= 0; i--) {\n const v = data[i].value\n if (v == null) break\n if (v < target) n++\n else break\n }\n return n\n}\n\nfunction TrendLineCardInner({\n title,\n subtitle,\n data,\n currentValue,\n target,\n height = 240,\n}: TrendLineCardProps) {\n const below = consecutiveBelow(data, target)\n const warn = below >= 3\n const hasData = data.some(d => d.value != null)\n\n return (\n <div\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>{title}</div>\n {subtitle && (\n <div style={{ fontSize: 12, color: SUBDUE, marginTop: 2 }}>{subtitle}</div>\n )}\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginTop: 8, marginBottom: 4 }}>\n <div\n style={{\n fontSize: 32,\n fontWeight: 600,\n color: '#1d2129',\n lineHeight: 1.1,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {currentValue == null ? '--' : `${currentValue}%`}\n </div>\n <div style={{ fontSize: 12, color: SUBDUE }}>目标 {target}%</div>\n {warn && (\n <div style={{ fontSize: 11, color: FAIL, fontWeight: 500 }}>\n 连续 {below} 天低于目标\n </div>\n )}\n </div>\n <div style={{ flex: 1, minHeight: height }}>\n {!hasData ? (\n <div\n style={{\n height,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: SUBDUE,\n fontSize: 13,\n }}\n >\n 近 {data.length} 天无数据\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={height}>\n <LineChart data={data} margin={{ top: 8, right: 16, left: 0, bottom: 0 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"#f0f0f0\" />\n <XAxis dataKey=\"date\" fontSize={11} stroke={SUBDUE} />\n <YAxis\n domain={[0, 100]}\n fontSize={11}\n stroke={SUBDUE}\n tickFormatter={(v) => `${v}%`}\n />\n <Tooltip\n contentStyle={{ fontSize: 12, borderRadius: 4 }}\n formatter={(v) => [v == null ? '--' : `${v}%`, title] as [string, string]}\n />\n <ReferenceLine\n y={target}\n stroke={SUCCESS}\n strokeDasharray=\"4 4\"\n label={{\n value: `目标 ${target}%`,\n position: 'right',\n fontSize: 10,\n fill: SUCCESS,\n }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={PRIMARY}\n strokeWidth={2}\n dot={{ r: 3, fill: PRIMARY }}\n activeDot={{ r: 5 }}\n connectNulls={false}\n isAnimationActive={false}\n />\n </LineChart>\n </ResponsiveContainer>\n )}\n </div>\n </div>\n )\n}\n\n// Perf: memo prevents recharts d3 rebuild on parent re-render (data is stable).\nexport default memo(TrendLineCardInner)\n","/**\n * KbHeatmapCard — v6 KB 命中热力图。\n *\n * 视觉契约(health v6 / Step 2):\n * - 行:top 8 KB pages(按 windowDays 总命中数倒排)\n * - 列:windowDays 天\n * - 单元格:白(0) → 浅蓝 → #1890ff(线性映射,max 决定饱和度)\n * - 0 命中 row 整行浅灰背景\n * - hover tooltip:page · date · hit count\n * - 右侧 legend 色阶 0 → max\n * - 标题 14px 加粗 + 副标 12px 灰色\n */\n\nimport { memo, useMemo, useState } from 'react'\n\ninterface KbHeatmapCardProps {\n /** 按页聚合的 daily cells:[{ name, days: [n, n, ...] }] (长度=windowDays) */\n rows: Array<{ name: string; days: number[]; total: number }>\n /** date labels 长度=windowDays (e.g. ['05-19', '05-20', ...]) */\n dateLabels: string[]\n title?: string\n subtitle?: string\n}\n\nconst PRIMARY = '#1890ff'\nconst SUBDUE = '#86909c'\nconst ZERO_BG = '#fafafa'\n\n/** value 0..max → bg color 白 → PRIMARY */\nfunction cellColor(v: number, max: number): string {\n if (v <= 0 || max <= 0) return '#ffffff'\n const t = Math.min(1, v / max)\n // alpha 0.10 → 0.95 (avoid invisible cells but allow gradation)\n const alpha = 0.10 + 0.85 * t\n // PRIMARY = #1890ff = rgb(24, 144, 255)\n return `rgba(24, 144, 255, ${alpha.toFixed(3)})`\n}\n\nfunction KbHeatmapCardInner({ rows, dateLabels, title = 'KB 命中热力图', subtitle }: KbHeatmapCardProps) {\n const [hover, setHover] = useState<{ row: string; col: number; v: number } | null>(null)\n const max = useMemo(() => {\n let m = 0\n for (const r of rows) for (const d of r.days) if (d > m) m = d\n return m\n }, [rows])\n\n const hasData = max > 0\n\n return (\n <div\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>{title}</div>\n <div style={{ fontSize: 12, color: SUBDUE, marginTop: 2, marginBottom: 12 }}>\n {subtitle || `Top ${rows.length} pages · 近 ${dateLabels.length} 天`}\n </div>\n {!hasData ? (\n <div\n style={{\n height: 240, display: 'flex', alignItems: 'center', justifyContent: 'center',\n color: SUBDUE, fontSize: 13,\n }}\n >\n 近 {dateLabels.length} 天无 KB 命中数据\n </div>\n ) : (\n <>\n <div style={{ flex: 1, overflow: 'hidden' }}>\n <table\n style={{\n width: '100%',\n borderCollapse: 'separate',\n borderSpacing: 2,\n fontSize: 11,\n tableLayout: 'fixed',\n }}\n >\n <thead>\n <tr>\n <th style={{ textAlign: 'left', color: SUBDUE, fontWeight: 500, paddingRight: 6, width: '38%' }}>page</th>\n {dateLabels.map(d => (\n <th key={d} style={{ color: SUBDUE, fontWeight: 500, textAlign: 'center' }}>{d}</th>\n ))}\n <th style={{ color: SUBDUE, fontWeight: 500, textAlign: 'right', paddingLeft: 6, width: '12%' }}>total</th>\n </tr>\n </thead>\n <tbody>\n {rows.map(r => {\n const isZero = r.total === 0\n return (\n <tr\n key={r.name}\n style={{ background: isZero ? ZERO_BG : 'transparent' }}\n >\n <td\n title={r.name}\n style={{\n fontFamily: 'monospace',\n color: isZero ? SUBDUE : '#1d2129',\n paddingRight: 6,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {r.name}\n </td>\n {r.days.map((v, i) => (\n <td\n key={i}\n onMouseEnter={() => setHover({ row: r.name, col: i, v })}\n onMouseLeave={() => setHover(null)}\n style={{\n background: cellColor(v, max),\n height: 22,\n borderRadius: 3,\n cursor: 'default',\n textAlign: 'center',\n color: v / max > 0.55 ? '#fff' : '#1d2129',\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {v > 0 ? v : ''}\n </td>\n ))}\n <td\n style={{\n textAlign: 'right',\n paddingLeft: 6,\n fontWeight: 600,\n fontVariantNumeric: 'tabular-nums',\n color: isZero ? SUBDUE : '#1d2129',\n }}\n >\n {r.total}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n {/* Legend + hover info */}\n <div\n style={{\n marginTop: 12,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontSize: 11,\n color: SUBDUE,\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span>少</span>\n {[0.15, 0.35, 0.55, 0.75, 0.95].map(a => (\n <span\n key={a}\n style={{\n display: 'inline-block',\n width: 14, height: 14, borderRadius: 3,\n background: `rgba(24, 144, 255, ${a})`,\n }}\n />\n ))}\n <span>多 (max={max})</span>\n </div>\n <div style={{ minHeight: 14 }}>\n {hover && (\n <span>\n <span style={{ fontFamily: 'monospace', color: '#1d2129' }}>{hover.row}</span>\n {' · '}\n {dateLabels[hover.col]}\n {' · '}\n <span style={{ color: PRIMARY, fontWeight: 600 }}>{hover.v} hits</span>\n </span>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n )\n}\n\n// Perf: memo skips heatmap rebuild (n*windowDays cells) when props unchanged.\nexport default memo(KbHeatmapCardInner)\n","/**\n * SkillBarCard — v6 Skill 调用水平条形图 (top 10)。\n *\n * 视觉契约(health v6 / Step 2):\n * - bar 长度 = invocation_7d(max 归一)\n * - bar 颜色:success_rate ≥ 0.8 → #52c41a,0.6-0.8 → #faad14,<0.6 → #f5222d\n * - 标签:`skill_id · N · success K%`\n * - 0-result rate ≥ 30% → 行末标 \"⚠ 0 命中率高\"(纯文字)\n * - 标题 14px 加粗 + 副标 12px 灰色\n * - 整卡内最多 10 行,每行 28px 高,整体撑满 240px+\n */\n\nimport { memo } from 'react'\n\ninterface SkillRow {\n skill_id: string\n invocations: number\n success_rate: number // 0-1\n zero_result_rate?: number // 0-1 (optional, not all sources have it)\n}\ninterface SkillBarCardProps {\n rows: SkillRow[]\n title?: string\n subtitle?: string\n}\n\nconst SUCCESS = '#52c41a'\nconst PARTIAL = '#faad14'\nconst FAIL = '#f5222d'\nconst SUBDUE = '#86909c'\nconst TRACK = '#f0f0f0'\n\nfunction barColor(rate: number): string {\n if (rate >= 0.8) return SUCCESS\n if (rate >= 0.6) return PARTIAL\n return FAIL\n}\n\nfunction SkillBarCardInner({ rows, title = 'Skill 调用 Top 10', subtitle }: SkillBarCardProps) {\n const sorted = [...rows].sort((a, b) => b.invocations - a.invocations).slice(0, 10)\n const max = sorted.reduce((m, r) => Math.max(m, r.invocations), 0)\n\n return (\n <div\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>{title}</div>\n <div style={{ fontSize: 12, color: SUBDUE, marginTop: 2, marginBottom: 12 }}>\n {subtitle || `按调用次数倒排 · 颜色=成功率`}\n </div>\n {sorted.length === 0 ? (\n <div\n style={{\n height: 240, display: 'flex', alignItems: 'center', justifyContent: 'center',\n color: SUBDUE, fontSize: 13,\n }}\n >\n 近期无 Skill 调用数据\n </div>\n ) : (\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>\n {sorted.map((r) => {\n const w = max > 0 ? (r.invocations / max) * 100 : 0\n const color = barColor(r.success_rate)\n const zeroHi = (r.zero_result_rate ?? 0) >= 0.3\n return (\n <div\n key={r.skill_id}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n fontSize: 12,\n }}\n >\n <div\n title={r.skill_id}\n style={{\n width: '32%',\n fontFamily: 'monospace',\n color: '#1d2129',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {r.skill_id}\n </div>\n <div style={{ flex: 1, position: 'relative', height: 18, background: TRACK, borderRadius: 3 }}>\n <div\n style={{\n position: 'absolute',\n left: 0, top: 0, bottom: 0,\n width: `${w}%`,\n background: color,\n borderRadius: 3,\n transition: 'width 0.2s',\n }}\n />\n </div>\n <div\n style={{\n width: 110,\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n color: SUBDUE,\n }}\n >\n <span style={{ color: '#1d2129', fontWeight: 600 }}>{r.invocations}</span>\n {' · '}\n <span style={{ color }}>{Math.round(r.success_rate * 100)}%</span>\n </div>\n {zeroHi && (\n <div style={{ fontSize: 10, color: FAIL, fontWeight: 500, whiteSpace: 'nowrap' }}>\n ⚠ 0 命中率高\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\n// Perf: memo avoids rebuilding 10 bar rows on parent re-render with stable rows.\nexport default memo(SkillBarCardInner)\n","/**\n * CommitCalendarCard — v6 ROW 5 左 — GitHub-style 30d 任务活跃度热力图\n *\n * 视觉契约(health v6 / Step 3):\n * - 7 行(周一→周日)× ~5 列(近 windowDays 按周分桶)\n * - cell 颜色:0 → 浅灰 → 浅绿 → 主色绿 (#52c41a)\n * - 失败 task > 30% 的天用红色(#f5222d 同调)\n * - cell hover tooltip:date · N tasks · M succeeded · K failed\n * - 右侧 legend 色阶 0 → max\n * - 标题 14px 加粗 + 副标 12px 灰色\n */\nimport { memo, useMemo, useState } from 'react'\n\ninterface DayCell {\n date: string // ISO yyyy-mm-dd\n total: number\n succeeded: number\n failed: number\n}\n\ninterface CommitCalendarCardProps {\n /** length=windowDays, idx 0 = 最早一天,末尾 = 今天 */\n days: DayCell[]\n title?: string\n subtitle?: string\n}\n\nconst SUBDUE = '#86909c'\nconst SUCCESS = '#52c41a'\nconst FAIL = '#f5222d'\nconst EMPTY_BG = '#f0f0f0'\n\n/** 0..max → 浅灰 → 浅绿 → SUCCESS(alpha 渐变) */\nfunction cellColor(d: DayCell, max: number): string {\n if (d.total <= 0 || max <= 0) return EMPTY_BG\n const failRatio = d.failed / d.total\n if (failRatio > 0.3) {\n // 失败主导:红色梯度\n const t = Math.min(1, d.total / max)\n const alpha = 0.30 + 0.65 * t\n return `rgba(245, 34, 45, ${alpha.toFixed(3)})`\n }\n const t = Math.min(1, d.total / max)\n const alpha = 0.20 + 0.75 * t\n return `rgba(82, 196, 26, ${alpha.toFixed(3)})`\n}\n\n/** 把 days 数组按周分桶。每列 7 行(周一=0 .. 周日=6)。\n * 最末列 = 包含今天的那一周;空槽位返回 null。 */\nfunction bucketByWeek(days: DayCell[]): Array<Array<DayCell | null>> {\n if (days.length === 0) return []\n const out: Array<Array<DayCell | null>> = []\n // 找到第一天的星期偏移(Mon=0, Sun=6)\n const firstDow = ((new Date(days[0].date).getDay() + 6) % 7)\n let col: Array<DayCell | null> = Array(7).fill(null)\n // 第一周前置 null\n for (let r = 0; r < firstDow; r++) col[r] = null\n let row = firstDow\n for (const d of days) {\n col[row] = d\n row++\n if (row === 7) {\n out.push(col)\n col = Array(7).fill(null)\n row = 0\n }\n }\n if (row !== 0) out.push(col)\n return out\n}\n\nconst DOW_LABELS = ['一', '二', '三', '四', '五', '六', '日']\n\nfunction CommitCalendarCardInner({\n days,\n title = '近 30 天任务活跃度',\n subtitle,\n}: CommitCalendarCardProps) {\n const [hover, setHover] = useState<DayCell | null>(null)\n\n const weeks = useMemo(() => bucketByWeek(days), [days])\n const max = useMemo(() => days.reduce((m, d) => Math.max(m, d.total), 0), [days])\n const totalTasks = useMemo(() => days.reduce((s, d) => s + d.total, 0), [days])\n\n const hasData = max > 0\n\n return (\n <div\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>\n <div>\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>{title}</div>\n <div style={{ fontSize: 12, color: SUBDUE, marginTop: 2 }}>\n {subtitle || `${days.length} 天 · 共 ${totalTasks} 个 task · 失败主导日标红`}\n </div>\n </div>\n <div style={{ fontSize: 11, color: SUBDUE, fontVariantNumeric: 'tabular-nums' }}>\n max/day {max}\n </div>\n </div>\n\n {!hasData ? (\n <div\n style={{\n flex: 1, minHeight: 200, display: 'flex',\n alignItems: 'center', justifyContent: 'center',\n color: SUBDUE, fontSize: 13,\n }}\n >\n 近 {days.length} 天无 task 数据\n </div>\n ) : (\n <>\n <div style={{ display: 'flex', gap: 6, marginTop: 16, flex: 1 }}>\n {/* dow label 列 */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 3, paddingTop: 0 }}>\n {DOW_LABELS.map((d, i) => (\n <div\n key={d}\n style={{\n fontSize: 10, color: SUBDUE, height: 18, lineHeight: '18px',\n visibility: i % 2 === 0 ? 'visible' : 'hidden',\n }}\n >\n {d}\n </div>\n ))}\n </div>\n {/* week columns */}\n <div style={{ display: 'flex', gap: 3, flex: 1 }}>\n {weeks.map((col, wi) => (\n <div key={wi} style={{ display: 'flex', flexDirection: 'column', gap: 3, flex: 1 }}>\n {col.map((cell, ri) => (\n <div\n key={ri}\n onMouseEnter={() => cell && setHover(cell)}\n onMouseLeave={() => setHover(null)}\n style={{\n height: 18,\n borderRadius: 3,\n background: cell ? cellColor(cell, max) : 'transparent',\n cursor: cell && cell.total > 0 ? 'default' : 'default',\n }}\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n\n {/* Legend + hover info */}\n <div\n style={{\n marginTop: 14,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontSize: 11,\n color: SUBDUE,\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span>少</span>\n {[0.20, 0.40, 0.60, 0.80, 0.95].map(a => (\n <span\n key={a}\n style={{\n display: 'inline-block', width: 14, height: 14, borderRadius: 3,\n background: `rgba(82, 196, 26, ${a})`,\n }}\n />\n ))}\n <span>多</span>\n <span style={{ marginLeft: 12, display: 'inline-flex', alignItems: 'center', gap: 4 }}>\n <span\n style={{\n display: 'inline-block', width: 14, height: 14, borderRadius: 3,\n background: `rgba(245, 34, 45, 0.75)`,\n }}\n />\n 失败 > 30%\n </span>\n </div>\n <div style={{ minHeight: 14 }}>\n {hover && (\n <span>\n <span style={{ fontFamily: 'monospace', color: '#1d2129' }}>{hover.date}</span>\n {' · '}\n <span style={{ color: '#1d2129', fontWeight: 600 }}>{hover.total} tasks</span>\n {' · '}\n <span style={{ color: SUCCESS }}>{hover.succeeded} ok</span>\n {' · '}\n <span style={{ color: FAIL }}>{hover.failed} fail</span>\n </span>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n )\n}\n\n// Perf: memo skips week-bucketing + 30 cell rerender on parent re-renders.\nexport default memo(CommitCalendarCardInner)\n","/**\n * InterceptRuleBarCard — v6 ROW 5 右 — 近 N 天拦截规则 Top 10 横向 bar\n *\n * 数据来源(0 后端改动):复用 tuning.summary.rule_violations_top(已被\n * HealthHomePage fetch),结构 { rule_id, n }。\n *\n * 颜色规则(基于 rule_id 名称启发式):\n * - 包含 'hard' / 'deny' → #f5222d (红 = deny)\n * - 包含 'warn' → #faad14 (黄 = warn)\n * - 其它 → #1890ff (主色蓝)\n *\n * 视觉契约(health v6 / Step 3):\n * - 横向 bar 按 n 倒排\n * - 标签:rule_id · N hits · 推断 action\n * - 卡片标题 14px 加粗 + 副标 12px 灰色\n */\n\nimport { memo } from 'react'\n\ninterface Rule {\n rule_id: string\n n: number\n}\n\ninterface InterceptRuleBarCardProps {\n rules: Rule[]\n title?: string\n subtitle?: string\n}\n\nconst SUBDUE = '#86909c'\nconst PRIMARY = '#1890ff'\nconst WARN = '#faad14'\nconst FAIL = '#f5222d'\n\ntype Action = 'deny' | 'warn' | 'other'\n\nfunction inferAction(ruleId: string): Action {\n const id = ruleId.toLowerCase()\n if (id.includes('hard') || id.includes('deny')) return 'deny'\n if (id.includes('warn') || id.includes('soft')) return 'warn'\n return 'other'\n}\n\nfunction actionColor(a: Action): string {\n if (a === 'deny') return FAIL\n if (a === 'warn') return WARN\n return PRIMARY\n}\n\nfunction actionLabel(a: Action): string {\n if (a === 'deny') return 'deny'\n if (a === 'warn') return 'warn'\n return 'mixed'\n}\n\nfunction InterceptRuleBarCardInner({\n rules,\n title = '近 7 天拦截规则 Top 10',\n subtitle = 'PreToolUse 命中分布 — 高频规则可能扰民,需调优',\n}: InterceptRuleBarCardProps) {\n const top = rules.slice(0, 10)\n const max = top.reduce((m, r) => Math.max(m, r.n), 0)\n\n return (\n <div\n style={{\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>{title}</div>\n <div style={{ fontSize: 12, color: SUBDUE, marginTop: 2, marginBottom: 16 }}>\n {subtitle}\n </div>\n\n {top.length === 0 || max === 0 ? (\n <div\n style={{\n flex: 1, minHeight: 200, display: 'flex',\n alignItems: 'center', justifyContent: 'center',\n color: SUBDUE, fontSize: 13,\n }}\n >\n 无拦截规则命中数据(健康)\n </div>\n ) : (\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}>\n {top.map((r) => {\n const action = inferAction(r.rule_id)\n const color = actionColor(action)\n const w = Math.max(2, Math.round((r.n / max) * 100))\n return (\n <div key={r.rule_id} title={`${r.rule_id} · ${r.n} hits · ${actionLabel(action)}`}>\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n fontSize: 11,\n marginBottom: 3,\n color: '#1d2129',\n }}\n >\n <span\n style={{\n fontFamily: 'monospace',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '70%',\n }}\n >\n {r.rule_id}\n </span>\n <span style={{ fontVariantNumeric: 'tabular-nums', color: SUBDUE }}>\n {r.n} hits · <span style={{ color }}>{actionLabel(action)}</span>\n </span>\n </div>\n <div\n style={{\n background: '#f0f0f0',\n borderRadius: 3,\n overflow: 'hidden',\n height: 10,\n }}\n >\n <div\n style={{\n width: `${w}%`,\n height: '100%',\n background: color,\n borderRadius: 3,\n transition: 'width 0.3s',\n }}\n />\n </div>\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\n// Perf: memo avoids rebuilding 10-row bar chart on parent re-render.\nexport default memo(InterceptRuleBarCardInner)\n","/**\n * /health — 整体健康度(v6 Datadog/Grafana 风格,2026-05-25)\n *\n * Step 1 (ship): ROW 1 KPI strip + sparkline / ROW 2 Outcome stacked area + donut\n * Step 2 (ship): ROW 3 C4/C8 line trend / ROW 4 KB heatmap + Skill horizontal bar\n * Step 3 (this): ROW 5 Commit calendar + Intercept rule bar / ROW 6 TL;DR + 异常 inline\n * 删除 v5 残留:独立 vs-上周 Table + 独立 Top5 异常 Card\n *\n * 数据源(0 后端改动):\n * GET /api/insights/agent-board?window=N → cards(含 kb_pages / intercepts / outcome)\n * GET /api/insights/agent-board?window=2N → 用于减算上周 cards\n * GET /api/insights/tuning?window=N → summary(含 skill_top / kb_top_pages / rule_violations_top)\n * GET /api/insights/tuning?window=2N → 上周比较基线\n *\n * 降级注解(cards 字段不全时的策略):\n * - C4 daily: cards 无 obeyed flag → 用 \"有拦截 AND outcome != failed\" proxy\n * - C8 daily: cards 无 spec_generated/approved → 用 \"已分类中 success+partial 比例\" proxy\n * - KB daily breakdown: cards.kb_pages 直接按 start_time bucket(真 daily 数据)\n * - Skill bar: tuning.skill_top 已聚合,无 daily 需求\n * - Intercept rules: 用 tuning.rule_violations_top(无 action 字段,按 rule_id 启发推断)\n */\nimport { useEffect, useMemo, useState } from 'react'\nimport { useNavigate, useSearchParams } from 'react-router-dom'\nimport { useQuery } from '@tanstack/react-query'\nimport Grid from '@arco-design/web-react/es/Grid'\nimport Typography from '@arco-design/web-react/es/Typography'\nimport Tag from '@arco-design/web-react/es/Tag'\nimport Skeleton from '@arco-design/web-react/es/Skeleton'\nimport Empty from '@arco-design/web-react/es/Empty'\nimport Radio from '@arco-design/web-react/es/Radio'\nimport Button from '@arco-design/web-react/es/Button'\nimport {\n AreaChart, Area, ResponsiveContainer, XAxis, YAxis, Tooltip, Legend, CartesianGrid,\n} from 'recharts'\nimport KpiSparkCard from '../components/health/KpiSparkCard'\nimport OutcomeDonutChart from '../components/health/OutcomeDonutChart'\nimport TrendLineCard from '../components/health/TrendLineCard'\nimport KbHeatmapCard from '../components/health/KbHeatmapCard'\nimport SkillBarCard from '../components/health/SkillBarCard'\nimport CommitCalendarCard from '../components/health/CommitCalendarCard'\nimport InterceptRuleBarCard from '../components/health/InterceptRuleBarCard'\nimport { OUTCOME_COLOR, OUTCOME_LABEL } from '../utils/outcome'\nimport { parseTimestamp } from '../utils/time'\n\nconst Row = Grid.Row\nconst Col = Grid.Col\n\n// v6: Datadog/Grafana-ish color palette (限定 5 色 — 与 OUTCOME_COLOR 对齐 + 主色 #1890ff)\nconst COLOR_PRIMARY = '#1890ff'\nconst COLOR_FAIL = '#f5222d'\n\n/** 按天分桶:返回长度 windowDays 的数字数组(idx 0 = 最早一天,末尾 = 今天) */\nfunction dailyBuckets<T>(items: T[], windowDays: number, getTimestamp: (it: T) => string, accessor: (it: T) => number): number[] {\n const buckets = Array(windowDays).fill(0)\n const dayMs = 86_400_000\n const now = Date.now()\n for (const it of items) {\n const ts = new Date(getTimestamp(it)).getTime()\n if (!Number.isFinite(ts)) continue\n const idx = windowDays - 1 - Math.floor((now - ts) / dayMs)\n if (idx >= 0 && idx < windowDays) buckets[idx] += accessor(it)\n }\n return buckets\n}\n\ntype Window = 7 | 30\n\n// ─── Types mirror server response shape ────────────────────────────────────\ninterface AgentBoardCard {\n task_id: string\n title: string\n start_time: string\n outcome: 'success' | 'partial' | 'failed' | 'abandoned' | null\n intercepts?: { deny: number; warn: number }\n user_violation_count?: number\n reverted_commits?: number\n kb_pages?: Array<{ name: string; cited: number }>\n}\ninterface AgentBoardResp {\n lanes: Record<string, AgentBoardCard[]>\n summary: { total: number }\n /** Unbounded task count in window (post-noise / post-project filter).\n * Distinct from summary.total which is capped by the lane LIMIT (default\n * 40, perf cap). KPI \"总任务\" must bind here to avoid the 1-in-9 undercount\n * reported on /health (TasksHubPage fix landed first, 61448f01). */\n total_in_window?: number\n}\ninterface TuningSummary {\n rule_violations_top: Array<{ rule_id: string; n: number }>\n intercept_obeyed_ratio: number\n intercept_obeyed_band: 'green' | 'yellow' | 'red' | 'unknown'\n workflow_acceptance_ratio: number\n workflow_acceptance_band: 'green' | 'yellow' | 'red' | 'unknown'\n kb_inject_count: number\n kb_top_pages: Array<{ name: string; cited: number }>\n skill_top: Array<{ skill_id: string; n: number; success_rate: number }>\n session_outcome: { success: number; partial: number; failed: number; abandoned: number; unclassified: number }\n}\ninterface TuningResp { summary: TuningSummary; suggestions: Array<{ suggestion: string; kind?: string }> }\n\n// ─── Fetchers ──────────────────────────────────────────────────────────────\nasync function fetchAgentBoard(window: number): Promise<AgentBoardResp> {\n const r = await fetch(`/api/insights/agent-board?window=${window}`)\n if (!r.ok) throw new Error(`agent-board ${r.status}`)\n return r.json()\n}\nasync function fetchTuning(window: number): Promise<TuningResp | null> {\n const r = await fetch(`/api/insights/tuning?window=${window}`)\n if (r.status === 410) return null\n if (!r.ok) throw new Error(`tuning ${r.status}`)\n return r.json()\n}\n\n// Violations fetcher — used for the markdown digest \"拦截次数\" and KPI binding.\n// Pulls limit=1 because we only need the unbounded total_by_signal_in_window\n// counts; we don't render the list rows on /health. Added 2026-05-26.\ninterface ViolationsBySignalResp {\n total_in_window: number\n total_by_signal_in_window: {\n routing_disobeyed: number\n user_keyword: number\n tool_intercept: number\n }\n}\nasync function fetchViolationsSignal(days: number): Promise<ViolationsBySignalResp> {\n const r = await fetch(`/api/violations?days=${days}&limit=1`)\n if (!r.ok) throw new Error(`violations ${r.status}`)\n const body = await r.json() as ViolationsBySignalResp\n // Defensive default — older daemons (pre-02529c6d) didn't ship these fields.\n return {\n total_in_window: body.total_in_window ?? 0,\n total_by_signal_in_window: body.total_by_signal_in_window ?? {\n routing_disobeyed: 0,\n user_keyword: 0,\n tool_intercept: 0,\n },\n }\n}\n\n// Commit-activity fetcher — unbounded daily counts for the 30d Commit calendar.\n// Replaces deriving from /api/insights/agent-board?window=30 (capped at 40).\n// Added 2026-05-26.\ninterface CommitActivityDay {\n date: string\n total: number\n succeeded: number\n failed: number\n commits: number\n}\ninterface CommitActivityResp {\n days: number\n daily: CommitActivityDay[]\n total_tasks: number\n total_commits: number\n}\nasync function fetchCommitActivity(days: number): Promise<CommitActivityResp> {\n const r = await fetch(`/api/insights/commit-activity?days=${days}`)\n if (!r.ok) throw new Error(`commit-activity ${r.status}`)\n return r.json()\n}\n\nfunction allCards(resp: AgentBoardResp | undefined): AgentBoardCard[] {\n if (!resp) return []\n const out: AgentBoardCard[] = []\n for (const lane of Object.values(resp.lanes || {})) out.push(...lane)\n return out\n}\n\nfunction bucketByDay(cards: AgentBoardCard[], windowDays: Window) {\n const map = new Map<string, { success: number; partial: number; failed: number; abandoned: number }>()\n const now = Date.now()\n for (let i = windowDays - 1; i >= 0; i--) {\n const d = new Date(now - i * 86400000).toISOString().slice(0, 10)\n map.set(d, { success: 0, partial: 0, failed: 0, abandoned: 0 })\n }\n for (const c of cards) {\n if (!c.outcome) continue\n const day = (c.start_time || '').slice(0, 10)\n const row = map.get(day)\n if (row) row[c.outcome]++\n }\n return [...map.entries()].map(([date, v]) => ({ date: date.slice(5), ...v }))\n}\n\nfunction shortText(s: string, n = 60): string {\n if (!s) return ''\n return s.length > n ? `${s.slice(0, n)}…` : s\n}\n\n// OUTCOME_COLOR / OUTCOME_LABEL — imported from '../utils/outcome' (Arco palette canonical, 2026-05-26).\n\n// ─── Week metrics (derived from cards + summary) ───────────────────────────\ninterface WeekMetrics {\n total: number\n success: number\n partial: number\n failed: number\n abandoned: number\n classified: number\n successRate: number // 0-100\n failedRate: number // 0-100 (for tooltip)\n kbInjectCount: number\n workflowAcceptance: number // 0-1\n interceptObeyed: number // 0-1\n interceptCount: number // deny + warn aggregated across all tasks\n zeroSkillCount: number // n of skills with 0 invocations in window (proxy from skill_top)\n zeroSkillNames: string[]\n topFailedTitle: string | null\n}\n\nfunction deriveMetrics(\n cards: AgentBoardCard[],\n tuning: TuningResp | null,\n /** Unbounded task count in window from agent-board's total_in_window field.\n * When provided, overrides cards.length for the `total` KPI so it isn't\n * capped by the lane LIMIT. Fix 2026-05-26 — same root cause as TasksHub\n * bug 61448f01 (the page just hadn't been updated to bind here). */\n totalOverride?: number,\n /** Unbounded tool-intercept count from /api/violations total_by_signal_in_window.\n * When provided, overrides the cards.intercepts sum (which is capped by the\n * agent-board lane LIMIT). Drives both the markdown digest \"拦截次数\" row and\n * the KPI strip when present. Added 2026-05-26 followup to 02529c6d. */\n interceptCountOverride?: number,\n): WeekMetrics {\n const outcome = tuning?.summary.session_outcome ?? { success: 0, partial: 0, failed: 0, abandoned: 0, unclassified: 0 }\n const classified = outcome.success + outcome.partial + outcome.failed + outcome.abandoned\n const interceptCountFromCards = cards.reduce((s, c) => s + (c.intercepts?.deny ?? 0) + (c.intercepts?.warn ?? 0), 0)\n const interceptCount = interceptCountOverride ?? interceptCountFromCards\n const skills = tuning?.summary.skill_top ?? []\n // Heuristic: skill_top with n === 0 → zero-trigger. Server already filters\n // mostly active ones, so n=0 is rare; fall back to \"first skill with lowest n\".\n const zeroSkillNames = skills.filter(s => s.n === 0).map(s => s.skill_id)\n const failedCards = cards\n .filter(c => c.outcome === 'failed')\n .sort((a, b) => (b.start_time || '').localeCompare(a.start_time || ''))\n return {\n total: totalOverride ?? cards.length,\n success: outcome.success,\n partial: outcome.partial,\n failed: outcome.failed,\n abandoned: outcome.abandoned,\n classified,\n successRate: classified > 0 ? Math.round((outcome.success / classified) * 1000) / 10 : 0,\n failedRate: classified > 0 ? Math.round((outcome.failed / classified) * 1000) / 10 : 0,\n kbInjectCount: tuning?.summary.kb_inject_count ?? 0,\n workflowAcceptance: tuning?.summary.workflow_acceptance_ratio ?? 0,\n interceptObeyed: tuning?.summary.intercept_obeyed_ratio ?? 0,\n interceptCount,\n zeroSkillCount: zeroSkillNames.length,\n zeroSkillNames,\n topFailedTitle: failedCards[0]?.title ?? null,\n }\n}\n\n// ─── Diff helpers ──────────────────────────────────────────────────────────\nfunction diffSign(thisVal: number, prevVal: number, higherIsBetter: boolean): {\n delta: number\n arrow: '↑' | '↓' | '→'\n worsened: boolean\n text: string\n} {\n const delta = thisVal - prevVal\n if (delta === 0) return { delta: 0, arrow: '→', worsened: false, text: '0' }\n const arrow = delta > 0 ? '↑' : '↓'\n const worsened = higherIsBetter ? delta < 0 : delta > 0\n const sign = delta > 0 ? '+' : ''\n return { delta, arrow, worsened, text: `${sign}${Number.isInteger(delta) ? delta : delta.toFixed(1)}` }\n}\n\n// ─── Narrative generator (pure) ────────────────────────────────────────────\nfunction generateNarrative(week: WeekMetrics, prev: WeekMetrics): string[] {\n const lines: string[] = []\n // 1. 任务量 + 推荐采纳\n const acceptPct = Math.round(week.workflowAcceptance * 100)\n lines.push(\n `过去 7 天提交了 ${week.total} 个任务(上周 ${prev.total}),编排推荐采纳率 ${acceptPct}%。`,\n )\n // 2. KB / Skill 健康\n const kbStatus = week.kbInjectCount > prev.kbInjectCount\n ? '✓ 上升'\n : week.kbInjectCount === prev.kbInjectCount ? '→ 持平' : '⚠ 下降'\n const skillNote = week.zeroSkillCount > 0\n ? `Skill ${week.zeroSkillNames[0]} 0 触发 ⚠`\n : 'Skill 调用正常 ✓'\n lines.push(\n `KB 注入 ${week.kbInjectCount} 次(上周 ${prev.kbInjectCount})${kbStatus};${skillNote}。`,\n )\n // 3. 关注点:失败趋势\n const fdiff = diffSign(week.failed, prev.failed, false)\n const concern = week.failed > prev.failed\n ? `失败上升,建议关注 \"${shortText(week.topFailedTitle || '失败任务', 32)}\"`\n : week.failed < prev.failed ? '失败下降,状态改善 ✓' : '失败与上周持平'\n lines.push(\n `失败任务 ${week.failed} 个(上周 ${prev.failed})${fdiff.arrow} ${concern}。`,\n )\n return lines\n}\n\n// ─── Markdown weekly digest (client-side) ──────────────────────────────────\nfunction buildMarkdownDigest(week: WeekMetrics, prev: WeekMetrics, narrative: string[], summary: TuningSummary | null): string {\n const ts = new Date().toISOString().slice(0, 16).replace('T', ' ')\n const lines: string[] = [\n `# 整体健康度周报(${ts})`,\n '',\n '## TL;DR',\n ...narrative.map(s => `- ${s}`),\n '',\n '## 本周 vs 上周',\n '| 指标 | 本周 | 上周 | Δ |',\n '|---|---|---|---|',\n `| 总任务 | ${week.total} | ${prev.total} | ${diffSign(week.total, prev.total, true).text} |`,\n `| 成功率 | ${week.successRate}% | ${prev.successRate}% | ${diffSign(week.successRate, prev.successRate, true).text}% |`,\n `| 失败数 | ${week.failed} | ${prev.failed} | ${diffSign(week.failed, prev.failed, false).text} |`,\n `| KB 注入次数 | ${week.kbInjectCount} | ${prev.kbInjectCount} | ${diffSign(week.kbInjectCount, prev.kbInjectCount, true).text} |`,\n `| 编排采纳率 | ${Math.round(week.workflowAcceptance * 100)}% | ${Math.round(prev.workflowAcceptance * 100)}% | ${diffSign(Math.round(week.workflowAcceptance * 100), Math.round(prev.workflowAcceptance * 100), true).text}% |`,\n `| 拦截次数 | ${week.interceptCount} | ${prev.interceptCount} | ${diffSign(week.interceptCount, prev.interceptCount, true).text} |`,\n '',\n '## 编排遵守率',\n `- C4 主线程听劝率:${Math.round(week.interceptObeyed * 100)}%`,\n `- C8 Agent 推荐采纳率:${Math.round(week.workflowAcceptance * 100)}%`,\n '',\n ]\n if (summary?.kb_top_pages?.length) {\n lines.push('## KB Top 5')\n summary.kb_top_pages.slice(0, 5).forEach(p => lines.push(`- ${p.name} — cited ${p.cited}`))\n lines.push('')\n }\n if (summary?.skill_top?.length) {\n lines.push('## Skill Top 5')\n summary.skill_top.slice(0, 5).forEach(s => lines.push(`- ${s.skill_id} — n=${s.n}, success=${Math.round((s.success_rate ?? 0) * 100)}%`))\n lines.push('')\n }\n return lines.join('\\n')\n}\n\nfunction downloadMarkdown(content: string, filename: string) {\n const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n}\n\n// ─── Component ─────────────────────────────────────────────────────────────\nexport default function HealthHomePage() {\n const [searchParams, setSearchParams] = useSearchParams()\n const navigate = useNavigate()\n const windowParam = (parseInt(searchParams.get('window') || '7', 10) === 30 ? 30 : 7) as Window\n\n // window=7 → fetch 7 and 14 to compute \"上周\" diff (14d set minus 7d set).\n // window=30 → fetch 30 and 60 (best-effort: server caps at 90, OK).\n const baseWindow = windowParam\n const longWindow = windowParam * 2\n\n // Perf: defer \"vs 上周\" queries (window=14) until 1s after mount. The big\n // numbers + sparklines render immediately from boardThis/tuningThis; trend\n // pills then fill in once boardLong/tuningLong arrive. Saves ~1.2s on first\n // paint of /health (audit §2 §6 P0.2).\n const [enableLongQuery, setEnableLongQuery] = useState(false)\n useEffect(() => {\n const t = setTimeout(() => setEnableLongQuery(true), 1000)\n return () => clearTimeout(t)\n }, [])\n\n const { data: boardThis, isLoading: bThisLoading } = useQuery({\n queryKey: ['hh-board-this', baseWindow],\n queryFn: () => fetchAgentBoard(baseWindow),\n refetchInterval: 60_000,\n // Pause the 60s refetch when the tab is in the background — the /health\n // board (cards + summary) is the heaviest aggregated query on this page;\n // burning a fetch every minute behind a hidden tab gave nothing back.\n // Foreground keeps fresh @ 60s, background quiet — see polish spec #3.\n refetchIntervalInBackground: false,\n })\n const { data: boardLong, isLoading: bLongLoading } = useQuery({\n queryKey: ['hh-board-long', longWindow],\n queryFn: () => fetchAgentBoard(longWindow),\n enabled: enableLongQuery,\n })\n const { data: tuningThis, isLoading: tThisLoading } = useQuery({\n queryKey: ['hh-tuning-this', baseWindow],\n queryFn: () => fetchTuning(baseWindow),\n })\n const { data: tuningLong, isLoading: tLongLoading } = useQuery({\n queryKey: ['hh-tuning-long', longWindow],\n queryFn: () => fetchTuning(longWindow),\n enabled: enableLongQuery,\n })\n\n // ── /api/violations: unbounded tool-intercept count for markdown digest + KPI\n //\n // 2026-05-26 followup to 02529c6d/61448f01:\n // - 'this week' = days = baseWindow\n // - 'last week' = (days=longWindow) minus (days=baseWindow) tool_intercept\n // count. Best-effort subtraction; backend doesn't expose a since/until\n // pair on this endpoint so the long_window query is the cheapest baseline.\n const { data: violationsThis } = useQuery({\n queryKey: ['hh-violations-this', baseWindow],\n queryFn: () => fetchViolationsSignal(baseWindow),\n staleTime: 60_000,\n refetchIntervalInBackground: false,\n })\n const { data: violationsLong } = useQuery({\n queryKey: ['hh-violations-long', longWindow],\n queryFn: () => fetchViolationsSignal(longWindow),\n enabled: enableLongQuery,\n staleTime: 60_000,\n })\n\n const thisInterceptCount = violationsThis?.total_by_signal_in_window.tool_intercept\n const prevInterceptCount = useMemo(() => {\n if (violationsLong?.total_by_signal_in_window.tool_intercept == null) return undefined\n const thisVal = violationsThis?.total_by_signal_in_window.tool_intercept ?? 0\n return Math.max(0, violationsLong.total_by_signal_in_window.tool_intercept - thisVal)\n }, [violationsLong, violationsThis])\n\n // Only count \"this week\" queries toward loading state for first paint.\n // Long-window queries fill in trend pills after 1s — they shouldn't block UI.\n const loading = bThisLoading || tThisLoading\n // Reference long-loading flags so unused-var lint doesn't fire; they remain\n // available for future skeleton overlays on trend pills.\n void bLongLoading\n void tLongLoading\n\n // ── Compute \"this week\" and \"previous week\" splits ────────────────────────\n const thisCards = useMemo(() => allCards(boardThis), [boardThis])\n const longCards = useMemo(() => allCards(boardLong), [boardLong])\n const cutoffMs = useMemo(() => Date.now() - baseWindow * 86_400_000, [baseWindow])\n const prevCards = useMemo(\n () => longCards.filter(c => parseTimestamp(c.start_time).getTime() < cutoffMs),\n [longCards, cutoffMs],\n )\n\n // tuning summary diff: prev = (long - this), best-effort (a few metrics aren't\n // perfectly subtractive but counts like kb_inject / outcome are additive).\n const prevTuningSummary = useMemo<TuningSummary | null>(() => {\n if (!tuningLong) return null\n if (!tuningThis) return tuningLong.summary\n const l = tuningLong.summary, t = tuningThis.summary\n const sub = (a: number, b: number) => Math.max(0, a - b)\n return {\n ...l,\n kb_inject_count: sub(l.kb_inject_count, t.kb_inject_count),\n session_outcome: {\n success: sub(l.session_outcome.success, t.session_outcome.success),\n partial: sub(l.session_outcome.partial, t.session_outcome.partial),\n failed: sub(l.session_outcome.failed, t.session_outcome.failed),\n abandoned: sub(l.session_outcome.abandoned, t.session_outcome.abandoned),\n unclassified: sub(l.session_outcome.unclassified, t.session_outcome.unclassified),\n },\n // ratios kept as the longer-window ratio (not strictly the previous-week\n // ratio, but a reasonable baseline). Marked with caveat in tooltip.\n }\n }, [tuningLong, tuningThis])\n\n // Unbounded \"this week\" task count — agent-board exposes this directly so\n // the KPI doesn't get capped by the lane LIMIT (default 40). The previous\n // week's count is derived by subtraction (long_window total minus this_week\n // total), best-effort guarded against negative drift.\n const thisTotalInWindow = boardThis?.total_in_window\n const prevTotalInWindow = useMemo(() => {\n if (boardLong?.total_in_window == null) return undefined\n if (boardThis?.total_in_window == null) return boardLong.total_in_window\n return Math.max(0, boardLong.total_in_window - boardThis.total_in_window)\n }, [boardLong, boardThis])\n\n const thisMetrics = useMemo(\n () => deriveMetrics(thisCards, tuningThis ?? null, thisTotalInWindow, thisInterceptCount),\n [thisCards, tuningThis, thisTotalInWindow, thisInterceptCount],\n )\n const prevMetrics = useMemo(\n () => deriveMetrics(\n prevCards,\n prevTuningSummary ? { summary: prevTuningSummary, suggestions: [] } : null,\n prevTotalInWindow,\n prevInterceptCount,\n ),\n [prevCards, prevTuningSummary, prevTotalInWindow, prevInterceptCount],\n )\n\n const narrative = useMemo(() => generateNarrative(thisMetrics, prevMetrics), [thisMetrics, prevMetrics])\n const trendData = useMemo(() => bucketByDay(thisCards, windowParam), [thisCards, windowParam])\n\n const ruleTop = tuningThis?.summary.rule_violations_top?.slice(0, 5) ?? []\n\n // Top 5 anomaly mix: failed tasks (most recent) + high-intercept tasks.\n const topAnomalies = useMemo(() => {\n const failed = thisCards\n .filter(c => c.outcome === 'failed')\n .sort((a, b) => (b.start_time || '').localeCompare(a.start_time || ''))\n .slice(0, 3)\n .map(c => ({\n kind: 'failed' as const,\n task_id: c.task_id,\n label: shortText(c.title || '(空)', 60),\n detail: '失败',\n }))\n const high = thisCards\n .map(c => ({ card: c, total: (c.intercepts?.deny ?? 0) + (c.intercepts?.warn ?? 0) }))\n .filter(x => x.total >= 5)\n .sort((a, b) => b.total - a.total)\n .slice(0, 3)\n .map(x => ({\n kind: 'intercept' as const,\n task_id: x.card.task_id,\n label: shortText(x.card.title || '(空)', 60),\n detail: `${x.total} 次拦截`,\n }))\n return [...failed, ...high].slice(0, 5)\n }, [thisCards])\n\n const setWindow = (w: Window) => {\n const next = new URLSearchParams(searchParams)\n next.set('window', String(w))\n setSearchParams(next, { replace: true })\n }\n\n const onDownload = () => {\n const md = buildMarkdownDigest(thisMetrics, prevMetrics, narrative, tuningThis?.summary ?? null)\n const stamp = new Date().toISOString().slice(0, 10)\n downloadMarkdown(md, `health-weekly-${stamp}.md`)\n }\n\n // ─── v6 Step 1: KPI sparklines + Outcome split data ──────────────────────\n // Per-day buckets over current window for sparklines.\n //\n // 2026-05-26 fix: when the lane LIMIT (default 40) is smaller than the true\n // 7d task count (e.g. 370), bucketing `thisCards` produces a sparkline whose\n // sum is 40 — visually contradicting the headline KPI which now binds to\n // the unbounded `total_in_window` (370). Rescale the shape-preserving\n // buckets so their sum matches `thisMetrics.total`. We keep the relative\n // daily distribution (the most-recent-N is a roughly uniform sample over\n // active days) but the magnitude tracks the real count.\n const sparkTotalRaw = useMemo(\n () => dailyBuckets(thisCards, windowParam, c => c.start_time, () => 1),\n [thisCards, windowParam],\n )\n const sparkTotal = useMemo(() => {\n const totalSeen = sparkTotalRaw.reduce((a, b) => a + b, 0)\n const realTotal = thisTotalInWindow\n if (totalSeen === 0 || realTotal == null || realTotal === totalSeen) return sparkTotalRaw\n const scale = realTotal / totalSeen\n return sparkTotalRaw.map(v => Math.round(v * scale))\n }, [sparkTotalRaw, thisTotalInWindow])\n const sparkFailed = useMemo(\n () => dailyBuckets(thisCards, windowParam, c => c.start_time, c => (c.outcome === 'failed' ? 1 : 0)),\n [thisCards, windowParam],\n )\n // 成功率 sparkline = daily success / daily classified (0 if no classified that day)\n const sparkSuccessRate = useMemo(() => {\n const succ = dailyBuckets(thisCards, windowParam, c => c.start_time, c => (c.outcome === 'success' ? 1 : 0))\n const classified = dailyBuckets(\n thisCards, windowParam, c => c.start_time,\n c => (c.outcome === 'success' || c.outcome === 'partial' || c.outcome === 'failed' || c.outcome === 'abandoned' ? 1 : 0),\n )\n return succ.map((s, i) => (classified[i] > 0 ? Math.round((s / classified[i]) * 100) : 0))\n }, [thisCards, windowParam])\n // KB 注入 sparkline: 后端只给 window 总数,没有 daily 序列。退化策略:均摊到每天,再\n // 用前后窗口差异制造一点起伏(演示 sparkline,不误导)。后续可加 daily 接口。\n const sparkKb = useMemo(() => {\n const total = thisMetrics.kbInjectCount\n if (total === 0) return Array(windowParam).fill(0)\n const avg = total / windowParam\n // pseudo-trend: scale by daily task count so 注入趋势 ~跟随活跃度\n const totals = sparkTotal\n const totalSum = totals.reduce((a, b) => a + b, 0) || 1\n return totals.map(t => Math.round((t / totalSum) * total) || (avg >= 1 ? Math.round(avg) : 0))\n }, [thisMetrics.kbInjectCount, sparkTotal, windowParam])\n\n // ─── v6 Step 2: ROW 3 C4/C8 daily trend ──────────────────────────────────\n // Date labels (e.g. ['05-19', ..., '05-25'])\n const dateLabels = useMemo(() => {\n const now = Date.now()\n const out: string[] = []\n for (let i = windowParam - 1; i >= 0; i--) {\n out.push(new Date(now - i * 86_400_000).toISOString().slice(5, 10))\n }\n return out\n }, [windowParam])\n\n /** group cards by day index (0..windowParam-1) */\n const cardsByDay = useMemo(() => {\n const buckets: AgentBoardCard[][] = Array.from({ length: windowParam }, () => [])\n const now = Date.now()\n for (const c of thisCards) {\n const ts = parseTimestamp(c.start_time).getTime()\n if (!Number.isFinite(ts)) continue\n const idx = windowParam - 1 - Math.floor((now - ts) / 86_400_000)\n if (idx >= 0 && idx < windowParam) buckets[idx].push(c)\n }\n return buckets\n }, [thisCards, windowParam])\n\n // C4 听劝率 daily:当日有 intercepts 总数 > 0 的 task 中,最终 outcome != failed 的比例。\n // 降级注解:后端无 daily C4 endpoint,这里用 \"高拦截但不失败\" 当作 \"听劝\" proxy;与\n // tuning.intercept_obeyed_ratio 同一思路。无数据天 value=null(line 断开)。\n const c4Trend = useMemo(() => dateLabels.map((d, i) => {\n const day = cardsByDay[i] || []\n const withIntercept = day.filter(c => (c.intercepts?.deny ?? 0) + (c.intercepts?.warn ?? 0) > 0)\n if (withIntercept.length === 0) return { date: d, value: null as number | null }\n const obeyed = withIntercept.filter(c => c.outcome !== 'failed').length\n return { date: d, value: Math.round((obeyed / withIntercept.length) * 100) }\n }), [dateLabels, cardsByDay])\n\n // C8 Spec 采纳率 daily:后端 cards 无 spec_generated/spec_approved 字段。降级:用\n // \"已分类(success+partial+failed+abandoned)\" 中 success+partial 的比例做 proxy\n // —— 等同于 \"完成的 task 中产出有效结果\" 的占比。后续等 spec 字段接入后再切换。\n const c8Trend = useMemo(() => dateLabels.map((d, i) => {\n const day = cardsByDay[i] || []\n const classified = day.filter(c => c.outcome === 'success' || c.outcome === 'partial' || c.outcome === 'failed' || c.outcome === 'abandoned')\n if (classified.length === 0) return { date: d, value: null as number | null }\n const ok = classified.filter(c => c.outcome === 'success' || c.outcome === 'partial').length\n return { date: d, value: Math.round((ok / classified.length) * 100) }\n }), [dateLabels, cardsByDay])\n\n const c4Current = useMemo(() => {\n const last = [...c4Trend].reverse().find(p => p.value != null)\n return last?.value ?? null\n }, [c4Trend])\n const c8Current = useMemo(() => {\n const last = [...c8Trend].reverse().find(p => p.value != null)\n return last?.value ?? null\n }, [c8Trend])\n\n // ─── v6 Step 2: ROW 4 KB heatmap + Skill bar ──────────────────────────────\n // KB heatmap rows: aggregate cards.kb_pages by (page, day idx).\n const kbHeatmapRows = useMemo(() => {\n const map = new Map<string, number[]>()\n cardsByDay.forEach((day, idx) => {\n for (const c of day) {\n for (const p of (c.kb_pages || [])) {\n if (!map.has(p.name)) map.set(p.name, Array(windowParam).fill(0))\n map.get(p.name)![idx] += (p.cited ?? 0)\n }\n }\n })\n // Merge in tuning.kb_top_pages so 0-hit-this-window-but-historical pages still\n // appear as 浅灰 rows (visualizes \"本周冷下来了\").\n for (const p of tuningThis?.summary.kb_top_pages ?? []) {\n if (!map.has(p.name)) map.set(p.name, Array(windowParam).fill(0))\n }\n return [...map.entries()]\n .map(([name, days]) => ({ name, days, total: days.reduce((a, b) => a + b, 0) }))\n .sort((a, b) => b.total - a.total)\n .slice(0, 8)\n }, [cardsByDay, tuningThis, windowParam])\n\n // Skill bar rows: use tuning.skill_top (no separate /api/skills/stats endpoint).\n // zero_result_rate not exposed by tuning; default 0 (warning never shown unless\n // future endpoint adds the field).\n const skillBarRows = useMemo(() =>\n (tuningThis?.summary.skill_top ?? []).map(s => ({\n skill_id: s.skill_id,\n invocations: s.n,\n success_rate: s.success_rate ?? 0,\n })),\n [tuningThis])\n\n // ─── v6 Step 3: ROW 5 commit calendar + intercept rules ──────────────────\n // Commit calendar always shows 30 days regardless of windowParam (它 是独立的\n // \"近 30 天活跃度\" 视图,更接近 GitHub 风格).\n //\n // 2026-05-26 fix: switched data source from /api/insights/agent-board?window=30\n // (cards capped at lane LIMIT=40) to /api/insights/commit-activity?days=30\n // (unbounded daily aggregate straight off tasks table). Previous version\n // undercounted high-volume days when the 30d task count exceeded the cap.\n const { data: commitActivity } = useQuery({\n queryKey: ['hh-commit-activity-30d'],\n queryFn: () => fetchCommitActivity(30),\n staleTime: 60_000,\n refetchIntervalInBackground: false,\n })\n const calDays = useMemo(() => {\n const daily = commitActivity?.daily\n if (!daily) {\n // Zero-filled fallback so the calendar still renders before first fetch.\n const dayMs = 86_400_000\n const now = Date.now()\n const out: Array<{ date: string; total: number; succeeded: number; failed: number }> = []\n for (let i = 29; i >= 0; i--) {\n out.push({\n date: new Date(now - i * dayMs).toISOString().slice(0, 10),\n total: 0,\n succeeded: 0,\n failed: 0,\n })\n }\n return out\n }\n // Server-side aggregate maps 1:1 to CommitCalendarCard's DayCell shape\n // (commits field unused by the calendar — kept on the API for future use).\n return daily.map(d => ({\n date: d.date,\n total: d.total,\n succeeded: d.succeeded,\n failed: d.failed,\n }))\n }, [commitActivity])\n\n // Intercept rules: 复用 tuning.rule_violations_top (no extra fetch).\n const interceptRules = useMemo(\n () => tuningThis?.summary.rule_violations_top?.slice(0, 10) ?? [],\n [tuningThis],\n )\n\n // ─── Render ───────────────────────────────────────────────────────────────\n return (\n <div style={{ padding: 24, background: '#fafafa', minHeight: '100%' }}>\n <Row align=\"center\" justify=\"space-between\" style={{ marginBottom: 20 }}>\n <Col>\n <div style={{ fontSize: 24, fontWeight: 600, color: '#1d2129', lineHeight: 1.2 }}>\n 整体健康度\n </div>\n <div style={{ fontSize: 12, color: '#86909c', marginTop: 4 }}>\n Past {windowParam} days · 当前项目\n </div>\n </Col>\n <Col>\n <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <Radio.Group\n type=\"button\"\n size=\"small\"\n value={windowParam}\n onChange={(v) => setWindow(v as Window)}\n options={[\n { label: '近 7 天', value: 7 },\n { label: '近 30 天', value: 30 },\n ]}\n />\n <Button size=\"small\" type=\"primary\" onClick={onDownload} disabled={loading}>\n 导出周报\n </Button>\n </div>\n </Col>\n </Row>\n\n {/* ROW 1 — KPI strip (4 列) ───────────────────────────────────────────── */}\n <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>\n <Col xs={24} sm={12} md={6}>\n <KpiSparkCard\n label=\"总任务\"\n value={thisMetrics.total}\n sparkData={sparkTotal}\n sparkColor={COLOR_PRIMARY}\n thisVal={thisMetrics.total}\n prevVal={prevMetrics.total}\n higherIsBetter\n />\n </Col>\n <Col xs={24} sm={12} md={6}>\n <KpiSparkCard\n label=\"成功率\"\n value={`${thisMetrics.successRate}%`}\n suffix=\"%\"\n sparkData={sparkSuccessRate}\n sparkColor={COLOR_PRIMARY}\n thisVal={thisMetrics.successRate}\n prevVal={prevMetrics.successRate}\n higherIsBetter\n />\n </Col>\n <Col xs={24} sm={12} md={6}>\n <KpiSparkCard\n label=\"失败数\"\n value={thisMetrics.failed}\n sparkData={sparkFailed}\n sparkColor={COLOR_FAIL}\n thisVal={thisMetrics.failed}\n prevVal={prevMetrics.failed}\n higherIsBetter={false}\n onClick={() => navigate('/tasks?outcome=failed')}\n />\n </Col>\n <Col xs={24} sm={12} md={6}>\n <KpiSparkCard\n label=\"≈ KB 注入\"\n value={thisMetrics.kbInjectCount}\n sparkData={sparkKb}\n sparkColor={COLOR_PRIMARY}\n thisVal={thisMetrics.kbInjectCount}\n prevVal={prevMetrics.kbInjectCount}\n higherIsBetter\n tooltip=\"近似分布:后端只返回窗口总数,前端按每日任务数比例分摊到 sparkline。等待 /api/insights/kb-inject-daily 完整支持。\"\n />\n </Col>\n </Row>\n\n {/* ROW 2 — Outcome split (12:12) ───────────────────────────────────── */}\n <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>\n <Col xs={24} md={12}>\n <div\n style={{\n background: '#fff', borderRadius: 8, boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n }}\n >\n <div style={{ fontSize: 13, fontWeight: 600, color: '#1d2129', marginBottom: 12 }}>\n Outcome {windowParam}d 趋势\n </div>\n {bThisLoading ? (\n <div style={{ height: 240 }}><Skeleton animation /></div>\n ) : trendData.every((d) => d.success + d.partial + d.failed + d.abandoned === 0) ? (\n <div\n style={{\n height: 240, display: 'flex', alignItems: 'center', justifyContent: 'center',\n color: '#86909c', fontSize: 13,\n }}\n >\n 近 {windowParam} 天无已分类任务数据\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={240}>\n <AreaChart data={trendData} margin={{ top: 8, right: 16, left: 0, bottom: 0 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"#f0f0f0\" />\n <XAxis dataKey=\"date\" fontSize={11} stroke=\"#86909c\" />\n <YAxis fontSize={11} allowDecimals={false} stroke=\"#86909c\" />\n <Tooltip contentStyle={{ fontSize: 12, borderRadius: 4 }} />\n <Legend wrapperStyle={{ fontSize: 11 }} iconType=\"circle\" />\n <Area type=\"monotone\" dataKey=\"success\" stackId=\"a\" stroke={OUTCOME_COLOR.success} fill={OUTCOME_COLOR.success} fillOpacity={0.6} name={OUTCOME_LABEL.success} />\n <Area type=\"monotone\" dataKey=\"partial\" stackId=\"a\" stroke={OUTCOME_COLOR.partial} fill={OUTCOME_COLOR.partial} fillOpacity={0.6} name={OUTCOME_LABEL.partial} />\n <Area type=\"monotone\" dataKey=\"failed\" stackId=\"a\" stroke={OUTCOME_COLOR.failed} fill={OUTCOME_COLOR.failed} fillOpacity={0.6} name={OUTCOME_LABEL.failed} />\n <Area type=\"monotone\" dataKey=\"abandoned\" stackId=\"a\" stroke={OUTCOME_COLOR.abandoned} fill={OUTCOME_COLOR.abandoned} fillOpacity={0.6} name={OUTCOME_LABEL.abandoned} />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </div>\n </Col>\n <Col xs={24} md={12}>\n <div\n style={{\n background: '#fff', borderRadius: 8, boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20,\n }}\n >\n <div style={{ fontSize: 13, fontWeight: 600, color: '#1d2129', marginBottom: 12 }}>\n Outcome 总览\n </div>\n {tThisLoading ? (\n <div style={{ height: 240 }}><Skeleton animation /></div>\n ) : (\n <OutcomeDonutChart\n success={thisMetrics.success}\n partial={thisMetrics.partial}\n failed={thisMetrics.failed}\n abandoned={thisMetrics.abandoned}\n height={240}\n />\n )}\n </div>\n </Col>\n </Row>\n\n {/* ROW 3 — 编排遵守率 line trend (12:12) ─────────────────────────── */}\n <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>\n <Col xs={24} md={12}>\n {tThisLoading ? (\n <div style={{ background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>\n <Skeleton animation />\n </div>\n ) : (\n <TrendLineCard\n title=\"C4 主线程听劝率\"\n subtitle={`近 ${windowParam} 天每日有拦截 task 中未失败比例`}\n data={c4Trend}\n currentValue={c4Current}\n target={70}\n />\n )}\n </Col>\n <Col xs={24} md={12}>\n {tThisLoading ? (\n <div style={{ background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>\n <Skeleton animation />\n </div>\n ) : (\n <TrendLineCard\n title=\"C8 Spec 采纳率\"\n subtitle={`近 ${windowParam} 天每日完成 task 中 success+partial 比例`}\n data={c8Trend}\n currentValue={c8Current}\n target={60}\n />\n )}\n </Col>\n </Row>\n\n {/* ROW 4 — KB heatmap + Skill bar (12:12) ─────────────────────────── */}\n <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>\n <Col xs={24} md={12}>\n {bThisLoading ? (\n <div style={{ background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>\n <Skeleton animation />\n </div>\n ) : (\n <KbHeatmapCard\n rows={kbHeatmapRows}\n dateLabels={dateLabels}\n title=\"KB 命中热力图\"\n subtitle={`Top ${kbHeatmapRows.length} pages · 近 ${windowParam} 天 daily 命中`}\n />\n )}\n </Col>\n <Col xs={24} md={12}>\n {tThisLoading ? (\n <div style={{ background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>\n <Skeleton animation />\n </div>\n ) : (\n <SkillBarCard\n rows={skillBarRows}\n title=\"Skill 调用 Top 10\"\n subtitle={`近 ${windowParam} 天调用次数 · 颜色=成功率`}\n />\n )}\n </Col>\n </Row>\n\n {/* ROW 5 — Commit calendar + Intercept rule bar (12:12) ───────────── */}\n <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>\n <Col xs={24} md={12}>\n <CommitCalendarCard days={calDays} />\n </Col>\n <Col xs={24} md={12}>\n <InterceptRuleBarCard rules={interceptRules} />\n </Col>\n </Row>\n\n {/* ROW 6 — TL;DR 叙事 + 本周异常 inline ─────────────────────────── */}\n <div\n style={{\n background: '#fff', borderRadius: 8, boxShadow: '0 1px 3px rgba(0,0,0,0.05)',\n padding: 20, marginBottom: 16,\n }}\n >\n <div\n style={{\n display: 'flex', justifyContent: 'space-between',\n alignItems: 'baseline', marginBottom: 12,\n }}\n >\n <div>\n <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2129' }}>TL;DR 周报摘要</div>\n <div style={{ fontSize: 12, color: '#86909c', marginTop: 2 }}>\n 基于近 {windowParam} 天指标自动生成 · 点击「导出周报」下载 Markdown\n </div>\n </div>\n <a\n onClick={() => navigate('/violations')}\n style={{ cursor: 'pointer', fontSize: 12, color: '#1890ff' }}\n >\n 查看 violations →\n </a>\n </div>\n\n {loading && narrative.length === 0 ? (\n <Skeleton animation />\n ) : (\n <div style={{ fontSize: 13, lineHeight: 1.9, color: '#1d2129' }}>\n {narrative.map((line, i) => (\n <div key={i}>{line}</div>\n ))}\n </div>\n )}\n\n {/* 本周异常 inline */}\n <div style={{ marginTop: 16, paddingTop: 12, borderTop: '1px solid #f0f0f0' }}>\n <div\n style={{\n fontSize: 13, fontWeight: 600, color: '#1d2129', marginBottom: 8,\n }}\n >\n 本周异常\n </div>\n {bThisLoading ? (\n <Skeleton animation />\n ) : topAnomalies.length === 0 && ruleTop.length === 0 ? (\n <Empty description=\"无明显异常项\" style={{ padding: 12 }} />\n ) : (\n <>\n {topAnomalies.map((a, i) => (\n <div\n key={i}\n onClick={() => {\n const next = new URLSearchParams()\n next.set('task', a.task_id)\n navigate(`/tasks?${next.toString()}`)\n }}\n style={{\n padding: '6px 0',\n cursor: 'pointer', display: 'flex', gap: 10, alignItems: 'center',\n borderBottom: '1px dashed #f0f0f0',\n }}\n >\n <Tag color={a.kind === 'failed' ? 'red' : 'orange'} size=\"small\" style={{ flexShrink: 0 }}>\n {a.kind === 'failed' ? '失败' : '高拦截'}\n </Tag>\n <div style={{ flex: 1, minWidth: 0, fontSize: 12 }}>{a.label}</div>\n <Typography.Text type=\"secondary\" style={{ fontSize: 11 }}>{a.detail}</Typography.Text>\n </div>\n ))}\n {ruleTop.length > 0 && (\n <div\n style={{\n padding: '8px 0 0', fontSize: 11, color: '#86909c',\n display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 4,\n }}\n >\n <span>Top 违规规则:</span>\n {ruleTop.map((r) => (\n <Tag key={r.rule_id} size=\"small\" color=\"red\">\n {r.rule_id} × {r.n}\n </Tag>\n ))}\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n"],"names":["COLOR_UP_GOOD","COLOR_DOWN_BAD","COLOR_FLAT","trendPill","thisVal","prevVal","higherIsBetter","suffix","delta","arrow","color","sign","txt","KpiSparkCardInner","label","value","sparkData","sparkColor","tooltip","onClick","pill","chartData","v","i","gradId","jsxs","e","jsx","ArcoTooltip","ResponsiveContainer","AreaChart","Tooltip","Area","KpiSparkCard","memo","OutcomeDonutChartInner","success","partial","failed","abandoned","height","total","successRate","data","LABEL","d","PieChart","Pie","Cell","COLOR","n","num","pct","Legend","OutcomeDonutChart","PRIMARY","SUCCESS","FAIL","SUBDUE","consecutiveBelow","target","TrendLineCardInner","title","subtitle","currentValue","below","warn","hasData","LineChart","CartesianGrid","XAxis","YAxis","ReferenceLine","Line","TrendLineCard","ZERO_BG","cellColor","max","KbHeatmapCardInner","rows","dateLabels","hover","setHover","useState","useMemo","m","r","Fragment","isZero","a","KbHeatmapCard","PARTIAL","TRACK","barColor","rate","SkillBarCardInner","sorted","b","w","zeroHi","SkillBarCard","EMPTY_BG","bucketByWeek","days","out","firstDow","col","row","DOW_LABELS","CommitCalendarCardInner","weeks","totalTasks","s","wi","cell","ri","CommitCalendarCard","WARN","inferAction","ruleId","id","actionColor","actionLabel","InterceptRuleBarCardInner","rules","top","action","InterceptRuleBarCard","Row","Grid","Col","COLOR_PRIMARY","COLOR_FAIL","dailyBuckets","items","windowDays","getTimestamp","accessor","buckets","dayMs","now","it","ts","idx","fetchAgentBoard","window","fetchTuning","fetchViolationsSignal","body","fetchCommitActivity","allCards","resp","lane","bucketByDay","cards","map","c","day","date","shortText","deriveMetrics","tuning","totalOverride","interceptCountOverride","outcome","classified","interceptCountFromCards","_a","_b","interceptCount","zeroSkillNames","failedCards","diffSign","worsened","generateNarrative","week","prev","lines","acceptPct","kbStatus","skillNote","fdiff","concern","buildMarkdownDigest","narrative","summary","p","downloadMarkdown","content","filename","blob","url","HealthHomePage","searchParams","setSearchParams","useSearchParams","navigate","useNavigate","windowParam","baseWindow","longWindow","enableLongQuery","setEnableLongQuery","useEffect","t","boardThis","bThisLoading","useQuery","boardLong","bLongLoading","tuningThis","tThisLoading","tuningLong","tLongLoading","violationsThis","violationsLong","thisInterceptCount","prevInterceptCount","loading","thisCards","longCards","cutoffMs","prevCards","parseTimestamp","prevTuningSummary","l","sub","thisTotalInWindow","prevTotalInWindow","thisMetrics","prevMetrics","trendData","ruleTop","topAnomalies","high","x","setWindow","next","onDownload","md","stamp","sparkTotalRaw","sparkTotal","totalSeen","realTotal","scale","sparkFailed","sparkSuccessRate","succ","sparkKb","avg","totals","totalSum","cardsByDay","c4Trend","withIntercept","obeyed","c8Trend","ok","c4Current","last","c8Current","kbHeatmapRows","name","skillBarRows","commitActivity","calDays","daily","interceptRules","Radio","Button","Skeleton","OUTCOME_COLOR","OUTCOME_LABEL","line","Empty","Tag","Typography"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,r as g}from"./react-vendor-tkvCrao7.js";import{u as z}from"./query-S6X1S7K9.js";import{d as k}from"./react-router-JVUrkhdd.js";import{U as v,d as _,E as j,T as m,Y as E,b as c,R as b,X as K}from"./arco-Bhi3a6Qp.js";import{M as $}from"./MarkdownRenderer-DZmTl-8J.js";import{g as C}from"./ProjectSwitcher-D3lZMFd3.js";import"./vendor-DWgdB1eY.js";import"./syntax-highlighter-BkZfCDsz.js";function F(n){return e.jsx(c,{size:"small",color:n==="cross"?"arcoblue":"gray",children:n})}function P(n){return n==="ok"?e.jsx(c,{color:"green",size:"small",children:"ok"}):n==="stale"?e.jsx(c,{color:"orange",size:"small",children:"stale"}):e.jsx(c,{color:"red",size:"small",children:n})}function R(n){const o=n.lineEnd&&n.lineEnd!==n.lineStart?`${n.lineStart}-${n.lineEnd}`:String(n.lineStart),l=`${n.file}:${o}`;return n.symbol?`${l} (${n.symbol})`:l}function I({open:n,pageName:o,onClose:l}){var u;const{data:s,isLoading:y,isError:x,error:i}=z({queryKey:["ctx-kb-page",o],enabled:!!o&&n,queryFn:()=>C(o),staleTime:3e5}),f=e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap"},children:[e.jsx("span",{style:{fontWeight:600},children:(s==null?void 0:s.title)||o||"-"}),e.jsx("span",{style:{fontFamily:"monospace",fontSize:12,color:"var(--color-text-3)"},children:o??"-"}),s&&F(s.kind),s&&P(s.audit_status)]});return e.jsxs(v,{visible:n,onCancel:l,onOk:l,title:f,width:760,footer:null,unmountOnExit:!0,children:[y&&e.jsx(_,{animation:!0,text:{rows:8}}),x&&(String(i).includes("page_not_found")?e.jsx(j,{description:e.jsxs("span",{children:["未找到 KB 页面 ",e.jsx("code",{style:{fontFamily:"monospace"},children:o}),"。"]})}):e.jsx(j,{description:e.jsxs(m.Text,{type:"error",children:["加载失败: ",String(i)]})})),s&&e.jsxs(e.Fragment,{children:[e.jsx("div",{style:{maxHeight:"calc(100vh - 320px)",overflow:"auto",border:"1px solid var(--color-border-2)",borderRadius:4,padding:12},children:e.jsx($,{content:s.content})}),e.jsx(E,{colon:" :",size:"mini",column:1,title:e.jsx(m.Text,{style:{fontSize:13},children:"元信息"}),data:[{label:"generated_at",value:s.generated_at||"-"},{label:"source_hash",value:s.source_hash||"-"},...s.ai_model?[{label:"ai_model",value:s.ai_model}]:[],{label:`refs (${((u=s.refs)==null?void 0:u.length)??0})`,value:s.refs&&s.refs.length>0?e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:2},children:s.refs.map((w,h)=>e.jsx("span",{style:{fontFamily:"monospace",fontSize:12},children:R(w)},h))}):"-"}],style:{marginTop:16}})]})]})}function W(){const[n]=k(),o=n.get("project")??"",[l,s]=g.useState("all"),[y,x]=g.useState(null),i=z({queryKey:["ctx-kb-list",o],queryFn:async()=>{const t=new URLSearchParams;o&&t.set("project",o);const r=await fetch(`/api/knowledge/list?${t.toString()}`);if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json()},staleTime:6e4}),f=z({queryKey:["ctx-kb-board",o],queryFn:async()=>{const t=new URLSearchParams({window:"7"});o&&t.set("project",o);const r=await fetch(`/api/insights/agent-board?${t.toString()}`);if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json()},staleTime:6e4}),u=g.useMemo(()=>{const t=new Map,r=f.data;if(!r)return t;const p=[...r.lanes.pending??[],...r.lanes.active??[],...r.lanes.abandoned??[],...r.lanes.completed??[]];for(const a of p)for(const d of a.kb_pages??[])t.set(d.name,(t.get(d.name)??0)+(d.cited??0));return t},[f.data]),w=g.useMemo(()=>{var p;const r=(((p=i.data)==null?void 0:p.pages)??[]).map(a=>({...a,cited_7d:u.get(a.name)??0}));return l==="zero"?r.filter(a=>a.cited_7d===0):l==="low"?r.filter(a=>a.cited_7d>0&&a.cited_7d<5):r.sort((a,d)=>d.cited_7d-a.cited_7d||a.name.localeCompare(d.name))},[i.data,u,l]),h=g.useMemo(()=>{var a;const t=((a=i.data)==null?void 0:a.pages)??[];let r=0,p=0;for(const d of t){const S=u.get(d.name)??0;S===0?r++:S<5&&p++}return{all:t.length,zero:r,low:p}},[i.data,u]);if(i.isError)return e.jsx(j,{description:e.jsxs(m.Text,{type:"error",children:["KB 列表加载失败: ",String(i.error)]})});if(i.isLoading||f.isLoading)return e.jsx(_,{animation:!0,text:{rows:8}});if(i.data&&!i.data.kb_built)return e.jsx(j,{description:e.jsxs("span",{children:[e.jsx(m.Text,{type:"secondary",children:"KB 未构建"}),e.jsx("br",{}),e.jsx(m.Text,{style:{fontSize:12},code:!0,children:"cf knowledge build --apply"})]})});const T=[{title:"页面 (page_name)",dataIndex:"name",width:320,render:(t,r)=>e.jsx("a",{onClick:()=>x(t),title:r.title||t,style:{fontFamily:"monospace",fontSize:12,cursor:"pointer"},children:t})},{title:"类型",dataIndex:"kind",width:90,render:t=>e.jsx(c,{size:"small",color:t==="cross"?"arcoblue":"gray",children:t})},{title:"cited (7d)",dataIndex:"cited_7d",width:110,sorter:(t,r)=>t.cited_7d-r.cited_7d,render:t=>t===0?e.jsx(c,{color:"red",size:"small",children:"未触发"}):e.jsx("span",{style:{fontFamily:"monospace",fontWeight:600},children:t})},{title:"状态",dataIndex:"audit_status",width:100,render:t=>t==="ok"?e.jsx(c,{color:"green",size:"small",children:"ok"}):t==="stale"?e.jsx(c,{color:"orange",size:"small",children:"stale"}):e.jsx(c,{color:"red",size:"small",children:t})},{title:"操作",dataIndex:"name",key:"op",width:110,render:t=>e.jsx("a",{onClick:()=>x(t),style:{fontSize:12,cursor:"pointer"},children:"查看页面 →"})}];return e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:12},children:[e.jsxs(m.Text,{style:{fontSize:13,color:"var(--color-text-3)"},children:["按 cited (7d) 排序 · 共 ",h.all," 个页面 · 0 命中 ",h.zero," · 低命中 (1-4) ",h.low]}),e.jsxs(b.Group,{type:"button",size:"small",value:l,onChange:t=>s(t),style:{marginLeft:"auto"},children:[e.jsx(b,{value:"all",children:"全部"}),e.jsx(b,{value:"zero",children:"0 命中"}),e.jsx(b,{value:"low",children:"低命中"})]})]}),e.jsx(K,{rowKey:"name",data:w,columns:T,pagination:{pageSize:20,showTotal:!0},size:"small",border:{wrapper:!0,cell:!0},noDataElement:e.jsx(j,{description:"无匹配 KB 页面"})}),e.jsx(I,{open:!!y,pageName:y,onClose:()=>x(null)})]})}export{W as default};
|
|
2
|
-
//# sourceMappingURL=KbHitRateTable-ByEIWujF.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"KbHitRateTable-ByEIWujF.js","sources":["../../src/components/context/KbContentDrawer.tsx","../../src/components/context/KbHitRateTable.tsx"],"sourcesContent":["/**\n * KbContentDrawer — right-side Arco Drawer that shows a KB page's full\n * markdown content when a row in KbHitRateTable is clicked.\n *\n * Mirrors SkillContentDrawer: right Drawer + MarkdownRenderer scrollable body.\n * Previously KbHitRateTable linked to the raw backend API response\n * (/api/knowledge/page/:name) which left the SPA and rendered unstyled.\n *\n * Layout:\n * - Header: title + name (monospace) + kind tag + audit_status tag.\n * - Body: page markdown in a scrollable container (MarkdownRenderer + Prism).\n * - Footer: optional meta (refs file:line, generated_at, source_hash, ai_model).\n *\n * Data: react-query + getKbPage(name). getKbPage already appends the project\n * param internally (withProject), so no extra project plumbing here.\n */\nimport { useQuery } from '@tanstack/react-query'\nimport Drawer from '@arco-design/web-react/es/Drawer'\nimport Descriptions from '@arco-design/web-react/es/Descriptions'\nimport Empty from '@arco-design/web-react/es/Empty'\nimport Skeleton from '@arco-design/web-react/es/Skeleton'\nimport Typography from '@arco-design/web-react/es/Typography'\nimport Tag from '@arco-design/web-react/es/Tag'\nimport MarkdownRenderer from '../MarkdownRenderer'\nimport { getKbPage, type KbPageDetail } from '../../utils/kbApi'\n\ninterface Props {\n open: boolean\n pageName: string | null\n onClose: () => void\n}\n\nfunction kindTag(kind: 'module' | 'cross') {\n return (\n <Tag size=\"small\" color={kind === 'cross' ? 'arcoblue' : 'gray'}>\n {kind}\n </Tag>\n )\n}\n\nfunction auditTag(status: 'ok' | 'stale' | 'error') {\n if (status === 'ok') return <Tag color=\"green\" size=\"small\">ok</Tag>\n if (status === 'stale') return <Tag color=\"orange\" size=\"small\">stale</Tag>\n return <Tag color=\"red\" size=\"small\">{status}</Tag>\n}\n\nfunction refLabel(r: KbPageDetail['refs'][number]): string {\n const line = r.lineEnd && r.lineEnd !== r.lineStart\n ? `${r.lineStart}-${r.lineEnd}`\n : String(r.lineStart)\n const base = `${r.file}:${line}`\n return r.symbol ? `${base} (${r.symbol})` : base\n}\n\nexport default function KbContentDrawer({ open, pageName, onClose }: Props) {\n const { data, isLoading, isError, error } = useQuery<KbPageDetail>({\n queryKey: ['ctx-kb-page', pageName],\n enabled: !!pageName && open,\n queryFn: () => getKbPage(pageName!),\n staleTime: 5 * 60_000,\n })\n\n const title = (\n <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>\n <span style={{ fontWeight: 600 }}>{data?.title || pageName || '-'}</span>\n <span style={{ fontFamily: 'monospace', fontSize: 12, color: 'var(--color-text-3)' }}>\n {pageName ?? '-'}\n </span>\n {data && kindTag(data.kind)}\n {data && auditTag(data.audit_status)}\n </div>\n )\n\n return (\n <Drawer\n visible={open}\n onCancel={onClose}\n onOk={onClose}\n title={title}\n width={760}\n footer={null}\n unmountOnExit\n >\n {isLoading && <Skeleton animation text={{ rows: 8 }} />}\n {isError && (\n String(error).includes('page_not_found')\n ? (\n <Empty\n description={\n <span>\n 未找到 KB 页面 <code style={{ fontFamily: 'monospace' }}>{pageName}</code>。\n </span>\n }\n />\n )\n : <Empty description={<Typography.Text type=\"error\">加载失败: {String(error)}</Typography.Text>} />\n )}\n\n {data && (\n <>\n <div\n style={{\n maxHeight: 'calc(100vh - 320px)',\n overflow: 'auto',\n border: '1px solid var(--color-border-2)',\n borderRadius: 4,\n padding: 12,\n }}\n >\n <MarkdownRenderer content={data.content} />\n </div>\n\n <Descriptions\n colon=\" :\"\n size=\"mini\"\n column={1}\n title={<Typography.Text style={{ fontSize: 13 }}>元信息</Typography.Text>}\n data={[\n { label: 'generated_at', value: data.generated_at || '-' },\n { label: 'source_hash', value: data.source_hash || '-' },\n ...(data.ai_model ? [{ label: 'ai_model', value: data.ai_model }] : []),\n {\n label: `refs (${data.refs?.length ?? 0})`,\n value: data.refs && data.refs.length > 0\n ? (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {data.refs.map((r, i) => (\n <span key={i} style={{ fontFamily: 'monospace', fontSize: 12 }}>\n {refLabel(r)}\n </span>\n ))}\n </div>\n )\n : '-',\n },\n ]}\n style={{ marginTop: 16 }}\n />\n </>\n )}\n </Drawer>\n )\n}\n","/**\n * KbHitRateTable — Tab 1 of /context (📚 KB 命中).\n *\n * 数据来源:\n * 1. GET /api/knowledge/list — 全量 KB 页面清单(name / title / kind)\n * 2. GET /api/insights/agent-board?window=7 — 每个 task.kb_pages[{name,cited}]\n * 前端聚合 → per-page cited_7d 计数。\n *\n * 不新增后端 endpoint。0 命中页面以红色 badge 凸显,便于诊断 \"声明了但没用上\"。\n */\nimport { useMemo, useState } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSearchParams } from 'react-router-dom'\nimport Table from '@arco-design/web-react/es/Table'\nimport Tag from '@arco-design/web-react/es/Tag'\nimport Radio from '@arco-design/web-react/es/Radio'\nimport Empty from '@arco-design/web-react/es/Empty'\nimport Skeleton from '@arco-design/web-react/es/Skeleton'\nimport Typography from '@arco-design/web-react/es/Typography'\nimport KbContentDrawer from './KbContentDrawer'\n\ninterface KbListRow {\n name: string\n title: string\n kind: 'module' | 'cross'\n audit_status: 'ok' | 'stale' | 'error'\n}\ninterface KbListResponse {\n kb_built: boolean\n pages?: KbListRow[]\n}\ninterface BoardKbEntry { name: string; cited: number }\ninterface BoardTask { kb_pages: BoardKbEntry[] }\ninterface BoardResponse {\n lanes: { pending: BoardTask[]; active: BoardTask[]; abandoned: BoardTask[]; completed: BoardTask[] }\n}\n\ntype FilterKey = 'all' | 'zero' | 'low'\n\nexport default function KbHitRateTable() {\n const [searchParams] = useSearchParams()\n const project = searchParams.get('project') ?? ''\n const [filter, setFilter] = useState<FilterKey>('all')\n const [selectedPage, setSelectedPage] = useState<string | null>(null)\n\n const listQuery = useQuery<KbListResponse>({\n queryKey: ['ctx-kb-list', project],\n queryFn: async () => {\n const p = new URLSearchParams()\n if (project) p.set('project', project)\n const r = await fetch(`/api/knowledge/list?${p.toString()}`)\n if (!r.ok) throw new Error(`HTTP ${r.status}`)\n return r.json()\n },\n staleTime: 60_000,\n })\n\n const boardQuery = useQuery<BoardResponse>({\n queryKey: ['ctx-kb-board', project],\n queryFn: async () => {\n const p = new URLSearchParams({ window: '7' })\n if (project) p.set('project', project)\n const r = await fetch(`/api/insights/agent-board?${p.toString()}`)\n if (!r.ok) throw new Error(`HTTP ${r.status}`)\n return r.json()\n },\n staleTime: 60_000,\n })\n\n // Aggregate per-page cited counts across all lanes' tasks.\n const citedMap = useMemo<Map<string, number>>(() => {\n const m = new Map<string, number>()\n const b = boardQuery.data\n if (!b) return m\n const all: BoardTask[] = [\n ...(b.lanes.pending ?? []),\n ...(b.lanes.active ?? []),\n ...(b.lanes.abandoned ?? []),\n ...(b.lanes.completed ?? []),\n ]\n for (const t of all) {\n for (const kp of t.kb_pages ?? []) {\n m.set(kp.name, (m.get(kp.name) ?? 0) + (kp.cited ?? 0))\n }\n }\n return m\n }, [boardQuery.data])\n\n const rows = useMemo(() => {\n const pages = listQuery.data?.pages ?? []\n const enriched = pages.map((p) => ({\n ...p,\n cited_7d: citedMap.get(p.name) ?? 0,\n }))\n if (filter === 'zero') return enriched.filter((r) => r.cited_7d === 0)\n if (filter === 'low') return enriched.filter((r) => r.cited_7d > 0 && r.cited_7d < 5)\n // sort: cited desc then name\n return enriched.sort((a, b) => (b.cited_7d - a.cited_7d) || a.name.localeCompare(b.name))\n }, [listQuery.data, citedMap, filter])\n\n const counts = useMemo(() => {\n const pages = listQuery.data?.pages ?? []\n let zero = 0, low = 0\n for (const p of pages) {\n const c = citedMap.get(p.name) ?? 0\n if (c === 0) zero++\n else if (c < 5) low++\n }\n return { all: pages.length, zero, low }\n }, [listQuery.data, citedMap])\n\n if (listQuery.isError) {\n return <Empty description={<Typography.Text type=\"error\">KB 列表加载失败: {String(listQuery.error)}</Typography.Text>} />\n }\n if (listQuery.isLoading || boardQuery.isLoading) {\n return <Skeleton animation text={{ rows: 8 }} />\n }\n if (listQuery.data && !listQuery.data.kb_built) {\n return (\n <Empty description={\n <span>\n <Typography.Text type=\"secondary\">KB 未构建</Typography.Text>\n <br />\n <Typography.Text style={{ fontSize: 12 }} code>cf knowledge build --apply</Typography.Text>\n </span>\n } />\n )\n }\n\n const columns = [\n {\n title: '页面 (page_name)',\n dataIndex: 'name',\n width: 320,\n render: (name: string, r: { kind: string; title: string }) => (\n <a\n onClick={() => setSelectedPage(name)}\n title={r.title || name}\n style={{ fontFamily: 'monospace', fontSize: 12, cursor: 'pointer' }}\n >\n {name}\n </a>\n ),\n },\n {\n title: '类型',\n dataIndex: 'kind',\n width: 90,\n render: (k: string) => <Tag size=\"small\" color={k === 'cross' ? 'arcoblue' : 'gray'}>{k}</Tag>,\n },\n {\n title: 'cited (7d)',\n dataIndex: 'cited_7d',\n width: 110,\n sorter: (a: { cited_7d: number }, b: { cited_7d: number }) => a.cited_7d - b.cited_7d,\n render: (n: number) => (\n n === 0\n ? <Tag color=\"red\" size=\"small\">未触发</Tag>\n : <span style={{ fontFamily: 'monospace', fontWeight: 600 }}>{n}</span>\n ),\n },\n {\n title: '状态',\n dataIndex: 'audit_status',\n width: 100,\n render: (s: string) => (\n s === 'ok' ? <Tag color=\"green\" size=\"small\">ok</Tag> :\n s === 'stale' ? <Tag color=\"orange\" size=\"small\">stale</Tag> :\n <Tag color=\"red\" size=\"small\">{s}</Tag>\n ),\n },\n {\n title: '操作',\n dataIndex: 'name',\n key: 'op',\n width: 110,\n render: (name: string) => (\n <a\n onClick={() => setSelectedPage(name)}\n style={{ fontSize: 12, cursor: 'pointer' }}\n >\n 查看页面 →\n </a>\n ),\n },\n ]\n\n return (\n <div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>\n <Typography.Text style={{ fontSize: 13, color: 'var(--color-text-3)' }}>\n 按 cited (7d) 排序 · 共 {counts.all} 个页面 · 0 命中 {counts.zero} · 低命中 (1-4) {counts.low}\n </Typography.Text>\n <Radio.Group\n type=\"button\"\n size=\"small\"\n value={filter}\n onChange={(v: FilterKey) => setFilter(v)}\n style={{ marginLeft: 'auto' }}\n >\n <Radio value=\"all\">全部</Radio>\n <Radio value=\"zero\">0 命中</Radio>\n <Radio value=\"low\">低命中</Radio>\n </Radio.Group>\n </div>\n <Table\n rowKey=\"name\"\n data={rows}\n columns={columns}\n pagination={{ pageSize: 20, showTotal: true }}\n size=\"small\"\n border={{ wrapper: true, cell: true }}\n noDataElement={<Empty description=\"无匹配 KB 页面\" />}\n />\n <KbContentDrawer\n open={!!selectedPage}\n pageName={selectedPage}\n onClose={() => setSelectedPage(null)}\n />\n </div>\n )\n}\n"],"names":["kindTag","kind","jsx","Tag","auditTag","status","refLabel","r","line","base","KbContentDrawer","open","pageName","onClose","data","isLoading","isError","error","useQuery","getKbPage","title","jsxs","Drawer","Skeleton","Empty","Typography","Fragment","MarkdownRenderer","Descriptions","_a","i","KbHitRateTable","searchParams","useSearchParams","project","filter","setFilter","useState","selectedPage","setSelectedPage","listQuery","p","boardQuery","citedMap","useMemo","m","b","all","t","kp","rows","enriched","counts","pages","zero","low","c","columns","name","k","a","n","s","Radio","v","Table"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as t}from"./react-vendor-tkvCrao7.js";import{a,L as i}from"./react-router-JVUrkhdd.js";import{T as n,t as r}from"./arco-Bhi3a6Qp.js";import"./vendor-DWgdB1eY.js";function c(){const e=a(),o=`${e.pathname}${e.search}`;return t.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"64px 16px",gap:12},"data-testid":"not-found-page",children:[t.jsx(n.Title,{heading:2,style:{marginBottom:0},children:"404"}),t.jsx(n.Text,{type:"secondary",style:{fontSize:14},children:"找不到这个页面"}),t.jsx("code",{style:{fontSize:12,padding:"4px 8px",background:"var(--color-fill-2)",borderRadius:4,maxWidth:600,overflow:"auto",whiteSpace:"nowrap"},title:o,children:o}),t.jsx(i,{to:"/health",replace:!0,children:t.jsx(r,{type:"primary",style:{marginTop:12},children:"返回 Health 主页"})})]})}export{c as default};
|
|
2
|
-
//# sourceMappingURL=NotFound-BQPh0vaF.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as o}from"./react-vendor-tkvCrao7.js";import{u as x,b as w}from"./query-S6X1S7K9.js";import{d as b}from"./react-router-JVUrkhdd.js";import{ae as l,af as j,ag as S}from"./arco-Bhi3a6Qp.js";function k(t,n){if(!n)return t;const c=t.includes("?")?"&":"?";return`${t}${c}project=${encodeURIComponent(n)}`}function P(){return["kb-projects"]}const v=[["kb-list"],["kb-page"],["kb-audit"],["kb-stats"]];function _(){return typeof window>"u"?"":new URLSearchParams(window.location.search).get("project")??""}function C(t){return k(t,_())}async function I(){const t=await fetch("/api/knowledge/projects");if(!t.ok)throw new Error(`projects failed: ${t.status}`);return t.json()}async function q(t){const n=await fetch(C(`/api/knowledge/page/${encodeURIComponent(t)}`));if(n.status===404)throw new Error("page_not_found");if(!n.ok)throw new Error(`page failed: ${n.status}`);return n.json()}const z="claude-forge:kb:last-project";function E(t){if(!(typeof window>"u"))try{window.localStorage.setItem(z,t)}catch{}}const K=j.Option;function Q({showAll:t=!1}){const{data:n,isLoading:c}=x({queryKey:P(),queryFn:I,staleTime:3e4}),[f,m]=b(),y=w(),u=f.get("project")??"",p=(n==null?void 0:n.current)??"",r=u||p,i=((n==null?void 0:n.projects)??[]).filter(e=>t||e.kb_built),g=e=>{const s=new URLSearchParams(f);e&&e!==p?s.set("project",e):s.delete("project"),s.delete("page"),m(s),e&&E(e);for(const h of v)y.invalidateQueries({queryKey:h})};if(c)return o.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,fontSize:13,color:"var(--color-text-3)"},children:[o.jsx(l,{style:{fontSize:14}}),o.jsx("span",{children:"Loading projects..."})]});if(i.length===0)return o.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,fontSize:13,color:"var(--color-text-3)"},children:[o.jsx(l,{style:{fontSize:14}}),o.jsx("span",{style:{fontFamily:"monospace"},children:r||"no project"}),o.jsx("span",{style:{fontSize:11,color:"var(--color-text-4)"},children:"(KB not built)"})]});const a=i.find(e=>e.path===r),d=!!(a!=null&&a.kb_built);return o.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},title:"切换知识库项目(每个项目独立缓存,切换后会自动刷新页面列表)",children:[o.jsx(l,{style:{fontSize:14,color:"var(--color-text-3)"}}),o.jsx(j,{value:r,onChange:e=>g(e),size:"small",style:{width:240,fontFamily:"monospace"},children:i.map(e=>o.jsxs(K,{value:e.path,children:[e.name," ",e.kb_built?`(${e.page_count})`:"(empty)",e.is_current&&!u?" • current":""]},e.path))}),r&&o.jsx("span",{style:{fontSize:11,display:"inline-flex",alignItems:"center",gap:2,color:d?"var(--color-success-6, #52c41a)":"var(--color-text-4)"},children:d?o.jsxs(o.Fragment,{children:[o.jsx(S,{style:{fontSize:11}}),"built"]}):"not built"})]})}export{Q as P,q as g};
|
|
2
|
-
//# sourceMappingURL=ProjectSwitcher-D3lZMFd3.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectSwitcher-D3lZMFd3.js","sources":["../../src/utils/kbProject.ts","../../src/utils/kbApi.ts","../../src/utils/kbDefaultProject.ts","../../src/components/ProjectSwitcher.tsx"],"sourcesContent":["/**\n * Pure helpers for KB multi-project URL/cache-key construction.\n *\n * Extracted from kbApi.ts so they are unit-testable in plain Node without a\n * DOM. The web bundle imports both this module and its DOM-bound wrappers\n * (readProjectFromUrl) from kbApi.\n *\n * Hotfix v9.5.x: query keys MUST include the project string so that\n * react-query treats different projects as separate cache entries —\n * otherwise switching projects shows stale content from the previous one.\n */\n\n/**\n * Append `?project=<path>` (or `&project=...` if a query string is already\n * present) to `url`. When `project` is empty the url is returned unchanged.\n *\n * Behavior is intentionally conservative: we never mutate or de-dupe an\n * existing `project=` parameter; callers are expected to compose the URL\n * fresh per request.\n */\nexport function appendProject(url: string, project: string): string {\n if (!project) return url;\n const sep = url.includes('?') ? '&' : '?';\n return `${url}${sep}project=${encodeURIComponent(project)}`;\n}\n\n/**\n * Build a react-query key for a KB endpoint. The project string is the LAST\n * element so the prefix `[domain]` (e.g. `['kb-list']`) still works for\n * partial-match invalidation of every project at once.\n */\nexport function kbListKey(project: string): readonly unknown[] {\n return ['kb-list', project] as const;\n}\n\nexport function kbPageKey(name: string | null, project: string): readonly unknown[] {\n return ['kb-page', name, project] as const;\n}\n\nexport function kbAuditKey(project: string): readonly unknown[] {\n return ['kb-audit', project] as const;\n}\n\nexport function kbStatsKey(project: string): readonly unknown[] {\n return ['kb-stats', project] as const;\n}\n\nexport function kbProjectsKey(): readonly unknown[] {\n return ['kb-projects'] as const;\n}\n\n/**\n * Prefix used by `queryClient.invalidateQueries({ queryKey: KB_QUERY_PREFIX })`\n * to invalidate every KB-scoped query in one shot.\n *\n * NOTE: react-query does prefix matching, so `['kb-']` does NOT match\n * `['kb-list']`. Each domain (list, page, audit, stats) needs an explicit\n * invalidate call OR all keys must share a common prefix array element.\n *\n * We standardize on the convention \"every KB query starts with a string\n * beginning with `kb-`\" and provide an explicit list of prefixes for\n * invalidation.\n */\nexport const KB_QUERY_PREFIXES: ReadonlyArray<readonly unknown[]> = [\n ['kb-list'],\n ['kb-page'],\n ['kb-audit'],\n ['kb-stats'],\n];\n","/**\n * KB-related fetch helpers for the dashboard SPA.\n *\n * All read endpoints use plain `fetch` (anonymous). Write endpoints use\n * `authFetch` so the daemon bearer token is attached.\n *\n * v9.5: All endpoints support ?project=<path> query param. Read it from\n * window.location URL and append via `withProject()` helper.\n *\n * Hotfix: pure URL/key helpers live in kbProject.ts (Node-testable).\n */\n\nimport { authFetch } from './auth'\nimport { appendProject } from './kbProject'\n\n// ─── v9.5: project parameter ────────────────────────────────────────────────\n\n/** Read current project selection from URL (?project=...). */\nfunction readProjectFromUrl(): string {\n if (typeof window === 'undefined') return ''\n return new URLSearchParams(window.location.search).get('project') ?? ''\n}\n\n/** Append ?project=<path> from URL state to any KB endpoint URL. */\nfunction withProject(url: string): string {\n return appendProject(url, readProjectFromUrl())\n}\n\nexport interface KbProject {\n path: string\n name: string\n kb_built: boolean\n last_event_at: string | null\n page_count: number\n is_current: boolean\n}\n\nexport interface KbProjectsResponse {\n current: string\n projects: KbProject[]\n}\n\nexport interface KbPageSummary {\n name: string\n title: string\n kind: 'module' | 'cross'\n refs_count: number\n audit_status: 'ok' | 'stale' | 'error'\n source_hash: string\n generated_at: string\n ai_model?: string\n}\n\nexport interface KbListResponse {\n kb_built: boolean\n hint?: string\n pages?: KbPageSummary[]\n stats?: {\n total: number\n by_kind: { module: number; cross: number }\n by_audit_status: { ok: number; stale: number; error: number }\n }\n generated_at?: string\n source_hash?: string\n}\n\nexport interface KbPageDetail {\n name: string\n title: string\n content: string\n kind: 'module' | 'cross'\n refs: Array<{ file: string; lineStart: number; lineEnd?: number; symbol?: string }>\n generated_at: string\n source_hash: string\n audit_status: 'ok' | 'stale' | 'error'\n ai_model?: string\n}\n\nexport interface KbQueryHit {\n page: { name: string; title: string; content: string; refs: any[] }\n score: number\n matched_keywords: string[]\n matched_in: string[]\n}\n\nexport interface KbQueryResponse {\n query: string\n pages: KbQueryHit[]\n repo_map_hits?: Array<{\n file: string\n line: number\n name: string\n kind: string\n signature: string\n }>\n strategy: 'keyword' | 'rerank'\n total_pages_scanned: number\n rerank_failed?: boolean\n}\n\nexport interface RepoMapResponse {\n total: number\n truncated: boolean\n symbols: Array<{\n file: string\n line: number\n kind: string\n name: string\n signature: string\n exports: boolean\n docComment?: string\n }>\n repo_map_meta: {\n version: string\n generated_at: string\n source_hash: string\n symbol_count: number\n file_count: number\n }\n}\n\nexport interface AuditResponse {\n kb_built: boolean\n hint?: string\n summary?: { total: number; ok: number; stale: number; error: number }\n per_page?: Array<{\n name: string\n kind: 'module' | 'cross'\n audit_status: 'ok' | 'stale' | 'error'\n refs_count: number\n source_hash: string\n ai_model: string\n }>\n log?: string\n log_truncated?: boolean\n generated_at?: string\n}\n\nexport interface KbStatsResponse {\n kb_built: boolean\n kb_size_bytes: number\n page_count: number\n last_build_at: string | null\n last_build_stats: {\n built: number\n cached: number\n total: number\n crossModuleBuilt?: number\n crossModuleCached?: number\n } | null\n last_build_status?: 'running' | 'done' | 'error' | null\n total_queries: number\n rerank_hit_rate: number | null\n rerank_failure_rate?: number | null\n total_ai_calls?: number\n total_tokens?: { input: number; output: number }\n}\n\nexport interface StartBuildBody {\n apply?: boolean\n force?: boolean\n skipCrossModule?: boolean\n modules?: string[] | null\n}\n\nexport async function listKbProjects(): Promise<KbProjectsResponse> {\n const r = await fetch('/api/knowledge/projects')\n if (!r.ok) throw new Error(`projects failed: ${r.status}`)\n return r.json()\n}\n\nexport async function listKb(kind?: 'module' | 'cross'): Promise<KbListResponse> {\n const qs = kind ? `?kind=${encodeURIComponent(kind)}` : ''\n const r = await fetch(withProject(`/api/knowledge/list${qs}`))\n if (!r.ok) throw new Error(`list failed: ${r.status}`)\n return r.json()\n}\n\nexport async function getKbPage(name: string): Promise<KbPageDetail> {\n const r = await fetch(withProject(`/api/knowledge/page/${encodeURIComponent(name)}`))\n if (r.status === 404) throw new Error('page_not_found')\n if (!r.ok) throw new Error(`page failed: ${r.status}`)\n return r.json()\n}\n\nexport async function queryKb(opts: {\n q: string\n strategy?: 'keyword' | 'rerank'\n pool?: number\n max?: number\n includeContent?: boolean\n}): Promise<KbQueryResponse> {\n const params = new URLSearchParams()\n params.set('q', opts.q)\n if (opts.strategy) params.set('strategy', opts.strategy)\n if (opts.pool != null) params.set('pool', String(opts.pool))\n if (opts.max != null) params.set('max', String(opts.max))\n if (opts.includeContent) params.set('include_content', 'true')\n const r = await fetch(withProject(`/api/knowledge/query?${params.toString()}`))\n if (!r.ok) throw new Error(`query failed: ${r.status}`)\n return r.json()\n}\n\nexport async function queryRepoMap(opts: {\n symbol?: string\n contains?: string\n module?: string\n kind?: string\n max?: number\n}): Promise<RepoMapResponse> {\n const params = new URLSearchParams()\n if (opts.symbol) params.set('symbol', opts.symbol)\n if (opts.contains) params.set('contains', opts.contains)\n if (opts.module) params.set('module', opts.module)\n if (opts.kind) params.set('kind', opts.kind)\n if (opts.max != null) params.set('max', String(opts.max))\n const r = await fetch(withProject(`/api/knowledge/repo-map?${params.toString()}`))\n if (!r.ok) throw new Error(`repo-map failed: ${r.status}`)\n return r.json()\n}\n\nexport async function getAudit(): Promise<AuditResponse> {\n const r = await fetch(withProject('/api/knowledge/audit'))\n if (!r.ok) throw new Error(`audit failed: ${r.status}`)\n return r.json()\n}\n\nexport async function runAuditFix(\n pageName?: string,\n): Promise<{ ok: boolean; summary: any; outcome: any; scope: string | null }> {\n const base = '/api/knowledge/audit/fix'\n const url = pageName\n ? withProject(`${base}?page=${encodeURIComponent(pageName)}`)\n : withProject(base)\n const r = await authFetch(url, { method: 'POST' })\n if (!r.ok) throw new Error(`audit fix failed: ${r.status}`)\n return r.json()\n}\n\nexport async function startBuild(body: StartBuildBody): Promise<{ buildId: string; streamUrl: string }> {\n const r = await authFetch(withProject('/api/knowledge/build'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (r.status === 409) throw new Error('build_in_progress')\n if (!r.ok) throw new Error(`build failed: ${r.status}`)\n return r.json()\n}\n\nexport async function getKbStats(): Promise<KbStatsResponse> {\n const r = await fetch(withProject('/api/knowledge/stats'))\n if (!r.ok) throw new Error(`stats failed: ${r.status}`)\n return r.json()\n}\n\nexport interface KbUsageStatsResponse {\n total_calls: number\n by_tool: Record<string, number>\n recent_sessions: string[]\n last_used_at: string | null\n}\n\n/**\n * Fetch usage stats for the current project (default) or an explicit project.\n *\n * When `projectOverride` is provided, that project is used regardless of the\n * URL `?project=` value. Used by the multi-project ranking panel.\n */\nexport async function getKbUsageStats(\n days?: number,\n projectOverride?: string,\n): Promise<KbUsageStatsResponse> {\n const qs = days ? `?days=${days}` : ''\n const baseUrl = `/api/knowledge/usage-stats${qs}`\n const url =\n projectOverride !== undefined\n ? appendProject(baseUrl, projectOverride)\n : withProject(baseUrl)\n const r = await fetch(url)\n if (!r.ok) throw new Error(`usage-stats failed: ${r.status}`)\n return r.json()\n}\n\nexport interface KbBuildReportResponse {\n exists: boolean\n content?: string\n generated_at?: string\n size_bytes?: number\n}\n\n/** Fetch the global build report markdown (`~/.claude-forge/knowledge-build-report.md`). */\nexport async function getKbBuildReport(): Promise<KbBuildReportResponse> {\n const r = await fetch('/api/knowledge/build-report')\n if (!r.ok) throw new Error(`build-report failed: ${r.status}`)\n return r.json()\n}\n\n/** Get current project param from URL (for EventSource etc). */\nexport function getCurrentProject(): string {\n return readProjectFromUrl()\n}\n\n// ─── Build SSE helpers ──────────────────────────────────────────────────────\n\nexport type BuildEventType =\n | 'connected'\n | 'start'\n | 'repo-map'\n | 'module'\n | 'cross-module'\n | 'done'\n | 'error'\n\nexport interface BuildEvent {\n type: BuildEventType\n name?: string\n status?: 'compiling' | 'ok' | 'cached' | 'issues' | 'failed' | string\n modules?: string[]\n symbolCount?: number\n fileCount?: number\n sourceHash?: string\n refsValid?: number\n refsTotal?: number\n durationMs?: number\n cached?: boolean\n force?: boolean\n apply?: boolean\n skipCrossModule?: boolean\n stats?: {\n built: number\n cached: number\n total: number\n crossModuleBuilt?: number\n crossModuleCached?: number\n }\n reportPath?: string\n appliedTo?: string\n message?: string\n step?: string\n}\n\n/** Build the `/api/knowledge/build/:id/stream` URL with project param. */\nexport function buildStreamUrl(buildId: string): string {\n return withProject(`/api/knowledge/build/${encodeURIComponent(buildId)}/stream`)\n}\n\nconst BUILD_EVENT_TYPES: BuildEventType[] = [\n 'connected',\n 'start',\n 'repo-map',\n 'module',\n 'cross-module',\n 'done',\n 'error',\n]\n\n/**\n * Subscribe to a build's SSE stream. Returns a cleanup function that closes\n * the EventSource. The `onEvent` callback receives every parsed event with\n * its `type` populated. `onTerminal` is called once when `done` or `error`\n * arrives (the EventSource is auto-closed in that case).\n */\nexport function subscribeBuild(\n buildId: string,\n handlers: {\n onEvent: (ev: BuildEvent) => void\n onTerminal?: (ev: BuildEvent) => void\n },\n): () => void {\n const es = new EventSource(buildStreamUrl(buildId))\n const dispatch = (type: BuildEventType) => (msg: MessageEvent) => {\n let data: any = {}\n try {\n data = JSON.parse(msg.data || '{}')\n } catch {\n // ignore malformed payload\n }\n const ev: BuildEvent = { ...data, type }\n handlers.onEvent(ev)\n if (type === 'done' || type === 'error') {\n handlers.onTerminal?.(ev)\n es.close()\n }\n }\n for (const t of BUILD_EVENT_TYPES) {\n es.addEventListener(t, dispatch(t))\n }\n return () => es.close()\n}\n\n/**\n * Map raw error messages from `startBuild()` and SSE error payloads to\n * friendly Chinese strings shown in the UI.\n */\nexport function friendlyBuildError(message: string | undefined): string {\n if (!message) return '构建失败,未知错误'\n const m = message.trim()\n if (m === 'build_in_progress') return '已有构建在进行中,请稍后再试'\n if (m === 'unauthorized' || m.startsWith('build failed: 401')) {\n return '未授权:请检查 daemon token 配置'\n }\n if (m.startsWith('build failed: 403')) return '无权限触发构建(403)'\n if (m.startsWith('build failed: 404')) return '构建端点不存在(404),请确认 daemon 版本'\n if (m.startsWith('build failed: 500')) return '后端构建出错(500),请查看 daemon 日志'\n if (m.startsWith('build failed: 503')) return '后端暂时不可用,请稍后再试'\n if (m.toLowerCase().includes('failed to fetch')) return '无法连接 daemon,请确认服务已启动'\n if (m.startsWith('build failed: ')) return `启动构建失败:${m.replace('build failed: ', '')}`\n return m\n}\n","/**\n * Pure picker + DOM wrappers for resolving the default KB project at /knowledge\n * landing time (URL has no ?project=).\n *\n * Priority for picking:\n * (a) localStorage last selection — must still be in projects list and built\n * (b) project flagged `is_current` by daemon and built\n * (c) most recently active by last_event_at (built only)\n * (d) first built project alphabetically (by path) as final fallback\n *\n * pickDefaultProject is pure (no DOM), so it is exhaustively unit-testable\n * without jsdom. readLastProject/writeLastProject wrap localStorage with\n * try/catch to degrade gracefully when storage is disabled.\n */\n\nimport type { KbProject } from './kbApi'\n\nexport const LAST_PROJECT_LS_KEY = 'claude-forge:kb:last-project'\n\nexport interface PickInput {\n projects: KbProject[]\n lastSaved: string | null\n}\n\nexport function pickDefaultProject(input: PickInput): string | null {\n const built = input.projects.filter((p) => p.kb_built)\n if (built.length === 0) return null\n\n if (input.lastSaved) {\n const match = built.find((p) => p.path === input.lastSaved)\n if (match) return match.path\n }\n\n const current = built.find((p) => p.is_current)\n if (current) return current.path\n\n const byRecency = [...built].sort((a, b) => {\n const ta = a.last_event_at ? Date.parse(a.last_event_at) : 0\n const tb = b.last_event_at ? Date.parse(b.last_event_at) : 0\n if (tb !== ta) return tb - ta\n // Stable tie-break: alphabetical by path so test outcomes are deterministic\n // even when all built projects have null last_event_at.\n return a.path.localeCompare(b.path)\n })\n return byRecency[0]?.path ?? null\n}\n\nexport function readLastProject(): string | null {\n if (typeof window === 'undefined') return null\n try {\n return window.localStorage.getItem(LAST_PROJECT_LS_KEY)\n } catch {\n return null\n }\n}\n\nexport function writeLastProject(path: string): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(LAST_PROJECT_LS_KEY, path)\n } catch {\n /* ignore quota / disabled storage */\n }\n}\n","/**\n * ProjectSwitcher (v9.5 → 2026-05-26 Arco-ified)\n *\n * Top-of-page dropdown for switching the KB project context.\n * Persists selection in URL (?project=<path>), invalidates all KB\n * queries on change so every tab refetches with the new project.\n *\n * Hotfix: previously invalidated ['kb'] which never matched any actual\n * query key ('kb-list', 'kb-page', 'kb-audit', 'kb-stats'). Now we\n * invalidate each known prefix from KB_QUERY_PREFIXES, AND we strip\n * page-detail-specific URL params (?page=) so the new project doesn't\n * try to load a page name that only exists in the previous project.\n *\n * 2026-05-26 polish: native <select> → Arco <Select> + lucide → Arco icons.\n * lucide-react stays in package.json (4 other components still use it).\n */\n\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useSearchParams } from 'react-router-dom'\nimport Select from '@arco-design/web-react/es/Select'\nimport { IconStorage, IconCheck } from '@arco-design/web-react/icon'\nimport { listKbProjects, type KbProject } from '../utils/kbApi'\nimport { KB_QUERY_PREFIXES, kbProjectsKey } from '../utils/kbProject'\nimport { writeLastProject } from '../utils/kbDefaultProject'\n\nconst Option = Select.Option\n\ninterface ProjectSwitcherProps {\n /** Show projects without .forge-knowledge/ built (default: false). */\n showAll?: boolean\n}\n\nexport function ProjectSwitcher({ showAll = false }: ProjectSwitcherProps) {\n const { data, isLoading } = useQuery({\n queryKey: kbProjectsKey(),\n queryFn: listKbProjects,\n staleTime: 30_000,\n })\n const [searchParams, setSearchParams] = useSearchParams()\n const queryClient = useQueryClient()\n\n const currentParam = searchParams.get('project') ?? ''\n const currentDaemon = data?.current ?? ''\n const effectiveCurrent = currentParam || currentDaemon\n\n const allProjects: KbProject[] = data?.projects ?? []\n const projects = allProjects.filter((p) => showAll || p.kb_built)\n\n const onChange = (newPath: string) => {\n const next = new URLSearchParams(searchParams)\n if (newPath && newPath !== currentDaemon) {\n next.set('project', newPath)\n } else {\n next.delete('project')\n }\n // Hotfix: clear ?page= so KbPages doesn't try to fetch a page that\n // only existed in the previous project (the cause of \"page_not_found\"\n // toast on project switch).\n next.delete('page')\n setSearchParams(next)\n if (newPath) writeLastProject(newPath)\n for (const prefix of KB_QUERY_PREFIXES) {\n queryClient.invalidateQueries({ queryKey: prefix })\n }\n }\n\n if (isLoading) {\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, color: 'var(--color-text-3)' }}>\n <IconStorage style={{ fontSize: 14 }} />\n <span>Loading projects...</span>\n </div>\n )\n }\n\n if (projects.length === 0) {\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, color: 'var(--color-text-3)' }}>\n <IconStorage style={{ fontSize: 14 }} />\n <span style={{ fontFamily: 'monospace' }}>{effectiveCurrent || 'no project'}</span>\n <span style={{ fontSize: 11, color: 'var(--color-text-4)' }}>(KB not built)</span>\n </div>\n )\n }\n\n const currentProject = projects.find((p) => p.path === effectiveCurrent)\n const isBuilt = !!currentProject?.kb_built\n\n return (\n <div\n style={{ display: 'flex', alignItems: 'center', gap: 8 }}\n title=\"切换知识库项目(每个项目独立缓存,切换后会自动刷新页面列表)\"\n >\n <IconStorage style={{ fontSize: 14, color: 'var(--color-text-3)' }} />\n <Select\n value={effectiveCurrent}\n onChange={(v: string) => onChange(v)}\n size=\"small\"\n style={{ width: 240, fontFamily: 'monospace' }}\n >\n {projects.map((p) => (\n <Option key={p.path} value={p.path}>\n {p.name} {p.kb_built ? `(${p.page_count})` : '(empty)'}\n {p.is_current && !currentParam ? ' • current' : ''}\n </Option>\n ))}\n </Select>\n {effectiveCurrent && (\n <span\n style={{\n fontSize: 11,\n display: 'inline-flex',\n alignItems: 'center',\n gap: 2,\n color: isBuilt ? 'var(--color-success-6, #52c41a)' : 'var(--color-text-4)',\n }}\n >\n {isBuilt ? (\n <>\n <IconCheck style={{ fontSize: 11 }} />\n built\n </>\n ) : (\n 'not built'\n )}\n </span>\n )}\n </div>\n )\n}\n"],"names":["appendProject","url","project","sep","kbProjectsKey","KB_QUERY_PREFIXES","readProjectFromUrl","withProject","listKbProjects","r","getKbPage","name","LAST_PROJECT_LS_KEY","writeLastProject","path","Option","Select","ProjectSwitcher","showAll","data","isLoading","useQuery","searchParams","setSearchParams","useSearchParams","queryClient","useQueryClient","currentParam","currentDaemon","effectiveCurrent","projects","p","onChange","newPath","next","prefix","jsxs","jsx","IconStorage","currentProject","isBuilt","v","Fragment","IconCheck"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e}from"./react-vendor-tkvCrao7.js";import{c as i,T as a,R as c,k as h,$ as p}from"./arco-Bhi3a6Qp.js";import{b as x}from"./index-BIYnq1Dx.js";import{u as g}from"./useTabsParam-k8qte_0C.js";import"./vendor-DWgdB1eY.js";import"./react-router-JVUrkhdd.js";import"./query-S6X1S7K9.js";import"./lucide-CnlPQoG8.js";const n=i.TabPane,m=c.Group,u=["theme","about"];function I(){const{tab:t,setTab:r}=g(u,"theme","section"),[o,l]=x(),d=s=>r(s);return e.jsx("div",{"data-testid":"settings-page","data-section":t,style:{padding:24},children:e.jsxs(i,{activeTab:t,onChange:d,type:"line",tabPosition:"left",children:[e.jsx(n,{title:e.jsxs("span",{children:[e.jsx(h,{})," 主题"]}),children:e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12,padding:16},children:[e.jsx(a.Title,{heading:6,children:"外观"}),e.jsx(m,{type:"button",value:o,onChange:s=>l(s),options:[{value:"light",label:"浅色"},{value:"dark",label:"深色"}]}),e.jsxs(a.Paragraph,{type:"secondary",style:{fontSize:12},children:["主题偏好保存在 localStorage ",e.jsx("code",{children:"claude-forge:theme"}),",刷新页面后保留。"]})]})},"theme"),e.jsx(n,{title:e.jsxs("span",{children:[e.jsx(p,{})," 关于"]}),children:e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8,padding:16},children:[e.jsx(a.Title,{heading:6,children:"claude-forge"}),e.jsxs(a.Paragraph,{style:{fontSize:13},children:["版本: ",e.jsxs("code",{children:["v","9.2.0"]})]}),e.jsxs(a.Paragraph,{style:{fontSize:13},children:["仓库: ",e.jsx("a",{href:"https://github.com/winspan/claude-forge",target:"_blank",rel:"noreferrer",children:"winspan/claude-forge"})]}),e.jsx(a.Paragraph,{type:"secondary",style:{fontSize:12},children:"v4 IA 重写(2026-05)— 6 问句路由 + task 为核心实体。"}),e.jsxs(a.Paragraph,{type:"secondary",style:{fontSize:12},children:["AI 配置已下线(spec ",e.jsx("code",{children:"0902"})," / sub-spec D R-C)。所有 AI 流程 (CLAUDE.md 生成、Knowledge build、Patch preview)改走本地",e.jsx("code",{children:"claude"})," CLI 子进程,daemon 自身不再持 API key。"]})]})},"about")]})})}export{I as default};
|
|
2
|
-
//# sourceMappingURL=SettingsPage-oLJBNzQj.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage-oLJBNzQj.js","sources":["../../src/pages/SettingsPage.tsx"],"sourcesContent":["/**\n * /settings — 设置 (v4 IA Step 5)\n *\n * sub-spec D R-C (2026-05-29, spec 0902 / decision f5a5b030): 删除原 `ai` tab\n * (`legacy-pages/AIConfig.tsx`, 247 LOC). AI Provider 配置在子 spec A/B/C 已\n * 迁出到 `claude` CLI 子进程,daemon 自身不再持 API key,Web UI 上的配置面\n * 失去意义。剩余 2 tab:\n * - theme (默认) → 浅 / 深 toggle,复用 lib/theme.ts useTheme hook\n * - about → package.json version + repo link\n *\n * URL: ?section=theme|about (默认 theme → 不写 query)\n * 老 ?section=ai 链接被 falsy 检查兜底回到默认 (theme),并由 LegacyRedirect\n * 在 App.tsx 收编 `/ai-config` → `/settings`。\n */\nimport Tabs from '@arco-design/web-react/es/Tabs'\nimport Typography from '@arco-design/web-react/es/Typography'\nimport Radio from '@arco-design/web-react/es/Radio'\nimport {\n IconSun,\n IconInfoCircle,\n} from '@arco-design/web-react/icon'\nimport { useTheme } from '../lib/theme'\nimport { useTabsParam } from '../hooks/useTabsParam'\n\nconst TabPane = Tabs.TabPane\nconst RadioGroup = Radio.Group\n\ntype SettingsSection = 'theme' | 'about'\nconst KNOWN: readonly SettingsSection[] = ['theme', 'about'] as const\n\nexport default function SettingsPage() {\n const { tab: section, setTab } = useTabsParam<SettingsSection>(KNOWN, 'theme', 'section')\n const [theme, setTheme] = useTheme()\n\n const handleChange = (key: string) => setTab(key as SettingsSection)\n\n return (\n <div data-testid=\"settings-page\" data-section={section} style={{ padding: 24 }}>\n <Tabs activeTab={section} onChange={handleChange} type=\"line\" tabPosition=\"left\">\n <TabPane key=\"theme\" title={<span><IconSun /> 主题</span>}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12, padding: 16 }}>\n <Typography.Title heading={6}>外观</Typography.Title>\n <RadioGroup\n type=\"button\"\n value={theme}\n onChange={(v: 'light' | 'dark') => setTheme(v)}\n options={[\n { value: 'light', label: '浅色' },\n { value: 'dark', label: '深色' },\n ]}\n />\n <Typography.Paragraph type=\"secondary\" style={{ fontSize: 12 }}>\n 主题偏好保存在 localStorage <code>claude-forge:theme</code>,刷新页面后保留。\n </Typography.Paragraph>\n </div>\n </TabPane>\n <TabPane key=\"about\" title={<span><IconInfoCircle /> 关于</span>}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: 16 }}>\n <Typography.Title heading={6}>claude-forge</Typography.Title>\n <Typography.Paragraph style={{ fontSize: 13 }}>\n 版本: <code>v{__APP_VERSION__}</code>\n </Typography.Paragraph>\n <Typography.Paragraph style={{ fontSize: 13 }}>\n 仓库: <a href=\"https://github.com/winspan/claude-forge\" target=\"_blank\" rel=\"noreferrer\">winspan/claude-forge</a>\n </Typography.Paragraph>\n <Typography.Paragraph type=\"secondary\" style={{ fontSize: 12 }}>\n v4 IA 重写(2026-05)— 6 问句路由 + task 为核心实体。\n </Typography.Paragraph>\n <Typography.Paragraph type=\"secondary\" style={{ fontSize: 12 }}>\n AI 配置已下线(spec <code>0902</code> / sub-spec D R-C)。所有 AI 流程\n (CLAUDE.md 生成、Knowledge build、Patch preview)改走本地\n <code>claude</code> CLI 子进程,daemon 自身不再持 API key。\n </Typography.Paragraph>\n </div>\n </TabPane>\n </Tabs>\n </div>\n )\n}\n"],"names":["TabPane","Tabs","RadioGroup","Radio","KNOWN","SettingsPage","section","setTab","useTabsParam","theme","setTheme","useTheme","handleChange","key","jsx","jsxs","IconSun","Typography","v","IconInfoCircle"],"mappings":"kUAwBA,MAAMA,EAAUC,EAAK,QACfC,EAAaC,EAAM,MAGnBC,EAAoC,CAAC,QAAS,OAAO,EAE3D,SAAwBC,GAAe,CACrC,KAAM,CAAE,IAAKC,EAAS,OAAAC,CAAA,EAAWC,EAA8BJ,EAAO,QAAS,SAAS,EAClF,CAACK,EAAOC,CAAQ,EAAIC,EAAA,EAEpBC,EAAgBC,GAAgBN,EAAOM,CAAsB,EAEnE,OACEC,MAAC,OAAI,cAAY,gBAAgB,eAAcR,EAAS,MAAO,CAAE,QAAS,EAAA,EACxE,SAAAS,EAAAA,KAACd,EAAA,CAAK,UAAWK,EAAS,SAAUM,EAAc,KAAK,OAAO,YAAY,OACxE,SAAA,OAACZ,EAAA,CAAoB,MAAOe,EAAAA,KAAC,OAAA,CAAK,SAAA,CAAAD,EAAAA,IAACE,EAAA,EAAQ,EAAE,KAAA,EAAG,EAC9C,SAAAD,EAAAA,KAAC,MAAA,CAAI,MAAO,CAAE,QAAS,OAAQ,cAAe,SAAU,IAAK,GAAI,QAAS,EAAA,EACxE,SAAA,CAAAD,EAAAA,IAACG,EAAW,MAAX,CAAiB,QAAS,EAAG,SAAA,KAAE,EAChCH,EAAAA,IAACZ,EAAA,CACC,KAAK,SACL,MAAOO,EACP,SAAWS,GAAwBR,EAASQ,CAAC,EAC7C,QAAS,CACP,CAAE,MAAO,QAAS,MAAO,IAAA,EACzB,CAAE,MAAO,OAAQ,MAAO,IAAA,CAAK,CAC/B,CAAA,EAEFH,EAAAA,KAACE,EAAW,UAAX,CAAqB,KAAK,YAAY,MAAO,CAAE,SAAU,EAAA,EAAM,SAAA,CAAA,wBACzCH,EAAAA,IAAC,QAAK,SAAA,qBAAkB,EAAO,WAAA,EACtD,CAAA,CAAA,CACF,CAAA,EAfW,OAgBb,QACCd,EAAA,CAAoB,MAAOe,EAAAA,KAAC,OAAA,CAAK,SAAA,CAAAD,EAAAA,IAACK,EAAA,EAAe,EAAE,KAAA,EAAG,EACrD,SAAAJ,EAAAA,KAAC,MAAA,CAAI,MAAO,CAAE,QAAS,OAAQ,cAAe,SAAU,IAAK,EAAG,QAAS,EAAA,EACvE,SAAA,CAAAD,EAAAA,IAACG,EAAW,MAAX,CAAiB,QAAS,EAAG,SAAA,eAAY,EAC1CF,OAACE,EAAW,UAAX,CAAqB,MAAO,CAAE,SAAU,EAAA,EAAM,SAAA,CAAA,cACxC,OAAA,CAAK,SAAA,CAAA,IAAE,OAAA,EAAgB,CAAA,EAC9B,EACAF,OAACE,EAAW,UAAX,CAAqB,MAAO,CAAE,SAAU,EAAA,EAAM,SAAA,CAAA,OACzCH,EAAAA,IAAC,KAAE,KAAK,0CAA0C,OAAO,SAAS,IAAI,aAAa,SAAA,sBAAA,CAAoB,CAAA,EAC7G,EACAA,EAAAA,IAACG,EAAW,UAAX,CAAqB,KAAK,YAAY,MAAO,CAAE,SAAU,EAAA,EAAM,SAAA,0CAEhE,EACAF,EAAAA,KAACE,EAAW,UAAX,CAAqB,KAAK,YAAY,MAAO,CAAE,SAAU,EAAA,EAAM,SAAA,CAAA,iBAChDH,EAAAA,IAAC,QAAK,SAAA,OAAI,EAAO,+EAE/BA,EAAAA,IAAC,QAAK,SAAA,SAAM,EAAO,gCAAA,EACrB,CAAA,CAAA,CACF,CAAA,EAjBW,OAkBb,CAAA,CAAA,CACF,CAAA,CACF,CAEJ"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e}from"./react-vendor-tkvCrao7.js";import{b as m,L as p}from"./react-router-JVUrkhdd.js";import{u as x}from"./query-S6X1S7K9.js";import{m as s,u,T as i,d as y,E as a,Y as f,b as o}from"./arco-Bhi3a6Qp.js";import{M as j}from"./MarkdownRenderer-DZmTl-8J.js";import"./vendor-DWgdB1eY.js";import"./syntax-highlighter-BkZfCDsz.js";const h=["name","description","tools","model","version","spawn_agent","tags"];function g(t){if(t==null)return"-";if(Array.isArray(t))return t.join(", ");if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);try{return JSON.stringify(t)}catch{return String(t)}}function T(t){return t==="official"?e.jsx(o,{color:"arcoblue",children:"official"}):t==="distilled"?e.jsx(o,{color:"purple",children:"distilled"}):e.jsx(o,{color:"gray",children:"missing"})}function E(){const{id:t}=m(),{data:r,isLoading:l,isError:c,error:d}=x({queryKey:["skill-content-page",t],enabled:!!t,queryFn:async()=>{const n=await fetch(`/api/skills/${encodeURIComponent(t)}/content`);if(!n.ok)throw new Error(`HTTP ${n.status}`);return n.json()},staleTime:5*6e4});return e.jsxs("div",{style:{padding:16,display:"flex",flexDirection:"column",gap:12},children:[e.jsxs(s,{children:[e.jsx(s.Item,{children:e.jsx(p,{to:"/context?tab=skills",children:"/context"})}),e.jsx(s.Item,{children:"skill"}),e.jsx(s.Item,{children:e.jsx("span",{style:{fontFamily:"monospace"},children:t})})]}),e.jsxs(u,{bordered:!0,children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:12},children:[e.jsx(i.Title,{heading:5,style:{margin:0,fontFamily:"monospace"},children:t}),r&&T(r.source)]}),l&&e.jsx(y,{animation:!0,text:{rows:10}}),c&&e.jsx(a,{description:e.jsxs(i.Text,{type:"error",children:["加载失败: ",String(d)]})}),r&&!r.exists&&e.jsx(a,{description:e.jsxs("span",{children:["未找到 ",e.jsxs("code",{style:{fontFamily:"monospace"},children:[t,".md"]}),"。 可能不是一个真实文件(hit-rate 列出的 keyword id 没有对应 markdown)。"]})}),r&&r.exists&&e.jsxs(e.Fragment,{children:[e.jsx(f,{colon:" :",size:"small",column:2,title:e.jsx(i.Text,{style:{fontSize:13},children:"Frontmatter"}),data:h.map(n=>({label:n,value:g(r.frontmatter[n])})),style:{marginBottom:16}}),e.jsxs(i.Text,{type:"secondary",style:{fontSize:12},children:["path: ",r.path," · ",r.bytes," bytes"]}),e.jsx("div",{style:{marginTop:12},children:e.jsx(j,{content:r.body})})]})]})]})}export{E as default};
|
|
2
|
-
//# sourceMappingURL=SkillContentPage-DK5rgfgw.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SkillContentPage-DK5rgfgw.js","sources":["../../src/pages/SkillContentPage.tsx"],"sourcesContent":["/**\n * /context/skill/:id — full-page view of a skill's markdown.\n *\n * Standalone fallback for long skills (e.g. official-spec-driven-design.md\n * with 600+ LOC) where the 760px drawer feels cramped. Linked from\n * SkillContentDrawer's \"Open in full page\" anchor.\n *\n * Breadcrumb: /context → /context/skill/:id\n */\nimport { useParams, Link } from 'react-router-dom'\nimport { useQuery } from '@tanstack/react-query'\nimport Breadcrumb from '@arco-design/web-react/es/Breadcrumb'\nimport Card from '@arco-design/web-react/es/Card'\nimport Descriptions from '@arco-design/web-react/es/Descriptions'\nimport Empty from '@arco-design/web-react/es/Empty'\nimport Skeleton from '@arco-design/web-react/es/Skeleton'\nimport Tag from '@arco-design/web-react/es/Tag'\nimport Typography from '@arco-design/web-react/es/Typography'\nimport MarkdownRenderer from '../components/MarkdownRenderer'\n\ninterface SkillContentResponse {\n id: string\n source: 'official' | 'distilled' | 'missing'\n exists: boolean\n path: string | null\n bytes: number\n body: string\n frontmatter: Record<string, unknown>\n}\n\nconst WHITELIST: ReadonlyArray<string> = [\n 'name',\n 'description',\n 'tools',\n 'model',\n 'version',\n 'spawn_agent',\n 'tags',\n]\n\nfunction renderFmValue(v: unknown): string {\n if (v == null) return '-'\n if (Array.isArray(v)) return v.join(', ')\n if (typeof v === 'string') return v\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n try {\n return JSON.stringify(v)\n } catch {\n return String(v)\n }\n}\n\nfunction sourceTag(source: 'official' | 'distilled' | 'missing') {\n if (source === 'official') return <Tag color=\"arcoblue\">official</Tag>\n if (source === 'distilled') return <Tag color=\"purple\">distilled</Tag>\n return <Tag color=\"gray\">missing</Tag>\n}\n\nexport default function SkillContentPage() {\n const { id } = useParams<{ id: string }>()\n const { data, isLoading, isError, error } = useQuery<SkillContentResponse>({\n queryKey: ['skill-content-page', id],\n enabled: !!id,\n queryFn: async () => {\n const r = await fetch(`/api/skills/${encodeURIComponent(id!)}/content`)\n if (!r.ok) throw new Error(`HTTP ${r.status}`)\n return r.json()\n },\n staleTime: 5 * 60_000,\n })\n\n return (\n <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>\n <Breadcrumb>\n <Breadcrumb.Item>\n <Link to=\"/context?tab=skills\">/context</Link>\n </Breadcrumb.Item>\n <Breadcrumb.Item>skill</Breadcrumb.Item>\n <Breadcrumb.Item>\n <span style={{ fontFamily: 'monospace' }}>{id}</span>\n </Breadcrumb.Item>\n </Breadcrumb>\n\n <Card bordered>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>\n <Typography.Title heading={5} style={{ margin: 0, fontFamily: 'monospace' }}>{id}</Typography.Title>\n {data && sourceTag(data.source)}\n </div>\n\n {isLoading && <Skeleton animation text={{ rows: 10 }} />}\n {isError && (\n <Empty description={<Typography.Text type=\"error\">加载失败: {String(error)}</Typography.Text>} />\n )}\n {data && !data.exists && (\n <Empty\n description={\n <span>\n 未找到 <code style={{ fontFamily: 'monospace' }}>{id}.md</code>。\n 可能不是一个真实文件(hit-rate 列出的 keyword id 没有对应 markdown)。\n </span>\n }\n />\n )}\n {data && data.exists && (\n <>\n <Descriptions\n colon=\" :\"\n size=\"small\"\n column={2}\n title={<Typography.Text style={{ fontSize: 13 }}>Frontmatter</Typography.Text>}\n data={WHITELIST.map((key) => ({\n label: key,\n value: renderFmValue(data.frontmatter[key]),\n }))}\n style={{ marginBottom: 16 }}\n />\n <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>\n path: {data.path} · {data.bytes} bytes\n </Typography.Text>\n <div style={{ marginTop: 12 }}>\n <MarkdownRenderer content={data.body} />\n </div>\n </>\n )}\n </Card>\n </div>\n )\n}\n"],"names":["WHITELIST","renderFmValue","v","sourceTag","source","Tag","jsx","SkillContentPage","id","useParams","data","isLoading","isError","error","useQuery","r","jsxs","Breadcrumb","Link","Card","Typography","Skeleton","Empty","Fragment","Descriptions","key","MarkdownRenderer"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,r as y}from"./react-vendor-tkvCrao7.js";import{u as j}from"./query-S6X1S7K9.js";import{ai as w,U as v,Y as h,d as g,E as m,T as a,b as c,X as T}from"./arco-Bhi3a6Qp.js";import{M as k}from"./MarkdownRenderer-DZmTl-8J.js";import"./vendor-DWgdB1eY.js";import"./syntax-highlighter-BkZfCDsz.js";const z=["name","description","tools","model","version","spawn_agent","tags"];function S(r){if(r==null)return"-";if(Array.isArray(r))return r.join(", ");if(typeof r=="string")return r;if(typeof r=="number"||typeof r=="boolean")return String(r);try{return JSON.stringify(r)}catch{return String(r)}}function b(r){return r==="official"?e.jsx(c,{color:"arcoblue",size:"small",children:"official"}):r==="distilled"?e.jsx(c,{color:"purple",size:"small",children:"distilled"}):e.jsx(c,{color:"gray",size:"small",children:"missing"})}function _({open:r,skillId:o,stats:s,onClose:d}){const{data:l,isLoading:x,isError:u,error:p}=j({queryKey:["skill-content",o],enabled:!!o&&r,queryFn:async()=>{const i=await fetch(`/api/skills/${encodeURIComponent(o)}/content`);if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()},staleTime:3e5}),t=e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap"},children:[e.jsx("span",{style:{fontFamily:"monospace",fontSize:13},children:o??"-"}),l&&b(l.source),o&&e.jsxs("a",{href:`/context/skill/${encodeURIComponent(o)}`,target:"_blank",rel:"noopener noreferrer",style:{marginLeft:"auto",fontSize:12,display:"inline-flex",alignItems:"center",gap:4},children:[e.jsx(w,{})," Open in full page"]})]});return e.jsxs(v,{visible:r,onCancel:d,onOk:d,title:t,width:760,footer:null,unmountOnExit:!0,children:[s&&e.jsx(h,{colon:" :",size:"small",column:4,data:[{label:"invocations",value:s.invocations},{label:"success",value:s.success},{label:"failed",value:s.failed},{label:"rate",value:s.rate}],style:{marginBottom:12}}),x&&e.jsx(g,{animation:!0,text:{rows:6}}),u&&e.jsx(m,{description:e.jsxs(a.Text,{type:"error",children:["加载失败: ",String(p)]})}),l&&!l.exists&&e.jsx(m,{description:e.jsxs("span",{children:["未找到 ",e.jsxs("code",{style:{fontFamily:"monospace"},children:[o,".md"]}),"。",e.jsx("br",{}),"hit-rate 表中的 id 可能没有对应文件(如未蒸馏的 keyword)。"]})}),l&&l.exists&&e.jsxs(e.Fragment,{children:[e.jsx(h,{colon:" :",size:"mini",column:2,title:e.jsx(a.Text,{style:{fontSize:13},children:"Frontmatter"}),data:z.map(i=>({label:String(i),value:S(l.frontmatter[i])})),style:{marginBottom:16}}),e.jsx(a.Text,{style:{fontSize:13,fontWeight:600},children:"Body"}),e.jsx("div",{style:{marginTop:8,maxHeight:"calc(100vh - 360px)",overflow:"auto",border:"1px solid var(--color-border-2)",borderRadius:4,padding:12},children:e.jsx(k,{content:l.body})})]})]})}function O(){const[r,o]=y.useState(null),{data:s,isLoading:d,isError:l,error:x}=j({queryKey:["ctx-skill-hit-rate"],queryFn:async()=>{const t=await fetch("/api/rules/hit-rate?days=7");if(!t.ok)throw new Error(`HTTP ${t.status}`);return t.json()},staleTime:6e4}),u=y.useMemo(()=>{if(!s)return[];const t=s.skills.map(n=>({skill_id:n.skill_id,invocations:n.total,success:n.success,failed:n.failed,success_rate:n.total>0?n.success/n.total:0,rate:n.rate})),i=s.neverTriggered.map(n=>({skill_id:n,invocations:0,success:0,failed:0,success_rate:0,rate:"0%"}));return[...t,...i].sort((n,f)=>f.invocations-n.invocations)},[s]);if(l)return e.jsx(m,{description:e.jsxs(a.Text,{type:"error",children:["加载失败: ",String(x)]})});if(d||!s)return e.jsx(g,{animation:!0,text:{rows:8}});const p=[{title:"skill_id",dataIndex:"skill_id",render:t=>e.jsx("span",{style:{fontFamily:"monospace",fontSize:12},children:t})},{title:"invocations (7d)",dataIndex:"invocations",width:150,sorter:(t,i)=>t.invocations-i.invocations,render:t=>t===0?e.jsx(c,{color:"red",size:"small",children:"未触发"}):e.jsx("span",{style:{fontFamily:"monospace",fontWeight:600},children:t})},{title:"success_rate",dataIndex:"success_rate",width:140,render:(t,i)=>{if(i.invocations===0)return e.jsx(a.Text,{type:"secondary",style:{fontSize:12},children:"—"});const n=Math.round(t*1e3)/10,f=n>=90?"green":n>=70?"orange":"red";return e.jsxs("span",{children:[e.jsxs(c,{color:f,size:"small",children:[n,"%"]}),e.jsxs(a.Text,{type:"secondary",style:{fontSize:11,marginLeft:6},children:["(",i.success,"✓/",i.failed,"✗)"]})]})}},{title:"rate (vs 全部 prompt)",dataIndex:"rate",width:160,render:t=>e.jsx(a.Text,{style:{fontSize:12},children:t})},{title:"状态",dataIndex:"invocations",key:"status",width:110,render:t=>t===0?e.jsx(c,{color:"red",size:"small",children:"zero-call"}):t<3?e.jsx(c,{color:"orange",size:"small",children:"low"}):e.jsx(c,{color:"green",size:"small",children:"active"})}];return e.jsxs("div",{children:[e.jsx("div",{style:{marginBottom:12},children:e.jsxs(a.Text,{style:{fontSize:13,color:"var(--color-text-3)"},children:["近 7d · 总 invocation ",s.summary.skillInvocations," / ",s.summary.totalPrompts," prompts · skill rate ",s.summary.skillRate," · 共 ",u.length," 个 skill · 未触发 ",s.neverTriggered.length," 个"]})}),e.jsx(T,{rowKey:"skill_id",data:u,columns:p,pagination:{pageSize:20,showTotal:!0},size:"small",border:{wrapper:!0,cell:!0},onRow:t=>({onClick:()=>o(t),style:{cursor:"pointer"}}),noDataElement:e.jsx(m,{description:"无 skill 数据"})}),e.jsx(_,{open:r!==null,skillId:(r==null?void 0:r.skill_id)??null,stats:r,onClose:()=>o(null)})]})}export{O as default};
|
|
2
|
-
//# sourceMappingURL=SkillStatsTable-DYMzjEUV.js.map
|