openlore 2.0.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/LICENSE +21 -0
- package/README.md +268 -0
- package/dist/api/analyze.d.ts +17 -0
- package/dist/api/analyze.d.ts.map +1 -0
- package/dist/api/analyze.js +143 -0
- package/dist/api/analyze.js.map +1 -0
- package/dist/api/audit.d.ts +10 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +117 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/decisions.d.ts +55 -0
- package/dist/api/decisions.d.ts.map +1 -0
- package/dist/api/decisions.js +157 -0
- package/dist/api/decisions.js.map +1 -0
- package/dist/api/drift.d.ts +21 -0
- package/dist/api/drift.d.ts.map +1 -0
- package/dist/api/drift.js +152 -0
- package/dist/api/drift.js.map +1 -0
- package/dist/api/generate.d.ts +18 -0
- package/dist/api/generate.d.ts.map +1 -0
- package/dist/api/generate.js +259 -0
- package/dist/api/generate.js.map +1 -0
- package/dist/api/index.d.ts +41 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +34 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/init.d.ts +18 -0
- package/dist/api/init.d.ts.map +1 -0
- package/dist/api/init.js +83 -0
- package/dist/api/init.js.map +1 -0
- package/dist/api/run.d.ts +19 -0
- package/dist/api/run.d.ts.map +1 -0
- package/dist/api/run.js +312 -0
- package/dist/api/run.js.map +1 -0
- package/dist/api/specs.d.ts +49 -0
- package/dist/api/specs.d.ts.map +1 -0
- package/dist/api/specs.js +137 -0
- package/dist/api/specs.js.map +1 -0
- package/dist/api/types.d.ts +201 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +9 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/verify.d.ts +20 -0
- package/dist/api/verify.d.ts.map +1 -0
- package/dist/api/verify.js +117 -0
- package/dist/api/verify.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +30 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +683 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +9 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +98 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/decisions.d.ts +16 -0
- package/dist/cli/commands/decisions.d.ts.map +1 -0
- package/dist/cli/commands/decisions.js +864 -0
- package/dist/cli/commands/decisions.js.map +1 -0
- package/dist/cli/commands/digest.d.ts +9 -0
- package/dist/cli/commands/digest.d.ts.map +1 -0
- package/dist/cli/commands/digest.js +61 -0
- package/dist/cli/commands/digest.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +9 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +398 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/drift.d.ts +9 -0
- package/dist/cli/commands/drift.d.ts.map +1 -0
- package/dist/cli/commands/drift.js +550 -0
- package/dist/cli/commands/drift.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +9 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +565 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +173 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +2235 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +1384 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/refresh-stories.d.ts +10 -0
- package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
- package/dist/cli/commands/refresh-stories.js +314 -0
- package/dist/cli/commands/refresh-stories.js.map +1 -0
- package/dist/cli/commands/run.d.ts +9 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +459 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +19 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +355 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/test.d.ts +22 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +180 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +9 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +383 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/commands/view.d.ts +13 -0
- package/dist/cli/commands/view.d.ts.map +1 -0
- package/dist/cli/commands/view.js +547 -0
- package/dist/cli/commands/view.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +118 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui-approval.d.ts +11 -0
- package/dist/cli/tui-approval.d.ts.map +1 -0
- package/dist/cli/tui-approval.js +129 -0
- package/dist/cli/tui-approval.js.map +1 -0
- package/dist/constants.d.ts +314 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +382 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/analyzer/ai-config-generator.d.ts +54 -0
- package/dist/core/analyzer/ai-config-generator.d.ts.map +1 -0
- package/dist/core/analyzer/ai-config-generator.js +98 -0
- package/dist/core/analyzer/ai-config-generator.js.map +1 -0
- package/dist/core/analyzer/architecture-writer.d.ts +67 -0
- package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
- package/dist/core/analyzer/architecture-writer.js +209 -0
- package/dist/core/analyzer/architecture-writer.js.map +1 -0
- package/dist/core/analyzer/artifact-generator.d.ts +261 -0
- package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
- package/dist/core/analyzer/artifact-generator.js +909 -0
- package/dist/core/analyzer/artifact-generator.js.map +1 -0
- package/dist/core/analyzer/ast-chunker.d.ts +24 -0
- package/dist/core/analyzer/ast-chunker.d.ts.map +1 -0
- package/dist/core/analyzer/ast-chunker.js +198 -0
- package/dist/core/analyzer/ast-chunker.js.map +1 -0
- package/dist/core/analyzer/call-graph.d.ts +162 -0
- package/dist/core/analyzer/call-graph.d.ts.map +1 -0
- package/dist/core/analyzer/call-graph.js +2040 -0
- package/dist/core/analyzer/call-graph.js.map +1 -0
- package/dist/core/analyzer/code-shaper.d.ts +33 -0
- package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
- package/dist/core/analyzer/code-shaper.js +154 -0
- package/dist/core/analyzer/code-shaper.js.map +1 -0
- package/dist/core/analyzer/codebase-digest.d.ts +40 -0
- package/dist/core/analyzer/codebase-digest.d.ts.map +1 -0
- package/dist/core/analyzer/codebase-digest.js +195 -0
- package/dist/core/analyzer/codebase-digest.js.map +1 -0
- package/dist/core/analyzer/cpp-header-resolver.d.ts +30 -0
- package/dist/core/analyzer/cpp-header-resolver.d.ts.map +1 -0
- package/dist/core/analyzer/cpp-header-resolver.js +71 -0
- package/dist/core/analyzer/cpp-header-resolver.js.map +1 -0
- package/dist/core/analyzer/dependency-graph.d.ts +230 -0
- package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
- package/dist/core/analyzer/dependency-graph.js +752 -0
- package/dist/core/analyzer/dependency-graph.js.map +1 -0
- package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
- package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
- package/dist/core/analyzer/duplicate-detector.js +289 -0
- package/dist/core/analyzer/duplicate-detector.js.map +1 -0
- package/dist/core/analyzer/embedding-service.d.ts +56 -0
- package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
- package/dist/core/analyzer/embedding-service.js +118 -0
- package/dist/core/analyzer/embedding-service.js.map +1 -0
- package/dist/core/analyzer/env-extractor.d.ts +33 -0
- package/dist/core/analyzer/env-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/env-extractor.js +196 -0
- package/dist/core/analyzer/env-extractor.js.map +1 -0
- package/dist/core/analyzer/external-packages.d.ts +20 -0
- package/dist/core/analyzer/external-packages.d.ts.map +1 -0
- package/dist/core/analyzer/external-packages.js +175 -0
- package/dist/core/analyzer/external-packages.js.map +1 -0
- package/dist/core/analyzer/file-walker.d.ts +78 -0
- package/dist/core/analyzer/file-walker.d.ts.map +1 -0
- package/dist/core/analyzer/file-walker.js +532 -0
- package/dist/core/analyzer/file-walker.js.map +1 -0
- package/dist/core/analyzer/function-registry-trie.d.ts +21 -0
- package/dist/core/analyzer/function-registry-trie.d.ts.map +1 -0
- package/dist/core/analyzer/function-registry-trie.js +39 -0
- package/dist/core/analyzer/function-registry-trie.js.map +1 -0
- package/dist/core/analyzer/http-route-parser.d.ts +152 -0
- package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
- package/dist/core/analyzer/http-route-parser.js +971 -0
- package/dist/core/analyzer/http-route-parser.js.map +1 -0
- package/dist/core/analyzer/import-parser.d.ts +100 -0
- package/dist/core/analyzer/import-parser.d.ts.map +1 -0
- package/dist/core/analyzer/import-parser.js +952 -0
- package/dist/core/analyzer/import-parser.js.map +1 -0
- package/dist/core/analyzer/import-resolver-bridge.d.ts +25 -0
- package/dist/core/analyzer/import-resolver-bridge.d.ts.map +1 -0
- package/dist/core/analyzer/import-resolver-bridge.js +99 -0
- package/dist/core/analyzer/import-resolver-bridge.js.map +1 -0
- package/dist/core/analyzer/index.d.ts +10 -0
- package/dist/core/analyzer/index.d.ts.map +1 -0
- package/dist/core/analyzer/index.js +10 -0
- package/dist/core/analyzer/index.js.map +1 -0
- package/dist/core/analyzer/middleware-extractor.d.ts +29 -0
- package/dist/core/analyzer/middleware-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/middleware-extractor.js +195 -0
- package/dist/core/analyzer/middleware-extractor.js.map +1 -0
- package/dist/core/analyzer/refactor-analyzer.d.ts +83 -0
- package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
- package/dist/core/analyzer/refactor-analyzer.js +351 -0
- package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
- package/dist/core/analyzer/repository-mapper.d.ts +150 -0
- package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
- package/dist/core/analyzer/repository-mapper.js +740 -0
- package/dist/core/analyzer/repository-mapper.js.map +1 -0
- package/dist/core/analyzer/schema-extractor.d.ts +41 -0
- package/dist/core/analyzer/schema-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/schema-extractor.js +229 -0
- package/dist/core/analyzer/schema-extractor.js.map +1 -0
- package/dist/core/analyzer/signature-extractor.d.ts +31 -0
- package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/signature-extractor.js +675 -0
- package/dist/core/analyzer/signature-extractor.js.map +1 -0
- package/dist/core/analyzer/significance-scorer.d.ts +79 -0
- package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
- package/dist/core/analyzer/significance-scorer.js +407 -0
- package/dist/core/analyzer/significance-scorer.js.map +1 -0
- package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
- package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
- package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
- package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
- package/dist/core/analyzer/spec-vector-index.d.ts +68 -0
- package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -0
- package/dist/core/analyzer/spec-vector-index.js +340 -0
- package/dist/core/analyzer/spec-vector-index.js.map +1 -0
- package/dist/core/analyzer/subgraph-extractor.d.ts +51 -0
- package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/subgraph-extractor.js +147 -0
- package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
- package/dist/core/analyzer/type-inference-engine.d.ts +23 -0
- package/dist/core/analyzer/type-inference-engine.d.ts.map +1 -0
- package/dist/core/analyzer/type-inference-engine.js +130 -0
- package/dist/core/analyzer/type-inference-engine.js.map +1 -0
- package/dist/core/analyzer/ui-component-extractor.d.ts +43 -0
- package/dist/core/analyzer/ui-component-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/ui-component-extractor.js +245 -0
- package/dist/core/analyzer/ui-component-extractor.js.map +1 -0
- package/dist/core/analyzer/unified-search.d.ts +116 -0
- package/dist/core/analyzer/unified-search.d.ts.map +1 -0
- package/dist/core/analyzer/unified-search.js +231 -0
- package/dist/core/analyzer/unified-search.js.map +1 -0
- package/dist/core/analyzer/vector-index.d.ts +92 -0
- package/dist/core/analyzer/vector-index.d.ts.map +1 -0
- package/dist/core/analyzer/vector-index.js +451 -0
- package/dist/core/analyzer/vector-index.js.map +1 -0
- package/dist/core/decisions/consolidator.d.ts +14 -0
- package/dist/core/decisions/consolidator.d.ts.map +1 -0
- package/dist/core/decisions/consolidator.js +169 -0
- package/dist/core/decisions/consolidator.js.map +1 -0
- package/dist/core/decisions/extractor.d.ts +26 -0
- package/dist/core/decisions/extractor.d.ts.map +1 -0
- package/dist/core/decisions/extractor.js +156 -0
- package/dist/core/decisions/extractor.js.map +1 -0
- package/dist/core/decisions/index.d.ts +19 -0
- package/dist/core/decisions/index.d.ts.map +1 -0
- package/dist/core/decisions/index.js +16 -0
- package/dist/core/decisions/index.js.map +1 -0
- package/dist/core/decisions/store.d.ts +36 -0
- package/dist/core/decisions/store.d.ts.map +1 -0
- package/dist/core/decisions/store.js +109 -0
- package/dist/core/decisions/store.js.map +1 -0
- package/dist/core/decisions/syncer.d.ts +27 -0
- package/dist/core/decisions/syncer.d.ts.map +1 -0
- package/dist/core/decisions/syncer.js +214 -0
- package/dist/core/decisions/syncer.js.map +1 -0
- package/dist/core/decisions/verifier.d.ts +20 -0
- package/dist/core/decisions/verifier.d.ts.map +1 -0
- package/dist/core/decisions/verifier.js +115 -0
- package/dist/core/decisions/verifier.js.map +1 -0
- package/dist/core/digest/digest-generator.d.ts +29 -0
- package/dist/core/digest/digest-generator.d.ts.map +1 -0
- package/dist/core/digest/digest-generator.js +181 -0
- package/dist/core/digest/digest-generator.js.map +1 -0
- package/dist/core/drift/drift-detector.d.ts +102 -0
- package/dist/core/drift/drift-detector.d.ts.map +1 -0
- package/dist/core/drift/drift-detector.js +598 -0
- package/dist/core/drift/drift-detector.js.map +1 -0
- package/dist/core/drift/git-diff.d.ts +60 -0
- package/dist/core/drift/git-diff.d.ts.map +1 -0
- package/dist/core/drift/git-diff.js +383 -0
- package/dist/core/drift/git-diff.js.map +1 -0
- package/dist/core/drift/index.d.ts +12 -0
- package/dist/core/drift/index.d.ts.map +1 -0
- package/dist/core/drift/index.js +9 -0
- package/dist/core/drift/index.js.map +1 -0
- package/dist/core/drift/spec-mapper.d.ts +73 -0
- package/dist/core/drift/spec-mapper.d.ts.map +1 -0
- package/dist/core/drift/spec-mapper.js +353 -0
- package/dist/core/drift/spec-mapper.js.map +1 -0
- package/dist/core/drift/test-suggester.d.ts +18 -0
- package/dist/core/drift/test-suggester.d.ts.map +1 -0
- package/dist/core/drift/test-suggester.js +107 -0
- package/dist/core/drift/test-suggester.js.map +1 -0
- package/dist/core/generator/adr-generator.d.ts +32 -0
- package/dist/core/generator/adr-generator.d.ts.map +1 -0
- package/dist/core/generator/adr-generator.js +192 -0
- package/dist/core/generator/adr-generator.js.map +1 -0
- package/dist/core/generator/index.d.ts +9 -0
- package/dist/core/generator/index.d.ts.map +1 -0
- package/dist/core/generator/index.js +12 -0
- package/dist/core/generator/index.js.map +1 -0
- package/dist/core/generator/mapping-generator.d.ts +54 -0
- package/dist/core/generator/mapping-generator.d.ts.map +1 -0
- package/dist/core/generator/mapping-generator.js +240 -0
- package/dist/core/generator/mapping-generator.js.map +1 -0
- package/dist/core/generator/openspec-compat.d.ts +160 -0
- package/dist/core/generator/openspec-compat.d.ts.map +1 -0
- package/dist/core/generator/openspec-compat.js +524 -0
- package/dist/core/generator/openspec-compat.js.map +1 -0
- package/dist/core/generator/openspec-format-generator.d.ts +131 -0
- package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
- package/dist/core/generator/openspec-format-generator.js +963 -0
- package/dist/core/generator/openspec-format-generator.js.map +1 -0
- package/dist/core/generator/openspec-writer.d.ts +130 -0
- package/dist/core/generator/openspec-writer.d.ts.map +1 -0
- package/dist/core/generator/openspec-writer.js +404 -0
- package/dist/core/generator/openspec-writer.js.map +1 -0
- package/dist/core/generator/prompts.d.ts +35 -0
- package/dist/core/generator/prompts.d.ts.map +1 -0
- package/dist/core/generator/prompts.js +212 -0
- package/dist/core/generator/prompts.js.map +1 -0
- package/dist/core/generator/rag-manifest-generator.d.ts +37 -0
- package/dist/core/generator/rag-manifest-generator.d.ts.map +1 -0
- package/dist/core/generator/rag-manifest-generator.js +134 -0
- package/dist/core/generator/rag-manifest-generator.js.map +1 -0
- package/dist/core/generator/schemas.d.ts +365 -0
- package/dist/core/generator/schemas.d.ts.map +1 -0
- package/dist/core/generator/schemas.js +190 -0
- package/dist/core/generator/schemas.js.map +1 -0
- package/dist/core/generator/spec-pipeline.d.ts +123 -0
- package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
- package/dist/core/generator/spec-pipeline.js +699 -0
- package/dist/core/generator/spec-pipeline.js.map +1 -0
- package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
- package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
- package/dist/core/generator/stages/stage1-survey.js +171 -0
- package/dist/core/generator/stages/stage1-survey.js.map +1 -0
- package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
- package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
- package/dist/core/generator/stages/stage2-entities.js +74 -0
- package/dist/core/generator/stages/stage2-entities.js.map +1 -0
- package/dist/core/generator/stages/stage3-services.d.ts +11 -0
- package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
- package/dist/core/generator/stages/stage3-services.js +85 -0
- package/dist/core/generator/stages/stage3-services.js.map +1 -0
- package/dist/core/generator/stages/stage4-api.d.ts +11 -0
- package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
- package/dist/core/generator/stages/stage4-api.js +72 -0
- package/dist/core/generator/stages/stage4-api.js.map +1 -0
- package/dist/core/generator/stages/stage5-architecture.d.ts +11 -0
- package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
- package/dist/core/generator/stages/stage5-architecture.js +75 -0
- package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
- package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
- package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
- package/dist/core/generator/stages/stage6-adr.js +47 -0
- package/dist/core/generator/stages/stage6-adr.js.map +1 -0
- package/dist/core/services/chat-agent.d.ts +50 -0
- package/dist/core/services/chat-agent.d.ts.map +1 -0
- package/dist/core/services/chat-agent.js +369 -0
- package/dist/core/services/chat-agent.js.map +1 -0
- package/dist/core/services/chat-tools.d.ts +32 -0
- package/dist/core/services/chat-tools.d.ts.map +1 -0
- package/dist/core/services/chat-tools.js +494 -0
- package/dist/core/services/chat-tools.js.map +1 -0
- package/dist/core/services/config-manager.d.ts +61 -0
- package/dist/core/services/config-manager.d.ts.map +1 -0
- package/dist/core/services/config-manager.js +149 -0
- package/dist/core/services/config-manager.js.map +1 -0
- package/dist/core/services/edge-store.d.ts +57 -0
- package/dist/core/services/edge-store.d.ts.map +1 -0
- package/dist/core/services/edge-store.js +419 -0
- package/dist/core/services/edge-store.js.map +1 -0
- package/dist/core/services/gitignore-manager.d.ts +29 -0
- package/dist/core/services/gitignore-manager.d.ts.map +1 -0
- package/dist/core/services/gitignore-manager.js +95 -0
- package/dist/core/services/gitignore-manager.js.map +1 -0
- package/dist/core/services/index.d.ts +8 -0
- package/dist/core/services/index.d.ts.map +1 -0
- package/dist/core/services/index.js +8 -0
- package/dist/core/services/index.js.map +1 -0
- package/dist/core/services/llm-service.d.ts +379 -0
- package/dist/core/services/llm-service.d.ts.map +1 -0
- package/dist/core/services/llm-service.js +1553 -0
- package/dist/core/services/llm-service.js.map +1 -0
- package/dist/core/services/mcp-handlers/analysis.d.ts +127 -0
- package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/analysis.js +1185 -0
- package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
- package/dist/core/services/mcp-handlers/change.d.ts +14 -0
- package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/change.js +416 -0
- package/dist/core/services/mcp-handlers/change.js.map +1 -0
- package/dist/core/services/mcp-handlers/decisions.d.ts +16 -0
- package/dist/core/services/mcp-handlers/decisions.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/decisions.js +239 -0
- package/dist/core/services/mcp-handlers/decisions.js.map +1 -0
- package/dist/core/services/mcp-handlers/graph.d.ts +94 -0
- package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/graph.js +693 -0
- package/dist/core/services/mcp-handlers/graph.js.map +1 -0
- package/dist/core/services/mcp-handlers/orient.d.ts +17 -0
- package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/orient.js +357 -0
- package/dist/core/services/mcp-handlers/orient.js.map +1 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts +66 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/semantic.js +432 -0
- package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
- package/dist/core/services/mcp-handlers/utils.d.ts +85 -0
- package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/utils.js +262 -0
- package/dist/core/services/mcp-handlers/utils.js.map +1 -0
- package/dist/core/services/mcp-watcher.d.ts +41 -0
- package/dist/core/services/mcp-watcher.d.ts.map +1 -0
- package/dist/core/services/mcp-watcher.js +254 -0
- package/dist/core/services/mcp-watcher.js.map +1 -0
- package/dist/core/services/project-detector.d.ts +32 -0
- package/dist/core/services/project-detector.d.ts.map +1 -0
- package/dist/core/services/project-detector.js +100 -0
- package/dist/core/services/project-detector.js.map +1 -0
- package/dist/core/test-generator/coverage-analyzer.d.ts +27 -0
- package/dist/core/test-generator/coverage-analyzer.d.ts.map +1 -0
- package/dist/core/test-generator/coverage-analyzer.js +285 -0
- package/dist/core/test-generator/coverage-analyzer.js.map +1 -0
- package/dist/core/test-generator/framework-detector.d.ts +17 -0
- package/dist/core/test-generator/framework-detector.d.ts.map +1 -0
- package/dist/core/test-generator/framework-detector.js +65 -0
- package/dist/core/test-generator/framework-detector.js.map +1 -0
- package/dist/core/test-generator/index.d.ts +14 -0
- package/dist/core/test-generator/index.d.ts.map +1 -0
- package/dist/core/test-generator/index.js +11 -0
- package/dist/core/test-generator/index.js.map +1 -0
- package/dist/core/test-generator/renderers/catch2.d.ts +8 -0
- package/dist/core/test-generator/renderers/catch2.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/catch2.js +47 -0
- package/dist/core/test-generator/renderers/catch2.js.map +1 -0
- package/dist/core/test-generator/renderers/gtest.d.ts +8 -0
- package/dist/core/test-generator/renderers/gtest.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/gtest.js +45 -0
- package/dist/core/test-generator/renderers/gtest.js.map +1 -0
- package/dist/core/test-generator/renderers/index.d.ts +20 -0
- package/dist/core/test-generator/renderers/index.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/index.js +35 -0
- package/dist/core/test-generator/renderers/index.js.map +1 -0
- package/dist/core/test-generator/renderers/playwright.d.ts +8 -0
- package/dist/core/test-generator/renderers/playwright.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/playwright.js +44 -0
- package/dist/core/test-generator/renderers/playwright.js.map +1 -0
- package/dist/core/test-generator/renderers/pytest.d.ts +8 -0
- package/dist/core/test-generator/renderers/pytest.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/pytest.js +44 -0
- package/dist/core/test-generator/renderers/pytest.js.map +1 -0
- package/dist/core/test-generator/renderers/shared.d.ts +21 -0
- package/dist/core/test-generator/renderers/shared.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/shared.js +56 -0
- package/dist/core/test-generator/renderers/shared.js.map +1 -0
- package/dist/core/test-generator/renderers/vitest.d.ts +8 -0
- package/dist/core/test-generator/renderers/vitest.d.ts.map +1 -0
- package/dist/core/test-generator/renderers/vitest.js +52 -0
- package/dist/core/test-generator/renderers/vitest.js.map +1 -0
- package/dist/core/test-generator/scenario-parser.d.ts +33 -0
- package/dist/core/test-generator/scenario-parser.d.ts.map +1 -0
- package/dist/core/test-generator/scenario-parser.js +244 -0
- package/dist/core/test-generator/scenario-parser.js.map +1 -0
- package/dist/core/test-generator/test-generator.d.ts +30 -0
- package/dist/core/test-generator/test-generator.d.ts.map +1 -0
- package/dist/core/test-generator/test-generator.js +174 -0
- package/dist/core/test-generator/test-generator.js.map +1 -0
- package/dist/core/test-generator/test-writer.d.ts +25 -0
- package/dist/core/test-generator/test-writer.d.ts.map +1 -0
- package/dist/core/test-generator/test-writer.js +128 -0
- package/dist/core/test-generator/test-writer.js.map +1 -0
- package/dist/core/test-generator/then-matchers.d.ts +35 -0
- package/dist/core/test-generator/then-matchers.d.ts.map +1 -0
- package/dist/core/test-generator/then-matchers.js +211 -0
- package/dist/core/test-generator/then-matchers.js.map +1 -0
- package/dist/core/verifier/index.d.ts +5 -0
- package/dist/core/verifier/index.d.ts.map +1 -0
- package/dist/core/verifier/index.js +5 -0
- package/dist/core/verifier/index.js.map +1 -0
- package/dist/core/verifier/verification-engine.d.ts +293 -0
- package/dist/core/verifier/verification-engine.d.ts.map +1 -0
- package/dist/core/verifier/verification-engine.js +919 -0
- package/dist/core/verifier/verification-engine.js.map +1 -0
- package/dist/types/index.d.ts +368 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pipeline.d.ts +167 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +5 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/types/test-generator.d.ts +103 -0
- package/dist/types/test-generator.d.ts.map +1 -0
- package/dist/types/test-generator.js +17 -0
- package/dist/types/test-generator.js.map +1 -0
- package/dist/utils/command-helpers.d.ts +68 -0
- package/dist/utils/command-helpers.d.ts.map +1 -0
- package/dist/utils/command-helpers.js +150 -0
- package/dist/utils/command-helpers.js.map +1 -0
- package/dist/utils/errors.d.ts +51 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +129 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +149 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +342 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/misc.d.ts +10 -0
- package/dist/utils/misc.d.ts.map +1 -0
- package/dist/utils/misc.js +21 -0
- package/dist/utils/misc.js.map +1 -0
- package/dist/utils/progress.d.ts +142 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +283 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/prompts.d.ts +53 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +199 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/shutdown.d.ts +89 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +238 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/examples/bmad/README.md +113 -0
- package/examples/bmad/agents/architect.md +226 -0
- package/examples/bmad/agents/dev-brownfield.md +69 -0
- package/examples/bmad/setup/architect.customize.yaml +14 -0
- package/examples/bmad/tasks/implement-story.md +254 -0
- package/examples/bmad/tasks/onboarding.md +169 -0
- package/examples/bmad/tasks/refactor.md +178 -0
- package/examples/bmad/tasks/sprint-planning.md +168 -0
- package/examples/bmad/templates/story.md +108 -0
- package/examples/cline-workflows/openlore-analyze-codebase.md +101 -0
- package/examples/cline-workflows/openlore-check-spec-drift.md +102 -0
- package/examples/cline-workflows/openlore-execute-refactor.md +212 -0
- package/examples/cline-workflows/openlore-implement-feature.md +266 -0
- package/examples/cline-workflows/openlore-plan-refactor.md +279 -0
- package/examples/cline-workflows/openlore-refactor-codebase.md +16 -0
- package/examples/cline-workflows/openlore-write-tests.md +177 -0
- package/examples/drift-demo/openspec/config.yaml +14 -0
- package/examples/drift-demo/openspec/specs/architecture/spec.md +30 -0
- package/examples/drift-demo/openspec/specs/auth/spec.md +71 -0
- package/examples/drift-demo/openspec/specs/database/spec.md +33 -0
- package/examples/drift-demo/openspec/specs/overview/spec.md +20 -0
- package/examples/drift-demo/openspec/specs/projects/spec.md +55 -0
- package/examples/drift-demo/openspec/specs/tasks/spec.md +78 -0
- package/examples/drift-demo/package.json +21 -0
- package/examples/drift-demo/src/auth/auth-middleware.ts +30 -0
- package/examples/drift-demo/src/auth/auth-routes.ts +29 -0
- package/examples/drift-demo/src/auth/auth-service.ts +45 -0
- package/examples/drift-demo/src/database/connection.ts +27 -0
- package/examples/drift-demo/src/index.ts +16 -0
- package/examples/drift-demo/src/projects/project-model.ts +15 -0
- package/examples/drift-demo/src/projects/project-service.ts +34 -0
- package/examples/drift-demo/src/tasks/task-model.ts +37 -0
- package/examples/drift-demo/src/tasks/task-routes.ts +53 -0
- package/examples/drift-demo/src/tasks/task-service.ts +60 -0
- package/examples/drift-demo/src/utils/validation.ts +11 -0
- package/examples/drift-demo/tests/auth.test.ts +4 -0
- package/examples/drift-demo/tests/tasks.test.ts +4 -0
- package/examples/drift-demo/tsconfig.json +10 -0
- package/examples/drift-test/run-drift-test.sh +1087 -0
- package/examples/gsd/README.md +119 -0
- package/examples/gsd/commands/gsd/openlore-drift.md +111 -0
- package/examples/gsd/commands/gsd/openlore-orient.md +191 -0
- package/examples/mistral-vibe/README.md +101 -0
- package/examples/mistral-vibe/antipatterns-template.md +18 -0
- package/examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md +124 -0
- package/examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md +379 -0
- package/examples/mistral-vibe/skills/openlore-debug/SKILL.md +330 -0
- package/examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md +291 -0
- package/examples/mistral-vibe/skills/openlore-generate/SKILL.md +245 -0
- package/examples/mistral-vibe/skills/openlore-implement-story/SKILL.md +326 -0
- package/examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md +365 -0
- package/examples/mistral-vibe/skills/openlore-review-changes/SKILL.md +128 -0
- package/examples/mistral-vibe/skills/openlore-write-tests/SKILL.md +261 -0
- package/examples/opencode/agent-guard.ts +170 -0
- package/examples/opencode/plugins/anti-laziness.ts +202 -0
- package/examples/opencode/plugins/lib/openlore-context-injector-helpers.ts +116 -0
- package/examples/opencode/plugins/lib/openlore-decision-extractor-helpers.ts +65 -0
- package/examples/opencode/plugins/openlore-context-injector.test.ts +211 -0
- package/examples/opencode/plugins/openlore-context-injector.ts +165 -0
- package/examples/opencode/plugins/openlore-decision-extractor.test.ts +131 -0
- package/examples/opencode/plugins/openlore-decision-extractor.ts +322 -0
- package/examples/opencode/plugins/openlore-enforcer.ts +227 -0
- package/examples/opencode/prompts/sisyphus-sdd.md +150 -0
- package/examples/opencode-skills/openlore-analyze-codebase/SKILL.md +101 -0
- package/examples/opencode-skills/openlore-brainstorm/SKILL.md +354 -0
- package/examples/opencode-skills/openlore-debug/SKILL.md +291 -0
- package/examples/opencode-skills/openlore-execute-refactor/SKILL.md +241 -0
- package/examples/opencode-skills/openlore-generate/SKILL.md +236 -0
- package/examples/opencode-skills/openlore-implement-story/SKILL.md +251 -0
- package/examples/opencode-skills/openlore-plan-refactor/SKILL.md +298 -0
- package/examples/opencode-skills/openlore-review-changes/SKILL.md +134 -0
- package/examples/opencode-skills/openlore-write-tests/SKILL.md +230 -0
- package/examples/openspec-analysis/README.md +59 -0
- package/examples/openspec-analysis/SUMMARY.md +72 -0
- package/examples/openspec-analysis/config.json +16 -0
- package/examples/openspec-analysis/dependencies.mermaid +35 -0
- package/examples/openspec-analysis/dependency-graph.json +12116 -0
- package/examples/openspec-analysis/llm-context.json +119 -0
- package/examples/openspec-analysis/repo-structure.json +871 -0
- package/examples/openspec-cli/README.md +67 -0
- package/examples/openspec-cli/openspec/config.yaml +26 -0
- package/examples/openspec-cli/openspec/specs/architecture/spec.md +178 -0
- package/examples/openspec-cli/openspec/specs/artifact-graph/spec.md +143 -0
- package/examples/openspec-cli/openspec/specs/cli/spec.md +138 -0
- package/examples/openspec-cli/openspec/specs/overview/spec.md +60 -0
- package/examples/openspec-cli/openspec/specs/parsing/spec.md +123 -0
- package/examples/openspec-cli/openspec/specs/validation/spec.md +108 -0
- package/examples/spec-kit/README.md +104 -0
- package/examples/spec-kit/commands/drift.md +87 -0
- package/examples/spec-kit/commands/orient.md +138 -0
- package/examples/spec-kit/extension.yml +54 -0
- package/package.json +125 -0
- package/src/viewer/InteractiveGraphViewer.jsx +1600 -0
- package/src/viewer/app/index.html +17 -0
- package/src/viewer/app/main.jsx +13 -0
- package/src/viewer/components/ArchitectureView.jsx +177 -0
- package/src/viewer/components/ChatPanel.jsx +450 -0
- package/src/viewer/components/ClassGraph.jsx +782 -0
- package/src/viewer/components/ClusterGraph.jsx +469 -0
- package/src/viewer/components/FilterBar.jsx +179 -0
- package/src/viewer/components/FlatGraph.jsx +282 -0
- package/src/viewer/components/MicroComponents.jsx +85 -0
- package/src/viewer/hooks/usePanZoom.js +79 -0
- package/src/viewer/utils/constants.js +64 -0
- package/src/viewer/utils/graph-helpers.js +303 -0
- package/src/viewer/utils/graph-helpers.test.ts +39 -0
- package/src/viewer/utils/themes.js +206 -0
- package/stubs/tree-sitter-cli-stub/package.json +6 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph Service
|
|
3
|
+
*
|
|
4
|
+
* Builds a complete graph of how files relate to each other through imports.
|
|
5
|
+
* Provides metrics like in-degree, out-degree, betweenness centrality, and PageRank.
|
|
6
|
+
* Detects clusters, cycles, and special nodes (centers, leaves, bridges, orphans).
|
|
7
|
+
*/
|
|
8
|
+
import { ImportExportParser, resolveImport } from './import-parser.js';
|
|
9
|
+
import { extractAllHttpEdges } from './http-route-parser.js';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ============================================================================
|
|
13
|
+
const CLUSTER_PALETTE = [
|
|
14
|
+
'#7c6af7',
|
|
15
|
+
'#3ecfcf',
|
|
16
|
+
'#f77c6a',
|
|
17
|
+
'#6af7a0',
|
|
18
|
+
'#f7c76a',
|
|
19
|
+
'#f76ac8',
|
|
20
|
+
'#6aaff7',
|
|
21
|
+
'#c8f76a',
|
|
22
|
+
'#f7a06a',
|
|
23
|
+
'#a0a0ff',
|
|
24
|
+
'#ff6b9d',
|
|
25
|
+
'#00d4aa',
|
|
26
|
+
'#ffb347',
|
|
27
|
+
];
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// DEPENDENCY GRAPH BUILDER
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Builds and analyzes a dependency graph from scored files
|
|
33
|
+
*/
|
|
34
|
+
export class DependencyGraphBuilder {
|
|
35
|
+
nodes = new Map();
|
|
36
|
+
edges = [];
|
|
37
|
+
adjacencyList = new Map();
|
|
38
|
+
reverseAdjacencyList = new Map();
|
|
39
|
+
httpEdgeCount = 0;
|
|
40
|
+
parser;
|
|
41
|
+
options;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.parser = new ImportExportParser();
|
|
44
|
+
this.options = {
|
|
45
|
+
rootDir: options.rootDir,
|
|
46
|
+
extensions: options.extensions ?? [], // empty = auto-detect per file in resolveImport
|
|
47
|
+
minClusterSize: options.minClusterSize ?? 2,
|
|
48
|
+
dampingFactor: options.dampingFactor ?? 0.85,
|
|
49
|
+
maxIterations: options.maxIterations ?? 100,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the dependency graph from scored files
|
|
54
|
+
*/
|
|
55
|
+
async build(files) {
|
|
56
|
+
// Clear any previous state
|
|
57
|
+
this.nodes.clear();
|
|
58
|
+
this.edges = [];
|
|
59
|
+
this.adjacencyList.clear();
|
|
60
|
+
this.reverseAdjacencyList.clear();
|
|
61
|
+
this.httpEdgeCount = 0;
|
|
62
|
+
// Parse all files and create nodes
|
|
63
|
+
const analyses = await this.parseFiles(files);
|
|
64
|
+
// Create nodes for each file
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
const analysis = analyses.get(file.absolutePath);
|
|
67
|
+
this.nodes.set(file.absolutePath, {
|
|
68
|
+
id: file.absolutePath,
|
|
69
|
+
file,
|
|
70
|
+
exports: analysis?.exports ?? [],
|
|
71
|
+
metrics: {
|
|
72
|
+
inDegree: 0,
|
|
73
|
+
outDegree: 0,
|
|
74
|
+
betweenness: 0,
|
|
75
|
+
pageRank: 1 / files.length,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
this.adjacencyList.set(file.absolutePath, new Set());
|
|
79
|
+
this.reverseAdjacencyList.set(file.absolutePath, new Set());
|
|
80
|
+
}
|
|
81
|
+
// Create edges from imports
|
|
82
|
+
await this.buildEdges(files, analyses);
|
|
83
|
+
// Create cross-language edges from HTTP calls (fetch/axios → FastAPI routes)
|
|
84
|
+
await this.buildHttpCrossEdges(files);
|
|
85
|
+
// Calculate metrics
|
|
86
|
+
this.calculateDegrees();
|
|
87
|
+
this.calculateBetweenness();
|
|
88
|
+
this.calculatePageRank();
|
|
89
|
+
// Detect clusters
|
|
90
|
+
const clusters = this.detectClusters();
|
|
91
|
+
// Assign clusters to nodes
|
|
92
|
+
const clusterByNode = {};
|
|
93
|
+
clusters.forEach((cl) => {
|
|
94
|
+
cl.files.forEach((fid) => {
|
|
95
|
+
clusterByNode[fid] = { id: cl.id, name: cl.name, color: cl.color };
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
for (const [id, node] of this.nodes) {
|
|
99
|
+
node.cluster = clusterByNode[id];
|
|
100
|
+
}
|
|
101
|
+
// Detect cycles
|
|
102
|
+
const cycles = this.detectCycles();
|
|
103
|
+
// Generate rankings
|
|
104
|
+
const rankings = this.generateRankings(clusters);
|
|
105
|
+
// Calculate statistics
|
|
106
|
+
const statistics = this.calculateStatistics(clusters, cycles);
|
|
107
|
+
const structuralClusters = clusters.filter(c => c.isStructural);
|
|
108
|
+
return {
|
|
109
|
+
nodes: Array.from(this.nodes.values()),
|
|
110
|
+
edges: this.edges,
|
|
111
|
+
clusters,
|
|
112
|
+
structuralClusters,
|
|
113
|
+
rankings,
|
|
114
|
+
cycles,
|
|
115
|
+
statistics,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Parse all files to extract imports/exports
|
|
120
|
+
*/
|
|
121
|
+
async parseFiles(files) {
|
|
122
|
+
const analyses = new Map();
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
try {
|
|
125
|
+
const analysis = await this.parser.parseFile(file.absolutePath);
|
|
126
|
+
analyses.set(file.absolutePath, analysis);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// File couldn't be parsed (binary, syntax error, etc.) — skip it
|
|
130
|
+
if (process.env.DEBUG) {
|
|
131
|
+
console.debug(`[dep-graph] Failed to parse ${file.absolutePath}: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return analyses;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Build cross-language edges by matching HTTP calls in JS/TS files to
|
|
139
|
+
* FastAPI/Flask/Django route definitions in Python files.
|
|
140
|
+
* Only creates edges between files that are already nodes in the graph.
|
|
141
|
+
*/
|
|
142
|
+
async buildHttpCrossEdges(files) {
|
|
143
|
+
const fileSet = new Set(files.map(f => f.absolutePath));
|
|
144
|
+
const filePaths = Array.from(fileSet);
|
|
145
|
+
// Skip HTTP edge detection entirely when there cannot be any cross-language
|
|
146
|
+
// edges: we need at least one caller file AND at least one handler file.
|
|
147
|
+
// Callers are JS/TS. Handlers can be Python, Java, or TS/JS (server-side).
|
|
148
|
+
const jsExts = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']);
|
|
149
|
+
const pyExts = new Set(['.py', '.pyw']);
|
|
150
|
+
const hasJs = filePaths.some(fp => jsExts.has(fp.slice(fp.lastIndexOf('.')).toLowerCase()));
|
|
151
|
+
const hasHandler = filePaths.some(fp => {
|
|
152
|
+
const ext = fp.slice(fp.lastIndexOf('.')).toLowerCase();
|
|
153
|
+
return pyExts.has(ext) || ext === '.java';
|
|
154
|
+
});
|
|
155
|
+
if (!hasJs || !hasHandler)
|
|
156
|
+
return;
|
|
157
|
+
const { edges: httpEdges } = await extractAllHttpEdges(filePaths);
|
|
158
|
+
for (const httpEdge of httpEdges) {
|
|
159
|
+
// Both ends must be known nodes
|
|
160
|
+
if (!fileSet.has(httpEdge.callerFile) || !fileSet.has(httpEdge.handlerFile))
|
|
161
|
+
continue;
|
|
162
|
+
// Skip self-loops
|
|
163
|
+
if (httpEdge.callerFile === httpEdge.handlerFile)
|
|
164
|
+
continue;
|
|
165
|
+
// Weight by confidence: exact=1.0, path=0.75, fuzzy=0.5
|
|
166
|
+
const weight = httpEdge.confidence === 'exact' ? 1.0 :
|
|
167
|
+
httpEdge.confidence === 'path' ? 0.75 : 0.5;
|
|
168
|
+
const edge = {
|
|
169
|
+
source: httpEdge.callerFile,
|
|
170
|
+
target: httpEdge.handlerFile,
|
|
171
|
+
importedNames: [httpEdge.route.handlerName],
|
|
172
|
+
isTypeOnly: false,
|
|
173
|
+
weight,
|
|
174
|
+
httpEdge,
|
|
175
|
+
};
|
|
176
|
+
this.edges.push(edge);
|
|
177
|
+
this.httpEdgeCount++;
|
|
178
|
+
this.adjacencyList.get(httpEdge.callerFile)?.add(httpEdge.handlerFile);
|
|
179
|
+
this.reverseAdjacencyList.get(httpEdge.handlerFile)?.add(httpEdge.callerFile);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build edges from import relationships
|
|
184
|
+
*/
|
|
185
|
+
async buildEdges(files, analyses) {
|
|
186
|
+
const fileSet = new Set(files.map(f => f.absolutePath));
|
|
187
|
+
for (const file of files) {
|
|
188
|
+
const analysis = analyses.get(file.absolutePath);
|
|
189
|
+
if (!analysis)
|
|
190
|
+
continue;
|
|
191
|
+
const isPythonFile = file.absolutePath.endsWith('.py') || file.absolutePath.endsWith('.pyw');
|
|
192
|
+
const isJavaFile = file.absolutePath.endsWith('.java');
|
|
193
|
+
for (const imp of analysis.imports) {
|
|
194
|
+
// Skip non-relative imports for JS/TS (those are always npm packages).
|
|
195
|
+
// For Python files we must NOT skip: `from services.retriever import X`
|
|
196
|
+
// is flagged isRelative=false but may resolve to a local module.
|
|
197
|
+
// For Java files we also must NOT skip: imports are always absolute
|
|
198
|
+
// class FQNs and we try to resolve them against the project source root.
|
|
199
|
+
if (!imp.isRelative && !isPythonFile && !isJavaFile)
|
|
200
|
+
continue;
|
|
201
|
+
// Skip known builtins and third-party packages that can't resolve to
|
|
202
|
+
// a file inside the project (Python stdlib, JDK, Spring, etc.).
|
|
203
|
+
if (!imp.isRelative && imp.isBuiltin)
|
|
204
|
+
continue;
|
|
205
|
+
// Resolve the import to an absolute path
|
|
206
|
+
const resolvedPath = await resolveImport(imp.source, file.absolutePath, {
|
|
207
|
+
baseDir: this.options.rootDir,
|
|
208
|
+
extensions: this.options.extensions.length > 0 ? this.options.extensions : undefined,
|
|
209
|
+
sourcePackage: isJavaFile ? analysis.javaPackage : undefined,
|
|
210
|
+
});
|
|
211
|
+
// Skip if not resolved or not in our file set
|
|
212
|
+
if (!resolvedPath || !fileSet.has(resolvedPath))
|
|
213
|
+
continue;
|
|
214
|
+
// Create edge
|
|
215
|
+
const edge = {
|
|
216
|
+
source: file.absolutePath,
|
|
217
|
+
target: resolvedPath,
|
|
218
|
+
importedNames: imp.importedNames,
|
|
219
|
+
isTypeOnly: imp.isTypeOnly,
|
|
220
|
+
weight: imp.isTypeOnly ? 0.5 : 1,
|
|
221
|
+
};
|
|
222
|
+
this.edges.push(edge);
|
|
223
|
+
// Update adjacency lists
|
|
224
|
+
this.adjacencyList.get(file.absolutePath)?.add(resolvedPath);
|
|
225
|
+
this.reverseAdjacencyList.get(resolvedPath)?.add(file.absolutePath);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Calculate in-degree and out-degree for each node
|
|
231
|
+
*/
|
|
232
|
+
calculateDegrees() {
|
|
233
|
+
for (const [nodeId, node] of this.nodes) {
|
|
234
|
+
node.metrics.outDegree = this.adjacencyList.get(nodeId)?.size ?? 0;
|
|
235
|
+
node.metrics.inDegree = this.reverseAdjacencyList.get(nodeId)?.size ?? 0;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Calculate betweenness centrality using Brandes' algorithm
|
|
240
|
+
*/
|
|
241
|
+
calculateBetweenness() {
|
|
242
|
+
const nodeIds = Array.from(this.nodes.keys());
|
|
243
|
+
const betweenness = new Map();
|
|
244
|
+
// Initialize betweenness to 0
|
|
245
|
+
for (const id of nodeIds) {
|
|
246
|
+
betweenness.set(id, 0);
|
|
247
|
+
}
|
|
248
|
+
// Brandes' algorithm
|
|
249
|
+
for (const source of nodeIds) {
|
|
250
|
+
const stack = [];
|
|
251
|
+
const predecessors = new Map();
|
|
252
|
+
const sigma = new Map();
|
|
253
|
+
const distance = new Map();
|
|
254
|
+
const delta = new Map();
|
|
255
|
+
// Initialize
|
|
256
|
+
for (const v of nodeIds) {
|
|
257
|
+
predecessors.set(v, []);
|
|
258
|
+
sigma.set(v, 0);
|
|
259
|
+
distance.set(v, -1);
|
|
260
|
+
delta.set(v, 0);
|
|
261
|
+
}
|
|
262
|
+
sigma.set(source, 1);
|
|
263
|
+
distance.set(source, 0);
|
|
264
|
+
// BFS
|
|
265
|
+
const queue = [source];
|
|
266
|
+
while (queue.length > 0) {
|
|
267
|
+
const v = queue.shift();
|
|
268
|
+
stack.push(v);
|
|
269
|
+
const neighbors = this.adjacencyList.get(v) ?? new Set();
|
|
270
|
+
for (const w of neighbors) {
|
|
271
|
+
// First visit?
|
|
272
|
+
if (distance.get(w) < 0) {
|
|
273
|
+
queue.push(w);
|
|
274
|
+
distance.set(w, distance.get(v) + 1);
|
|
275
|
+
}
|
|
276
|
+
// Shortest path to w via v?
|
|
277
|
+
if (distance.get(w) === distance.get(v) + 1) {
|
|
278
|
+
sigma.set(w, sigma.get(w) + sigma.get(v));
|
|
279
|
+
predecessors.get(w).push(v);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Back-propagation
|
|
284
|
+
while (stack.length > 0) {
|
|
285
|
+
const w = stack.pop();
|
|
286
|
+
for (const v of predecessors.get(w)) {
|
|
287
|
+
delta.set(v, delta.get(v) + (sigma.get(v) / sigma.get(w)) * (1 + delta.get(w)));
|
|
288
|
+
}
|
|
289
|
+
if (w !== source) {
|
|
290
|
+
betweenness.set(w, betweenness.get(w) + delta.get(w));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Normalize and update nodes
|
|
295
|
+
const maxBetweenness = Array.from(betweenness.values()).reduce((a, b) => Math.max(a, b), 1);
|
|
296
|
+
for (const [nodeId, node] of this.nodes) {
|
|
297
|
+
node.metrics.betweenness = betweenness.get(nodeId) / maxBetweenness;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Calculate PageRank-style importance scores
|
|
302
|
+
*/
|
|
303
|
+
calculatePageRank() {
|
|
304
|
+
const nodeIds = Array.from(this.nodes.keys());
|
|
305
|
+
const n = nodeIds.length;
|
|
306
|
+
if (n === 0)
|
|
307
|
+
return;
|
|
308
|
+
const d = this.options.dampingFactor;
|
|
309
|
+
let pageRank = new Map();
|
|
310
|
+
let newPageRank = new Map();
|
|
311
|
+
// Initialize
|
|
312
|
+
for (const id of nodeIds) {
|
|
313
|
+
pageRank.set(id, 1 / n);
|
|
314
|
+
}
|
|
315
|
+
// Iterate until convergence
|
|
316
|
+
for (let iter = 0; iter < this.options.maxIterations; iter++) {
|
|
317
|
+
let maxDiff = 0;
|
|
318
|
+
for (const id of nodeIds) {
|
|
319
|
+
// Sum contributions from nodes that link to this one
|
|
320
|
+
let sum = 0;
|
|
321
|
+
const incomingNodes = this.reverseAdjacencyList.get(id) ?? new Set();
|
|
322
|
+
for (const source of incomingNodes) {
|
|
323
|
+
const outDegree = this.adjacencyList.get(source)?.size ?? 1;
|
|
324
|
+
sum += pageRank.get(source) / outDegree;
|
|
325
|
+
}
|
|
326
|
+
const newRank = (1 - d) / n + d * sum;
|
|
327
|
+
newPageRank.set(id, newRank);
|
|
328
|
+
maxDiff = Math.max(maxDiff, Math.abs(newRank - pageRank.get(id)));
|
|
329
|
+
}
|
|
330
|
+
// Swap
|
|
331
|
+
[pageRank, newPageRank] = [newPageRank, pageRank];
|
|
332
|
+
// Check convergence
|
|
333
|
+
if (maxDiff < 1e-6)
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
// Normalize and update nodes
|
|
337
|
+
const maxPageRank = Array.from(pageRank.values()).reduce((a, b) => Math.max(a, b), 0.001);
|
|
338
|
+
for (const [nodeId, node] of this.nodes) {
|
|
339
|
+
node.metrics.pageRank = pageRank.get(nodeId) / maxPageRank;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Detect clusters using a simple community detection approach
|
|
344
|
+
* Groups files by their common directory prefix and connectivity
|
|
345
|
+
*/
|
|
346
|
+
detectClusters() {
|
|
347
|
+
const clusters = [];
|
|
348
|
+
const nodeIds = Array.from(this.nodes.keys());
|
|
349
|
+
// Group by directory
|
|
350
|
+
const dirGroups = new Map();
|
|
351
|
+
for (const nodeId of nodeIds) {
|
|
352
|
+
const node = this.nodes.get(nodeId);
|
|
353
|
+
const dir = node.file.directory || '(root)';
|
|
354
|
+
if (!dirGroups.has(dir)) {
|
|
355
|
+
dirGroups.set(dir, []);
|
|
356
|
+
}
|
|
357
|
+
dirGroups.get(dir).push(nodeId);
|
|
358
|
+
}
|
|
359
|
+
// Create clusters from directory groups
|
|
360
|
+
let clusterId = 0;
|
|
361
|
+
for (const [dir, files] of dirGroups) {
|
|
362
|
+
if (files.length < this.options.minClusterSize)
|
|
363
|
+
continue;
|
|
364
|
+
// Calculate internal and external edges
|
|
365
|
+
let internalEdges = 0;
|
|
366
|
+
let externalEdges = 0;
|
|
367
|
+
const fileSet = new Set(files);
|
|
368
|
+
for (const edge of this.edges) {
|
|
369
|
+
const sourceInCluster = fileSet.has(edge.source);
|
|
370
|
+
const targetInCluster = fileSet.has(edge.target);
|
|
371
|
+
if (sourceInCluster && targetInCluster) {
|
|
372
|
+
internalEdges++;
|
|
373
|
+
}
|
|
374
|
+
else if (sourceInCluster || targetInCluster) {
|
|
375
|
+
externalEdges++;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Calculate cohesion (internal density)
|
|
379
|
+
const possibleInternalEdges = files.length * (files.length - 1);
|
|
380
|
+
const cohesion = possibleInternalEdges > 0 ? internalEdges / possibleInternalEdges : 0;
|
|
381
|
+
// Calculate coupling (external connections)
|
|
382
|
+
const totalEdges = internalEdges + externalEdges;
|
|
383
|
+
const coupling = totalEdges > 0 ? externalEdges / totalEdges : 0;
|
|
384
|
+
// Generate suggested domain name
|
|
385
|
+
const suggestedDomain = this.suggestDomainName(dir, files);
|
|
386
|
+
clusters.push({
|
|
387
|
+
id: `cluster-${clusterId}`,
|
|
388
|
+
name: dir,
|
|
389
|
+
files,
|
|
390
|
+
internalEdges,
|
|
391
|
+
externalEdges,
|
|
392
|
+
cohesion,
|
|
393
|
+
coupling,
|
|
394
|
+
suggestedDomain,
|
|
395
|
+
color: CLUSTER_PALETTE[clusterId++ % CLUSTER_PALETTE.length],
|
|
396
|
+
isStructural: internalEdges > 0,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return clusters;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Suggest a domain name based on directory and file contents
|
|
403
|
+
*/
|
|
404
|
+
suggestDomainName(dir, files) {
|
|
405
|
+
// Extract meaningful name from directory
|
|
406
|
+
const parts = dir.split('/').filter(p => p && p !== '(root)');
|
|
407
|
+
// Common patterns to convert
|
|
408
|
+
const patterns = [
|
|
409
|
+
[/^src$/i, ''],
|
|
410
|
+
[/^lib$/i, ''],
|
|
411
|
+
[/^app$/i, ''],
|
|
412
|
+
[/^(api|routes|endpoints?)$/i, 'api'],
|
|
413
|
+
[/^(models?|entities|schemas?)$/i, 'domain'],
|
|
414
|
+
[/^(services?)$/i, 'services'],
|
|
415
|
+
[/^(controllers?)$/i, 'controllers'],
|
|
416
|
+
[/^(utils?|helpers?|common)$/i, 'utilities'],
|
|
417
|
+
[/^(components?)$/i, 'components'],
|
|
418
|
+
[/^(hooks?)$/i, 'hooks'],
|
|
419
|
+
[/^(auth|authentication)$/i, 'authentication'],
|
|
420
|
+
[/^(users?)$/i, 'users'],
|
|
421
|
+
[/^(products?)$/i, 'products'],
|
|
422
|
+
[/^(orders?)$/i, 'orders'],
|
|
423
|
+
[/^(payments?)$/i, 'payments'],
|
|
424
|
+
[/^(core)$/i, 'core'],
|
|
425
|
+
];
|
|
426
|
+
// Try to find a meaningful name
|
|
427
|
+
for (const part of parts.reverse()) {
|
|
428
|
+
for (const [pattern, replacement] of patterns) {
|
|
429
|
+
if (pattern.test(part)) {
|
|
430
|
+
return replacement || part.toLowerCase();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// If no pattern matches, use the part as-is
|
|
434
|
+
if (!/^(src|lib|app)$/i.test(part)) {
|
|
435
|
+
return part.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Fallback: use first file's name pattern
|
|
439
|
+
if (files.length > 0) {
|
|
440
|
+
const firstFile = this.nodes.get(files[0])?.file.name ?? 'unknown';
|
|
441
|
+
return firstFile.replace(/\.(ts|js|tsx|jsx|py)x?$/, '').toLowerCase();
|
|
442
|
+
}
|
|
443
|
+
return 'misc';
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Detect cycles in the dependency graph using DFS
|
|
447
|
+
*/
|
|
448
|
+
detectCycles() {
|
|
449
|
+
const cycles = [];
|
|
450
|
+
const visited = new Set();
|
|
451
|
+
const recursionStack = new Set();
|
|
452
|
+
const path = [];
|
|
453
|
+
const dfs = (node) => {
|
|
454
|
+
visited.add(node);
|
|
455
|
+
recursionStack.add(node);
|
|
456
|
+
path.push(node);
|
|
457
|
+
const neighbors = this.adjacencyList.get(node) ?? new Set();
|
|
458
|
+
for (const neighbor of neighbors) {
|
|
459
|
+
if (!visited.has(neighbor)) {
|
|
460
|
+
dfs(neighbor);
|
|
461
|
+
}
|
|
462
|
+
else if (recursionStack.has(neighbor)) {
|
|
463
|
+
// Found a cycle
|
|
464
|
+
const cycleStart = path.indexOf(neighbor);
|
|
465
|
+
const cycle = path.slice(cycleStart);
|
|
466
|
+
cycle.push(neighbor); // Complete the cycle
|
|
467
|
+
// Check if this cycle is not a duplicate (or rotation of existing)
|
|
468
|
+
if (!this.isDuplicateCycle(cycles, cycle)) {
|
|
469
|
+
cycles.push(cycle);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
path.pop();
|
|
474
|
+
recursionStack.delete(node);
|
|
475
|
+
};
|
|
476
|
+
for (const nodeId of this.nodes.keys()) {
|
|
477
|
+
if (!visited.has(nodeId)) {
|
|
478
|
+
dfs(nodeId);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return cycles;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Check if a cycle is a duplicate or rotation of an existing cycle
|
|
485
|
+
*/
|
|
486
|
+
isDuplicateCycle(existingCycles, newCycle) {
|
|
487
|
+
const normalizedNew = this.normalizeCycle(newCycle);
|
|
488
|
+
for (const existing of existingCycles) {
|
|
489
|
+
const normalizedExisting = this.normalizeCycle(existing);
|
|
490
|
+
if (normalizedNew === normalizedExisting) {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Normalize a cycle for comparison (smallest element first, then compare)
|
|
498
|
+
*/
|
|
499
|
+
normalizeCycle(cycle) {
|
|
500
|
+
// Remove the duplicate closing element
|
|
501
|
+
const clean = cycle.slice(0, -1);
|
|
502
|
+
if (clean.length === 0)
|
|
503
|
+
return '';
|
|
504
|
+
// Find the smallest element
|
|
505
|
+
const minIdx = clean.indexOf(clean.reduce((min, curr) => (curr < min ? curr : min)));
|
|
506
|
+
// Rotate so smallest is first
|
|
507
|
+
const rotated = [...clean.slice(minIdx), ...clean.slice(0, minIdx)];
|
|
508
|
+
return rotated.join('|');
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Generate various rankings of nodes
|
|
512
|
+
*/
|
|
513
|
+
generateRankings(clusters) {
|
|
514
|
+
const nodes = Array.from(this.nodes.values());
|
|
515
|
+
// By PageRank importance — use .slice() to avoid mutating the shared array
|
|
516
|
+
const byImportance = nodes
|
|
517
|
+
.slice()
|
|
518
|
+
.sort((a, b) => b.metrics.pageRank - a.metrics.pageRank)
|
|
519
|
+
.map(n => n.id);
|
|
520
|
+
// By total connectivity (in + out degree)
|
|
521
|
+
const byConnectivity = nodes
|
|
522
|
+
.slice()
|
|
523
|
+
.sort((a, b) => (b.metrics.inDegree + b.metrics.outDegree) -
|
|
524
|
+
(a.metrics.inDegree + a.metrics.outDegree))
|
|
525
|
+
.map(n => n.id);
|
|
526
|
+
// Cluster centers (highest in-degree within each cluster)
|
|
527
|
+
const clusterCenters = [];
|
|
528
|
+
for (const cluster of clusters) {
|
|
529
|
+
const clusterNodes = cluster.files
|
|
530
|
+
.map(f => this.nodes.get(f))
|
|
531
|
+
.filter((n) => n !== undefined);
|
|
532
|
+
if (clusterNodes.length > 0) {
|
|
533
|
+
const center = clusterNodes.reduce((max, n) => n.metrics.inDegree > max.metrics.inDegree ? n : max);
|
|
534
|
+
clusterCenters.push(center.id);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Leaf nodes (high out-degree, low in-degree)
|
|
538
|
+
const leafNodes = nodes
|
|
539
|
+
.filter(n => n.metrics.outDegree > 0 && n.metrics.inDegree === 0)
|
|
540
|
+
.sort((a, b) => b.metrics.outDegree - a.metrics.outDegree)
|
|
541
|
+
.map(n => n.id);
|
|
542
|
+
// Bridge nodes (high betweenness)
|
|
543
|
+
const bridgeNodes = nodes
|
|
544
|
+
.filter(n => n.metrics.betweenness > 0.1)
|
|
545
|
+
.sort((a, b) => b.metrics.betweenness - a.metrics.betweenness)
|
|
546
|
+
.map(n => n.id);
|
|
547
|
+
// Orphan nodes (no connections)
|
|
548
|
+
const orphanNodes = nodes
|
|
549
|
+
.filter(n => n.metrics.inDegree === 0 && n.metrics.outDegree === 0)
|
|
550
|
+
.map(n => n.id);
|
|
551
|
+
return {
|
|
552
|
+
byImportance,
|
|
553
|
+
byConnectivity,
|
|
554
|
+
clusterCenters,
|
|
555
|
+
leafNodes,
|
|
556
|
+
bridgeNodes,
|
|
557
|
+
orphanNodes,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Calculate overall statistics
|
|
562
|
+
*/
|
|
563
|
+
calculateStatistics(clusters, cycles) {
|
|
564
|
+
const nodeCount = this.nodes.size;
|
|
565
|
+
const edgeCount = this.edges.length;
|
|
566
|
+
// Average degree
|
|
567
|
+
let totalDegree = 0;
|
|
568
|
+
for (const node of this.nodes.values()) {
|
|
569
|
+
totalDegree += node.metrics.inDegree + node.metrics.outDegree;
|
|
570
|
+
}
|
|
571
|
+
const avgDegree = nodeCount > 0 ? totalDegree / nodeCount : 0;
|
|
572
|
+
// Density: actual edges / possible edges
|
|
573
|
+
const possibleEdges = nodeCount * (nodeCount - 1);
|
|
574
|
+
const density = possibleEdges > 0 ? edgeCount / possibleEdges : 0;
|
|
575
|
+
return {
|
|
576
|
+
nodeCount,
|
|
577
|
+
edgeCount,
|
|
578
|
+
importEdgeCount: edgeCount - this.httpEdgeCount,
|
|
579
|
+
httpEdgeCount: this.httpEdgeCount,
|
|
580
|
+
avgDegree,
|
|
581
|
+
density,
|
|
582
|
+
clusterCount: clusters.length,
|
|
583
|
+
structuralClusterCount: clusters.filter(c => c.isStructural).length,
|
|
584
|
+
cycleCount: cycles.length,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// ============================================================================
|
|
589
|
+
// CONVENIENCE FUNCTIONS
|
|
590
|
+
// ============================================================================
|
|
591
|
+
/**
|
|
592
|
+
* Build a dependency graph from scored files
|
|
593
|
+
*/
|
|
594
|
+
export async function buildDependencyGraph(files, options) {
|
|
595
|
+
const builder = new DependencyGraphBuilder(options);
|
|
596
|
+
return builder.build(files);
|
|
597
|
+
}
|
|
598
|
+
// ============================================================================
|
|
599
|
+
// CALL-GRAPH EDGE INJECTION
|
|
600
|
+
// ============================================================================
|
|
601
|
+
/**
|
|
602
|
+
* Languages that share a module-level namespace without explicit intra-file imports.
|
|
603
|
+
* For these, dependency edges must be derived from the call graph rather than imports.
|
|
604
|
+
*/
|
|
605
|
+
const IMPLICIT_IMPORT_LANGS = new Set(['Swift', 'C++', 'C']);
|
|
606
|
+
/**
|
|
607
|
+
* Synthesize dependency edges from cross-file call edges and inject them into
|
|
608
|
+
* an existing DependencyGraphResult in-place.
|
|
609
|
+
*
|
|
610
|
+
* Use this for languages like Swift or C++ that don't have explicit intra-module
|
|
611
|
+
* imports, resulting in an empty dep graph when only import-based edges are built.
|
|
612
|
+
*/
|
|
613
|
+
export function injectCallGraphEdges(depGraph, callEdges, nodeFilePath) {
|
|
614
|
+
// Build a Set of node IDs for quick membership test
|
|
615
|
+
const nodeIds = new Set(depGraph.nodes.map(n => n.id));
|
|
616
|
+
// Collect unique file-level edges
|
|
617
|
+
const seen = new Set();
|
|
618
|
+
const newEdges = [];
|
|
619
|
+
for (const ce of callEdges) {
|
|
620
|
+
const callerFile = nodeFilePath(ce.callerId);
|
|
621
|
+
const calleeFile = nodeFilePath(ce.calleeId);
|
|
622
|
+
if (!callerFile || !calleeFile || callerFile === calleeFile)
|
|
623
|
+
continue;
|
|
624
|
+
if (!nodeIds.has(callerFile) || !nodeIds.has(calleeFile))
|
|
625
|
+
continue;
|
|
626
|
+
const key = `${callerFile}→${calleeFile}`;
|
|
627
|
+
if (seen.has(key))
|
|
628
|
+
continue;
|
|
629
|
+
seen.add(key);
|
|
630
|
+
newEdges.push({ source: callerFile, target: calleeFile, importedNames: [], isTypeOnly: false, weight: 1, isCallEdge: true });
|
|
631
|
+
}
|
|
632
|
+
if (newEdges.length === 0)
|
|
633
|
+
return;
|
|
634
|
+
depGraph.edges.push(...newEdges);
|
|
635
|
+
// Recompute node in/out degrees
|
|
636
|
+
const inDeg = new Map();
|
|
637
|
+
const outDeg = new Map();
|
|
638
|
+
for (const e of depGraph.edges) {
|
|
639
|
+
outDeg.set(e.source, (outDeg.get(e.source) ?? 0) + 1);
|
|
640
|
+
inDeg.set(e.target, (inDeg.get(e.target) ?? 0) + 1);
|
|
641
|
+
}
|
|
642
|
+
for (const node of depGraph.nodes) {
|
|
643
|
+
node.metrics.inDegree = inDeg.get(node.id) ?? 0;
|
|
644
|
+
node.metrics.outDegree = outDeg.get(node.id) ?? 0;
|
|
645
|
+
}
|
|
646
|
+
// Update statistics
|
|
647
|
+
depGraph.statistics.edgeCount = depGraph.edges.length;
|
|
648
|
+
const totalDegree = depGraph.nodes.reduce((s, n) => s + n.metrics.inDegree + n.metrics.outDegree, 0);
|
|
649
|
+
depGraph.statistics.avgDegree = depGraph.nodes.length > 0 ? totalDegree / depGraph.nodes.length : 0;
|
|
650
|
+
const possible = depGraph.statistics.nodeCount * (depGraph.statistics.nodeCount - 1);
|
|
651
|
+
depGraph.statistics.density = possible > 0 ? depGraph.statistics.edgeCount / possible : 0;
|
|
652
|
+
// Recompute cluster internalEdges and rebuild structuralClusters.
|
|
653
|
+
// Without this, the viewer's cluster view stays empty even when edges exist.
|
|
654
|
+
const fileToCluster = new Map();
|
|
655
|
+
for (const cl of depGraph.clusters) {
|
|
656
|
+
for (const fid of cl.files)
|
|
657
|
+
fileToCluster.set(fid, cl.id);
|
|
658
|
+
}
|
|
659
|
+
const clusterInternalEdges = new Map();
|
|
660
|
+
for (const e of depGraph.edges) {
|
|
661
|
+
const sc = fileToCluster.get(e.source);
|
|
662
|
+
const tc = fileToCluster.get(e.target);
|
|
663
|
+
if (sc && sc === tc)
|
|
664
|
+
clusterInternalEdges.set(sc, (clusterInternalEdges.get(sc) ?? 0) + 1);
|
|
665
|
+
}
|
|
666
|
+
for (const cl of depGraph.clusters) {
|
|
667
|
+
cl.internalEdges = clusterInternalEdges.get(cl.id) ?? 0;
|
|
668
|
+
cl.isStructural = cl.internalEdges > 0;
|
|
669
|
+
}
|
|
670
|
+
depGraph.structuralClusters = depGraph.clusters.filter(cl => cl.internalEdges > 0);
|
|
671
|
+
depGraph.statistics.structuralClusterCount = depGraph.structuralClusters.length;
|
|
672
|
+
}
|
|
673
|
+
export { IMPLICIT_IMPORT_LANGS };
|
|
674
|
+
// ============================================================================
|
|
675
|
+
// EXPORT FORMATS
|
|
676
|
+
// ============================================================================
|
|
677
|
+
/**
|
|
678
|
+
* Convert graph to D3.js force graph format
|
|
679
|
+
*/
|
|
680
|
+
export function toD3Format(result) {
|
|
681
|
+
// Use only structural clusters for group colouring — directory-only
|
|
682
|
+
// clusters (cohesion=0) would produce too many indistinct colour groups.
|
|
683
|
+
const clusterIndex = new Map();
|
|
684
|
+
result.structuralClusters.forEach((cluster, idx) => {
|
|
685
|
+
for (const file of cluster.files) {
|
|
686
|
+
clusterIndex.set(file, idx);
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
return {
|
|
690
|
+
nodes: result.nodes.map(n => ({
|
|
691
|
+
id: n.file.path,
|
|
692
|
+
group: clusterIndex.get(n.id) ?? -1,
|
|
693
|
+
score: n.metrics.pageRank,
|
|
694
|
+
})),
|
|
695
|
+
links: result.edges.map(e => ({
|
|
696
|
+
source: result.nodes.find(n => n.id === e.source)?.file.path ?? e.source,
|
|
697
|
+
target: result.nodes.find(n => n.id === e.target)?.file.path ?? e.target,
|
|
698
|
+
value: e.weight,
|
|
699
|
+
})),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Convert graph to Mermaid diagram syntax
|
|
704
|
+
*/
|
|
705
|
+
export function toMermaidFormat(result, maxNodes = 50) {
|
|
706
|
+
const lines = ['graph TD'];
|
|
707
|
+
// Take top nodes by importance
|
|
708
|
+
const topNodes = result.rankings.byImportance.slice(0, maxNodes);
|
|
709
|
+
const nodeSet = new Set(topNodes);
|
|
710
|
+
// Create node labels
|
|
711
|
+
const nodeLabels = new Map();
|
|
712
|
+
result.nodes
|
|
713
|
+
.filter(n => nodeSet.has(n.id))
|
|
714
|
+
.forEach((n, idx) => {
|
|
715
|
+
const label = `N${idx}`;
|
|
716
|
+
nodeLabels.set(n.id, label);
|
|
717
|
+
const name = n.file.name.replace(/["[\]]/g, '');
|
|
718
|
+
lines.push(` ${label}["${name}"]`);
|
|
719
|
+
});
|
|
720
|
+
// Create edges
|
|
721
|
+
for (const edge of result.edges) {
|
|
722
|
+
const sourceLabel = nodeLabels.get(edge.source);
|
|
723
|
+
const targetLabel = nodeLabels.get(edge.target);
|
|
724
|
+
if (sourceLabel && targetLabel) {
|
|
725
|
+
const style = edge.isTypeOnly ? '-.->' : '-->';
|
|
726
|
+
lines.push(` ${sourceLabel} ${style} ${targetLabel}`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return lines.join('\n');
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Convert graph to DOT format (Graphviz)
|
|
733
|
+
*/
|
|
734
|
+
export function toDotFormat(result) {
|
|
735
|
+
const lines = ['digraph Dependencies {'];
|
|
736
|
+
lines.push(' rankdir=LR;');
|
|
737
|
+
lines.push(' node [shape=box];');
|
|
738
|
+
// Create node definitions with labels
|
|
739
|
+
for (const node of result.nodes) {
|
|
740
|
+
const name = node.file.name.replace(/"/g, '\\"');
|
|
741
|
+
const color = node.metrics.pageRank > 0.5 ? 'lightblue' : 'white';
|
|
742
|
+
lines.push(` "${node.id}" [label="${name}" fillcolor="${color}" style="filled"];`);
|
|
743
|
+
}
|
|
744
|
+
// Create edges
|
|
745
|
+
for (const edge of result.edges) {
|
|
746
|
+
const style = edge.isTypeOnly ? 'dashed' : 'solid';
|
|
747
|
+
lines.push(` "${edge.source}" -> "${edge.target}" [style="${style}"];`);
|
|
748
|
+
}
|
|
749
|
+
lines.push('}');
|
|
750
|
+
return lines.join('\n');
|
|
751
|
+
}
|
|
752
|
+
//# sourceMappingURL=dependency-graph.js.map
|