python-delphi-lsp 2.3.0__tar.gz → 2.3.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.
- {python_delphi_lsp-2.3.0/python_delphi_lsp.egg-info → python_delphi_lsp-2.3.1}/PKG-INFO +2 -2
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/README.md +1 -1
- python_delphi_lsp-2.3.1/delphi_lsp/_version.py +1 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_cache.py +128 -9
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_cli.py +120 -13
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_context.py +1 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/project_discovery.py +5 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/pyproject.toml +1 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1/python_delphi_lsp.egg-info}/PKG-INFO +2 -2
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/python_delphi_lsp.egg-info/SOURCES.txt +1 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_cache.py +126 -1
- python_delphi_lsp-2.3.1/tests/test_agent_cli_errors.py +222 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_context.py +37 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_worker.py +228 -6
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_package_metadata.py +3 -3
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_project_discovery.py +20 -0
- python_delphi_lsp-2.3.0/delphi_lsp/_version.py +0 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/.github/workflows/ci.yml +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/LICENSE +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/MANIFEST.in +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/__init__.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_layers.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_relations.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_templates.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/agent_workspace.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/binary.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/comment_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/consts.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/delphiast_tokens.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/grammar.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/lark_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/lark_tokens.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/lsp_server.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/nodes.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/parser_backend.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/preprocessor.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/progress.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/project_indexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/semantic.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/semantic_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/source_reader.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/workspace.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/delphi_lsp/writer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/opencode.json +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/python_delphi_lsp.egg-info/entry_points.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/python_delphi_lsp.egg-info/requires.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/python_delphi_lsp.egg-info/top_level.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/audit_delphi_language_features.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/benchmark_github_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/benchmark_parallel_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/bootstrap_vllm_codebase_skill_test.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/bootstrap_vllm_opencode_test.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/build_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/check_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/generate_progress_pdf.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/generate_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/prepare_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/run_opencode_lsp_probe.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/run_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/start_ornith_vllm.sh +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/scripts/summarize_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/setup.cfg +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/corpora.performance.lock.json +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/constset.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/properties.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/library_demo.dpr +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/package_demo.dpk +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/program_demo.dpr +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_advanced.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_attributes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_basic.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_consumer.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_generics.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_inheritance.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_math.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_properties.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_sections.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_statements.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_types.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_unresolved.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/fixtures/unit_with.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_codebase.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_relations.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_agent_workspace.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_bootstrap_vllm_codebase_skill.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_bootstrap_vllm_opencode.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_delphiast_native_compatibility.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_delphiast_semantic_compatibility.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_diagnostics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_language_feature_matrix.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_legacy_snippets.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_lsp_features.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_lsp_support.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_opencode_config.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_opencode_probe.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_ornith_cache_prepare.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_parser_backends.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_preprocessor.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_progress.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_project_indexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_semantic.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_vllm_start_script.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.1}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-delphi-lsp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -42,7 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
|
|
43
43
|
`python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
|
|
44
44
|
indexes, serves LSP, and provides bounded codebase navigation for agents.
|
|
45
|
-
Version 2.3.
|
|
45
|
+
Version 2.3.1 is authored by Dark Light and supports Windows, macOS, and Linux.
|
|
46
46
|
|
|
47
47
|
## Install and quick start
|
|
48
48
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
|
|
4
4
|
indexes, serves LSP, and provides bounded codebase navigation for agents.
|
|
5
|
-
Version 2.3.
|
|
5
|
+
Version 2.3.1 is authored by Dark Light and supports Windows, macOS, and Linux.
|
|
6
6
|
|
|
7
7
|
## Install and quick start
|
|
8
8
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.1"
|
|
@@ -45,6 +45,88 @@ _WATCHED_SUFFIXES = frozenset(
|
|
|
45
45
|
)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def current_process_rss_bytes() -> int:
|
|
49
|
+
"""Return the current resident set size, or zero when unavailable."""
|
|
50
|
+
try:
|
|
51
|
+
if sys.platform == "darwin":
|
|
52
|
+
return _darwin_process_rss_bytes()
|
|
53
|
+
if sys.platform.startswith("linux"):
|
|
54
|
+
statm = Path("/proc/self/statm").read_text(encoding="ascii").split()
|
|
55
|
+
return int(statm[1]) * int(os.sysconf("SC_PAGE_SIZE"))
|
|
56
|
+
if os.name == "nt":
|
|
57
|
+
return _windows_process_rss_bytes()
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _darwin_process_rss_bytes() -> int:
|
|
64
|
+
import ctypes
|
|
65
|
+
|
|
66
|
+
class TimeValue(ctypes.Structure):
|
|
67
|
+
_fields_ = [("seconds", ctypes.c_int), ("microseconds", ctypes.c_int)]
|
|
68
|
+
|
|
69
|
+
class MachTaskBasicInfo(ctypes.Structure):
|
|
70
|
+
_fields_ = [
|
|
71
|
+
("virtual_size", ctypes.c_uint64),
|
|
72
|
+
("resident_size", ctypes.c_uint64),
|
|
73
|
+
("resident_size_max", ctypes.c_uint64),
|
|
74
|
+
("user_time", TimeValue),
|
|
75
|
+
("system_time", TimeValue),
|
|
76
|
+
("policy", ctypes.c_int),
|
|
77
|
+
("suspend_count", ctypes.c_int),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
library = ctypes.CDLL(None)
|
|
81
|
+
library.mach_task_self.restype = ctypes.c_uint
|
|
82
|
+
library.task_info.argtypes = (
|
|
83
|
+
ctypes.c_uint,
|
|
84
|
+
ctypes.c_int,
|
|
85
|
+
ctypes.c_void_p,
|
|
86
|
+
ctypes.POINTER(ctypes.c_uint),
|
|
87
|
+
)
|
|
88
|
+
library.task_info.restype = ctypes.c_int
|
|
89
|
+
info = MachTaskBasicInfo()
|
|
90
|
+
count = ctypes.c_uint(ctypes.sizeof(info) // ctypes.sizeof(ctypes.c_int))
|
|
91
|
+
result = library.task_info(
|
|
92
|
+
library.mach_task_self(),
|
|
93
|
+
20,
|
|
94
|
+
ctypes.byref(info),
|
|
95
|
+
ctypes.byref(count),
|
|
96
|
+
)
|
|
97
|
+
return int(info.resident_size) if result == 0 else 0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _windows_process_rss_bytes() -> int:
|
|
101
|
+
import ctypes
|
|
102
|
+
from ctypes import wintypes
|
|
103
|
+
|
|
104
|
+
class ProcessMemoryCounters(ctypes.Structure):
|
|
105
|
+
_fields_ = [
|
|
106
|
+
("cb", wintypes.DWORD),
|
|
107
|
+
("PageFaultCount", wintypes.DWORD),
|
|
108
|
+
("PeakWorkingSetSize", ctypes.c_size_t),
|
|
109
|
+
("WorkingSetSize", ctypes.c_size_t),
|
|
110
|
+
("QuotaPeakPagedPoolUsage", ctypes.c_size_t),
|
|
111
|
+
("QuotaPagedPoolUsage", ctypes.c_size_t),
|
|
112
|
+
("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t),
|
|
113
|
+
("QuotaNonPagedPoolUsage", ctypes.c_size_t),
|
|
114
|
+
("PagefileUsage", ctypes.c_size_t),
|
|
115
|
+
("PeakPagefileUsage", ctypes.c_size_t),
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
counters = ProcessMemoryCounters()
|
|
119
|
+
counters.cb = ctypes.sizeof(counters)
|
|
120
|
+
process = ctypes.windll.kernel32.GetCurrentProcess()
|
|
121
|
+
if not ctypes.windll.psapi.GetProcessMemoryInfo(
|
|
122
|
+
process,
|
|
123
|
+
ctypes.byref(counters),
|
|
124
|
+
counters.cb,
|
|
125
|
+
):
|
|
126
|
+
return 0
|
|
127
|
+
return int(counters.WorkingSetSize)
|
|
128
|
+
|
|
129
|
+
|
|
48
130
|
def estimate_deep_size(value: object) -> int:
|
|
49
131
|
"""Estimate the memory retained by an owned object graph.
|
|
50
132
|
|
|
@@ -292,6 +374,11 @@ def _safe_navigation_cache_path(root: str | Path, *, create: bool = False) -> Pa
|
|
|
292
374
|
return result
|
|
293
375
|
|
|
294
376
|
|
|
377
|
+
def navigation_cache_path(root: str | Path, *, create: bool = False) -> Path:
|
|
378
|
+
"""Return the validated persistent navigation-cache directory."""
|
|
379
|
+
return _safe_navigation_cache_path(root, create=create)
|
|
380
|
+
|
|
381
|
+
|
|
295
382
|
def _metadata_mapping(metadata: CacheMetadata) -> dict[str, object]:
|
|
296
383
|
return {field.name: getattr(metadata, field.name) for field in fields(metadata)}
|
|
297
384
|
|
|
@@ -555,13 +642,14 @@ def _read_startup_tail(diagnostics: object, *, max_bytes: int = _STARTUP_DIAGNOS
|
|
|
555
642
|
class _CacheService:
|
|
556
643
|
def __init__(self, metadata: CacheMetadata) -> None:
|
|
557
644
|
self.metadata = metadata
|
|
645
|
+
self._rss_baseline_bytes = current_process_rss_bytes()
|
|
558
646
|
self.context = AgentContext.open(
|
|
559
647
|
metadata.root,
|
|
560
648
|
metadata.project_file or None,
|
|
561
649
|
workers=metadata.workers,
|
|
562
650
|
worker_memory_budget_bytes=metadata.max_memory_bytes,
|
|
563
651
|
revision_check_interval_seconds=_CACHE_REVISION_CHECK_INTERVAL_SECONDS,
|
|
564
|
-
navigation_cache_dir=
|
|
652
|
+
navigation_cache_dir=navigation_cache_path(
|
|
565
653
|
metadata.root,
|
|
566
654
|
create=True,
|
|
567
655
|
),
|
|
@@ -577,6 +665,15 @@ class _CacheService:
|
|
|
577
665
|
self.prewarm_seconds = 0.0
|
|
578
666
|
self.shutdown = threading.Event()
|
|
579
667
|
|
|
668
|
+
def _measure_retained_bytes(self) -> int:
|
|
669
|
+
process_rss = current_process_rss_bytes()
|
|
670
|
+
process_growth = (
|
|
671
|
+
max(0, process_rss - self._rss_baseline_bytes)
|
|
672
|
+
if process_rss > 0 and self._rss_baseline_bytes > 0
|
|
673
|
+
else 0
|
|
674
|
+
)
|
|
675
|
+
return max(self.context.estimated_cache_bytes, process_growth)
|
|
676
|
+
|
|
580
677
|
def prewarm(self) -> None:
|
|
581
678
|
started = time.monotonic()
|
|
582
679
|
try:
|
|
@@ -587,7 +684,7 @@ class _CacheService:
|
|
|
587
684
|
raise
|
|
588
685
|
self.cache_state = "ready"
|
|
589
686
|
self.last_budget = self.budget.enforce(
|
|
590
|
-
measure=
|
|
687
|
+
measure=self._measure_retained_bytes,
|
|
591
688
|
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
592
689
|
evict_navigation=self.context.evict_navigation_caches,
|
|
593
690
|
)
|
|
@@ -628,7 +725,7 @@ class _CacheService:
|
|
|
628
725
|
if before != after:
|
|
629
726
|
self.stats.invalidations += 1
|
|
630
727
|
self.last_budget = self.budget.enforce(
|
|
631
|
-
measure=
|
|
728
|
+
measure=self._measure_retained_bytes,
|
|
632
729
|
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
633
730
|
evict_navigation=self.context.evict_navigation_caches,
|
|
634
731
|
)
|
|
@@ -675,21 +772,34 @@ def _watch_filter(_change: object, path: str) -> bool:
|
|
|
675
772
|
return Path(path).suffix.casefold() in _WATCHED_SUFFIXES
|
|
676
773
|
|
|
677
774
|
|
|
678
|
-
def
|
|
775
|
+
def watch_workspace_changes(
|
|
776
|
+
root: str | Path,
|
|
777
|
+
*,
|
|
778
|
+
stop_event: threading.Event,
|
|
779
|
+
on_change: Callable[[], None],
|
|
780
|
+
) -> None:
|
|
679
781
|
try:
|
|
680
782
|
for changes in watch(
|
|
681
|
-
|
|
783
|
+
root,
|
|
682
784
|
watch_filter=_watch_filter,
|
|
683
|
-
stop_event=
|
|
785
|
+
stop_event=stop_event,
|
|
684
786
|
debounce=50,
|
|
685
787
|
step=20,
|
|
686
788
|
recursive=True,
|
|
687
789
|
raise_interrupt=False,
|
|
688
790
|
):
|
|
689
791
|
if changes:
|
|
690
|
-
|
|
792
|
+
on_change()
|
|
691
793
|
except (OSError, RuntimeError):
|
|
692
|
-
|
|
794
|
+
on_change()
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def _watch_workspace(service: _CacheService) -> None:
|
|
798
|
+
watch_workspace_changes(
|
|
799
|
+
service.metadata.root,
|
|
800
|
+
stop_event=service.shutdown,
|
|
801
|
+
on_change=service.context.invalidate_revision_cache,
|
|
802
|
+
)
|
|
693
803
|
|
|
694
804
|
|
|
695
805
|
def _serve_connection(connection: socket.socket, service: _CacheService) -> None:
|
|
@@ -867,6 +977,8 @@ def start_cache(
|
|
|
867
977
|
) -> CacheMetadata:
|
|
868
978
|
if type(workers) is not int or not 0 <= workers <= 32:
|
|
869
979
|
raise ValueError("workers must be auto or an integer from 1 through 32.")
|
|
980
|
+
if type(idle_timeout) is not int or idle_timeout <= 0:
|
|
981
|
+
raise ValueError("idle_timeout must be greater than zero.")
|
|
870
982
|
if not math.isfinite(startup_timeout) or startup_timeout <= 0:
|
|
871
983
|
raise ValueError("startup_timeout must be greater than zero.")
|
|
872
984
|
with _start_lock(root, startup_timeout):
|
|
@@ -930,7 +1042,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
930
1042
|
serve.add_argument("--project-file", default="")
|
|
931
1043
|
serve.add_argument("--max-memory", type=int, default=DEFAULT_MAX_MEMORY_BYTES)
|
|
932
1044
|
serve.add_argument("--workers", type=int, default=0)
|
|
933
|
-
serve.add_argument("--idle-timeout", type=
|
|
1045
|
+
serve.add_argument("--idle-timeout", type=_positive_integer, default=DEFAULT_IDLE_TIMEOUT)
|
|
934
1046
|
args = parser.parse_args(argv)
|
|
935
1047
|
if args.command == "serve":
|
|
936
1048
|
run_cache_daemon(
|
|
@@ -943,5 +1055,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
943
1055
|
return 0
|
|
944
1056
|
|
|
945
1057
|
|
|
1058
|
+
def _positive_integer(value: str) -> int:
|
|
1059
|
+
parsed = int(value)
|
|
1060
|
+
if parsed <= 0:
|
|
1061
|
+
raise argparse.ArgumentTypeError("value must be greater than zero")
|
|
1062
|
+
return parsed
|
|
1063
|
+
|
|
1064
|
+
|
|
946
1065
|
if __name__ == "__main__":
|
|
947
1066
|
raise SystemExit(main())
|
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
import math
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
+
import threading
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import BinaryIO, TextIO
|
|
10
11
|
|
|
@@ -15,15 +16,17 @@ from .agent_cache import (
|
|
|
15
16
|
DEFAULT_MAX_MEMORY_BYTES,
|
|
16
17
|
DEFAULT_STARTUP_TIMEOUT,
|
|
17
18
|
parse_memory_size,
|
|
19
|
+
navigation_cache_path,
|
|
18
20
|
query_cache,
|
|
19
21
|
run_cache_daemon,
|
|
20
22
|
start_cache,
|
|
21
23
|
stop_cache,
|
|
24
|
+
watch_workspace_changes,
|
|
22
25
|
)
|
|
23
26
|
from .agent_layers import build_codebase_index, layer_payload, render_layer
|
|
24
27
|
from .agent_protocol import AgentProtocolError, SUPPORTED_ACTIONS, SUPPORTED_DETAILS, SUPPORTED_RELATIONS
|
|
25
28
|
from .agent_templates import install_opencode_support, install_skill
|
|
26
|
-
from .parallel_outline import parse_worker_setting
|
|
29
|
+
from .parallel_outline import ParallelOutlineError, parse_worker_setting
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
_MAX_WORKER_RECORD_BYTES = 1024 * 1024
|
|
@@ -32,6 +35,14 @@ _INVALID_ENCODING_MESSAGE = "Invalid UTF-8 request."
|
|
|
32
35
|
_REQUEST_TOO_LARGE_MESSAGE = "Request exceeds the 1 MiB limit."
|
|
33
36
|
_INTERNAL_ERROR_MESSAGE = "Internal request error."
|
|
34
37
|
_SOURCE_UNAVAILABLE_MESSAGE = "Selected source is unavailable."
|
|
38
|
+
_WORKER_REVISION_CHECK_INTERVAL_SECONDS = 30.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _CliError(RuntimeError):
|
|
42
|
+
def __init__(self, code: str, message: str) -> None:
|
|
43
|
+
self.code = code
|
|
44
|
+
self.message = message
|
|
45
|
+
super().__init__(message)
|
|
35
46
|
|
|
36
47
|
|
|
37
48
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -120,7 +131,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
120
131
|
cache_serve.add_argument("--project-file", type=Path)
|
|
121
132
|
cache_serve.add_argument("--max-memory", type=parse_memory_size, required=True)
|
|
122
133
|
cache_serve.add_argument("--workers", type=parse_worker_setting, required=True)
|
|
123
|
-
cache_serve.add_argument("--idle-timeout", type=
|
|
134
|
+
cache_serve.add_argument("--idle-timeout", type=_positive_integer, required=True)
|
|
124
135
|
cache_serve.set_defaults(func=_cache_serve)
|
|
125
136
|
|
|
126
137
|
query = subcommands.add_parser("query", help="Send an ergonomic request to a running cache daemon.")
|
|
@@ -131,8 +142,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
131
142
|
query.add_argument("--detail", choices=SUPPORTED_DETAILS, default="summary")
|
|
132
143
|
query.add_argument("--relation", choices=SUPPORTED_RELATIONS)
|
|
133
144
|
query.add_argument("--cursor", default="")
|
|
134
|
-
query.add_argument("--max-items", type=
|
|
135
|
-
query.add_argument("--max-chars", type=
|
|
145
|
+
query.add_argument("--max-items", type=_max_items, default=12)
|
|
146
|
+
query.add_argument("--max-chars", type=_max_chars, default=12000)
|
|
136
147
|
query.set_defaults(func=_query)
|
|
137
148
|
|
|
138
149
|
return parser
|
|
@@ -143,7 +154,7 @@ def _add_cache_start_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
143
154
|
parser.add_argument("--project-file", type=Path)
|
|
144
155
|
parser.add_argument("--max-memory", type=parse_memory_size, default=DEFAULT_MAX_MEMORY_BYTES)
|
|
145
156
|
parser.add_argument("--workers", type=parse_worker_setting, default=0)
|
|
146
|
-
parser.add_argument("--idle-timeout", type=
|
|
157
|
+
parser.add_argument("--idle-timeout", type=_positive_integer, default=DEFAULT_IDLE_TIMEOUT)
|
|
147
158
|
parser.add_argument("--startup-timeout", type=_positive_float, default=DEFAULT_STARTUP_TIMEOUT)
|
|
148
159
|
|
|
149
160
|
|
|
@@ -154,6 +165,27 @@ def _positive_float(value: str) -> float:
|
|
|
154
165
|
return parsed
|
|
155
166
|
|
|
156
167
|
|
|
168
|
+
def _positive_integer(value: str) -> int:
|
|
169
|
+
parsed = int(value)
|
|
170
|
+
if parsed <= 0:
|
|
171
|
+
raise argparse.ArgumentTypeError("value must be greater than zero")
|
|
172
|
+
return parsed
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _max_items(value: str) -> int:
|
|
176
|
+
parsed = int(value)
|
|
177
|
+
if not 1 <= parsed <= 50:
|
|
178
|
+
raise argparse.ArgumentTypeError("value must be between 1 and 50")
|
|
179
|
+
return parsed
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _max_chars(value: str) -> int:
|
|
183
|
+
parsed = int(value)
|
|
184
|
+
if not 256 <= parsed <= 40000:
|
|
185
|
+
raise argparse.ArgumentTypeError("value must be between 256 and 40000")
|
|
186
|
+
return parsed
|
|
187
|
+
|
|
188
|
+
|
|
157
189
|
def main(argv: list[str] | None = None) -> int:
|
|
158
190
|
parser = build_parser()
|
|
159
191
|
args = parser.parse_args(argv)
|
|
@@ -161,9 +193,23 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
161
193
|
result = args.func(args)
|
|
162
194
|
if not getattr(sys.stdout, "closed", False):
|
|
163
195
|
sys.stdout.flush()
|
|
196
|
+
except _CliError as error:
|
|
197
|
+
sys.stderr.write(f"cli_error:{error.code}: {error.message}\n")
|
|
198
|
+
sys.stderr.flush()
|
|
199
|
+
return 1
|
|
200
|
+
except CacheClientError as error:
|
|
201
|
+
return _cache_error(error)
|
|
202
|
+
except ParallelOutlineError as error:
|
|
203
|
+
sys.stderr.write(f"cli_error:parallel_failed: {error}\n")
|
|
204
|
+
sys.stderr.flush()
|
|
205
|
+
return 1
|
|
164
206
|
except BrokenPipeError:
|
|
165
207
|
_discard_broken_stdout()
|
|
166
208
|
os._exit(1)
|
|
209
|
+
except OSError as error:
|
|
210
|
+
sys.stderr.write(f"cli_error:io_error: {error}\n")
|
|
211
|
+
sys.stderr.flush()
|
|
212
|
+
return 1
|
|
167
213
|
return result if isinstance(result, int) else 0
|
|
168
214
|
|
|
169
215
|
|
|
@@ -189,6 +235,7 @@ def _discard_broken_stdout() -> None:
|
|
|
189
235
|
|
|
190
236
|
|
|
191
237
|
def _view(args: argparse.Namespace) -> None:
|
|
238
|
+
args.root = _workspace_root(args.root)
|
|
192
239
|
index = build_codebase_index(
|
|
193
240
|
args.root,
|
|
194
241
|
project_file=args.project_file,
|
|
@@ -199,6 +246,7 @@ def _view(args: argparse.Namespace) -> None:
|
|
|
199
246
|
|
|
200
247
|
|
|
201
248
|
def _index(args: argparse.Namespace) -> None:
|
|
249
|
+
args.root = _workspace_root(args.root)
|
|
202
250
|
index = build_codebase_index(
|
|
203
251
|
args.root,
|
|
204
252
|
project_file=args.project_file,
|
|
@@ -216,33 +264,72 @@ def _index(args: argparse.Namespace) -> None:
|
|
|
216
264
|
|
|
217
265
|
|
|
218
266
|
def _skill_install(args: argparse.Namespace) -> None:
|
|
219
|
-
|
|
267
|
+
try:
|
|
268
|
+
skill_path = install_skill(args.target, force=args.force)
|
|
269
|
+
except FileExistsError as error:
|
|
270
|
+
raise _CliError("install_conflict", str(error)) from None
|
|
220
271
|
print(skill_path)
|
|
221
272
|
|
|
222
273
|
|
|
223
274
|
def _opencode_install(args: argparse.Namespace) -> None:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
275
|
+
try:
|
|
276
|
+
skill_path, plugin_path, agent_path = install_opencode_support(
|
|
277
|
+
args.target,
|
|
278
|
+
python_executable=args.python,
|
|
279
|
+
force=args.force,
|
|
280
|
+
write_config=args.write_config,
|
|
281
|
+
)
|
|
282
|
+
except FileExistsError as error:
|
|
283
|
+
raise _CliError("install_conflict", str(error)) from None
|
|
230
284
|
print(skill_path)
|
|
231
285
|
print(plugin_path)
|
|
232
286
|
print(agent_path)
|
|
233
287
|
|
|
234
288
|
|
|
235
289
|
def _worker(args: argparse.Namespace) -> None:
|
|
236
|
-
|
|
290
|
+
args.root = _workspace_root(args.root)
|
|
291
|
+
context = _open_worker_context(args.root, args.project_file, args.workers)
|
|
292
|
+
watcher_stop = threading.Event()
|
|
293
|
+
watcher = threading.Thread(
|
|
294
|
+
target=watch_workspace_changes,
|
|
295
|
+
kwargs={
|
|
296
|
+
"root": args.root,
|
|
297
|
+
"stop_event": watcher_stop,
|
|
298
|
+
"on_change": context.invalidate_revision_cache,
|
|
299
|
+
},
|
|
300
|
+
name="delphi-worker-watcher",
|
|
301
|
+
daemon=True,
|
|
302
|
+
)
|
|
303
|
+
watcher.start()
|
|
237
304
|
try:
|
|
238
305
|
_serve_worker(context, sys.stdin.buffer, sys.stdout.buffer, sys.stderr)
|
|
239
306
|
finally:
|
|
307
|
+
watcher_stop.set()
|
|
308
|
+
watcher.join(timeout=2.0)
|
|
240
309
|
try:
|
|
241
310
|
sys.stdout.close()
|
|
242
311
|
except (BrokenPipeError, OSError) as error:
|
|
243
312
|
raise BrokenPipeError from error
|
|
244
313
|
|
|
245
314
|
|
|
315
|
+
def _open_worker_context(
|
|
316
|
+
root: Path,
|
|
317
|
+
project_file: Path | None,
|
|
318
|
+
workers: int,
|
|
319
|
+
) -> AgentContext:
|
|
320
|
+
try:
|
|
321
|
+
cache_dir: Path | None = navigation_cache_path(root, create=True)
|
|
322
|
+
except (CacheClientError, OSError):
|
|
323
|
+
cache_dir = None
|
|
324
|
+
return AgentContext.open(
|
|
325
|
+
root,
|
|
326
|
+
project_file,
|
|
327
|
+
workers=workers,
|
|
328
|
+
revision_check_interval_seconds=_WORKER_REVISION_CHECK_INTERVAL_SECONDS,
|
|
329
|
+
navigation_cache_dir=cache_dir,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
246
333
|
def _write_json(payload: object) -> None:
|
|
247
334
|
sys.stdout.write(json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n")
|
|
248
335
|
|
|
@@ -260,6 +347,7 @@ def _cache_error(error: CacheClientError) -> int:
|
|
|
260
347
|
|
|
261
348
|
|
|
262
349
|
def _cache_start(args: argparse.Namespace) -> int:
|
|
350
|
+
args.root = _workspace_root(args.root)
|
|
263
351
|
try:
|
|
264
352
|
start_cache(
|
|
265
353
|
args.root,
|
|
@@ -278,6 +366,7 @@ def _cache_start(args: argparse.Namespace) -> int:
|
|
|
278
366
|
|
|
279
367
|
|
|
280
368
|
def _cache_status(args: argparse.Namespace) -> int:
|
|
369
|
+
args.root = _workspace_root(args.root)
|
|
281
370
|
try:
|
|
282
371
|
response = query_cache(args.root, {"action": "status"})
|
|
283
372
|
except CacheClientError as error:
|
|
@@ -295,6 +384,7 @@ def _cache_status(args: argparse.Namespace) -> int:
|
|
|
295
384
|
|
|
296
385
|
|
|
297
386
|
def _cache_stop(args: argparse.Namespace) -> int:
|
|
387
|
+
args.root = _workspace_root(args.root)
|
|
298
388
|
try:
|
|
299
389
|
response = query_cache(args.root, {"action": "status"})
|
|
300
390
|
except CacheClientError as error:
|
|
@@ -316,6 +406,7 @@ def _cache_stop(args: argparse.Namespace) -> int:
|
|
|
316
406
|
|
|
317
407
|
|
|
318
408
|
def _cache_serve(args: argparse.Namespace) -> int:
|
|
409
|
+
args.root = _workspace_root(args.root)
|
|
319
410
|
run_cache_daemon(
|
|
320
411
|
args.root,
|
|
321
412
|
project_file=str(args.project_file or ""),
|
|
@@ -327,6 +418,7 @@ def _cache_serve(args: argparse.Namespace) -> int:
|
|
|
327
418
|
|
|
328
419
|
|
|
329
420
|
def _query(args: argparse.Namespace) -> int:
|
|
421
|
+
args.root = _workspace_root(args.root)
|
|
330
422
|
request: dict[str, object] = {"action": args.action}
|
|
331
423
|
if args.value:
|
|
332
424
|
if args.action in {"find", "metrics"}:
|
|
@@ -350,6 +442,21 @@ def _query(args: argparse.Namespace) -> int:
|
|
|
350
442
|
return 0
|
|
351
443
|
|
|
352
444
|
|
|
445
|
+
def _workspace_root(value: str | Path) -> Path:
|
|
446
|
+
root = Path(value).expanduser()
|
|
447
|
+
if not root.exists():
|
|
448
|
+
raise _CliError(
|
|
449
|
+
"workspace_not_found",
|
|
450
|
+
f"Workspace root does not exist: {root}",
|
|
451
|
+
)
|
|
452
|
+
if not root.is_dir():
|
|
453
|
+
raise _CliError(
|
|
454
|
+
"workspace_not_directory",
|
|
455
|
+
f"Workspace root is not a directory: {root}",
|
|
456
|
+
)
|
|
457
|
+
return root.resolve()
|
|
458
|
+
|
|
459
|
+
|
|
353
460
|
def _serve_worker(context: AgentContext, input_stream: BinaryIO, output_stream: BinaryIO, error_stream: TextIO) -> None:
|
|
354
461
|
discarding_oversize_record = False
|
|
355
462
|
while True:
|
|
@@ -658,7 +658,7 @@ class AgentContext:
|
|
|
658
658
|
return revision
|
|
659
659
|
|
|
660
660
|
def invalidate_revision_cache(self) -> None:
|
|
661
|
-
self._last_revision_check_at =
|
|
661
|
+
self._last_revision_check_at = float("-inf")
|
|
662
662
|
|
|
663
663
|
def handle(self, request: AgentRequest | Mapping[str, object]) -> AgentResponse:
|
|
664
664
|
parsed = _validated_request(request)
|
|
@@ -75,7 +75,11 @@ def discover_delphi_project(
|
|
|
75
75
|
on_progress: ProgressCallback | None = None,
|
|
76
76
|
) -> DelphiProjectDiscovery:
|
|
77
77
|
root_path = Path(root).expanduser().resolve()
|
|
78
|
-
project_path = Path(project_file).expanduser()
|
|
78
|
+
project_path = Path(project_file).expanduser() if project_file is not None else None
|
|
79
|
+
if project_path is not None:
|
|
80
|
+
if not project_path.is_absolute():
|
|
81
|
+
project_path = root_path / project_path
|
|
82
|
+
project_path = project_path.resolve()
|
|
79
83
|
discovery = DelphiProjectDiscovery(root=str(root_path))
|
|
80
84
|
_emit_progress(on_progress, "discovery", str(root_path), 0, 0, None, "project discovery started")
|
|
81
85
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-delphi-lsp"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
description = "Python Delphi/Object Pascal parser, semantic indexer, and language server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-delphi-lsp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -42,7 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
|
|
43
43
|
`python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
|
|
44
44
|
indexes, serves LSP, and provides bounded codebase navigation for agents.
|
|
45
|
-
Version 2.3.
|
|
45
|
+
Version 2.3.1 is authored by Dark Light and supports Windows, macOS, and Linux.
|
|
46
46
|
|
|
47
47
|
## Install and quick start
|
|
48
48
|
|
|
@@ -65,6 +65,7 @@ scripts/summarize_opencode_request_payloads.py
|
|
|
65
65
|
scripts/ollama/ornith-lspctx.Modelfile
|
|
66
66
|
tests/corpora.performance.lock.json
|
|
67
67
|
tests/test_agent_cache.py
|
|
68
|
+
tests/test_agent_cli_errors.py
|
|
68
69
|
tests/test_agent_codebase.py
|
|
69
70
|
tests/test_agent_context.py
|
|
70
71
|
tests/test_agent_metrics.py
|