aios-core 4.2.15 → 4.4.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/.aios-core/cli/commands/validate/index.js +1 -1
- package/.aios-core/core/code-intel/code-intel-client.js +19 -5
- package/.aios-core/core/code-intel/helpers/creation-helper.js +183 -0
- package/.aios-core/core/code-intel/helpers/devops-helper.js +166 -0
- package/.aios-core/core/code-intel/helpers/planning-helper.js +248 -0
- package/.aios-core/core/code-intel/helpers/qa-helper.js +187 -0
- package/.aios-core/core/code-intel/helpers/story-helper.js +146 -0
- package/.aios-core/core/code-intel/hook-runtime.js +186 -0
- package/.aios-core/core/code-intel/index.js +2 -0
- package/.aios-core/core/code-intel/providers/code-graph-provider.js +8 -0
- package/.aios-core/core/code-intel/providers/provider-interface.js +9 -0
- package/.aios-core/core/code-intel/providers/registry-provider.js +515 -0
- package/.aios-core/core/config/schemas/framework-config.schema.json +155 -7
- package/.aios-core/core/config/schemas/project-config.schema.json +329 -15
- package/.aios-core/core/config/template-overrides.js +84 -0
- package/.aios-core/core/docs/troubleshooting-guide.md +1 -1
- package/.aios-core/core/doctor/checks/agent-memory.js +63 -0
- package/.aios-core/core/doctor/checks/claude-md.js +56 -0
- package/.aios-core/core/doctor/checks/code-intel.js +131 -0
- package/.aios-core/core/doctor/checks/commands-count.js +81 -0
- package/.aios-core/core/doctor/checks/core-config.js +53 -0
- package/.aios-core/core/doctor/checks/entity-registry.js +53 -0
- package/.aios-core/core/doctor/checks/git-hooks.js +50 -0
- package/.aios-core/core/doctor/checks/graph-dashboard.js +48 -0
- package/.aios-core/core/doctor/checks/hooks-claude-count.js +118 -0
- package/.aios-core/core/doctor/checks/ide-sync.js +85 -0
- package/.aios-core/core/doctor/checks/index.js +46 -0
- package/.aios-core/core/doctor/checks/node-version.js +33 -0
- package/.aios-core/core/doctor/checks/npm-packages.js +35 -0
- package/.aios-core/core/doctor/checks/rules-files.js +61 -0
- package/.aios-core/core/doctor/checks/settings-json.js +121 -0
- package/.aios-core/core/doctor/checks/skills-count.js +72 -0
- package/.aios-core/core/doctor/fix-handler.js +165 -0
- package/.aios-core/core/doctor/formatters/json.js +14 -0
- package/.aios-core/core/doctor/formatters/text.js +59 -0
- package/.aios-core/core/doctor/index.js +94 -0
- package/.aios-core/core/graph-dashboard/cli.js +361 -0
- package/.aios-core/core/graph-dashboard/data-sources/code-intel-source.js +234 -0
- package/.aios-core/core/graph-dashboard/data-sources/metrics-source.js +95 -0
- package/.aios-core/core/graph-dashboard/data-sources/registry-source.js +106 -0
- package/.aios-core/core/graph-dashboard/formatters/dot-formatter.js +45 -0
- package/.aios-core/core/graph-dashboard/formatters/html-formatter.js +1437 -0
- package/.aios-core/core/graph-dashboard/formatters/json-formatter.js +13 -0
- package/.aios-core/core/graph-dashboard/formatters/mermaid-formatter.js +59 -0
- package/.aios-core/core/graph-dashboard/index.js +21 -0
- package/.aios-core/core/graph-dashboard/renderers/stats-renderer.js +217 -0
- package/.aios-core/core/graph-dashboard/renderers/status-renderer.js +125 -0
- package/.aios-core/core/graph-dashboard/renderers/tree-renderer.js +119 -0
- package/.aios-core/core/health-check/base-check.js +1 -1
- package/.aios-core/core/health-check/check-registry.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/build-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/ci-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/deployment-readiness.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/docker-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/env-file.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/index.js +1 -1
- package/.aios-core/core/health-check/checks/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/disk-space.js +1 -1
- package/.aios-core/core/health-check/checks/local/environment-vars.js +1 -1
- package/.aios-core/core/health-check/checks/local/git-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/ide-detection.js +1 -1
- package/.aios-core/core/health-check/checks/local/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/memory.js +1 -1
- package/.aios-core/core/health-check/checks/local/network.js +1 -1
- package/.aios-core/core/health-check/checks/local/npm-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/shell-environment.js +1 -1
- package/.aios-core/core/health-check/checks/project/agent-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/aios-directory.js +1 -1
- package/.aios-core/core/health-check/checks/project/dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/project/framework-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/index.js +1 -1
- package/.aios-core/core/health-check/checks/project/node-version.js +1 -1
- package/.aios-core/core/health-check/checks/project/package-json.js +1 -1
- package/.aios-core/core/health-check/checks/project/task-definitions.js +1 -1
- package/.aios-core/core/health-check/checks/project/workflow-dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/repository/branch-protection.js +1 -1
- package/.aios-core/core/health-check/checks/repository/commit-history.js +1 -1
- package/.aios-core/core/health-check/checks/repository/conflicts.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-repo.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-status.js +1 -1
- package/.aios-core/core/health-check/checks/repository/gitignore.js +1 -1
- package/.aios-core/core/health-check/checks/repository/index.js +1 -1
- package/.aios-core/core/health-check/checks/repository/large-files.js +1 -1
- package/.aios-core/core/health-check/checks/repository/lockfile-integrity.js +1 -1
- package/.aios-core/core/health-check/checks/services/api-endpoints.js +1 -1
- package/.aios-core/core/health-check/checks/services/claude-code.js +1 -1
- package/.aios-core/core/health-check/checks/services/gemini-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/github-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/index.js +1 -1
- package/.aios-core/core/health-check/checks/services/mcp-integration.js +1 -1
- package/.aios-core/core/health-check/engine.js +1 -1
- package/.aios-core/core/health-check/healers/backup-manager.js +1 -1
- package/.aios-core/core/health-check/healers/index.js +1 -1
- package/.aios-core/core/health-check/index.js +9 -2
- package/.aios-core/core/health-check/reporters/console.js +1 -1
- package/.aios-core/core/health-check/reporters/index.js +1 -1
- package/.aios-core/core/health-check/reporters/json.js +1 -1
- package/.aios-core/core/health-check/reporters/markdown.js +1 -1
- package/.aios-core/core/ids/layer-classifier.js +65 -0
- package/.aios-core/core/ids/registry-updater.js +49 -0
- package/.aios-core/core/index.esm.js +1 -1
- package/.aios-core/core/index.js +1 -1
- package/.aios-core/core/session/context-detector.js +2 -7
- package/.aios-core/core/synapse/context/context-tracker.js +9 -1
- package/.aios-core/core/synapse/engine.js +33 -13
- package/.aios-core/core/synapse/memory/memory-bridge.js +17 -43
- package/.aios-core/core/synapse/memory/synapse-memory-provider.js +201 -0
- package/.aios-core/core/synapse/runtime/hook-runtime.js +40 -2
- package/.aios-core/core/synapse/session/session-manager.js +3 -2
- package/.aios-core/core/synapse/utils/atomic-write.js +79 -0
- package/.aios-core/core-config.yaml +34 -1
- package/.aios-core/data/aios-kb.md +2 -2
- package/.aios-core/data/capability-detection.js +290 -0
- package/.aios-core/data/entity-registry.yaml +10450 -2129
- package/.aios-core/data/mcp-discipline.js +166 -0
- package/.aios-core/data/mcp-tool-examples.yaml +215 -0
- package/.aios-core/data/tok2-validation.js +168 -0
- package/.aios-core/data/tok3-token-comparison.js +123 -0
- package/.aios-core/data/tool-registry.yaml +648 -0
- package/.aios-core/data/tool-search-validation.js +174 -0
- package/.aios-core/data/workflow-chains.yaml +156 -0
- package/.aios-core/development/agents/aios-master.md +17 -10
- package/.aios-core/development/agents/analyst/MEMORY.md +33 -0
- package/.aios-core/development/agents/analyst.md +17 -10
- package/.aios-core/development/agents/architect/MEMORY.md +39 -0
- package/.aios-core/development/agents/architect.md +17 -10
- package/.aios-core/development/agents/data-engineer/MEMORY.md +32 -0
- package/.aios-core/development/agents/data-engineer.md +17 -10
- package/.aios-core/development/agents/dev/MEMORY.md +46 -0
- package/.aios-core/development/agents/dev.md +18 -11
- package/.aios-core/development/agents/devops/MEMORY.md +39 -0
- package/.aios-core/development/agents/devops.md +44 -10
- package/.aios-core/development/agents/pm/MEMORY.md +38 -0
- package/.aios-core/development/agents/pm.md +17 -10
- package/.aios-core/development/agents/po/MEMORY.md +45 -0
- package/.aios-core/development/agents/po.md +17 -10
- package/.aios-core/development/agents/qa/MEMORY.md +42 -0
- package/.aios-core/development/agents/qa.md +18 -11
- package/.aios-core/development/agents/sm/MEMORY.md +31 -0
- package/.aios-core/development/agents/sm.md +17 -10
- package/.aios-core/development/agents/squad-creator.md +18 -9
- package/.aios-core/development/agents/ux/MEMORY.md +31 -0
- package/.aios-core/development/agents/ux-design-expert.md +16 -9
- package/.aios-core/development/checklists/issue-triage-checklist.md +35 -0
- package/.aios-core/development/checklists/memory-audit-checklist.md +53 -0
- package/.aios-core/development/scripts/issue-triage.js +171 -0
- package/.aios-core/development/scripts/populate-entity-registry.js +412 -19
- package/.aios-core/development/scripts/unified-activation-pipeline.js +31 -10
- package/.aios-core/development/tasks/analyze-project-structure.md +48 -0
- package/.aios-core/development/tasks/apply-qa-fixes.md +7 -0
- package/.aios-core/development/tasks/architect-analyze-impact.md +8 -1
- package/.aios-core/development/tasks/brownfield-create-epic.md +41 -0
- package/.aios-core/development/tasks/brownfield-create-story.md +7 -0
- package/.aios-core/development/tasks/build-autonomous.md +7 -0
- package/.aios-core/development/tasks/create-deep-research-prompt.md +7 -0
- package/.aios-core/development/tasks/create-doc.md +44 -0
- package/.aios-core/development/tasks/create-next-story.md +17 -0
- package/.aios-core/development/tasks/create-suite.md +7 -0
- package/.aios-core/development/tasks/dev-develop-story.md +9 -1
- package/.aios-core/development/tasks/execute-checklist.md +7 -0
- package/.aios-core/development/tasks/github-devops-github-pr-automation.md +56 -0
- package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +70 -0
- package/.aios-core/development/tasks/github-issue-triage.md +118 -0
- package/.aios-core/development/tasks/health-check.yaml +206 -171
- package/.aios-core/development/tasks/kb-mode-interaction.md +3 -3
- package/.aios-core/development/tasks/plan-create-context.md +47 -1
- package/.aios-core/development/tasks/plan-create-implementation.md +55 -0
- package/.aios-core/development/tasks/po-close-story.md +7 -0
- package/.aios-core/development/tasks/pr-automation.md +5 -5
- package/.aios-core/development/tasks/qa-create-fix-request.md +7 -0
- package/.aios-core/development/tasks/qa-fix-issues.md +7 -0
- package/.aios-core/development/tasks/qa-gate.md +56 -0
- package/.aios-core/development/tasks/qa-review-story.md +32 -1
- package/.aios-core/development/tasks/release-management.md +7 -0
- package/.aios-core/development/tasks/resolve-github-issue.md +608 -0
- package/.aios-core/development/tasks/review-contributor-pr.md +152 -0
- package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
- package/.aios-core/development/tasks/spec-critique.md +8 -0
- package/.aios-core/development/tasks/spec-gather-requirements.md +7 -0
- package/.aios-core/development/tasks/spec-research-dependencies.md +4 -0
- package/.aios-core/development/tasks/spec-write-spec.md +5 -0
- package/.aios-core/development/tasks/triage-github-issues.md +356 -0
- package/.aios-core/development/tasks/validate-agents.md +4 -0
- package/.aios-core/development/tasks/validate-next-story.md +17 -0
- package/.aios-core/development/templates/agent-handoff-tmpl.yaml +48 -0
- package/.aios-core/development/templates/code-intel-integration-pattern.md +199 -0
- package/.aios-core/development/templates/ptc-entity-validation.md +113 -0
- package/.aios-core/development/templates/ptc-qa-gate.md +100 -0
- package/.aios-core/development/templates/ptc-research-aggregation.md +94 -0
- package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
- package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
- package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
- package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
- package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
- package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
- package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
- package/.aios-core/development/templates/squad/agent-template.md +11 -0
- package/.aios-core/development/templates/squad/task-template.md +21 -0
- package/.aios-core/development/templates/squad-template/LICENSE +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +1 -1
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1 -1
- package/.aios-core/framework-config.yaml +8 -0
- package/.aios-core/index.esm.js +1 -1
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/integrations/ai-providers/index.js +1 -1
- package/.aios-core/infrastructure/schemas/task-v3-schema.json +6 -0
- package/.aios-core/infrastructure/scripts/collect-tool-usage.js +311 -0
- package/.aios-core/infrastructure/scripts/generate-optimization-report.js +497 -0
- package/.aios-core/infrastructure/scripts/generate-settings-json.js +300 -0
- package/.aios-core/infrastructure/scripts/git-config-detector.js +65 -9
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +3 -1
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/github-copilot.js +184 -0
- package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +182 -182
- package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
- package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
- package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
- package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
- package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
- package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
- package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
- package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
- package/.aios-core/install-manifest.yaml +613 -305
- package/.aios-core/lib/build.json +1 -0
- package/.aios-core/local-config.yaml.template +71 -71
- package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
- package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
- package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
- package/.aios-core/monitor/hooks/notification.py +29 -29
- package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
- package/.aios-core/monitor/hooks/pre_compact.py +29 -29
- package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
- package/.aios-core/monitor/hooks/stop.py +29 -29
- package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
- package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
- package/.aios-core/product/templates/adr.hbs +125 -125
- package/.aios-core/product/templates/dbdr.hbs +241 -241
- package/.aios-core/product/templates/epic.hbs +212 -212
- package/.aios-core/product/templates/ide-rules/claude-rules.md +125 -0
- package/.aios-core/product/templates/pmdr.hbs +186 -186
- package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
- package/.aios-core/product/templates/prd.hbs +201 -201
- package/.aios-core/product/templates/story.hbs +263 -263
- package/.aios-core/product/templates/task.hbs +170 -170
- package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
- package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
- package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
- package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
- package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
- package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
- package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
- package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
- package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
- package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
- package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
- package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
- package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
- package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
- package/.aios-core/product/templates/tmpl-view.sql +177 -177
- package/.aios-core/scripts/pm.sh +0 -0
- package/.aios-core/user-guide.md +15 -15
- package/.aios-core/utils/filters/constants.js +10 -0
- package/.aios-core/utils/filters/content-filter.js +223 -0
- package/.aios-core/utils/filters/field-filter.js +126 -0
- package/.aios-core/utils/filters/index.js +180 -0
- package/.aios-core/utils/filters/schema-filter.js +157 -0
- package/.claude/CLAUDE.md +62 -0
- package/.claude/hooks/enforce-architecture-first.py +196 -196
- package/.claude/hooks/enforce-git-push-authority.sh +33 -0
- package/.claude/hooks/mind-clone-governance.py +192 -192
- package/.claude/hooks/read-protection.py +151 -151
- package/.claude/hooks/slug-validation.py +176 -176
- package/.claude/hooks/sql-governance.py +182 -182
- package/.claude/hooks/synapse-engine.cjs +28 -5
- package/.claude/hooks/write-path-validation.py +194 -194
- package/.claude/rules/agent-authority.md +105 -0
- package/.claude/rules/agent-handoff.md +97 -0
- package/.claude/rules/agent-memory-imports.md +15 -0
- package/.claude/rules/coderabbit-integration.md +101 -0
- package/.claude/rules/ids-principles.md +119 -0
- package/.claude/rules/story-lifecycle.md +145 -0
- package/.claude/rules/tool-examples.md +64 -0
- package/.claude/rules/tool-response-filtering.md +57 -0
- package/.claude/rules/workflow-execution.md +150 -0
- package/LICENSE +33 -33
- package/bin/aios-graph.js +9 -0
- package/bin/aios-init.js +2 -2
- package/bin/aios-minimal.js +0 -0
- package/bin/aios.js +17 -221
- package/bin/utils/detect-fsmonitor.js +70 -0
- package/bin/utils/framework-guard.js +238 -0
- package/bin/utils/validate-publish.js +108 -0
- package/package.json +6 -3
- package/packages/aios-install/bin/aios-install.js +0 -0
- package/packages/aios-install/bin/edmcp.js +0 -0
- package/packages/aios-pro-cli/bin/aios-pro.js +2 -0
- package/packages/installer/src/config/templates/core-config-template.js +25 -0
- package/packages/installer/src/installer/brownfield-upgrader.js +68 -5
- package/packages/installer/src/merger/index.js +3 -0
- package/packages/installer/src/merger/strategies/index.js +6 -0
- package/packages/installer/src/merger/strategies/yaml-merger.js +181 -0
- package/packages/installer/src/updater/index.js +4 -4
- package/packages/installer/src/wizard/i18n.js +321 -3
- package/packages/installer/src/wizard/ide-config-generator.js +173 -25
- package/packages/installer/src/wizard/index.js +119 -1
- package/packages/installer/src/wizard/pro-setup.js +137 -121
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +271 -0
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +192 -0
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +610 -0
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +134 -0
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +186 -0
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +309 -0
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +230 -0
- package/packages/installer/tests/unit/merger/strategies.test.js +2 -2
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +327 -0
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/dashboard-parallel-dev.sh +0 -0
- package/scripts/dashboard-parallel-phase3.sh +0 -0
- package/scripts/dashboard-parallel-phase4.sh +0 -0
- package/scripts/install-monitor-hooks.sh +0 -0
- package/scripts/package-synapse.js +2 -1
- package/pro/README.md +0 -66
- package/pro/license/degradation.js +0 -220
- package/pro/license/errors.js +0 -450
- package/pro/license/feature-gate.js +0 -354
- package/pro/license/index.js +0 -181
- package/pro/license/license-api.js +0 -651
- package/pro/license/license-cache.js +0 -523
- package/pro/license/license-crypto.js +0 -303
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CodeIntelSource } = require('./data-sources/code-intel-source');
|
|
4
|
+
const { RegistrySource } = require('./data-sources/registry-source');
|
|
5
|
+
const { MetricsSource } = require('./data-sources/metrics-source');
|
|
6
|
+
const { renderTree } = require('./renderers/tree-renderer');
|
|
7
|
+
const { renderStats } = require('./renderers/stats-renderer');
|
|
8
|
+
const { renderStatus } = require('./renderers/status-renderer');
|
|
9
|
+
const { formatAsJson } = require('./formatters/json-formatter');
|
|
10
|
+
const { formatAsDot } = require('./formatters/dot-formatter');
|
|
11
|
+
const { formatAsMermaid } = require('./formatters/mermaid-formatter');
|
|
12
|
+
const { formatAsHtml } = require('./formatters/html-formatter');
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { exec } = require('child_process');
|
|
17
|
+
|
|
18
|
+
const MAX_SUMMARY_PER_CATEGORY = 5;
|
|
19
|
+
const DEFAULT_WATCH_INTERVAL_MS = 5000;
|
|
20
|
+
const DEBOUNCE_MS = 300;
|
|
21
|
+
|
|
22
|
+
const FORMAT_MAP = {
|
|
23
|
+
json: formatAsJson,
|
|
24
|
+
dot: formatAsDot,
|
|
25
|
+
mermaid: formatAsMermaid,
|
|
26
|
+
html: formatAsHtml,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const VALID_FORMATS = ['ascii', ...Object.keys(FORMAT_MAP)];
|
|
30
|
+
|
|
31
|
+
const WATCH_FORMAT_MAP = {
|
|
32
|
+
dot: { formatter: formatAsDot, filename: 'graph.dot' },
|
|
33
|
+
mermaid: { formatter: formatAsMermaid, filename: 'graph.mmd' },
|
|
34
|
+
html: {
|
|
35
|
+
formatter: (graphData) => formatAsHtml(graphData, { autoRefresh: true, refreshInterval: 5 }),
|
|
36
|
+
filename: 'graph.html',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const COMMANDS = {
|
|
41
|
+
'--deps': handleDeps,
|
|
42
|
+
'--stats': handleStats,
|
|
43
|
+
'--help': handleHelp,
|
|
44
|
+
'-h': handleHelp,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse CLI arguments into structured args object.
|
|
49
|
+
* @param {string[]} argv - Raw CLI arguments
|
|
50
|
+
* @returns {Object} Parsed args
|
|
51
|
+
*/
|
|
52
|
+
function parseArgs(argv) {
|
|
53
|
+
const args = {
|
|
54
|
+
command: null,
|
|
55
|
+
format: 'ascii',
|
|
56
|
+
file: null,
|
|
57
|
+
interval: 5,
|
|
58
|
+
watch: false,
|
|
59
|
+
help: false,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < argv.length; i++) {
|
|
63
|
+
const arg = argv[i];
|
|
64
|
+
|
|
65
|
+
if (arg === '--help' || arg === '-h') {
|
|
66
|
+
args.help = true;
|
|
67
|
+
args.command = '--help';
|
|
68
|
+
} else if (arg === '--deps') {
|
|
69
|
+
args.command = '--deps';
|
|
70
|
+
} else if (arg === '--stats') {
|
|
71
|
+
args.command = '--stats';
|
|
72
|
+
} else if (arg === '--watch') {
|
|
73
|
+
args.watch = true;
|
|
74
|
+
} else if (arg === '--format' && i + 1 < argv.length) {
|
|
75
|
+
args.format = argv[++i];
|
|
76
|
+
} else if (arg.startsWith('--format=')) {
|
|
77
|
+
args.format = arg.split('=')[1];
|
|
78
|
+
} else if (arg === '--interval' && i + 1 < argv.length) {
|
|
79
|
+
args.interval = parseInt(argv[++i], 10);
|
|
80
|
+
} else if (arg.startsWith('--interval=')) {
|
|
81
|
+
args.interval = parseInt(arg.split('=')[1], 10);
|
|
82
|
+
} else if (arg.startsWith('--') && !args.command) {
|
|
83
|
+
args.command = arg;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return args;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Handle --deps command: render dependency tree or formatted output.
|
|
92
|
+
* If --watch is set, delegates to handleWatch.
|
|
93
|
+
* @param {Object} args - Parsed CLI args
|
|
94
|
+
*/
|
|
95
|
+
async function handleDeps(args) {
|
|
96
|
+
const format = args.format || 'ascii';
|
|
97
|
+
|
|
98
|
+
if (format !== 'ascii' && !FORMAT_MAP[format]) {
|
|
99
|
+
console.error(`Unknown format: ${format}. Valid formats: ${VALID_FORMATS.join(', ')}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (args.watch) {
|
|
104
|
+
return handleWatch(args);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const source = new CodeIntelSource();
|
|
108
|
+
const graphData = await source.getData();
|
|
109
|
+
|
|
110
|
+
if (format === 'html') {
|
|
111
|
+
return handleHtmlOutput(graphData);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (format !== 'ascii') {
|
|
115
|
+
const formatter = FORMAT_MAP[format];
|
|
116
|
+
process.stdout.write(formatter(graphData) + '\n');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const isTTY = process.stdout.isTTY;
|
|
121
|
+
const output = renderTree(graphData, { color: isTTY, unicode: isTTY });
|
|
122
|
+
console.log(output);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Write HTML graph to .aios/graph.html and open in default browser.
|
|
127
|
+
* @param {Object} graphData - Normalized graph data
|
|
128
|
+
* @param {Object} [options] - Options passed to formatAsHtml
|
|
129
|
+
* @returns {string} Output file path
|
|
130
|
+
*/
|
|
131
|
+
function handleHtmlOutput(graphData, options = {}) {
|
|
132
|
+
const outputDir = path.resolve(process.cwd(), '.aios');
|
|
133
|
+
const outputPath = path.join(outputDir, 'graph.html');
|
|
134
|
+
|
|
135
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
136
|
+
|
|
137
|
+
const html = formatAsHtml(graphData, options);
|
|
138
|
+
fs.writeFileSync(outputPath, html, 'utf8');
|
|
139
|
+
|
|
140
|
+
const nodeCount = (graphData.nodes || []).length;
|
|
141
|
+
console.log(`HTML graph written to ${outputPath} (${nodeCount} entities)`);
|
|
142
|
+
|
|
143
|
+
openInBrowser(outputPath);
|
|
144
|
+
return outputPath;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Open a file in the default browser (cross-platform).
|
|
149
|
+
* @param {string} filePath - Absolute path to file
|
|
150
|
+
*/
|
|
151
|
+
function openInBrowser(filePath) {
|
|
152
|
+
const platform = process.platform;
|
|
153
|
+
const cmd = platform === 'win32' ? 'start ""' : platform === 'darwin' ? 'open' : 'xdg-open';
|
|
154
|
+
|
|
155
|
+
exec(`${cmd} "${filePath}"`, (err) => {
|
|
156
|
+
if (err) {
|
|
157
|
+
console.log(`Could not open browser automatically. Open manually: ${filePath}`);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Handle --watch mode: regenerate graph file on interval and on file changes.
|
|
164
|
+
* Writes to .aios/graph.dot, .aios/graph.mmd, or .aios/graph.html.
|
|
165
|
+
* @param {Object} args - Parsed CLI args
|
|
166
|
+
* @returns {Object} Watch state for cleanup (used by tests)
|
|
167
|
+
*/
|
|
168
|
+
async function handleWatch(args) {
|
|
169
|
+
const watchFormat = WATCH_FORMAT_MAP[args.format] ? args.format : 'dot';
|
|
170
|
+
const { formatter, filename } = WATCH_FORMAT_MAP[watchFormat];
|
|
171
|
+
const intervalMs = (args.interval || 5) * 1000;
|
|
172
|
+
const outputDir = path.resolve(process.cwd(), '.aios');
|
|
173
|
+
const outputPath = path.join(outputDir, filename);
|
|
174
|
+
|
|
175
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
176
|
+
|
|
177
|
+
const source = new CodeIntelSource();
|
|
178
|
+
|
|
179
|
+
async function regenerate() {
|
|
180
|
+
try {
|
|
181
|
+
const graphData = await source.getData();
|
|
182
|
+
const content = formatter(graphData);
|
|
183
|
+
fs.writeFileSync(outputPath, content, 'utf8');
|
|
184
|
+
const nodeCount = (graphData.nodes || []).length;
|
|
185
|
+
console.log(`[watch] ${filename} updated (${nodeCount} entities)`);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(`[watch] regeneration failed: ${err.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await regenerate();
|
|
192
|
+
|
|
193
|
+
const intervalId = setInterval(regenerate, intervalMs);
|
|
194
|
+
|
|
195
|
+
let fileWatcher = null;
|
|
196
|
+
let debounceTimer = null;
|
|
197
|
+
const registryPath = path.resolve(process.cwd(), '.aios-core/data/entity-registry.yaml');
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
if (fs.existsSync(registryPath)) {
|
|
201
|
+
fileWatcher = fs.watch(registryPath, () => {
|
|
202
|
+
if (debounceTimer) {
|
|
203
|
+
clearTimeout(debounceTimer);
|
|
204
|
+
}
|
|
205
|
+
debounceTimer = setTimeout(regenerate, DEBOUNCE_MS);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
} catch (_err) {
|
|
209
|
+
// fs.watch not available or path inaccessible; interval-only mode
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function cleanup() {
|
|
213
|
+
clearInterval(intervalId);
|
|
214
|
+
if (debounceTimer) {
|
|
215
|
+
clearTimeout(debounceTimer);
|
|
216
|
+
}
|
|
217
|
+
if (fileWatcher) {
|
|
218
|
+
fileWatcher.close();
|
|
219
|
+
}
|
|
220
|
+
console.log('[watch] stopped');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
process.once('SIGINT', () => {
|
|
224
|
+
cleanup();
|
|
225
|
+
process.exit(0);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return { intervalId, fileWatcher, cleanup, outputPath };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Handle --stats command: render entity statistics and cache metrics.
|
|
233
|
+
* @param {Object} args - Parsed CLI args
|
|
234
|
+
*/
|
|
235
|
+
async function handleStats(_args) {
|
|
236
|
+
const registrySource = new RegistrySource();
|
|
237
|
+
const metricsSource = new MetricsSource();
|
|
238
|
+
const [registryData, metricsData] = await Promise.all([
|
|
239
|
+
registrySource.getData(),
|
|
240
|
+
metricsSource.getData(),
|
|
241
|
+
]);
|
|
242
|
+
const isTTY = process.stdout.isTTY;
|
|
243
|
+
const output = renderStats(registryData, metricsData, { isTTY: !!isTTY });
|
|
244
|
+
|
|
245
|
+
process.stdout.write(output + '\n');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Handle --help command: show usage text.
|
|
250
|
+
*/
|
|
251
|
+
function handleHelp() {
|
|
252
|
+
const usage = `
|
|
253
|
+
Usage: aios graph [command] [options]
|
|
254
|
+
|
|
255
|
+
Commands:
|
|
256
|
+
--deps Show dependency tree as ASCII text
|
|
257
|
+
--stats Show entity statistics and cache metrics
|
|
258
|
+
--help, -h Show this help message
|
|
259
|
+
|
|
260
|
+
Options:
|
|
261
|
+
--format=FORMAT Output format: ascii (default), json, dot, mermaid, html
|
|
262
|
+
--watch Live mode: regenerate graph file on interval
|
|
263
|
+
--interval=N Seconds between regeneration in watch mode (default: 5)
|
|
264
|
+
|
|
265
|
+
Examples:
|
|
266
|
+
aios graph --deps Show dependency tree
|
|
267
|
+
aios graph --deps --format=json Output as JSON
|
|
268
|
+
aios graph --deps --format=html Interactive HTML graph (opens browser)
|
|
269
|
+
aios graph --deps --watch Live DOT file for VS Code preview
|
|
270
|
+
aios graph --deps --watch --format=html Live HTML with auto-refresh
|
|
271
|
+
aios graph --deps --watch --format=mermaid Live Mermaid file
|
|
272
|
+
aios graph --deps --watch --interval=10 Refresh every 10 seconds
|
|
273
|
+
aios graph --stats Show entity stats and cache metrics
|
|
274
|
+
`.trim();
|
|
275
|
+
|
|
276
|
+
console.log(usage);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Handle default summary view: dependency tree (compact) + stats + provider status.
|
|
281
|
+
* @param {Object} args - Parsed CLI args
|
|
282
|
+
*/
|
|
283
|
+
async function handleSummary(args) {
|
|
284
|
+
const codeIntelSource = new CodeIntelSource();
|
|
285
|
+
const registrySource = new RegistrySource();
|
|
286
|
+
const metricsSource = new MetricsSource();
|
|
287
|
+
|
|
288
|
+
const [graphData, registryData, metricsData] = await Promise.all([
|
|
289
|
+
codeIntelSource.getData(),
|
|
290
|
+
registrySource.getData(),
|
|
291
|
+
metricsSource.getData(),
|
|
292
|
+
]);
|
|
293
|
+
|
|
294
|
+
const isTTY = !!process.stdout.isTTY;
|
|
295
|
+
const sections = [];
|
|
296
|
+
|
|
297
|
+
sections.push('AIOS Graph Dashboard');
|
|
298
|
+
sections.push(isTTY ? '\u2550'.repeat(35) : '='.repeat(35));
|
|
299
|
+
sections.push('');
|
|
300
|
+
|
|
301
|
+
const treeOutput = renderTree(graphData, {
|
|
302
|
+
color: isTTY,
|
|
303
|
+
unicode: isTTY,
|
|
304
|
+
maxPerCategory: MAX_SUMMARY_PER_CATEGORY,
|
|
305
|
+
});
|
|
306
|
+
sections.push(treeOutput);
|
|
307
|
+
sections.push('');
|
|
308
|
+
|
|
309
|
+
const statsOutput = renderStats(registryData, metricsData, { isTTY });
|
|
310
|
+
sections.push(statsOutput);
|
|
311
|
+
sections.push('');
|
|
312
|
+
|
|
313
|
+
const statusOutput = renderStatus(metricsData, { isTTY });
|
|
314
|
+
sections.push(statusOutput);
|
|
315
|
+
|
|
316
|
+
process.stdout.write(sections.join('\n') + '\n');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Main CLI entry point.
|
|
321
|
+
* @param {string[]} argv - Raw CLI arguments
|
|
322
|
+
*/
|
|
323
|
+
async function run(argv) {
|
|
324
|
+
const args = parseArgs(argv);
|
|
325
|
+
|
|
326
|
+
if (args.help) {
|
|
327
|
+
handleHelp();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (args.command === null) {
|
|
332
|
+
return handleSummary(args);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const handler = COMMANDS[args.command];
|
|
336
|
+
if (!handler) {
|
|
337
|
+
console.error(`Unknown command: ${args.command}`);
|
|
338
|
+
handleHelp();
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return handler(args);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
module.exports = {
|
|
346
|
+
run,
|
|
347
|
+
parseArgs,
|
|
348
|
+
handleDeps,
|
|
349
|
+
handleStats,
|
|
350
|
+
handleHelp,
|
|
351
|
+
handleWatch,
|
|
352
|
+
handleSummary,
|
|
353
|
+
handleHtmlOutput,
|
|
354
|
+
openInBrowser,
|
|
355
|
+
MAX_SUMMARY_PER_CATEGORY,
|
|
356
|
+
DEFAULT_WATCH_INTERVAL_MS,
|
|
357
|
+
DEBOUNCE_MS,
|
|
358
|
+
FORMAT_MAP,
|
|
359
|
+
VALID_FORMATS,
|
|
360
|
+
WATCH_FORMAT_MAP,
|
|
361
|
+
};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getClient, isCodeIntelAvailable } = require('../../code-intel');
|
|
4
|
+
const { RegistryLoader } = require('../../ids/registry-loader');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Data source that provides normalized graph data from code-intel
|
|
8
|
+
* or falls back to entity-registry.yaml when provider is offline.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Classify a script entity into subcategory based on its path.
|
|
12
|
+
* @param {string} filePath - Entity path
|
|
13
|
+
* @returns {string} 'scripts/task' | 'scripts/engine' | 'scripts/infra'
|
|
14
|
+
*/
|
|
15
|
+
function _classifyScript(filePath) {
|
|
16
|
+
if (filePath.includes('/development/scripts/')) return 'scripts/task';
|
|
17
|
+
if (filePath.includes('/core/')) return 'scripts/engine';
|
|
18
|
+
if (filePath.includes('/infrastructure/')) return 'scripts/infra';
|
|
19
|
+
return 'scripts/task';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Detect fine-grained category from entity path and base category.
|
|
24
|
+
* @param {string} baseCategory - Original category from registry/provider
|
|
25
|
+
* @param {string} filePath - Entity path
|
|
26
|
+
* @returns {string} Refined category
|
|
27
|
+
*/
|
|
28
|
+
function _detectCategory(baseCategory, filePath) {
|
|
29
|
+
const path = (filePath || '').toLowerCase();
|
|
30
|
+
|
|
31
|
+
if (path.includes('/checklists/')) return 'checklists';
|
|
32
|
+
if (path.includes('/workflows/')) return 'workflows';
|
|
33
|
+
if (path.includes('/utils/')) return 'utils';
|
|
34
|
+
if (path.includes('/data/')) return 'data';
|
|
35
|
+
if (path.includes('/tools/')) return 'tools';
|
|
36
|
+
|
|
37
|
+
if (baseCategory === 'scripts' || path.includes('/scripts/')) {
|
|
38
|
+
return _classifyScript(path);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return baseCategory;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class CodeIntelSource {
|
|
45
|
+
/**
|
|
46
|
+
* @param {Object} [options]
|
|
47
|
+
* @param {number} [options.cacheTTL=5000] - Cache TTL in milliseconds
|
|
48
|
+
*/
|
|
49
|
+
constructor(options = {}) {
|
|
50
|
+
this._cache = null;
|
|
51
|
+
this._cacheTimestamp = 0;
|
|
52
|
+
this._cacheTTL = options.cacheTTL || 5000;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get normalized graph data.
|
|
57
|
+
* Primary: code-intel provider (live data).
|
|
58
|
+
* Fallback: entity-registry.yaml (static data).
|
|
59
|
+
* @returns {Promise<Object>} { nodes, edges, source, isFallback, timestamp }
|
|
60
|
+
*/
|
|
61
|
+
async getData() {
|
|
62
|
+
if (this._cache && !this.isStale()) {
|
|
63
|
+
return this._cache;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let result;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (isCodeIntelAvailable()) {
|
|
70
|
+
const client = getClient();
|
|
71
|
+
const deps = await client.analyzeDependencies('.');
|
|
72
|
+
result = this._wrap(this._normalizeDeps(deps), 'code-intel', false);
|
|
73
|
+
} else {
|
|
74
|
+
result = this._getRegistryFallback();
|
|
75
|
+
}
|
|
76
|
+
} catch (_err) {
|
|
77
|
+
result = this._getRegistryFallback();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this._cache = result;
|
|
81
|
+
this._cacheTimestamp = Date.now();
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get timestamp of last successful data fetch.
|
|
87
|
+
* @returns {number}
|
|
88
|
+
*/
|
|
89
|
+
getLastUpdate() {
|
|
90
|
+
return this._cacheTimestamp;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if cached data is expired.
|
|
95
|
+
* @returns {boolean}
|
|
96
|
+
*/
|
|
97
|
+
isStale() {
|
|
98
|
+
return Date.now() - this._cacheTimestamp > this._cacheTTL;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Load graph data from entity-registry.yaml as fallback.
|
|
103
|
+
* @returns {Object} Wrapped graph data
|
|
104
|
+
*/
|
|
105
|
+
_getRegistryFallback() {
|
|
106
|
+
try {
|
|
107
|
+
const loader = new RegistryLoader();
|
|
108
|
+
const registry = loader.load();
|
|
109
|
+
return this._wrap(this._registryToTree(registry), 'registry', true);
|
|
110
|
+
} catch (_err) {
|
|
111
|
+
return this._wrap({ nodes: [], edges: [] }, 'registry', true);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Normalize analyzeDependencies output to { nodes, edges }.
|
|
117
|
+
* Handles various shapes from different providers.
|
|
118
|
+
* @param {*} deps - Raw output from analyzeDependencies
|
|
119
|
+
* @returns {Object} { nodes: Array, edges: Array }
|
|
120
|
+
*/
|
|
121
|
+
_normalizeDeps(deps) {
|
|
122
|
+
if (!deps) {
|
|
123
|
+
return { nodes: [], edges: [] };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Already normalized format
|
|
127
|
+
if (Array.isArray(deps.nodes) && Array.isArray(deps.edges)) {
|
|
128
|
+
return { nodes: deps.nodes, edges: deps.edges };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Array of dependency objects
|
|
132
|
+
if (Array.isArray(deps)) {
|
|
133
|
+
const nodes = [];
|
|
134
|
+
const edges = [];
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
|
|
137
|
+
for (const dep of deps) {
|
|
138
|
+
const id = dep.id || dep.name || dep.path;
|
|
139
|
+
if (!id || seen.has(id)) continue;
|
|
140
|
+
seen.add(id);
|
|
141
|
+
|
|
142
|
+
nodes.push({
|
|
143
|
+
id,
|
|
144
|
+
label: dep.label || dep.name || id,
|
|
145
|
+
type: dep.type || 'unknown',
|
|
146
|
+
path: dep.path || '',
|
|
147
|
+
category: _detectCategory(dep.category || dep.type || 'other', dep.path || ''),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
for (const target of dep.dependencies || dep.deps || []) {
|
|
151
|
+
edges.push({ from: id, to: target, type: 'depends' });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { nodes, edges };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Flat object with dependencies property
|
|
159
|
+
if (deps.dependencies && typeof deps.dependencies === 'object') {
|
|
160
|
+
return this._normalizeDeps(
|
|
161
|
+
Object.entries(deps.dependencies).map(([key, val]) => ({
|
|
162
|
+
id: key,
|
|
163
|
+
...((typeof val === 'object' && val) || {}),
|
|
164
|
+
}))
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { nodes: [], edges: [] };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Convert entity-registry to normalized graph format.
|
|
173
|
+
* Groups entities by category and maps dependencies/usedBy to edges.
|
|
174
|
+
* @param {Object} registry - Loaded registry data
|
|
175
|
+
* @returns {Object} { nodes: Array, edges: Array }
|
|
176
|
+
*/
|
|
177
|
+
_registryToTree(registry) {
|
|
178
|
+
const nodes = [];
|
|
179
|
+
const edges = [];
|
|
180
|
+
const edgeSet = new Set();
|
|
181
|
+
|
|
182
|
+
for (const [category, entities] of Object.entries(registry.entities || {})) {
|
|
183
|
+
if (!entities || typeof entities !== 'object') continue;
|
|
184
|
+
|
|
185
|
+
for (const [entityId, entity] of Object.entries(entities)) {
|
|
186
|
+
nodes.push({
|
|
187
|
+
id: entityId,
|
|
188
|
+
label: entityId,
|
|
189
|
+
type: entity.type || category,
|
|
190
|
+
path: entity.path || '',
|
|
191
|
+
category: _detectCategory(category, entity.path || ''),
|
|
192
|
+
lifecycle: entity.lifecycle || 'production', // NOG-16C: pass lifecycle for graph filtering
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
for (const dep of entity.dependencies || []) {
|
|
196
|
+
const edgeKey = `${entityId}->depends->${dep}`;
|
|
197
|
+
if (!edgeSet.has(edgeKey)) {
|
|
198
|
+
edgeSet.add(edgeKey);
|
|
199
|
+
edges.push({ from: entityId, to: dep, type: 'depends' });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const consumer of entity.usedBy || []) {
|
|
204
|
+
const edgeKey = `${consumer}->uses->${entityId}`;
|
|
205
|
+
if (!edgeSet.has(edgeKey)) {
|
|
206
|
+
edgeSet.add(edgeKey);
|
|
207
|
+
edges.push({ from: consumer, to: entityId, type: 'uses' });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { nodes, edges };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Wrap data in standard result envelope.
|
|
218
|
+
* @param {Object} data - { nodes, edges }
|
|
219
|
+
* @param {string} source - 'code-intel' | 'registry'
|
|
220
|
+
* @param {boolean} isFallback
|
|
221
|
+
* @returns {Object}
|
|
222
|
+
*/
|
|
223
|
+
_wrap(data, source, isFallback) {
|
|
224
|
+
return {
|
|
225
|
+
nodes: data.nodes || [],
|
|
226
|
+
edges: data.edges || [],
|
|
227
|
+
source,
|
|
228
|
+
isFallback,
|
|
229
|
+
timestamp: Date.now(),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = { CodeIntelSource, _classifyScript, _detectCategory };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getClient, isCodeIntelAvailable } = require('../../code-intel');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Data source that provides cache and latency metrics from the code-intel client.
|
|
7
|
+
* Falls back to offline data when Code Graph MCP is unavailable.
|
|
8
|
+
* Implements the same interface as CodeIntelSource: getData(), getLastUpdate(), isStale().
|
|
9
|
+
*/
|
|
10
|
+
class MetricsSource {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} [options]
|
|
13
|
+
* @param {number} [options.cacheTTL=5000] - Cache TTL in milliseconds
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this._cache = null;
|
|
17
|
+
this._cacheTimestamp = 0;
|
|
18
|
+
this._cacheTTL = options.cacheTTL || 5000;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get metrics from code-intel client.
|
|
23
|
+
* Primary: live metrics from active provider.
|
|
24
|
+
* Fallback: offline placeholder with providerAvailable=false.
|
|
25
|
+
* @returns {Promise<Object>} Metrics object
|
|
26
|
+
*/
|
|
27
|
+
async getData() {
|
|
28
|
+
if (this._cache && !this.isStale()) {
|
|
29
|
+
return this._cache;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let result;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
if (isCodeIntelAvailable()) {
|
|
36
|
+
const client = getClient();
|
|
37
|
+
const metrics = client.getMetrics();
|
|
38
|
+
result = {
|
|
39
|
+
cacheHits: metrics.cacheHits || 0,
|
|
40
|
+
cacheMisses: metrics.cacheMisses || 0,
|
|
41
|
+
cacheHitRate: metrics.cacheHitRate || 0,
|
|
42
|
+
circuitBreakerState: metrics.circuitBreakerState || 'CLOSED',
|
|
43
|
+
latencyLog: metrics.latencyLog || [],
|
|
44
|
+
providerAvailable: true,
|
|
45
|
+
activeProvider: metrics.activeProvider || null,
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
};
|
|
48
|
+
} else {
|
|
49
|
+
result = this._offlineMetrics();
|
|
50
|
+
}
|
|
51
|
+
} catch (_err) {
|
|
52
|
+
result = this._offlineMetrics();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this._cache = result;
|
|
56
|
+
this._cacheTimestamp = Date.now();
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get timestamp of last successful data fetch.
|
|
62
|
+
* @returns {number}
|
|
63
|
+
*/
|
|
64
|
+
getLastUpdate() {
|
|
65
|
+
return this._cacheTimestamp;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if cached data is expired.
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
isStale() {
|
|
73
|
+
return Date.now() - this._cacheTimestamp > this._cacheTTL;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Return offline placeholder metrics.
|
|
78
|
+
* @returns {Object}
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
_offlineMetrics() {
|
|
82
|
+
return {
|
|
83
|
+
cacheHits: 0,
|
|
84
|
+
cacheMisses: 0,
|
|
85
|
+
cacheHitRate: 0,
|
|
86
|
+
circuitBreakerState: 'CLOSED',
|
|
87
|
+
latencyLog: [],
|
|
88
|
+
providerAvailable: false,
|
|
89
|
+
activeProvider: null,
|
|
90
|
+
timestamp: Date.now(),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { MetricsSource };
|