mcp-efficiency-engine 0.1.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/.github/CODEOWNERS +40 -0
- package/.github/GITHUB_MCP_INTEGRATION.md +366 -0
- package/.github/ISSUE_TEMPLATE/bug.md +51 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
- package/.github/ISSUE_TEMPLATE/question.md +18 -0
- package/.github/agents/backend.agent.md +18 -0
- package/.github/agents/community-manager.agent.md +18 -0
- package/.github/agents/dba.agent.md +18 -0
- package/.github/agents/frontend-agent.agent.md +18 -0
- package/.github/agents/github-repository-manager.agent.md +338 -0
- package/.github/agents/iot.agent.md +20 -0
- package/.github/agents/legacy.agent.md +18 -0
- package/.github/agents/rag-azure.agent.md +18 -0
- package/.github/agents/rag-local.agent.md +18 -0
- package/.github/agents/snapshot.agent.md +18 -0
- package/.github/agents/ux-ui.agent.md +18 -0
- package/.github/agents/wiki-agent.agent.md +23 -0
- package/.github/copilot-instructions.md +44 -0
- package/.github/instructions/always-on-optimization.instructions.md +54 -0
- package/.github/prompts/auto-route.prompt.md +33 -0
- package/.github/prompts/azure-rag.query.prompt.md +19 -0
- package/.github/prompts/backend.fix-bug.prompt.md +13 -0
- package/.github/prompts/cavecrew.prompt.md +16 -0
- package/.github/prompts/caveman-commit.prompt.md +14 -0
- package/.github/prompts/caveman-compress.prompt.md +15 -0
- package/.github/prompts/caveman-debug.prompt.md +13 -0
- package/.github/prompts/caveman-help.prompt.md +11 -0
- package/.github/prompts/caveman-review.prompt.md +15 -0
- package/.github/prompts/caveman-stats.prompt.md +11 -0
- package/.github/prompts/caveman.prompt.md +11 -0
- package/.github/prompts/community.post.prompt.md +14 -0
- package/.github/prompts/dba.query-review.prompt.md +15 -0
- package/.github/prompts/dev.fix-bug.prompt.md +13 -0
- package/.github/prompts/frontend.code.prompt.md +13 -0
- package/.github/prompts/legacy.impact-analysis.prompt.md +14 -0
- package/.github/prompts/project.kickoff-analysis.prompt.md +51 -0
- package/.github/prompts/rag.knowledge-answer.prompt.md +13 -0
- package/.github/prompts/token-saver-review.prompt.md +14 -0
- package/.github/prompts/ux-ui.review.prompt.md +14 -0
- package/.github/pull_request_template.md +71 -0
- package/.github/skills/ahorro-tokens/SKILL.md +36 -0
- package/.github/skills/azure-rag-enterprise/SKILL.md +24 -0
- package/.github/skills/backend-coding/SKILL.md +24 -0
- package/.github/skills/cavecrew/SKILL.md +17 -0
- package/.github/skills/caveman/SKILL.md +25 -0
- package/.github/skills/caveman-commit/SKILL.md +15 -0
- package/.github/skills/caveman-compress/SKILL.md +14 -0
- package/.github/skills/caveman-help/SKILL.md +19 -0
- package/.github/skills/caveman-mode/SKILL.md +43 -0
- package/.github/skills/caveman-review/SKILL.md +16 -0
- package/.github/skills/caveman-stats/SKILL.md +13 -0
- package/.github/skills/community-content/SKILL.md +22 -0
- package/.github/skills/database-analysis/SKILL.md +23 -0
- package/.github/skills/dev-coding/SKILL.md +24 -0
- package/.github/skills/frontend-coding/SKILL.md +24 -0
- package/.github/skills/human-in-the-loop/SKILL.md +47 -0
- package/.github/skills/iot-architecture/SKILL.md +22 -0
- package/.github/skills/legacy-migration/SKILL.md +22 -0
- package/.github/skills/rag-knowledge/SKILL.md +21 -0
- package/.github/skills/token-saver/SKILL.md +36 -0
- package/.github/skills/ux-ui-governance/SKILL.md +24 -0
- package/.github/skills/wiki-tools.json +24 -0
- package/.github/workflows/auto-devlog.yml +56 -0
- package/.github/workflows/auto-manage-issues.yml +64 -0
- package/.github/workflows/auto-sync.yml +54 -0
- package/.github/workflows/autodocs-sync.yml +114 -0
- package/.github/workflows/ci.yml +68 -0
- package/.github/workflows/pages.yml +59 -0
- package/.github/workflows/routing-evals.yml +59 -0
- package/.github/workflows/techriders-coverage-effective.yml +91 -0
- package/.vscode/mcp.json +44 -0
- package/AGENTS.md +160 -0
- package/ARCHITECTURE.md +57 -0
- package/FINAL_USAGE_GUIDE.md +106 -0
- package/LICENSE +21 -0
- package/README.md +385 -0
- package/README_WIKI.md +130 -0
- package/autodocs/README.md +22 -0
- package/autodocs/schema/wiki-node.schema.json +56 -0
- package/autolearning/feedback-loop.md +90 -0
- package/bin/install-host.js +326 -0
- package/bin/mcpee.js +116 -0
- package/context/repomix/repomix.config.json +20 -0
- package/memory/cross-memory-reasoning.md +17 -0
- package/memory/mempry-registry.yml +16 -0
- package/observability/evals/chat-token-usage-report.json +64 -0
- package/observability/evals/chat-token-usage-report.md +33 -0
- package/observability/evals/iteration-value-report.json +76 -0
- package/observability/evals/iteration-value-report.md +39 -0
- package/observability/evals/learning-loop-report.json +268 -0
- package/observability/evals/learning-loop-report.md +37 -0
- package/observability/evals/repo-intake-eval-cases.md +23 -0
- package/observability/evals/routing-eval-cases.json +68 -0
- package/observability/evals/routing-eval-cases.md +25 -0
- package/observability/evals/routing-eval-report.json +72 -0
- package/observability/evaluation.md +34 -0
- package/observability/logs/copilot-usage-ingest-report.json +13 -0
- package/observability/logs/iteration-metrics.jsonl +75 -0
- package/observability/logs/learning-feedback.jsonl +152 -0
- package/observability/logs/routing-decisions.jsonl +333 -0
- package/observability/logs/session/agent-pipeline-preflight-20260701-004146.log +8 -0
- package/observability/logs/session/agent-pipeline-preflight-20260701-005554.log +8 -0
- package/observability/logs/session/agent-pipeline-preflight-20260701-010351.log +8 -0
- package/observability/logs/session/agent-pipeline-preflight-20260704-145829.log +8 -0
- package/observability/logs/session/agent-pipeline-preflight-20260704-150021.log +8 -0
- package/observability/logs/session/agent-pipeline-preflight-20260704-154328.log +8 -0
- package/observability/logs/session/bye-20260629-221513.json +39 -0
- package/observability/logs/session/bye-20260629-221545.json +59 -0
- package/observability/logs/session/bye-20260629-222007.json +52 -0
- package/observability/logs/session/bye-20260629-222133.json +59 -0
- package/observability/logs/session/bye-20260629-222412.json +45 -0
- package/observability/logs/session/bye-20260629-223224.json +52 -0
- package/observability/logs/session/bye-20260629-224050.json +59 -0
- package/observability/logs/session/bye-20260630-105732.json +24 -0
- package/observability/logs/session/bye-20260630-211536.json +87 -0
- package/observability/logs/session/bye-20260630-223423.json +87 -0
- package/observability/logs/session/bye-20260630-232534.json +87 -0
- package/observability/logs/session/bye-20260701-003638.json +126 -0
- package/observability/logs/session/bye-20260701-003849.json +126 -0
- package/observability/logs/session/bye-20260701-004315.json +138 -0
- package/observability/logs/session/bye-20260701-004559.json +76 -0
- package/observability/logs/session/bye-20260701-005913.json +138 -0
- package/observability/logs/session/bye-20260701-010753.json +138 -0
- package/observability/logs/session/bye-20260703-194106.json +24 -0
- package/observability/logs/session/bye-20260703-233443.json +76 -0
- package/observability/logs/session/bye-20260703-233653.json +76 -0
- package/observability/logs/session/bye-20260704-150317.json +152 -0
- package/observability/logs/session/bye-20260704-150929.json +75 -0
- package/observability/logs/session/bye-20260704-151050.json +75 -0
- package/observability/logs/session/bye-20260705-115458.json +165 -0
- package/observability/logs/session/codegraph-status-20260701-004228.log +39 -0
- package/observability/logs/session/codegraph-status-20260701-004310.log +39 -0
- package/observability/logs/session/codegraph-status-20260701-005637.log +39 -0
- package/observability/logs/session/codegraph-status-20260701-005908.log +39 -0
- package/observability/logs/session/codegraph-status-20260701-010444.log +39 -0
- package/observability/logs/session/codegraph-status-20260701-010747.log +39 -0
- package/observability/logs/session/codegraph-status-20260704-150137.log +26 -0
- package/observability/logs/session/codegraph-status-20260704-150313.log +28 -0
- package/observability/logs/session/codegraph-status-20260704-154445.log +26 -0
- package/observability/logs/session/codegraph-status-20260705-115454.log +27 -0
- package/observability/logs/session/codegraph-sync-20260701-004211.log +88 -0
- package/observability/logs/session/codegraph-sync-20260701-004241.log +82 -0
- package/observability/logs/session/codegraph-sync-20260701-005620.log +89 -0
- package/observability/logs/session/codegraph-sync-20260701-005840.log +88 -0
- package/observability/logs/session/codegraph-sync-20260701-010419.log +93 -0
- package/observability/logs/session/codegraph-sync-20260701-010609.log +90 -0
- package/observability/logs/session/codegraph-sync-20260704-145856.log +70 -0
- package/observability/logs/session/codegraph-sync-20260704-150047.log +53 -0
- package/observability/logs/session/codegraph-sync-20260704-150204.log +44 -0
- package/observability/logs/session/codegraph-sync-20260704-154353.log +26 -0
- package/observability/logs/session/codegraph-sync-20260705-115337.log +32 -0
- package/observability/logs/session/hi-20260629-221459.json +38 -0
- package/observability/logs/session/hi-20260629-221945.json +59 -0
- package/observability/logs/session/hi-20260629-222108.json +66 -0
- package/observability/logs/session/hi-20260629-222352.json +45 -0
- package/observability/logs/session/hi-20260629-225719.json +59 -0
- package/observability/logs/session/hi-20260629-233722.json +52 -0
- package/observability/logs/session/hi-20260629-233913.json +198 -0
- package/observability/logs/session/hi-20260630-110156.json +177 -0
- package/observability/logs/session/hi-20260630-191243.json +184 -0
- package/observability/logs/session/hi-20260630-230050.json +234 -0
- package/observability/logs/session/hi-20260630-230523.json +227 -0
- package/observability/logs/session/hi-20260630-230855.json +227 -0
- package/observability/logs/session/hi-20260630-231638.json +227 -0
- package/observability/logs/session/hi-20260630-231756.json +219 -0
- package/observability/logs/session/hi-20260701-002842.json +234 -0
- package/observability/logs/session/hi-20260701-003458.json +282 -0
- package/observability/logs/session/hi-20260701-004233.json +290 -0
- package/observability/logs/session/hi-20260701-004554.json +236 -0
- package/observability/logs/session/hi-20260701-005641.json +290 -0
- package/observability/logs/session/hi-20260701-010450.json +290 -0
- package/observability/logs/session/hi-20260703-233854.json +312 -0
- package/observability/logs/session/hi-20260703-234448.json +318 -0
- package/observability/logs/session/hi-20260704-145907.json +353 -0
- package/observability/logs/session/hi-20260704-150141.json +392 -0
- package/observability/logs/session/hi-20260704-154448.json +392 -0
- package/observability/logs/session/iteration-value-refresh-20260701-004309.log +3 -0
- package/observability/logs/session/iteration-value-refresh-20260701-005907.log +3 -0
- package/observability/logs/session/iteration-value-refresh-20260701-010747.log +3 -0
- package/observability/logs/session/iteration-value-refresh-20260704-150312.log +3 -0
- package/observability/logs/session/iteration-value-refresh-20260705-115453.log +3 -0
- package/observability/logs/session/learning-loop-refresh-20260701-004309.log +3 -0
- package/observability/logs/session/learning-loop-refresh-20260701-005906.log +3 -0
- package/observability/logs/session/learning-loop-refresh-20260701-010745.log +3 -0
- package/observability/logs/session/learning-loop-refresh-20260704-150309.log +3 -0
- package/observability/logs/session/learning-loop-refresh-20260705-115450.log +3 -0
- package/observability/logs/session/project-notes-refresh-20260701-004227.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260701-004308.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260701-005636.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260701-005906.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260701-010443.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260701-010744.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260704-150133.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260704-150309.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260704-154442.log +1 -0
- package/observability/logs/session/project-notes-refresh-20260705-115449.log +1 -0
- package/observability/logs/session/repo-intake-20260701-004218.log +6 -0
- package/observability/logs/session/repo-intake-20260701-005626.log +6 -0
- package/observability/logs/session/repo-intake-20260701-010433.log +6 -0
- package/observability/logs/session/repo-intake-20260704-150053.log +6 -0
- package/observability/logs/session/repo-intake-20260704-154404.log +6 -0
- package/observability/logs/session/repo-intake-refresh-20260701-004247.log +6 -0
- package/observability/logs/session/repo-intake-refresh-20260701-005847.log +6 -0
- package/observability/logs/session/repo-intake-refresh-20260701-010615.log +6 -0
- package/observability/logs/session/repo-intake-refresh-20260704-150209.log +6 -0
- package/observability/logs/session/repo-intake-refresh-20260705-115342.log +6 -0
- package/observability/logs/session/repo-registry-validation-20260701-004234.log +2 -0
- package/observability/logs/session/repo-registry-validation-20260701-004556.log +2 -0
- package/observability/logs/session/repo-registry-validation-20260701-005833.log +2 -0
- package/observability/logs/session/repo-registry-validation-20260701-010601.log +2 -0
- package/observability/logs/session/repo-registry-validation-20260704-150155.log +2 -0
- package/observability/logs/session/repo-registry-validation-20260705-115323.log +2 -0
- package/observability/logs/session/repomix-refresh-20260701-003823.log +4236 -0
- package/observability/logs/session/repomix-refresh-20260701-004251.log +4275 -0
- package/observability/logs/session/repomix-refresh-20260701-005850.log +4248 -0
- package/observability/logs/session/repomix-refresh-20260701-010619.log +6531 -0
- package/observability/logs/session/repomix-refresh-20260704-150239.log +652 -0
- package/observability/logs/session/repomix-refresh-20260705-115419.log +178 -0
- package/observability/logs/session/routing-evals-20260701-004221.log +2 -0
- package/observability/logs/session/routing-evals-20260701-004235.log +2 -0
- package/observability/logs/session/routing-evals-20260701-005629.log +2 -0
- package/observability/logs/session/routing-evals-20260701-005834.log +2 -0
- package/observability/logs/session/routing-evals-20260701-010437.log +2 -0
- package/observability/logs/session/routing-evals-20260701-010603.log +2 -0
- package/observability/logs/session/routing-evals-20260704-150125.log +2 -0
- package/observability/logs/session/routing-evals-20260704-150157.log +2 -0
- package/observability/logs/session/routing-evals-20260704-154434.log +2 -0
- package/observability/logs/session/routing-evals-20260705-115327.log +2 -0
- package/observability/logs/session/sibling-discovery-refresh-20260701-004250.log +3 -0
- package/observability/logs/session/sibling-discovery-refresh-20260701-005850.log +3 -0
- package/observability/logs/session/sibling-discovery-refresh-20260701-010618.log +3 -0
- package/observability/logs/session/sibling-discovery-refresh-20260704-150238.log +3 -0
- package/observability/logs/session/sibling-discovery-refresh-20260705-115418.log +3 -0
- package/observability/logs.schema.json +95 -0
- package/observability/logs.schema.md +26 -0
- package/observability/metrics.md +35 -0
- package/observability/routing-evaluator.md +37 -0
- package/observability/scoring-rubric.md +38 -0
- package/optimization/ALWAYS_ON_OPTIMIZATION.md +98 -0
- package/optimization/caveman-mode.md +62 -0
- package/optimization/examples/login-bug.md +25 -0
- package/optimization/examples/sla-query.md +25 -0
- package/optimization/optimization-routing.md +30 -0
- package/optimization/policies/response-style.policy.md +45 -0
- package/optimization/policies/token-budget.policy.md +28 -0
- package/optimization/profiles/always-on.profile.md +19 -0
- package/optimization/profiles/architecture.profile.md +20 -0
- package/optimization/profiles/debug.profile.md +20 -0
- package/optimization/profiles/enterprise-rag.profile.md +20 -0
- package/optimization/profiles/training.profile.md +20 -0
- package/optimization/token-saver.md +58 -0
- package/orchestrator/corporate-routing.md +51 -0
- package/orchestrator/decision-matrix.md +33 -0
- package/orchestrator/fallback.md +29 -0
- package/orchestrator/repo-routing.md +58 -0
- package/orchestrator/router.md +75 -0
- package/orchestrator/wiki/__init__.py +5 -0
- package/orchestrator/wiki/graph_consolidator.py +506 -0
- package/orchestrator/wiki/incremental_engine.py +57 -0
- package/orchestrator/wiki/plugin_manager.py +40 -0
- package/package.json +70 -0
- package/policies/context-policy.md +25 -0
- package/policies/cost-policy.md +25 -0
- package/policies/repo-intake-policy.md +36 -0
- package/policies/security-policy.md +25 -0
- package/repo-intake/README.md +81 -0
- package/repo-intake/templates/agent.template.md +4 -0
- package/repo-intake/templates/context-manifest.template.md +4 -0
- package/repo-intake/templates/skill.template.md +3 -0
- package/repo-registry/repos.schema.json +102 -0
- package/repo-registry/repos.template.json +26 -0
- package/requirements.txt +1 -0
- package/scripts/README.md +109 -0
- package/scripts/__init__.py +0 -0
- package/scripts/bootstrap-portable.cmd +4 -0
- package/scripts/bootstrap-portable.ps1 +90 -0
- package/scripts/context/azure-rag-mcp-adapter.py +1 -0
- package/scripts/context/build-graphify.ps1 +1 -0
- package/scripts/context/build-repomix.ps1 +1 -0
- package/scripts/discovery/discover-boost-repos.cmd +4 -0
- package/scripts/discovery/discover-boost-repos.py +186 -0
- package/scripts/discovery/refresh-project-notes.py +179 -0
- package/scripts/github/INSTALL.md +295 -0
- package/scripts/github/README.md +455 -0
- package/scripts/github/apply-boost-templates.ps1 +163 -0
- package/scripts/github/apply-single-repo.ps1 +85 -0
- package/scripts/github/create-devlog.ps1 +380 -0
- package/scripts/github/manage-issues.ps1 +332 -0
- package/scripts/github/sync-repo.ps1 +267 -0
- package/scripts/intake/agent-pipeline-preflight.py +151 -0
- package/scripts/intake/init-template-registry.cmd +4 -0
- package/scripts/intake/init-template-registry.ps1 +258 -0
- package/scripts/intake/repo-intake.py +354 -0
- package/scripts/intake/resolve-routing.py +620 -0
- package/scripts/intake/run-repo-intake.cmd +4 -0
- package/scripts/intake/run-repo-intake.ps1 +46 -0
- package/scripts/intake/run-routing-evals.py +147 -0
- package/scripts/intake/validate-database-routing.py +91 -0
- package/scripts/intake/validate-rag-routing.py +85 -0
- package/scripts/intake/validate-repo-registry.ps1 +20 -0
- package/scripts/intake/validate-repo-registry.py +286 -0
- package/scripts/intake/validate-security.py +170 -0
- package/scripts/learning/autolearning-gate.py +71 -0
- package/scripts/learning/chat-token-usage-report.py +326 -0
- package/scripts/learning/ingest-copilot-session-usage.py +421 -0
- package/scripts/learning/iteration-value-report.py +275 -0
- package/scripts/learning/learning-loop-report.py +272 -0
- package/scripts/learning/record-iteration-metrics.py +116 -0
- package/scripts/learning/record-learning-feedback.py +91 -0
- package/scripts/ops/bye.ps1 +620 -0
- package/scripts/ops/compare-demo-results.ps1 +826 -0
- package/scripts/ops/demo1.ps1 +11 -0
- package/scripts/ops/demo2.ps1 +11 -0
- package/scripts/ops/demo3.ps1 +11 -0
- package/scripts/ops/demo4.ps1 +11 -0
- package/scripts/ops/hi.ps1 +1045 -0
- package/scripts/ops/llm-token-probe.py +164 -0
- package/scripts/ops/run-all-demos.ps1 +39 -0
- package/scripts/ops/run-demo-block.ps1 +85 -0
- package/scripts/ops/run-demo-session.ps1 +532 -0
- package/scripts/setup/optimize-engines.ps1 +183 -0
- package/scripts/setup/setup-codegraph.ps1 +3 -0
- package/scripts/setup/setup-gitnexus.ps1 +3 -0
- package/scripts/setup/setup-graphify.ps1 +2 -0
- package/scripts/setup/setup-prerequisites.ps1 +205 -0
- package/scripts/setup/start-gitnexus.ps1 +71 -0
- package/scripts/setup/validate-context.ps1 +361 -0
- package/scripts/wiki/__init__.py +0 -0
- package/scripts/wiki/compiler_main.py +189 -0
- package/scripts/wiki/normalizer.py +206 -0
- package/scripts/wiki/providers/base_provider.py +25 -0
- package/scripts/wiki/providers/codegraph_provider.py +49 -0
- package/scripts/wiki/providers/graphify_provider.py +49 -0
- package/scripts/wiki/providers/repo_content_provider.py +408 -0
- package/scripts/wiki/query_autodocs.py +60 -0
- package/scripts/wiki/validator.py +183 -0
- package/scripts/wiki/wiki_compiler.py +5 -0
- package/specs/architecture.spec.md +32 -0
- package/specs/azure-rag.spec.md +28 -0
- package/specs/coding-standards.spec.md +24 -0
- package/specs/database.spec.md +29 -0
- package/specs/migration.spec.md +26 -0
- package/specs/observability.spec.md +24 -0
- package/specs/optimization.spec.md +31 -0
- package/specs/rag.spec.md +30 -0
- package/specs/repo-intake.spec.md +25 -0
- package/specs/routing.spec.md +24 -0
- package/specs/security.spec.md +30 -0
- package/specs/wiki/provider-contract.json +33 -0
- package/specs/wiki/unified-knowledge-graph.json +25 -0
- package/templates/BOOST_README_TEMPLATE.md +96 -0
- package/templates/CODEOWNERS_TEMPLATE.txt +29 -0
- package/templates/LICENSE_MIT_TEMPLATE.txt +21 -0
- package/templates/PERSONAL_README_TEMPLATE.md +90 -0
- package/templates/README_boost_RAG_Azure.md +46 -0
- package/templates/README_boost_azure_iot_edge.md +48 -0
- package/templates/README_boost_backend.md +47 -0
- package/templates/README_boost_dba.md +57 -0
- package/templates/ci.yml.template +73 -0
- package/tooling/tooling.manifest.json +72 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
|
+
|
|
7
|
+
from scripts.wiki.normalizer import normalize_node, section_catalog
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
SECTIONS = section_catalog()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GraphConsolidator:
|
|
14
|
+
def __init__(self, output_graph_path: Path, markdown_output_dir: Path) -> None:
|
|
15
|
+
self.output_graph_path = output_graph_path
|
|
16
|
+
self.markdown_output_dir = markdown_output_dir
|
|
17
|
+
self.generated_output_dir = output_graph_path.parent
|
|
18
|
+
|
|
19
|
+
def consolidate(self, provider_contracts: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
20
|
+
raw_nodes: Dict[str, Dict[str, Any]] = {}
|
|
21
|
+
wiki_nodes: Dict[str, Dict[str, Any]] = {}
|
|
22
|
+
for contract in provider_contracts:
|
|
23
|
+
provider_id = str(contract.get("provider_id", "unknown"))
|
|
24
|
+
entities = contract.get("entities", [])
|
|
25
|
+
if not isinstance(entities, list):
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
for entity in entities:
|
|
29
|
+
if not isinstance(entity, dict):
|
|
30
|
+
continue
|
|
31
|
+
entity_id = str(entity.get("id", "")).strip()
|
|
32
|
+
if not entity_id:
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
node_key = f"{provider_id}::{entity_id}"
|
|
36
|
+
relations = self._normalize_relations(provider_id, entity.get("relations", []))
|
|
37
|
+
raw_data = entity.get("raw_data", {})
|
|
38
|
+
metadata = raw_data if isinstance(raw_data, dict) else {}
|
|
39
|
+
|
|
40
|
+
raw_node = {
|
|
41
|
+
"node_id": node_key,
|
|
42
|
+
"provider": provider_id,
|
|
43
|
+
"type": str(entity.get("type", "unknown")),
|
|
44
|
+
"checksum": str(entity.get("checksum", "")),
|
|
45
|
+
"metadata": metadata,
|
|
46
|
+
"relations": relations,
|
|
47
|
+
"semantic_summary": str(metadata.get("semantic_summary", "")),
|
|
48
|
+
}
|
|
49
|
+
raw_nodes[node_key] = raw_node
|
|
50
|
+
wiki_nodes[node_key] = normalize_node(node_key, raw_node)
|
|
51
|
+
|
|
52
|
+
navigation_tree = self._build_navigation_tree(wiki_nodes)
|
|
53
|
+
search_index = self._build_search_index(wiki_nodes)
|
|
54
|
+
relations_index = self._build_relations_index(wiki_nodes)
|
|
55
|
+
section_manifest = self._build_section_manifest(wiki_nodes)
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"last_updated": self._deterministic_marker(wiki_nodes),
|
|
59
|
+
"nodes": wiki_nodes,
|
|
60
|
+
"raw_nodes": raw_nodes,
|
|
61
|
+
"wiki_nodes": wiki_nodes,
|
|
62
|
+
"navigation_tree": navigation_tree,
|
|
63
|
+
"search_index": search_index,
|
|
64
|
+
"relations_index": relations_index,
|
|
65
|
+
"section_manifest": section_manifest,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def _deterministic_marker(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> str:
|
|
69
|
+
payload = json.dumps(wiki_nodes, sort_keys=True, ensure_ascii=True, separators=(",", ":"))
|
|
70
|
+
digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
|
71
|
+
return f"sha256:{digest}"
|
|
72
|
+
|
|
73
|
+
def persist_graph(self, unified_graph: Dict[str, Any]) -> None:
|
|
74
|
+
self.output_graph_path.parent.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
with self.output_graph_path.open("w", encoding="utf-8") as handle:
|
|
76
|
+
json.dump(unified_graph, handle, indent=2, ensure_ascii=True)
|
|
77
|
+
|
|
78
|
+
def persist_manifests(self, unified_graph: Dict[str, Any]) -> Dict[str, int]:
|
|
79
|
+
self.generated_output_dir.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
artifacts = {
|
|
81
|
+
"search-index.json": unified_graph.get("search_index", []),
|
|
82
|
+
"relations-index.json": unified_graph.get("relations_index", []),
|
|
83
|
+
"section-manifest.json": unified_graph.get("section_manifest", []),
|
|
84
|
+
"navigation-tree.json": unified_graph.get("navigation_tree", {}),
|
|
85
|
+
}
|
|
86
|
+
for file_name, payload in artifacts.items():
|
|
87
|
+
output_path = self.generated_output_dir / file_name
|
|
88
|
+
with output_path.open("w", encoding="utf-8") as handle:
|
|
89
|
+
json.dump(payload, handle, indent=2, ensure_ascii=True)
|
|
90
|
+
return {"manifests": len(artifacts)}
|
|
91
|
+
|
|
92
|
+
def project_dirty_nodes(
|
|
93
|
+
self,
|
|
94
|
+
dirty_nodes: Dict[str, Dict[str, Any]],
|
|
95
|
+
deleted_nodes: List[str],
|
|
96
|
+
all_nodes: Dict[str, Dict[str, Any]],
|
|
97
|
+
cached_nodes: Dict[str, Dict[str, Any]],
|
|
98
|
+
) -> Dict[str, int]:
|
|
99
|
+
self.markdown_output_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
rendered = 0
|
|
102
|
+
for node_key, node in dirty_nodes.items():
|
|
103
|
+
previous_node = cached_nodes.get(node_key)
|
|
104
|
+
if isinstance(previous_node, dict):
|
|
105
|
+
previous_path = self._node_markdown_path(previous_node)
|
|
106
|
+
next_path = self._node_markdown_path(node)
|
|
107
|
+
if previous_path != next_path and previous_path.exists():
|
|
108
|
+
previous_path.unlink()
|
|
109
|
+
|
|
110
|
+
file_path = self._node_markdown_path(node)
|
|
111
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
with file_path.open("w", encoding="utf-8") as handle:
|
|
113
|
+
handle.write(self._render_node_markdown(node_key, node, all_nodes))
|
|
114
|
+
rendered += 1
|
|
115
|
+
|
|
116
|
+
deleted = 0
|
|
117
|
+
for node_key in deleted_nodes:
|
|
118
|
+
cached_node = cached_nodes.get(node_key)
|
|
119
|
+
if not isinstance(cached_node, dict):
|
|
120
|
+
continue
|
|
121
|
+
file_path = self._node_markdown_path(cached_node)
|
|
122
|
+
if file_path.exists():
|
|
123
|
+
file_path.unlink()
|
|
124
|
+
deleted += 1
|
|
125
|
+
|
|
126
|
+
indexes = self._render_index_pages(all_nodes)
|
|
127
|
+
return {
|
|
128
|
+
"rendered": rendered,
|
|
129
|
+
"deleted": deleted,
|
|
130
|
+
"indexes": indexes,
|
|
131
|
+
"manifests": 4,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
def _normalize_relations(self, provider_id: str, relations: Any) -> List[Dict[str, str]]:
|
|
135
|
+
if not isinstance(relations, list):
|
|
136
|
+
return []
|
|
137
|
+
normalized: List[Dict[str, str]] = []
|
|
138
|
+
for relation in relations:
|
|
139
|
+
if not isinstance(relation, dict):
|
|
140
|
+
continue
|
|
141
|
+
target = str(relation.get("target", "")).strip()
|
|
142
|
+
rel_type = str(relation.get("type", "related_to")).strip() or "related_to"
|
|
143
|
+
if not target:
|
|
144
|
+
continue
|
|
145
|
+
if "::" not in target:
|
|
146
|
+
target = f"{provider_id}::{target}"
|
|
147
|
+
normalized.append({"target": target, "type": rel_type})
|
|
148
|
+
return normalized
|
|
149
|
+
|
|
150
|
+
def _node_markdown_path(self, node: Dict[str, Any]) -> Path:
|
|
151
|
+
navigation = node.get("navigation", {}) if isinstance(node.get("navigation", {}), dict) else {}
|
|
152
|
+
section = str(navigation.get("section", "misc")).strip() or "misc"
|
|
153
|
+
slug = str(node.get("slug", "untitled")).strip() or "untitled"
|
|
154
|
+
return self.markdown_output_dir / section / f"{slug}.md"
|
|
155
|
+
|
|
156
|
+
def _render_node_markdown(
|
|
157
|
+
self,
|
|
158
|
+
node_key: str,
|
|
159
|
+
node: Dict[str, Any],
|
|
160
|
+
all_nodes: Dict[str, Dict[str, Any]],
|
|
161
|
+
) -> str:
|
|
162
|
+
lines: List[str] = []
|
|
163
|
+
lines.append(f"# {node.get('title', node_key)}")
|
|
164
|
+
lines.append("")
|
|
165
|
+
summary = str(node.get("summary", "")).strip()
|
|
166
|
+
if summary:
|
|
167
|
+
lines.append(summary)
|
|
168
|
+
lines.append("")
|
|
169
|
+
|
|
170
|
+
lines.append("## Contexto")
|
|
171
|
+
lines.append("")
|
|
172
|
+
lines.append(f"- kind: {node.get('kind', '')}")
|
|
173
|
+
lines.append(f"- domain: {node.get('domain', '')}")
|
|
174
|
+
lines.append(f"- section: {node.get('navigation', {}).get('section', '')}")
|
|
175
|
+
lines.append(f"- provider: {node.get('provider', '')}")
|
|
176
|
+
lines.append(f"- checksum: {node.get('checksum', '')}")
|
|
177
|
+
|
|
178
|
+
owner = str(node.get("owner", "")).strip()
|
|
179
|
+
if owner:
|
|
180
|
+
lines.append(f"- owner: {owner}")
|
|
181
|
+
|
|
182
|
+
tags = node.get("tags", [])
|
|
183
|
+
if isinstance(tags, list) and tags:
|
|
184
|
+
lines.append(f"- tags: {', '.join(str(tag) for tag in tags)}")
|
|
185
|
+
|
|
186
|
+
lines.append("")
|
|
187
|
+
|
|
188
|
+
lines.append("## Navegacion")
|
|
189
|
+
lines.append("")
|
|
190
|
+
section = str(node.get("navigation", {}).get("section", "misc"))
|
|
191
|
+
lines.append(f"- section_index: [{section}](index.md)")
|
|
192
|
+
lines.append(f"- wiki_home: [autodocs](../index.md)")
|
|
193
|
+
sibling_links = self._sibling_links(node, all_nodes)
|
|
194
|
+
if sibling_links:
|
|
195
|
+
lines.append(f"- related_in_section: {', '.join(sibling_links)}")
|
|
196
|
+
lines.append("")
|
|
197
|
+
|
|
198
|
+
lines.append("## Fuentes")
|
|
199
|
+
lines.append("")
|
|
200
|
+
source_refs = node.get("source_refs", [])
|
|
201
|
+
if isinstance(source_refs, list) and source_refs:
|
|
202
|
+
current_path = self._node_markdown_path(node)
|
|
203
|
+
for source_ref in source_refs:
|
|
204
|
+
target_path = self.output_graph_path.parents[1] / str(source_ref)
|
|
205
|
+
relative = os.path.relpath(target_path, current_path.parent).replace("\\", "/")
|
|
206
|
+
lines.append(f"- [{source_ref}]({relative})")
|
|
207
|
+
else:
|
|
208
|
+
lines.append("- none")
|
|
209
|
+
|
|
210
|
+
lines.append("")
|
|
211
|
+
lines.append("## Relaciones")
|
|
212
|
+
lines.append("")
|
|
213
|
+
lines.append("| relation_type | target |")
|
|
214
|
+
lines.append("|---|---|")
|
|
215
|
+
|
|
216
|
+
relation_count = 0
|
|
217
|
+
for relation in node.get("relations", []):
|
|
218
|
+
if not isinstance(relation, dict):
|
|
219
|
+
continue
|
|
220
|
+
target_key = str(relation.get("target", "")).strip()
|
|
221
|
+
if not target_key:
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
relation_count += 1
|
|
225
|
+
relation_type = str(relation.get("type", "related_to")).strip() or "related_to"
|
|
226
|
+
target_node = all_nodes.get(target_key)
|
|
227
|
+
|
|
228
|
+
if isinstance(target_node, dict):
|
|
229
|
+
current_path = self._node_markdown_path(node)
|
|
230
|
+
target_path = self._node_markdown_path(target_node)
|
|
231
|
+
relative = os.path.relpath(target_path, current_path.parent).replace("\\", "/")
|
|
232
|
+
target_title = str(target_node.get("title", target_key))
|
|
233
|
+
lines.append(f"| {relation_type} | [{target_title}]({relative}) |")
|
|
234
|
+
else:
|
|
235
|
+
lines.append(f"| {relation_type} | {target_key} |")
|
|
236
|
+
|
|
237
|
+
if relation_count == 0:
|
|
238
|
+
lines.append("| none | none |")
|
|
239
|
+
|
|
240
|
+
metadata = node.get("metadata", {}) if isinstance(node.get("metadata", {}), dict) else {}
|
|
241
|
+
if metadata:
|
|
242
|
+
lines.append("")
|
|
243
|
+
lines.append("## Datos tecnicos")
|
|
244
|
+
lines.append("")
|
|
245
|
+
lines.append("<details>")
|
|
246
|
+
lines.append("<summary>Ver payload normalizado</summary>")
|
|
247
|
+
lines.append("")
|
|
248
|
+
lines.append("```json")
|
|
249
|
+
lines.append(json.dumps(metadata, indent=2, ensure_ascii=True))
|
|
250
|
+
lines.append("```")
|
|
251
|
+
lines.append("</details>")
|
|
252
|
+
lines.append("")
|
|
253
|
+
|
|
254
|
+
return "\n".join(lines)
|
|
255
|
+
|
|
256
|
+
def _build_navigation_tree(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
257
|
+
sections = []
|
|
258
|
+
for section in SECTIONS:
|
|
259
|
+
section_id = section["id"]
|
|
260
|
+
pages = [
|
|
261
|
+
{
|
|
262
|
+
"title": node.get("title", ""),
|
|
263
|
+
"slug": node.get("slug", ""),
|
|
264
|
+
"path": node.get("path", ""),
|
|
265
|
+
}
|
|
266
|
+
for node in wiki_nodes.values()
|
|
267
|
+
if node.get("navigation", {}).get("section") == section_id
|
|
268
|
+
]
|
|
269
|
+
pages.sort(key=lambda item: item["title"])
|
|
270
|
+
sections.append(
|
|
271
|
+
{
|
|
272
|
+
"id": section_id,
|
|
273
|
+
"title": section["title"],
|
|
274
|
+
"description": section["description"],
|
|
275
|
+
"page_count": len(pages),
|
|
276
|
+
"pages": pages,
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
return {"sections": sections}
|
|
280
|
+
|
|
281
|
+
def _build_search_index(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
282
|
+
entries = []
|
|
283
|
+
for node in wiki_nodes.values():
|
|
284
|
+
entries.append(
|
|
285
|
+
{
|
|
286
|
+
"title": node.get("title", ""),
|
|
287
|
+
"summary": node.get("summary", ""),
|
|
288
|
+
"kind": node.get("kind", ""),
|
|
289
|
+
"domain": node.get("domain", ""),
|
|
290
|
+
"section": node.get("navigation", {}).get("section", ""),
|
|
291
|
+
"tags": node.get("tags", []),
|
|
292
|
+
"path": node.get("path", ""),
|
|
293
|
+
}
|
|
294
|
+
)
|
|
295
|
+
entries.sort(key=lambda item: (str(item.get("section", "")), str(item.get("title", ""))))
|
|
296
|
+
return entries
|
|
297
|
+
|
|
298
|
+
def _build_relations_index(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
299
|
+
edges: List[Dict[str, Any]] = []
|
|
300
|
+
for node_key, node in wiki_nodes.items():
|
|
301
|
+
for relation in node.get("relations", []):
|
|
302
|
+
if not isinstance(relation, dict):
|
|
303
|
+
continue
|
|
304
|
+
edges.append(
|
|
305
|
+
{
|
|
306
|
+
"source": node_key,
|
|
307
|
+
"source_title": node.get("title", node_key),
|
|
308
|
+
"type": relation.get("type", "related_to"),
|
|
309
|
+
"target": relation.get("target", ""),
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
edges.sort(key=lambda item: (str(item.get("type", "")), str(item.get("source_title", ""))))
|
|
313
|
+
return edges
|
|
314
|
+
|
|
315
|
+
def _build_section_manifest(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
316
|
+
manifests: List[Dict[str, Any]] = []
|
|
317
|
+
for section in SECTIONS:
|
|
318
|
+
section_id = section["id"]
|
|
319
|
+
pages = [
|
|
320
|
+
node.get("path", "")
|
|
321
|
+
for node in wiki_nodes.values()
|
|
322
|
+
if node.get("navigation", {}).get("section") == section_id
|
|
323
|
+
]
|
|
324
|
+
pages.sort()
|
|
325
|
+
manifests.append(
|
|
326
|
+
{
|
|
327
|
+
"id": section_id,
|
|
328
|
+
"title": section["title"],
|
|
329
|
+
"description": section["description"],
|
|
330
|
+
"page_count": len(pages),
|
|
331
|
+
"pages": pages,
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
return manifests
|
|
335
|
+
|
|
336
|
+
def _render_index_pages(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> int:
|
|
337
|
+
count = 0
|
|
338
|
+
self.markdown_output_dir.mkdir(parents=True, exist_ok=True)
|
|
339
|
+
|
|
340
|
+
root_index = self.markdown_output_dir / "index.md"
|
|
341
|
+
with root_index.open("w", encoding="utf-8") as handle:
|
|
342
|
+
handle.write(self._render_root_index(wiki_nodes))
|
|
343
|
+
count += 1
|
|
344
|
+
|
|
345
|
+
for section in SECTIONS:
|
|
346
|
+
section_dir = self.markdown_output_dir / section["id"]
|
|
347
|
+
section_dir.mkdir(parents=True, exist_ok=True)
|
|
348
|
+
section_index = section_dir / "index.md"
|
|
349
|
+
with section_index.open("w", encoding="utf-8") as handle:
|
|
350
|
+
handle.write(self._render_section_index(section, wiki_nodes))
|
|
351
|
+
count += 1
|
|
352
|
+
|
|
353
|
+
return count
|
|
354
|
+
|
|
355
|
+
def _render_root_index(self, wiki_nodes: Dict[str, Dict[str, Any]]) -> str:
|
|
356
|
+
top_sections = self._top_sections(wiki_nodes, limit=4)
|
|
357
|
+
featured_pages = self._featured_pages(wiki_nodes, limit=8)
|
|
358
|
+
lines = [
|
|
359
|
+
"# AutoDocs",
|
|
360
|
+
"",
|
|
361
|
+
"Wiki interna de MCP Efficiency Engine. El source of truth es el grafo",
|
|
362
|
+
"unificado y el Markdown es una proyeccion derivada para lectura humana.",
|
|
363
|
+
"",
|
|
364
|
+
"## Resumen",
|
|
365
|
+
"",
|
|
366
|
+
f"- total_pages: {len(wiki_nodes)}",
|
|
367
|
+
f"- generated_graph: {self.output_graph_path.as_posix().replace(str(self.output_graph_path.parents[1]).replace('\\', '/'), 'autodocs') if False else 'autodocs/generated/unified-graph.json'}",
|
|
368
|
+
f"- search_manifest: autodocs/generated/search-index.json",
|
|
369
|
+
f"- validation_report: autodocs/generated/validation-report.md",
|
|
370
|
+
"",
|
|
371
|
+
"## Entry Points",
|
|
372
|
+
"",
|
|
373
|
+
]
|
|
374
|
+
|
|
375
|
+
for section_id, page_count in top_sections:
|
|
376
|
+
section = next((item for item in SECTIONS if item["id"] == section_id), None)
|
|
377
|
+
if not section:
|
|
378
|
+
continue
|
|
379
|
+
lines.append(f"- [{section['title']}]({section_id}/index.md) - {page_count} paginas")
|
|
380
|
+
|
|
381
|
+
lines.extend([
|
|
382
|
+
"",
|
|
383
|
+
"## Destacados",
|
|
384
|
+
"",
|
|
385
|
+
])
|
|
386
|
+
|
|
387
|
+
for page in featured_pages:
|
|
388
|
+
lines.append(
|
|
389
|
+
f"- [{page.get('title', '')}]({page.get('path', '')}) - {page.get('summary', '')}"
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
lines.extend([
|
|
393
|
+
"",
|
|
394
|
+
"## Secciones",
|
|
395
|
+
"",
|
|
396
|
+
"| section | description | pages |",
|
|
397
|
+
"|---|---|---|",
|
|
398
|
+
])
|
|
399
|
+
|
|
400
|
+
for section in SECTIONS:
|
|
401
|
+
page_count = sum(
|
|
402
|
+
1
|
|
403
|
+
for node in wiki_nodes.values()
|
|
404
|
+
if node.get("navigation", {}).get("section") == section["id"]
|
|
405
|
+
)
|
|
406
|
+
lines.append(
|
|
407
|
+
f"| [{section['title']}]({section['id']}/index.md) | {section['description']} | {page_count} |"
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
lines.append("")
|
|
411
|
+
return "\n".join(lines)
|
|
412
|
+
|
|
413
|
+
def _render_section_index(self, section: Dict[str, str], wiki_nodes: Dict[str, Dict[str, Any]]) -> str:
|
|
414
|
+
pages = [
|
|
415
|
+
node
|
|
416
|
+
for node in wiki_nodes.values()
|
|
417
|
+
if node.get("navigation", {}).get("section") == section["id"]
|
|
418
|
+
]
|
|
419
|
+
pages.sort(key=lambda item: str(item.get("title", "")))
|
|
420
|
+
|
|
421
|
+
lines = [
|
|
422
|
+
f"# {section['title']}",
|
|
423
|
+
"",
|
|
424
|
+
section["description"],
|
|
425
|
+
"",
|
|
426
|
+
"[Volver a AutoDocs](../index.md)",
|
|
427
|
+
"",
|
|
428
|
+
]
|
|
429
|
+
|
|
430
|
+
if not pages:
|
|
431
|
+
lines.append("Sin contenido proyectado todavia.")
|
|
432
|
+
lines.append("")
|
|
433
|
+
return "\n".join(lines)
|
|
434
|
+
|
|
435
|
+
lines.append("## Resumen")
|
|
436
|
+
lines.append("")
|
|
437
|
+
lines.append(f"- total_pages: {len(pages)}")
|
|
438
|
+
domain_counts = self._domain_counts(pages)
|
|
439
|
+
if domain_counts:
|
|
440
|
+
lines.append(
|
|
441
|
+
"- domains: " + ", ".join(f"{domain} ({count})" for domain, count in domain_counts)
|
|
442
|
+
)
|
|
443
|
+
lines.append("")
|
|
444
|
+
|
|
445
|
+
lines.append("## Paginas")
|
|
446
|
+
lines.append("")
|
|
447
|
+
|
|
448
|
+
for page in pages:
|
|
449
|
+
page_path = Path(str(page.get("path", ""))).name
|
|
450
|
+
lines.append(f"- [{page.get('title', '')}]({page_path}) - {page.get('summary', '')}")
|
|
451
|
+
|
|
452
|
+
lines.append("")
|
|
453
|
+
return "\n".join(lines)
|
|
454
|
+
|
|
455
|
+
def _top_sections(self, wiki_nodes: Dict[str, Dict[str, Any]], limit: int) -> List[tuple[str, int]]:
|
|
456
|
+
counts = []
|
|
457
|
+
for section in SECTIONS:
|
|
458
|
+
page_count = sum(
|
|
459
|
+
1
|
|
460
|
+
for node in wiki_nodes.values()
|
|
461
|
+
if node.get("navigation", {}).get("section") == section["id"]
|
|
462
|
+
)
|
|
463
|
+
if page_count > 0:
|
|
464
|
+
counts.append((section["id"], page_count))
|
|
465
|
+
counts.sort(key=lambda item: (-item[1], item[0]))
|
|
466
|
+
return counts[:limit]
|
|
467
|
+
|
|
468
|
+
def _featured_pages(self, wiki_nodes: Dict[str, Dict[str, Any]], limit: int) -> List[Dict[str, Any]]:
|
|
469
|
+
preferred_sections = ["routing", "agents", "skills", "policies", "specs", "observability", "reports"]
|
|
470
|
+
featured: List[Dict[str, Any]] = []
|
|
471
|
+
for section_id in preferred_sections:
|
|
472
|
+
candidates = [
|
|
473
|
+
node
|
|
474
|
+
for node in wiki_nodes.values()
|
|
475
|
+
if node.get("navigation", {}).get("section") == section_id
|
|
476
|
+
]
|
|
477
|
+
candidates.sort(key=lambda item: str(item.get("title", "")))
|
|
478
|
+
if candidates:
|
|
479
|
+
featured.append(candidates[0])
|
|
480
|
+
if len(featured) >= limit:
|
|
481
|
+
break
|
|
482
|
+
return featured[:limit]
|
|
483
|
+
|
|
484
|
+
def _domain_counts(self, pages: List[Dict[str, Any]]) -> List[tuple[str, int]]:
|
|
485
|
+
counts: Dict[str, int] = {}
|
|
486
|
+
for page in pages:
|
|
487
|
+
domain = str(page.get("domain", "unknown")).strip() or "unknown"
|
|
488
|
+
counts[domain] = counts.get(domain, 0) + 1
|
|
489
|
+
ordered = sorted(counts.items(), key=lambda item: (-item[1], item[0]))
|
|
490
|
+
return ordered[:6]
|
|
491
|
+
|
|
492
|
+
def _sibling_links(self, node: Dict[str, Any], all_nodes: Dict[str, Dict[str, Any]]) -> List[str]:
|
|
493
|
+
section = str(node.get("navigation", {}).get("section", "misc"))
|
|
494
|
+
current_path = self._node_markdown_path(node)
|
|
495
|
+
siblings = [
|
|
496
|
+
candidate
|
|
497
|
+
for candidate in all_nodes.values()
|
|
498
|
+
if candidate.get("navigation", {}).get("section") == section and candidate.get("slug") != node.get("slug")
|
|
499
|
+
]
|
|
500
|
+
siblings.sort(key=lambda item: str(item.get("title", "")))
|
|
501
|
+
links: List[str] = []
|
|
502
|
+
for sibling in siblings[:3]:
|
|
503
|
+
sibling_path = self._node_markdown_path(sibling)
|
|
504
|
+
relative = os.path.relpath(sibling_path, current_path.parent).replace("\\", "/")
|
|
505
|
+
links.append(f"[{sibling.get('title', '')}]({relative})")
|
|
506
|
+
return links
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IncrementalEngine:
|
|
7
|
+
def __init__(self, cache_path: Path) -> None:
|
|
8
|
+
self.cache_path = cache_path
|
|
9
|
+
|
|
10
|
+
def _extract_nodes(self, loaded: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
|
|
11
|
+
preferred = loaded.get("wiki_nodes")
|
|
12
|
+
if isinstance(preferred, dict):
|
|
13
|
+
return preferred
|
|
14
|
+
|
|
15
|
+
compatibility = loaded.get("nodes")
|
|
16
|
+
if isinstance(compatibility, dict):
|
|
17
|
+
return compatibility
|
|
18
|
+
|
|
19
|
+
return {}
|
|
20
|
+
|
|
21
|
+
def _load_cached_graph(self) -> Dict[str, Any]:
|
|
22
|
+
if not self.cache_path.exists():
|
|
23
|
+
return {"last_updated": "", "nodes": {}}
|
|
24
|
+
try:
|
|
25
|
+
with self.cache_path.open("r", encoding="utf-8") as handle:
|
|
26
|
+
loaded = json.load(handle)
|
|
27
|
+
|
|
28
|
+
if not isinstance(loaded, dict):
|
|
29
|
+
return {"last_updated": "", "nodes": {}}
|
|
30
|
+
|
|
31
|
+
nodes = self._extract_nodes(loaded)
|
|
32
|
+
return {
|
|
33
|
+
"last_updated": str(loaded.get("last_updated", "")),
|
|
34
|
+
"nodes": nodes,
|
|
35
|
+
}
|
|
36
|
+
except Exception:
|
|
37
|
+
return {"last_updated": "", "nodes": {}}
|
|
38
|
+
|
|
39
|
+
def diff(self, incoming_nodes: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
40
|
+
cached = self._load_cached_graph()
|
|
41
|
+
cached_nodes = cached.get("nodes", {})
|
|
42
|
+
|
|
43
|
+
dirty_nodes: Dict[str, Dict[str, Any]] = {}
|
|
44
|
+
for node_key, incoming in incoming_nodes.items():
|
|
45
|
+
old = cached_nodes.get(node_key)
|
|
46
|
+
if old is None:
|
|
47
|
+
dirty_nodes[node_key] = incoming
|
|
48
|
+
continue
|
|
49
|
+
if str(old.get("checksum", "")) != str(incoming.get("checksum", "")):
|
|
50
|
+
dirty_nodes[node_key] = incoming
|
|
51
|
+
|
|
52
|
+
deleted_nodes = [key for key in cached_nodes.keys() if key not in incoming_nodes]
|
|
53
|
+
return {
|
|
54
|
+
"dirty_nodes": dirty_nodes,
|
|
55
|
+
"deleted_nodes": deleted_nodes,
|
|
56
|
+
"cached_graph": cached,
|
|
57
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import Any, Dict, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PluginManager:
|
|
6
|
+
def __init__(self) -> None:
|
|
7
|
+
self._providers: List[Any] = []
|
|
8
|
+
|
|
9
|
+
def register_provider(self, provider: Any) -> None:
|
|
10
|
+
if not hasattr(provider, "provider_id"):
|
|
11
|
+
raise ValueError("Provider must define provider_id")
|
|
12
|
+
if not hasattr(provider, "gather_knowledge"):
|
|
13
|
+
raise ValueError("Provider must implement gather_knowledge()")
|
|
14
|
+
self._providers.append(provider)
|
|
15
|
+
|
|
16
|
+
def gather_all(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
17
|
+
contracts: List[Dict[str, Any]] = []
|
|
18
|
+
errors: List[Dict[str, Any]] = []
|
|
19
|
+
for provider in self._providers:
|
|
20
|
+
try:
|
|
21
|
+
contract = provider.gather_knowledge()
|
|
22
|
+
if isinstance(contract, dict):
|
|
23
|
+
contracts.append(contract)
|
|
24
|
+
else:
|
|
25
|
+
errors.append(
|
|
26
|
+
{
|
|
27
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
28
|
+
"provider_id": str(getattr(provider, "provider_id", "unknown")),
|
|
29
|
+
"error": "Provider returned non-dict contract",
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
except Exception as exc:
|
|
33
|
+
errors.append(
|
|
34
|
+
{
|
|
35
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
36
|
+
"provider_id": str(getattr(provider, "provider_id", "unknown")),
|
|
37
|
+
"error": str(exc),
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
return {"contracts": contracts, "errors": errors}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-efficiency-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Motor de orquestacion para agentes MCP con routing por dominio y bootstrap portable.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcpee": "./bin/mcpee.js",
|
|
9
|
+
"mcp-efficiency-engine": "./bin/mcpee.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"autodocs/README.md",
|
|
14
|
+
"autodocs/schema/",
|
|
15
|
+
"autolearning/",
|
|
16
|
+
"scripts/",
|
|
17
|
+
"tooling/",
|
|
18
|
+
"templates/",
|
|
19
|
+
"repo-intake/",
|
|
20
|
+
"repo-registry/repos.schema.json",
|
|
21
|
+
"repo-registry/repos.template.json",
|
|
22
|
+
"orchestrator/",
|
|
23
|
+
"observability/",
|
|
24
|
+
"optimization/",
|
|
25
|
+
"policies/",
|
|
26
|
+
"specs/",
|
|
27
|
+
"memory/",
|
|
28
|
+
"context/repomix/repomix.config.json",
|
|
29
|
+
".github/",
|
|
30
|
+
".vscode/mcp.json",
|
|
31
|
+
"AGENTS.md",
|
|
32
|
+
"ARCHITECTURE.md",
|
|
33
|
+
"FINAL_USAGE_GUIDE.md",
|
|
34
|
+
"README.md",
|
|
35
|
+
"README_WIKI.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"requirements.txt"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"postinstall": "node ./bin/install-host.js --postinstall",
|
|
41
|
+
"install:host": "node ./bin/install-host.js",
|
|
42
|
+
"bootstrap": "node ./bin/mcpee.js bootstrap",
|
|
43
|
+
"validate": "node ./bin/mcpee.js validate",
|
|
44
|
+
"hi": "node ./bin/mcpee.js hi",
|
|
45
|
+
"bye": "node ./bin/mcpee.js bye",
|
|
46
|
+
"intake": "node ./bin/mcpee.js intake",
|
|
47
|
+
"pack:check": "npm pack --dry-run"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"mcp",
|
|
54
|
+
"agents",
|
|
55
|
+
"routing",
|
|
56
|
+
"codegraph",
|
|
57
|
+
"gitnexus",
|
|
58
|
+
"bootstrap",
|
|
59
|
+
"autodocs",
|
|
60
|
+
"autolearning",
|
|
61
|
+
"observability"
|
|
62
|
+
],
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@storybook/addon-a11y": "^10.4.6",
|
|
65
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
66
|
+
"@storybook/addon-interactions": "^8.6.14",
|
|
67
|
+
"@storybook/angular": "^10.4.6",
|
|
68
|
+
"storybook": "^10.4.6"
|
|
69
|
+
}
|
|
70
|
+
}
|