cg-code-graph 0.23.0__tar.gz → 0.23.1__tar.gz
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.
- {cg_code_graph-0.23.0/cg_code_graph.egg-info → cg_code_graph-0.23.1}/PKG-INFO +2 -2
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/README.md +1 -1
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/__init__.py +1 -1
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/cli.py +6 -1
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/coverage.py +13 -0
- cg_code_graph-0.23.1/cg_code_graph/install_rules.py +88 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/link.py +22 -5
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/mcp_install.py +18 -1
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/mcp_server.py +36 -19
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nuxt/plugin.py +33 -2
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1/cg_code_graph.egg-info}/PKG-INFO +2 -2
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph.egg-info/SOURCES.txt +4 -0
- cg_code_graph-0.23.1/tests/test_install_rules.py +82 -0
- cg_code_graph-0.23.1/tests/test_link_origin.py +80 -0
- cg_code_graph-0.23.1/tests/test_mcp_instructions.py +35 -0
- cg_code_graph-0.23.1/tests/test_nuxt_side_dirs.py +76 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_nuxt.py +1 -1
- cg_code_graph-0.23.0/tests/test_link_origin.py +0 -39
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/LICENSE +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/MANIFEST.in +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/__main__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/affected.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/agent_rules.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/agents.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/aitools.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/android_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/apple_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/apps.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/blindspots.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/bridges.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/brokers.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/concepts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/config.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/cache.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/detect.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/env.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/extractors.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/fsutil.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/generated.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/model.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/paths.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/redact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/store.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/core/syntax_errors.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/dart_isolates.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/doctor.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/explore.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/external.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/graphql.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/guard_facts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/hooks.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/indexer.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/insecure_transport.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/jobs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/lint_async.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/local_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/local_sockets.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/mcp_graphs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/parity.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/parity_structure.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/payload.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plans.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/platform_scan.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/platforms.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/astro/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/astro/content.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/astro/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/cfamily/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/cfamily/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/cfamily/syntax.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/bridges.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/extractor/bin/extract.dart +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/extractor/pubspec.lock +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/extractor/pubspec.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/http.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/models.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/dart/program.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/extras.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/models.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/schemas.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/shapes.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/django/urls.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/express/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/express/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/flutter/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/flutter/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/java/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/java/exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/java/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/jvm/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/jvm/interop.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/jvm/spring.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/kotlin/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/kotlin/exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/kotlin/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/kotlin/reparse.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/broadcast.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/massassign.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/tests.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/laravel/values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/native/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/native/gates.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/native/runner.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/native/scipread.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/native/ts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nest/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nest/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nextjs/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nextjs/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/nuxt/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/external.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/extractor/composer.json +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/extractor/composer.lock +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/extractor/extract.php +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/gating.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/http_client.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/inline_guards.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/php/strings.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/aitools.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/external.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/fields.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/refs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/roots.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/socketio.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/subproc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/tests.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/python/values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/pyweb/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/pyweb/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/pyweb/values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/rust/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/rust/cargo.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/rust/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/rust/syntax.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/scip/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/scip/importer.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/scip/scip.proto +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/scip/scip_pb2.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/stubs/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/stubs/plugins.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/baseurl.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/indexstore.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/packages.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/swift/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/baseurl.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/extractor/extract.mjs +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/extractor/fw.mjs +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/extractor/package-lock.json +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/extractor/package.json +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/extractor/rr.mjs +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/nav.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/plugin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/ts/react_router.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/tsweb/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/tsweb/common.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/plugins/tsweb/data.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/c_cpp.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/common.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/dart.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/django-ninja.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/django.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/djangorestframework.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/express.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/java.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/kotlin.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/laravel.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/nest.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/nextjs.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/nuxt.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/php.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/python.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/react-router.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/rust.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/spring.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/swift.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/presets/typescript.yaml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/process_runs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/protocols/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/protocols/builtin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/protocols/matchers.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/protocols/view.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/query.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/realtime.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/realtime_events.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/realtime_native.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/realtime_ws.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/roundtrip.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/routes.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/rpc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/sdk_systems.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/sockets.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/ssrf_input.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/starters.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/surface.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/tests_index.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/udp_apps.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/__init__.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/graph.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/server.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/app.css +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/app.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/index.html +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/layered.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/vendor/VERSIONS.txt +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/vendor/cose-base.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/vendor/cytoscape-fcose.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/vendor/cytoscape.min.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/static/vendor/layout-base.js +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/tools/package-lock.json +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/tools/package.json +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/viz/tools/shoot.mjs +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/watch.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/webhook_subs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/webhooks.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph/xcode.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph.egg-info/dependency_links.txt +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph.egg-info/entry_points.txt +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph.egg-info/requires.txt +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/cg_code_graph.egg-info/top_level.txt +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/pyproject.toml +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/setup.cfg +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_affected.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_agents_install.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_agents_md.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ai_tools.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_android_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_apple_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_astro.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_astro_content.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_astro_server.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_bridges.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_bridges_pigeon_monorepo.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_brokers.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_cache_clean.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_cache_invalidation.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_cache_migration.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_channels_tests.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_cli_output.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_completeness.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_config_extras.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_coverage.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_coverage_ts_hint.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_dart_isolates.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_db_connections.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_dead_wrappers.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_dispatch_interfaces.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_django_flutter.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_docs_languages.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_docs_links.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_echo_ref.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_echo_wrapper.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_elysia.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_enum_constants.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_env.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_explore.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_external.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_fp_batch.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_gating.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_generated.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_graphql.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_hooks.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_impact_overrides.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_index_isolation.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_inherited_overrides.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_inline_guards.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_192.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_202.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_203.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_205.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_206.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_207.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_208.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_209.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_210.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_211.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_issue_211_used_at.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_java.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_java_exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_jobs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_js_refs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_js_runtime.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_binding.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_fields.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_mixed.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_nav_consts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_kotlin_props.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_laravel_determinism.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_laravel_resources.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_lint_async.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_listing_files.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_local_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_local_sockets.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_mass_assignment.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_mcp.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_mcp_index.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_mcp_install.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_mcp_multi_graph.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_native_cfamily.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_native_concurrency.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_native_rust.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_nuxt_import_dirs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_nuxt_rootsrc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_packaging.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_parity.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_parity_structure.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_php_http.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_php_props.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_plain_js.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_plans.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_platform_gaps.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_platform_type_variants.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_platforms.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_platforms_apple.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_presets_config.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_process_ipc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_process_runs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_protocols.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_app_tests_module.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_collection_chains.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_compat.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_entry_refs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_fields.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_infer_cycles.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_roots.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_subprocess_tests.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_tests.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_web_hosts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_web_routes.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_python_web_routes_more.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_react_router.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_readers_writers_specs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_realtime.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_realtime_native.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_redact_salt.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_request_keys.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_roundtrip.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_route_specs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_route_tables.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_routes_search_explain.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_rpc.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_scip_health.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_sdk_external.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_sdk_external_infra.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_sdk_external_mail.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_snippet.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_sockets.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_spring.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ssrf.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_starters_budget.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_surface.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_surface_transport.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_binding.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_exact.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_facts.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_fields.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_modules.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_more.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_nav_values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_parse_errors.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_props.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_sdk.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_testing.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_swift_values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_tests_scope.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_fields.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_fields_js_vue.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_file_specs.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_frameworks.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_node_http.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_ts_src_tsconfig.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_unguarded.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_values.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_viz.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_vue_nav.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_watch.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_webhooks.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_webhooks_152.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_webhooks_152b.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_websockets.py +0 -0
- {cg_code_graph-0.23.0 → cg_code_graph-0.23.1}/tests/test_workspace_link.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cg-code-graph
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.1
|
|
4
4
|
Summary: cg: a code graph of routes, calls, data access and platform boundaries for agents and humans (CLI + MCP server)
|
|
5
5
|
Author: cyberchronos00
|
|
6
6
|
License-Expression: MIT
|
|
@@ -48,7 +48,7 @@ For you and your AI agent (CLI + MCP). No LLM in the graph.
|
|
|
48
48
|
```bash
|
|
49
49
|
pipx install cg-code-graph # or: uv tool install cg-code-graph
|
|
50
50
|
cg index . --db out/graph.db # prints starter queries for your repo
|
|
51
|
-
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server
|
|
51
|
+
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server and a "use cg first" agent rule
|
|
52
52
|
cg reaches table:orders --db out/graph.db # any table, connection or config key
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -21,7 +21,7 @@ For you and your AI agent (CLI + MCP). No LLM in the graph.
|
|
|
21
21
|
```bash
|
|
22
22
|
pipx install cg-code-graph # or: uv tool install cg-code-graph
|
|
23
23
|
cg index . --db out/graph.db # prints starter queries for your repo
|
|
24
|
-
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server
|
|
24
|
+
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server and a "use cg first" agent rule
|
|
25
25
|
cg reaches table:orders --db out/graph.db # any table, connection or config key
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""code-graph: deterministic code knowledge graph (beta)."""
|
|
2
|
-
__version__ = "0.23.
|
|
2
|
+
__version__ = "0.23.1"
|
|
@@ -73,6 +73,10 @@ def _add_install_parser(sub, uninstall: bool):
|
|
|
73
73
|
p.add_argument("--uninstall", action="store_true", help="remove the entry instead of writing it")
|
|
74
74
|
p.add_argument("--dry-run", action="store_true", help="print the exact changes and write nothing")
|
|
75
75
|
p.add_argument("--yes", action="store_true", help="skip the confirmation prompt")
|
|
76
|
+
p.add_argument("--no-rules", dest="rules", action="store_false",
|
|
77
|
+
help=("leave AGENTS.md and .cursor/rules/cg.mdc alone" if uninstall else
|
|
78
|
+
"do not write the 'use cg first' agent rules (AGENTS.md section, .cursor/rules/cg.mdc); "
|
|
79
|
+
"project scope only"))
|
|
76
80
|
if not uninstall:
|
|
77
81
|
p.add_argument("--registry", action="store_true",
|
|
78
82
|
help="global single entry: cg-mcp with no --db; graphs come from the registry")
|
|
@@ -520,7 +524,8 @@ def main(argv=None):
|
|
|
520
524
|
return run_install(a.host, scope="global" if a.glob else "project", root=a.dir, db=a.db,
|
|
521
525
|
tools=a.tools, portable=a.portable, remove=a.cmd == "uninstall" or a.uninstall,
|
|
522
526
|
dry_run=a.dry_run, assume_yes=a.yes, registry=registry, migrate=migrate,
|
|
523
|
-
apply=bool(getattr(a, "apply", False)), graphs=getattr(a, "graphs", None)
|
|
527
|
+
apply=bool(getattr(a, "apply", False)), graphs=getattr(a, "graphs", None),
|
|
528
|
+
rules=a.rules)
|
|
524
529
|
if a.cmd == "hooks":
|
|
525
530
|
from . import hooks as HK
|
|
526
531
|
return HK.run(a.action, root=a.dir, db=a.db, dry_run=a.dry_run, assume_yes=a.yes)
|
|
@@ -344,6 +344,9 @@ def compute(root: str | Path, plugins: dict, scip_imported: bool = False, report
|
|
|
344
344
|
elif not e["files_complete"] and lang == "typescript" and e.get("unmapped"):
|
|
345
345
|
e["hint"] = ("files outside the source dirs and test trees were not read: list the directories that hold "
|
|
346
346
|
"source in .cg.yaml `include` (or a tsconfig.json `include`) and re-index")
|
|
347
|
+
if (nx := _nuxt_dirs(root)):
|
|
348
|
+
# Nuxt: name the dirs that were read, so a layer or module dir outside them is explained (#214)
|
|
349
|
+
e["hint"] = f"Nuxt project: the source dirs read are the srcDir plus server/ and shared/ ({nx}); " + e["hint"]
|
|
347
350
|
se = (rep or {}).get("syntax_errors")
|
|
348
351
|
if isinstance(se, list) and se and status not in ("skipped", "not_indexed"):
|
|
349
352
|
# files parsed with syntax errors (#73): error spans and the declarations lost there
|
|
@@ -434,6 +437,16 @@ def _ts_not_run(root, with_php: bool) -> tuple[str, str]:
|
|
|
434
437
|
return reason, hint
|
|
435
438
|
|
|
436
439
|
|
|
440
|
+
def _nuxt_dirs(root) -> str | None:
|
|
441
|
+
"""'app/, server/, shared/' for a Nuxt project (the dirs the Nuxt plugin reads), else None."""
|
|
442
|
+
from .plugins.nuxt.plugin import _CFG_NAMES, find_src_dir, side_dirs
|
|
443
|
+
r = Path(root)
|
|
444
|
+
if not any((r / n).exists() for n in _CFG_NAMES):
|
|
445
|
+
return None
|
|
446
|
+
src = find_src_dir(r)
|
|
447
|
+
return ", ".join(["the project root" if src == "." else f"{src}/", *(f"{d}/" for d in side_dirs(r, src))])
|
|
448
|
+
|
|
449
|
+
|
|
437
450
|
def _python_unmapped_hint(st: dict) -> str:
|
|
438
451
|
if st.get("roots_mode") in ("configured", "flag"):
|
|
439
452
|
where = "python.source_roots in .cg.yaml" if st["roots_mode"] == "configured" else "--python-root"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Agent rules written by `cg install` (project scope, on by default, `--no-rules` skips): a short "use cg first"
|
|
2
|
+
section in AGENTS.md (Claude Code, Codex and other agents read it) and an alwaysApply Cursor rule at
|
|
3
|
+
.cursor/rules/cg.mdc. Only the `<!-- cg:start --> ... <!-- cg:end -->` section is ever written: a re-run replaces
|
|
4
|
+
it in place, content around it is kept, and `cg uninstall` removes it."""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
START = "<!-- cg:start -->"
|
|
11
|
+
END = "<!-- cg:end -->"
|
|
12
|
+
AGENTS_FILE = "AGENTS.md"
|
|
13
|
+
CURSOR_FILE = ".cursor/rules/cg.mdc"
|
|
14
|
+
MDC_HEADER = "---\ndescription: Use the cg code graph before grep or opening whole files\nalwaysApply: true\n---\n"
|
|
15
|
+
_SECTION = re.compile(re.escape(START) + r".*?" + re.escape(END), re.S)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def body(db: str = "out/graph.db") -> str:
|
|
19
|
+
return (
|
|
20
|
+
"## Code graph (cg)\n\n"
|
|
21
|
+
"This repo has a cg code graph (MCP server `cg`). Use it before grep or opening whole files:\n"
|
|
22
|
+
"- `search` to find a symbol (file:line);\n"
|
|
23
|
+
"- `impact` before editing a function (or `reaches` for a table, column or env key) to see what the change affects;\n"
|
|
24
|
+
"- `snippet` to read one symbol instead of a whole file;\n"
|
|
25
|
+
"- `graphs` if unsure which graph to query.\n\n"
|
|
26
|
+
"Use normal search only for what cg reports as not covered (`coverage`). Without MCP: "
|
|
27
|
+
f"`cg search|impact|snippet <symbol> --db {db}`.\n")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def section(db: str = "out/graph.db") -> str:
|
|
31
|
+
return f"{START}\n{body(db)}{END}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _plan(path: Path, new: str | None, old: str, delete: bool = False) -> dict:
|
|
35
|
+
if delete:
|
|
36
|
+
return {"path": str(path), "kind": "text", "action": "remove", "old": old, "new": "", "delete_file": True}
|
|
37
|
+
if new == old:
|
|
38
|
+
return {"path": str(path), "kind": "text", "action": "noop", "old": old, "new": old, "current": True}
|
|
39
|
+
action = "insert" if not _SECTION.search(old) else ("remove" if not _SECTION.search(new) else "update")
|
|
40
|
+
return {"path": str(path), "kind": "text", "action": action, "old": old, "new": new}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _with_section(old: str, sec: str) -> str:
|
|
44
|
+
if _SECTION.search(old):
|
|
45
|
+
return _SECTION.sub(lambda _m: sec, old, count=1)
|
|
46
|
+
if not old:
|
|
47
|
+
return sec + "\n"
|
|
48
|
+
return old + ("" if old.endswith("\n\n") else "\n" if old.endswith("\n") else "\n\n") + sec + "\n"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _without_section(old: str) -> str:
|
|
52
|
+
m = _SECTION.search(old)
|
|
53
|
+
if not m:
|
|
54
|
+
return old
|
|
55
|
+
s, e = m.start(), m.end()
|
|
56
|
+
while e < len(old) and old[e] == "\n":
|
|
57
|
+
e += 1
|
|
58
|
+
while s > 0 and old[s - 1] == "\n":
|
|
59
|
+
s -= 1
|
|
60
|
+
head = old[:s]
|
|
61
|
+
return (head + "\n" + old[e:]) if head and old[e:] else (head + "\n" if head else old[e:])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def plans(root: Path, remove: bool = False, db: str = "out/graph.db") -> list[dict]:
|
|
65
|
+
"""One text plan per rules file (the shape `cg agents` previews and `cg install` applies)."""
|
|
66
|
+
out = []
|
|
67
|
+
sec = section(db)
|
|
68
|
+
agents = root / AGENTS_FILE
|
|
69
|
+
old = agents.read_text(encoding="utf-8") if agents.exists() else ""
|
|
70
|
+
if remove:
|
|
71
|
+
rest = _without_section(old)
|
|
72
|
+
out.append(_plan(agents, None, old, delete=True) if _SECTION.search(old) and not rest.strip() else _plan(agents, rest, old))
|
|
73
|
+
else:
|
|
74
|
+
out.append(_plan(agents, _with_section(old, sec), old))
|
|
75
|
+
mdc = root / CURSOR_FILE
|
|
76
|
+
old = mdc.read_text(encoding="utf-8") if mdc.exists() else ""
|
|
77
|
+
if remove:
|
|
78
|
+
rest = _without_section(old)
|
|
79
|
+
if mdc.exists() and _SECTION.search(old) and rest.strip() in ("", MDC_HEADER.strip()):
|
|
80
|
+
out.append(_plan(mdc, None, old, delete=True))
|
|
81
|
+
else:
|
|
82
|
+
out.append(_plan(mdc, rest, old))
|
|
83
|
+
else:
|
|
84
|
+
new = _with_section(old, sec)
|
|
85
|
+
if not new.startswith("---\n"):
|
|
86
|
+
new = MDC_HEADER + new # an alwaysApply header, added once in front of whatever is there
|
|
87
|
+
out.append(_plan(mdc, new, old))
|
|
88
|
+
return [p for p in out if not (remove and p["action"] == "noop" and not p["old"])]
|
|
@@ -137,7 +137,10 @@ def known_origin_text(attrs: dict) -> str | None:
|
|
|
137
137
|
return None
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
_GENERIC_ORIGIN = {"env", "base", "url", "api", "app", "http", "https", "host", "origin", "endpoint", "service",
|
|
140
|
+
_GENERIC_ORIGIN = {"env", "base", "url", "uri", "api", "app", "http", "https", "host", "origin", "endpoint", "service",
|
|
141
|
+
"internal", "server", "backend", "public", "private", "remote", "domain", "root", "prefix", "path",
|
|
142
|
+
"address", "addr", "runtime", "config", "runtimeconfig", "process", "import", "meta", "window",
|
|
143
|
+
"location", "nuxt", "next", "vite", "react", "expo", "vue", "localhost", "www", "com"}
|
|
141
144
|
|
|
142
145
|
|
|
143
146
|
def repos_for_origin(text: str, servers: list[str]) -> list[str] | None:
|
|
@@ -146,17 +149,20 @@ def repos_for_origin(text: str, servers: list[str]) -> list[str] | None:
|
|
|
146
149
|
``None`` when the origin is a generic API base (``API_BASE_URL``) and does not name a repo:
|
|
147
150
|
matching stays as it was. ``[]`` when it names a service that is not one of ``servers``.
|
|
148
151
|
"""
|
|
149
|
-
|
|
152
|
+
# camelCase splits (runtimeConfig.public.apiBase -> runtime config public api base); `-`, `_` and `.` all separate,
|
|
153
|
+
# so ORDERS_API_URL names an orders-api repo
|
|
154
|
+
split = re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", text).lower()
|
|
155
|
+
norm = "-" + "-".join(re.findall(r"[a-z0-9]+", split)) + "-"
|
|
150
156
|
hits = []
|
|
151
157
|
for name in servers:
|
|
152
|
-
token = name.lower()
|
|
158
|
+
token = "-".join(re.findall(r"[a-z0-9]+", re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", name).lower()))
|
|
153
159
|
if len(token) < 3:
|
|
154
160
|
continue
|
|
155
|
-
if
|
|
161
|
+
if f"-{token}-" in norm:
|
|
156
162
|
hits.append(name)
|
|
157
163
|
if hits:
|
|
158
164
|
return hits
|
|
159
|
-
tokens = [t for t in re.findall(r"[a-z][a-z0-9]+",
|
|
165
|
+
tokens = [t for t in re.findall(r"[a-z][a-z0-9]+", split) if t not in _GENERIC_ORIGIN]
|
|
160
166
|
if not tokens:
|
|
161
167
|
return None
|
|
162
168
|
return []
|
|
@@ -431,6 +437,7 @@ def link_many(repos: list[tuple[str, str, str]], out_db: str, *, allow: dict | N
|
|
|
431
437
|
by_server[rt["server"]].append(rt)
|
|
432
438
|
results = []
|
|
433
439
|
new_edges = []
|
|
440
|
+
matched_origins: set[tuple[str, str]] = set()
|
|
434
441
|
for client, ep_id, a, _test in http_rows:
|
|
435
442
|
targets = [s for s in server_names if (s != client or folded_self) and (client not in allow or s in allow[client])]
|
|
436
443
|
pool = [rt for s in targets for rt in by_server[s]]
|
|
@@ -443,7 +450,11 @@ def link_many(repos: list[tuple[str, str, str]], out_db: str, *, allow: dict | N
|
|
|
443
450
|
if origin_text and owners == [] and not res.get("matched") and a.get("origin_kind") != "other":
|
|
444
451
|
reason = res.get("reason") or ""
|
|
445
452
|
if reason.startswith(("no backend route", "not a backend URL")) or "suffix" in reason:
|
|
453
|
+
res["_reason_before_external"] = reason
|
|
454
|
+
res["_origin_text"] = origin_text
|
|
446
455
|
res["reason"] = "external (other origin)"
|
|
456
|
+
elif origin_text and res.get("matched"):
|
|
457
|
+
matched_origins.add((client, origin_text))
|
|
447
458
|
res.update({"endpoint": ep_id, "method": a["method"], "calls": calls_by_ep.get(ep_id, []),
|
|
448
459
|
"_client": client, "_servers": targets})
|
|
449
460
|
results.append(res)
|
|
@@ -452,6 +463,12 @@ def link_many(repos: list[tuple[str, str, str]], out_db: str, *, allow: dict | N
|
|
|
452
463
|
rr = rmap[m["route"]]
|
|
453
464
|
new_edges.append((ep_id, m["route"], "MATCHES_ROUTE", rr["file"], rr["line"], m["confidence"], CONFIDENCE_RANK[m["confidence"]],
|
|
454
465
|
json.dumps({"client_path": a["path"], "uri_variant": m["uri_variant"], "segments": m["segments"]}), None))
|
|
466
|
+
for res in results:
|
|
467
|
+
# an origin whose other calls from this client matched a linked server is that backend, not an external one
|
|
468
|
+
before = res.pop("_reason_before_external", None)
|
|
469
|
+
if before is not None and (res["_client"], res.pop("_origin_text")) in matched_origins:
|
|
470
|
+
res["reason"] = before
|
|
471
|
+
res.pop("_origin_text", None)
|
|
455
472
|
db.executemany("INSERT INTO edges(src,dst,kind,file,line,confidence,conf_rank,attrs,gate) VALUES (?,?,?,?,?,?,?,?,?)", new_edges)
|
|
456
473
|
db.commit()
|
|
457
474
|
subscriptions = pair_subscriber_urls(db, routes, server_names, allow, folded_self)
|
|
@@ -781,6 +781,9 @@ def _apply_file(plan: dict) -> None:
|
|
|
781
781
|
if path.exists():
|
|
782
782
|
path.unlink()
|
|
783
783
|
parent = path.parent
|
|
784
|
+
if parent.name == "rules" and parent.parent.name == ".cursor" and parent.exists() and not any(parent.iterdir()):
|
|
785
|
+
parent.rmdir() # .cursor/rules/cg.mdc was the only rule
|
|
786
|
+
parent = parent.parent
|
|
784
787
|
if parent.name in _EMPTY_PARENTS and parent.exists() and not any(parent.iterdir()):
|
|
785
788
|
parent.rmdir()
|
|
786
789
|
return
|
|
@@ -1023,7 +1026,7 @@ def run(hosts: list[str] | None, *, scope: str, root: str = ".", db: str | None
|
|
|
1023
1026
|
tools: str | None = None, portable: bool = False, remove: bool = False,
|
|
1024
1027
|
dry_run: bool = False, assume_yes: bool = False, input_fn=input, out=print,
|
|
1025
1028
|
registry: bool = False, migrate: bool = False, apply: bool = False,
|
|
1026
|
-
graphs: str | None = None) -> int:
|
|
1029
|
+
graphs: str | None = None, rules: bool = True) -> int:
|
|
1027
1030
|
if migrate:
|
|
1028
1031
|
return run_migrate(hosts, scope=scope, root=root, tools=tools, graphs=graphs, apply=apply and not dry_run, out=out)
|
|
1029
1032
|
rootp = Path(root).resolve()
|
|
@@ -1050,6 +1053,16 @@ def run(hosts: list[str] | None, *, scope: str, root: str = ".", db: str | None
|
|
|
1050
1053
|
dbp = None
|
|
1051
1054
|
plans = iter_plans(expanded, scope=scope, root=rootp, db=dbp, tools=tools, portable=portable, remove=remove,
|
|
1052
1055
|
registry=registry)
|
|
1056
|
+
if rules and scope == "project" and not registry:
|
|
1057
|
+
# "use cg first" rules for agents: AGENTS.md section + alwaysApply Cursor rule (cg_code_graph/install_rules.py)
|
|
1058
|
+
from . import install_rules as IR
|
|
1059
|
+
shown = "out/graph.db"
|
|
1060
|
+
if dbp is not None and not portable:
|
|
1061
|
+
try:
|
|
1062
|
+
shown = dbp.relative_to(rootp).as_posix()
|
|
1063
|
+
except ValueError:
|
|
1064
|
+
shown = str(dbp)
|
|
1065
|
+
plans = [*plans, *IR.plans(rootp, remove=remove, db=shown)]
|
|
1053
1066
|
if not portable and not remove and uses_module_fallback():
|
|
1054
1067
|
out(_FALLBACK_NOTE)
|
|
1055
1068
|
for plan in plans:
|
|
@@ -1088,6 +1101,10 @@ def run(hosts: list[str] | None, *, scope: str, root: str = ".", db: str | None
|
|
|
1088
1101
|
_apply_file(plan)
|
|
1089
1102
|
if plan.get("delete_file"):
|
|
1090
1103
|
out(f"removed {plan['path']}")
|
|
1104
|
+
elif plan.get("kind") == "text" and plan["action"] == "remove":
|
|
1105
|
+
out(f"removed the cg section from {plan['path']}")
|
|
1106
|
+
elif plan.get("kind") == "text":
|
|
1107
|
+
out(f"wrote {plan['path']} (cg agent rules, {plan['action']})")
|
|
1091
1108
|
else:
|
|
1092
1109
|
out(f"wrote {plan['path']} ({plan['action']})")
|
|
1093
1110
|
return 0
|
|
@@ -62,9 +62,9 @@ STATE = _State({"db": str(ROOT / "out" / "graph.db"), "root": None, "gates": Non
|
|
|
62
62
|
"graph_entry": None, "graph_name": None, "graph_arg": False})
|
|
63
63
|
_MCP_ROOTS: contextvars.ContextVar[list[str] | None] = contextvars.ContextVar("cg_mcp_roots", default=None)
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
# Reference text (stacks, spec forms, per-feature notes): the MCP resource cg://guide. The initialize
|
|
66
|
+
# `instructions` stay short and directive so agents use cg before grep / file reads.
|
|
67
|
+
GUIDE = (
|
|
68
68
|
"Code graph of a project: classes, functions, routes, commands, jobs, pages, DB tables/columns, connections, "
|
|
69
69
|
"config/env keys, each edge with file:line evidence and a confidence (exact / resolved / heuristic). Stacks: "
|
|
70
70
|
"For a broad question ('how does X work') start with `explore`: source, entry points, call paths and blast radius in one call. "
|
|
@@ -119,8 +119,23 @@ server = MCPServer(
|
|
|
119
119
|
"issue links) and overlays the graph without changing it. Workflow: write the plan -> `plan_check` (compact "
|
|
120
120
|
"summary with counts and the top items; details=true for every item with file:line, forbidden paths and open "
|
|
121
121
|
"findings) -> refine the plan -> `plan_baseline` -> implement -> `index` -> `plan_check(verify=true)` (planned "
|
|
122
|
-
"nodes/edges exist, forbidden paths gone or guarded, modified targets changed).")
|
|
123
|
-
|
|
122
|
+
"nodes/edges exist, forbidden paths gone or guarded, modified targets changed).")
|
|
123
|
+
|
|
124
|
+
INSTRUCTIONS = (
|
|
125
|
+
"Use cg before grep or opening files for questions about where code is, what calls what, or what a change "
|
|
126
|
+
"affects. `search` finds a symbol; before editing, `impact` (callers up to entry points) or `reaches` (every "
|
|
127
|
+
"dependent) shows what the change affects; `snippet` reads one symbol instead of a whole file; `explore` answers "
|
|
128
|
+
"a broad 'how does X work' in one call. Unsure which graph? Call `graphs`. Fall back to normal search only for "
|
|
129
|
+
"what cg reports as not covered (`coverage`, a `coverage note:` line). Spec forms and stack notes: resource "
|
|
130
|
+
"cg://guide.")
|
|
131
|
+
|
|
132
|
+
server = MCPServer(name="code-graph", instructions=INSTRUCTIONS)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@server.resource("cg://guide", name="guide", description="cg reference: stacks, spec forms, per-feature notes",
|
|
136
|
+
mime_type="text/plain")
|
|
137
|
+
def guide() -> str:
|
|
138
|
+
return GUIDE
|
|
124
139
|
|
|
125
140
|
|
|
126
141
|
def _rel_text(txt: str) -> str:
|
|
@@ -451,7 +466,8 @@ def _ename(e: dict) -> str:
|
|
|
451
466
|
def reaches(targets: list[str], min_confidence: str = "heuristic", group_by: str = "module",
|
|
452
467
|
include_gated: bool = True, max_per_group: int = 25, paths: bool = True, platform: str | None = None,
|
|
453
468
|
connection: str | None = None) -> str:
|
|
454
|
-
"""
|
|
469
|
+
"""Use before changing a column, table, connection, env key or symbol: everything that depends on it.
|
|
470
|
+
Reverse transitive dependents of one or more targets (union), e.g.
|
|
455
471
|
["orders.customer_id", "connection:warehouse", "connection:tenant_*"].
|
|
456
472
|
|
|
457
473
|
Groups: RUNTIME (live; reached from http routes/schedules/jobs/listeners, or main()/exported FFI symbols in
|
|
@@ -489,7 +505,7 @@ def reaches(targets: list[str], min_confidence: str = "heuristic", group_by: str
|
|
|
489
505
|
route_lines = [Q.route_failure(st, s)["text"] for s in missing if Q.route_failure(st, s)]
|
|
490
506
|
if route_lines and len(route_lines) == len(missing):
|
|
491
507
|
return "\n".join(route_lines)
|
|
492
|
-
return "\n".join(pline + [f"targets: {tl}"]) + f"\nno node matches {', '.join(map(repr, missing))}; try search() with part of the name (spec forms
|
|
508
|
+
return "\n".join(pline + [f"targets: {tl}"]) + f"\nno node matches {', '.join(map(repr, missing))}; try search() with part of the name (spec forms: resource cg://guide)"
|
|
493
509
|
if not items and connection:
|
|
494
510
|
return "\n".join(pline + [f"targets: {tl}", f"no dependents recorded for {', '.join(res['targets'])} on connection {connection!r} "
|
|
495
511
|
f"(connections seen: {Q.seen_text(res.get('connections_seen') or [])})."])
|
|
@@ -550,7 +566,7 @@ def reaches(targets: list[str], min_confidence: str = "heuristic", group_by: str
|
|
|
550
566
|
@tool
|
|
551
567
|
def impact(method: str, min_confidence: str = "heuristic", max_items: int = 60, platform: str | None = None,
|
|
552
568
|
paths: bool = True) -> str:
|
|
553
|
-
"""Reverse callers of a method (Class::method, short or FQN) up to entry points, with the shortest call path
|
|
569
|
+
"""Use before editing a function: what a change to it affects. Reverse callers of a method (Class::method, short or FQN) up to entry points, with the shortest call path
|
|
554
570
|
from each entry point.
|
|
555
571
|
platform: only code built for that target (windows, linux, macos, ios, android, web): code under a platform
|
|
556
572
|
condition that is false there (#[cfg], #if, Platform.OS / Platform.isX, kIsWeb, .ios.ts / .android.ts files,
|
|
@@ -666,7 +682,7 @@ CALLER_KINDS = ("CALLS", "INSTANTIATES", "IMPLEMENTED_BY", "OVERRIDDEN_BY", "BOU
|
|
|
666
682
|
|
|
667
683
|
@tool
|
|
668
684
|
def callers(symbol: str, min_confidence: str = "heuristic", limit: int = 60) -> str:
|
|
669
|
-
"""Direct callers of a function / method / class (one level: CALLS, INSTANTIATES, dispatch and framework edges
|
|
685
|
+
"""Use to see who calls X right now. Direct callers of a function / method / class (one level: CALLS, INSTANTIATES, dispatch and framework edges
|
|
670
686
|
into it), each with the call site file:line and confidence. For the full chain up to entry points use impact()."""
|
|
671
687
|
st = _st()
|
|
672
688
|
ids = Q.resolve_targets(st, symbol)
|
|
@@ -1042,8 +1058,9 @@ def affected(files: list[str] | None = None, base: str | None = None, max_depth:
|
|
|
1042
1058
|
|
|
1043
1059
|
@tool
|
|
1044
1060
|
def node(id_or_symbol: str) -> str:
|
|
1045
|
-
"""Details of one node: kind, FQN, file:line span, module, entry kinds, docblock (PHPDoc), and edge counts
|
|
1046
|
-
by kind (in/out) with a few neighbours.
|
|
1061
|
+
"""Use to inspect a symbol without reading its file. Details of one node: kind, FQN, file:line span, module, entry kinds, docblock (PHPDoc), and edge counts
|
|
1062
|
+
by kind (in/out) with a few neighbours. Specs: Class::method | file#name | table.column | table:<t> |
|
|
1063
|
+
route:<METHOD> <uri> | env:<KEY> | page:/path (more: resource cg://guide)."""
|
|
1047
1064
|
st = _st()
|
|
1048
1065
|
n = st.node(id_or_symbol)
|
|
1049
1066
|
if not n:
|
|
@@ -1076,7 +1093,7 @@ def node(id_or_symbol: str) -> str:
|
|
|
1076
1093
|
|
|
1077
1094
|
@tool
|
|
1078
1095
|
def snippet(id_or_symbol: str, context: int = 0, max_lines: int = 200) -> str:
|
|
1079
|
-
"""Source of one symbol: a `path:start-end` header then the body with line numbers. Resolves a
|
|
1096
|
+
"""Use instead of opening a whole file. Source of one symbol: a `path:start-end` header then the body with line numbers. Resolves a
|
|
1080
1097
|
node id or a symbol / FQN (as node() and search() do); an ambiguous name lists the candidates
|
|
1081
1098
|
instead of guessing. context adds lines around the span; max_lines caps the body (truncation noted)."""
|
|
1082
1099
|
return Q.render_snippet(Q.snippet(_st(), id_or_symbol, context=context, max_lines=max_lines))
|
|
@@ -1084,7 +1101,7 @@ def snippet(id_or_symbol: str, context: int = 0, max_lines: int = 200) -> str:
|
|
|
1084
1101
|
|
|
1085
1102
|
@tool
|
|
1086
1103
|
def search(name: str, kind: str | None = None, limit: int = 20, platform: str | None = None) -> str:
|
|
1087
|
-
"""
|
|
1104
|
+
"""Use first to find a symbol instead of grep. Finds nodes by name / FQN substring (case-insensitive), optionally filtered by kind
|
|
1088
1105
|
(class, method, route, command, table, column, connection, config, env, job, admin...). Also matches route
|
|
1089
1106
|
middleware, guards, auth and access checks (e.g. "auth" finds `auth:api`, `ApiKeyGuard`, `IsAuthenticated`) and
|
|
1090
1107
|
lists the routes that carry them. Platform-specific symbols carry their targets, e.g. `[ios, android]`.
|
|
@@ -1115,7 +1132,7 @@ def explore(query: str, budget_tokens: int = 3000, max_symbols: int = 4) -> str:
|
|
|
1115
1132
|
def routes(writes: str | None = None, reaches: list[str] | None = None, missing: str | None = None,
|
|
1116
1133
|
unguarded: bool = False, strict: bool = False, auth_pattern: str | None = None, max_items: int = 40, paths: bool = True,
|
|
1117
1134
|
min_confidence: str = "heuristic", platform: str | None = None) -> str:
|
|
1118
|
-
"""Routes with their middleware / guards / auth, in one call. Optional scope: writes="*" (routes that reach any
|
|
1135
|
+
"""Use for route, auth and middleware questions. Routes with their middleware / guards / auth, in one call. Optional scope: writes="*" (routes that reach any
|
|
1119
1136
|
DB write) or writes="<table>", reaches=[specs] (routes that reach any of these nodes: table, column, connection,
|
|
1120
1137
|
method, env key...). Optional filters: missing="<name>" keeps routes with no guard whose name contains it (e.g.
|
|
1121
1138
|
"auth:api", "ApiKeyGuard"), unguarded=true keeps routes with no auth guard (the framework presets' auth guards and
|
|
@@ -1154,7 +1171,7 @@ def doctor(root: str | None = None, scip: list[str] | None = None, json_output:
|
|
|
1154
1171
|
|
|
1155
1172
|
@tool
|
|
1156
1173
|
def coverage(path: str | None = None, all_files: bool = False, json_output: bool = False) -> str:
|
|
1157
|
-
"""Which languages and files this index covers. Per language: parser mode (exact, heuristic only when an
|
|
1174
|
+
"""Use when cg finds nothing or you need to know if a file is indexed. Which languages and files this index covers. Per language: parser mode (exact, heuristic only when an
|
|
1158
1175
|
exact-mode indexer such as rust-analyzer or scip-clang is missing, skipped when the toolchain is missing, with the
|
|
1159
1176
|
install hint) and file completeness (discovered vs indexed, with parse failures, files over the size limit,
|
|
1160
1177
|
unmapped files outside the source roots, excluded files); unsupported source types by extension or shebang
|
|
@@ -1242,7 +1259,7 @@ def stats() -> str:
|
|
|
1242
1259
|
@tool
|
|
1243
1260
|
def downstream(target: str, max_per_kind: int = 25, paths: bool = True, min_confidence: str = "heuristic",
|
|
1244
1261
|
platform: str | None = None) -> str:
|
|
1245
|
-
"""
|
|
1262
|
+
"""Use to see what X ends up calling or reading (page -> routes -> tables). Forward dependencies of a node. On a combined graph a frontend page goes
|
|
1246
1263
|
page -> composables/components -> HTTP endpoints -> backend routes -> controllers/services -> tables.
|
|
1247
1264
|
Lists backend routes, tables touched (directly or via columns), columns, connections, config/env keys, each
|
|
1248
1265
|
with one shortest evidence path. target: page:/reports/:id | app/pages/x.vue | Class::method | ...
|
|
@@ -1288,7 +1305,7 @@ def downstream(target: str, max_per_kind: int = 25, paths: bool = True, min_conf
|
|
|
1288
1305
|
|
|
1289
1306
|
@tool
|
|
1290
1307
|
def path(source: str, target: str, min_confidence: str = "heuristic", platform: str | None = None) -> str:
|
|
1291
|
-
"""Shortest forward evidence chain from source to target (e.g. page:/reports/:id ->
|
|
1308
|
+
"""Use to answer "how does A reach B". Shortest forward evidence chain from source to target (e.g. page:/reports/:id ->
|
|
1292
1309
|
SalesReportService::report), one hop per line with file:line and confidence.
|
|
1293
1310
|
platform: only code built for that target (windows, linux, macos, ios, android, web): code under a platform
|
|
1294
1311
|
condition that is false there (#[cfg], #if, Platform.OS / Platform.isX, kIsWeb, .ios.ts / .android.ts files,
|
|
@@ -1601,7 +1618,7 @@ def _index(root: str | None = None, gates: str | None = None, repo: str | None =
|
|
|
1601
1618
|
|
|
1602
1619
|
@tool
|
|
1603
1620
|
def index(root: str | None = None, gates: str | None = None, repo: str | None = None) -> str:
|
|
1604
|
-
"""
|
|
1621
|
+
"""Use after editing so later answers see the change. Re-index (static analysis only: never boots the app or touches a database).
|
|
1605
1622
|
On a combined graph (any number of repos): with no arguments every repo is re-indexed from its recorded root and the
|
|
1606
1623
|
cross-repo link is rebuilt; repo (a name used at link time) re-indexes just that repo; root is matched to the
|
|
1607
1624
|
recorded repo roots (a repo directory, a path inside one, or a parent of several). A root that matches no repo, or
|
|
@@ -1623,7 +1640,7 @@ def index(root: str | None = None, gates: str | None = None, repo: str | None =
|
|
|
1623
1640
|
|
|
1624
1641
|
@tool
|
|
1625
1642
|
def graphs() -> str:
|
|
1626
|
-
"""
|
|
1643
|
+
"""Use when unsure which graph a question belongs to. Lists the graphs this server can query: name, repo roots, languages, indexed commit and freshness.
|
|
1627
1644
|
One `cg-mcp` process serves every registered graph. Pass `graph` to other tools to choose one, or omit it
|
|
1628
1645
|
and the server resolves from a path in the call, then MCP roots or the working directory, then the configured
|
|
1629
1646
|
default. An ambiguous choice returns the list instead of guessing."""
|
|
@@ -87,6 +87,33 @@ _SCAN_EXTS = ("mts", "cts", "ts", "tsx", "mjs", "cjs", "js", "jsx")
|
|
|
87
87
|
_CFG_NAMES = ("nuxt.config.ts", "nuxt.config.mts", "nuxt.config.js", "nuxt.config.mjs", "nuxt.config.cjs")
|
|
88
88
|
# kept out of the generated tsconfig: build output, native shells, static assets (cg_code_graph/presets/nuxt.yaml)
|
|
89
89
|
EXCLUDE = sorted(presets.skip_dirs("nuxt"))
|
|
90
|
+
# Nuxt source dirs outside the srcDir: server/ (Nitro: api, routes, middleware, plugins, utils) and the Nuxt 4 shared/
|
|
91
|
+
# dir sit at the root next to app/ (#214)
|
|
92
|
+
SIDE_DIRS = ("server", "shared")
|
|
93
|
+
_SIDE_EXT = re.compile(r"\.(ts|tsx|mts|cts)$")
|
|
94
|
+
_SIDE_SKIP = re.compile(r"\.(test|spec)\.[cm]?[jt]sx?$|\.d\.ts$")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def side_dirs(root: Path, src_dir: str) -> list[str]:
|
|
98
|
+
"""Root-level server/ and shared/ of a Nuxt project whose srcDir is not the root (with a root srcDir they are
|
|
99
|
+
already inside it; Nuxt 3's src/server sits inside a src/ srcDir)."""
|
|
100
|
+
if src_dir in ("", "."):
|
|
101
|
+
return []
|
|
102
|
+
return [d for d in SIDE_DIRS if (root / d).is_dir()]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def side_files(root: Path, dirs: list[str]) -> list[str]:
|
|
106
|
+
"""TypeScript files under the side dirs (tests and declarations excluded): roots of a prepared run, whose
|
|
107
|
+
.nuxt/tsconfig.app.json leaves server/ to Nitro's own tsconfig."""
|
|
108
|
+
out = []
|
|
109
|
+
for d in dirs:
|
|
110
|
+
for f in sorted((root / d).rglob("*")):
|
|
111
|
+
rel = f.relative_to(root)
|
|
112
|
+
if any(p in COMMON_SKIP or p in EXCLUDE or p.startswith(".") for p in rel.parts[:-1]):
|
|
113
|
+
continue
|
|
114
|
+
if f.is_file() and _SIDE_EXT.search(f.name) and not _SIDE_SKIP.search(f.name):
|
|
115
|
+
out.append(rel.as_posix())
|
|
116
|
+
return out
|
|
90
117
|
|
|
91
118
|
|
|
92
119
|
def _exports(f: Path, _seen: set | None = None) -> list[tuple[str, str]]:
|
|
@@ -861,10 +888,14 @@ class NuxtPlugin(FrameworkPlugin):
|
|
|
861
888
|
f"components{'' if (root / 'node_modules').is_dir() else ', no node_modules so Vue/Nuxt built-ins stay unresolved'}",
|
|
862
889
|
file=sys.stderr)
|
|
863
890
|
cfg["kinds"] = kind_rules(self.src_dir)
|
|
864
|
-
|
|
891
|
+
# server/ and shared/ are source dirs too (#214); a prepared run lists their files as roots
|
|
892
|
+
self.side_dirs = side_dirs(root, self.src_dir)
|
|
893
|
+
cfg["src_dirs"] = [self.src_dir] + self.side_dirs
|
|
894
|
+
if tsc and self.side_dirs:
|
|
895
|
+
cfg["extra_files"] = list(dict.fromkeys([*(cfg.get("extra_files") or []), *side_files(root, self.side_dirs)]))
|
|
865
896
|
|
|
866
897
|
def contribute(self, project: Project, builder: GraphBuilder, ctx) -> dict:
|
|
867
|
-
st = {"prepared": self.prepared, "src_dir": self.src_dir, "pages": 0, "layouts": 0, "ui_global": 0,
|
|
898
|
+
st = {"prepared": self.prepared, "src_dir": self.src_dir, "side_dirs": getattr(self, "side_dirs", []), "pages": 0, "layouts": 0, "ui_global": 0,
|
|
868
899
|
"i18n_keys_used": 0, "i18n_keys_defined": 0}
|
|
869
900
|
if self.generated:
|
|
870
901
|
st["generated_types"] = self.generated
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cg-code-graph
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.1
|
|
4
4
|
Summary: cg: a code graph of routes, calls, data access and platform boundaries for agents and humans (CLI + MCP server)
|
|
5
5
|
Author: cyberchronos00
|
|
6
6
|
License-Expression: MIT
|
|
@@ -48,7 +48,7 @@ For you and your AI agent (CLI + MCP). No LLM in the graph.
|
|
|
48
48
|
```bash
|
|
49
49
|
pipx install cg-code-graph # or: uv tool install cg-code-graph
|
|
50
50
|
cg index . --db out/graph.db # prints starter queries for your repo
|
|
51
|
-
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server
|
|
51
|
+
cg install --host cursor # or claude, codex, vscode, …: registers the MCP server and a "use cg first" agent rule
|
|
52
52
|
cg reaches table:orders --db out/graph.db # any table, connection or config key
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -27,6 +27,7 @@ cg_code_graph/guard_facts.py
|
|
|
27
27
|
cg_code_graph/hooks.py
|
|
28
28
|
cg_code_graph/indexer.py
|
|
29
29
|
cg_code_graph/insecure_transport.py
|
|
30
|
+
cg_code_graph/install_rules.py
|
|
30
31
|
cg_code_graph/jobs.py
|
|
31
32
|
cg_code_graph/link.py
|
|
32
33
|
cg_code_graph/lint_async.py
|
|
@@ -271,6 +272,7 @@ tests/test_impact_overrides.py
|
|
|
271
272
|
tests/test_index_isolation.py
|
|
272
273
|
tests/test_inherited_overrides.py
|
|
273
274
|
tests/test_inline_guards.py
|
|
275
|
+
tests/test_install_rules.py
|
|
274
276
|
tests/test_issue_192.py
|
|
275
277
|
tests/test_issue_202.py
|
|
276
278
|
tests/test_issue_203.py
|
|
@@ -305,12 +307,14 @@ tests/test_mass_assignment.py
|
|
|
305
307
|
tests/test_mcp.py
|
|
306
308
|
tests/test_mcp_index.py
|
|
307
309
|
tests/test_mcp_install.py
|
|
310
|
+
tests/test_mcp_instructions.py
|
|
308
311
|
tests/test_mcp_multi_graph.py
|
|
309
312
|
tests/test_native_cfamily.py
|
|
310
313
|
tests/test_native_concurrency.py
|
|
311
314
|
tests/test_native_rust.py
|
|
312
315
|
tests/test_nuxt_import_dirs.py
|
|
313
316
|
tests/test_nuxt_rootsrc.py
|
|
317
|
+
tests/test_nuxt_side_dirs.py
|
|
314
318
|
tests/test_packaging.py
|
|
315
319
|
tests/test_parity.py
|
|
316
320
|
tests/test_parity_structure.py
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""`cg install` writes "use cg first" agent rules: a marked section in AGENTS.md and an alwaysApply
|
|
2
|
+
.cursor/rules/cg.mdc. Re-runs replace only the marked section; --no-rules and --global skip them."""
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from cg_code_graph import install_rules as IR
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def home(monkeypatch, tmp_path):
|
|
10
|
+
root = tmp_path / "home"
|
|
11
|
+
root.mkdir()
|
|
12
|
+
for k in ("HOME", "USERPROFILE"):
|
|
13
|
+
monkeypatch.setenv(k, str(root))
|
|
14
|
+
monkeypatch.setenv("APPDATA", str(root / "AppData" / "Roaming"))
|
|
15
|
+
monkeypatch.setenv("XDG_CONFIG_HOME", str(root / "xdg"))
|
|
16
|
+
monkeypatch.delenv("COPILOT_HOME", raising=False)
|
|
17
|
+
return root
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _main(argv):
|
|
21
|
+
from cg_code_graph.cli import main
|
|
22
|
+
return main(argv)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_install_writes_rules_idempotently_and_keeps_user_content(home, tmp_path, capsys):
|
|
26
|
+
proj = tmp_path / "recipe-site"
|
|
27
|
+
proj.mkdir()
|
|
28
|
+
(proj / "AGENTS.md").write_text("# Team notes\n\nRun the linter before pushing.\n")
|
|
29
|
+
assert _main(["install", "--host", "cursor", "--yes", "--dir", str(proj)]) == 0
|
|
30
|
+
out = capsys.readouterr().out
|
|
31
|
+
assert "AGENTS.md (cg agent rules, insert)" in out and "cg.mdc (cg agent rules, insert)" in out
|
|
32
|
+
agents = (proj / "AGENTS.md").read_text()
|
|
33
|
+
mdc = (proj / ".cursor" / "rules" / "cg.mdc").read_text()
|
|
34
|
+
assert agents.startswith("# Team notes\n\nRun the linter before pushing.\n\n" + IR.START)
|
|
35
|
+
assert mdc.startswith("---\n") and "alwaysApply: true" in mdc
|
|
36
|
+
for text in (agents, mdc):
|
|
37
|
+
assert text.count(IR.START) == 1 and text.count(IR.END) == 1
|
|
38
|
+
assert "`search`" in text and "`impact`" in text and "`snippet`" in text and "`graphs`" in text
|
|
39
|
+
# user edits after the section survive; a re-run changes nothing
|
|
40
|
+
(proj / "AGENTS.md").write_text(agents + "\n## Release\n\nTag from main.\n")
|
|
41
|
+
agents2 = (proj / "AGENTS.md").read_text()
|
|
42
|
+
assert _main(["install", "--host", "cursor", "--yes", "--dir", str(proj)]) == 0
|
|
43
|
+
assert (proj / "AGENTS.md").read_text() == agents2
|
|
44
|
+
assert (proj / ".cursor" / "rules" / "cg.mdc").read_text() == mdc
|
|
45
|
+
# an outdated section is replaced in place
|
|
46
|
+
stale = agents2.replace("- `snippet`", "- `old line`")
|
|
47
|
+
(proj / "AGENTS.md").write_text(stale)
|
|
48
|
+
assert _main(["install", "--host", "cursor", "--yes", "--dir", str(proj)]) == 0
|
|
49
|
+
assert (proj / "AGENTS.md").read_text() == agents2
|
|
50
|
+
# uninstall removes only the section, and the rule file it created
|
|
51
|
+
assert _main(["uninstall", "--host", "cursor", "--yes", "--dir", str(proj)]) == 0
|
|
52
|
+
left = (proj / "AGENTS.md").read_text()
|
|
53
|
+
assert IR.START not in left and "Run the linter before pushing." in left and "Tag from main." in left
|
|
54
|
+
assert not (proj / ".cursor").exists()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_existing_cursor_rule_gains_header_once(tmp_path):
|
|
58
|
+
proj = tmp_path / "p"
|
|
59
|
+
(proj / ".cursor" / "rules").mkdir(parents=True)
|
|
60
|
+
(proj / ".cursor" / "rules" / "cg.mdc").write_text("Prefer small diffs.\n")
|
|
61
|
+
plans = {p["path"].rsplit("/", 1)[-1]: p for p in IR.plans(proj)}
|
|
62
|
+
new = plans["cg.mdc"]["new"]
|
|
63
|
+
assert new.startswith(IR.MDC_HEADER) and "Prefer small diffs.\n" in new and new.count(IR.START) == 1
|
|
64
|
+
(proj / ".cursor" / "rules" / "cg.mdc").write_text(new)
|
|
65
|
+
assert {p["action"] for p in IR.plans(proj)} == {"insert", "noop"} # AGENTS.md still new, cg.mdc current
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_no_rules_and_global_skip(home, tmp_path, capsys):
|
|
69
|
+
proj = tmp_path / "p"
|
|
70
|
+
proj.mkdir()
|
|
71
|
+
assert _main(["install", "--host", "cursor", "--yes", "--no-rules", "--dir", str(proj)]) == 0
|
|
72
|
+
assert (proj / ".cursor" / "mcp.json").is_file()
|
|
73
|
+
assert not (proj / "AGENTS.md").exists() and not (proj / ".cursor" / "rules").exists()
|
|
74
|
+
db = tmp_path / "g.db"
|
|
75
|
+
db.write_bytes(b"")
|
|
76
|
+
assert _main(["install", "--global", "--host", "cursor", "--yes", "--db", str(db), "--dir", str(proj)]) == 0
|
|
77
|
+
assert not (proj / "AGENTS.md").exists() and not (proj / ".cursor" / "rules").exists()
|
|
78
|
+
assert not list(home.rglob("AGENTS.md")) and not list(home.rglob("cg.mdc"))
|
|
79
|
+
# dry run previews the rules and writes nothing
|
|
80
|
+
assert _main(["install", "--host", "cursor", "--dry-run", "--dir", str(proj)]) == 0
|
|
81
|
+
out = capsys.readouterr().out
|
|
82
|
+
assert IR.START in out and not (proj / "AGENTS.md").exists()
|