precis-cli 0.1.4__tar.gz → 0.1.6__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.
- {precis_cli-0.1.4 → precis_cli-0.1.6}/MANIFEST.in +0 -1
- precis_cli-0.1.6/PKG-INFO +160 -0
- precis_cli-0.1.6/PYPI_README.md +97 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shared_services/config_ops.py +52 -19
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shared_services/generation_ops.py +41 -12
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shared_services/project_ops.py +22 -3
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/__init__.py +39 -1
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/chat.py +1 -3
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/executor.py +1 -1
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/generate.py +2 -1
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/migrate.py +10 -2
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/switch.py +7 -6
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/check.py +8 -3
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/edit.py +9 -3
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/get.py +5 -5
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/set.py +9 -2
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/show.py +7 -3
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/help.py +6 -2
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/validate.py +22 -1
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/formatter.py +23 -12
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/interactive_menu.py +41 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/main.py +13 -6
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/start.py +20 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/mcp_server.py +31 -13
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/excel_loader.py +99 -15
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/main.py +27 -11
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/utils/path_utils.py +12 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config/loader.py +10 -23
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/openai.py +1 -1
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/schema_inference.py +37 -17
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/chunked_loader.py +25 -9
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/executor.py +3 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/report_export.py +17 -2
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/resolver.py +26 -1
- precis_cli-0.1.6/precis_cli.egg-info/PKG-INFO +160 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/precis_cli.egg-info/SOURCES.txt +1 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/pyproject.toml +5 -5
- precis_cli-0.1.4/PKG-INFO +0 -180
- precis_cli-0.1.4/precis_cli.egg-info/PKG-INFO +0 -180
- {precis_cli-0.1.4 → precis_cli-0.1.6}/README.md +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/dependencies.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/main.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/middleware/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/middleware/exception_handler.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/middleware/request_logging.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/middleware/token_auth.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/connection_rules.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/files.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/full_validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/project.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/projects.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/schema.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/v2_responses.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/models/workspace.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/chat.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/generate.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/hardware.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/jobs.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/migrate.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/ollama.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/providers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/router.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/stream.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/ai/utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/core/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/core/connection_rules.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/core/data_sources.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/core/regex.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/core/reporting.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/files/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/files/ops.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/files/transfer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/content_mode.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/header_row.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/path_mode.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/preview/router.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/constraint.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/full_config.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/full_config_writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/helpers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/inspection_fix.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/manifest.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/pattern.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/regex.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/scanner.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/schema.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/schema_helpers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/settings.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/template.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/view.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/project/workspaces.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/projects/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/projects/create.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/projects/open.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/projects/scan.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/common.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/content_mode.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/history.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/inline_mode.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/path_mode.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/routers/validation/router.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/services/full_validation_response_builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/services/io_error_messages.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/api/services/preview_service.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/__main__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shared_services/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/delete.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/diff.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/display.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/executor_utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/interaction.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/resolver.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/status.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/ai/utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/init.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/inspect.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/config/list.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/exit.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/infer_schema.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/open.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/project.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/provider.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/commands/system.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/completer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/config_storage.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/exceptions.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli/shell/parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/cli_main.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/app_version.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/config/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/config/server.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/converter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/csv_loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/extractor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/json_loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/sql_loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/strategies/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/strategies/array_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/strategies/lines_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/loaders/strategies/object_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/schema_info.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/csv_source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/excel_source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/file_base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/json_source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/data_source/specs/sql_source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/encoding.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/io/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/io/yaml.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/manifest_schema/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/manifest_schema/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/manifest_schema/version.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/patterns/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/patterns/loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/patterns/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/composite.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/conditional.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/foreign_key.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/scripted.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/single_column.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/builders/unique.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/factory.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/types/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/types/constraint_file.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/types/refs.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/constraint/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/config_inspector.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/embedded_constraints.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/file_loaders.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/inspection_ids.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/inspector_helpers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/inspector_id_checks.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/loading_error_messages.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/path_validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/loader_parts/runtime.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/loader/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/coverage.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/constants.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/data_source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/info.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/manifest.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/refs.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/settings.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/settings_file_processing.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/settings_script_security.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/settings_validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/types_parts/template.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manifest/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manual_data/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/manual_data/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/regex/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/regex/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/regex/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/column.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/column_utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/constraint.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/schema_id.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/source.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/source_options.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/types_parts/table.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/schema_ref_check.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/template/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/template/expander.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/template/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/template/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/transform/reader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/transform/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/project/transform/writer.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/pydantic_messages.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/dingtalk_app_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/email_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/feishu_app_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/local_file_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/reporter/reporters/wecom_app_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/utils/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/core/utils/regex_extract.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/allowed_values.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/charset.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/composite.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/condition_registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/conditional.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/date_logic.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/foreign_key.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/key_normalization.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/not_null.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/range.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/regex.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/scripted.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/constraints/unique.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_engine.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/composite.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/expression.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/extracted.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/json_types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/scalars.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/data_types_parts/sequence.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/dataset_schema.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/eval_sandbox.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/expression_system.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/regex_flags.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/schema/builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/schema/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/aggregate.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/cast_type.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/concat.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/conditional_assign.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/date_format.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/digits.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/drop_duplicates.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/fill_na.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/filter_rows.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/lookup.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/lower_case.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/map_value.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/math_expr.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/modulo.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/regex_extract.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/replace.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/sort_rows.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/string_split.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/strip.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/substring.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/upper_case.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/transforms/weighted_sum.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/domain/validation_constraints.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/apply_actions.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/ask_user.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/read_canvas.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/read_project.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/read_table.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/schemas.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/chat_tools/validate_table.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/executor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/memory.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/planner.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tool_registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/config_generate.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/config_refine.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/config_validate.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/merge_results.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/plan_chunks.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/tools/script_parse.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/agent/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/chat_agent_runner.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/chat_orchestrator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/failure_messages.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/job_storage.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/migrate_service.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/event_journal.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/orchestrator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/pending_interaction_store.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/sse_response.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/streaming/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/ai/utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/diff/config_diff.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/hardware.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_canvas_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_constraint_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_regex_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_schema_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_settings_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/_transform_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/action_handlers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/action_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/action_processor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/action_validator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/diff_compute.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/regex_handlers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/schema_handlers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/settings_handlers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/specs.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/transform_handlers.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/actions/validation_types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/cache/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/cache/response_cache.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/chat/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/chat/chat_system_prompt.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/chat/response_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config/crypto.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config/presets.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/config_generator.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/constraint_builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/constraint_deletion.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/constraint_id.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/frontend_instructions.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/constraints/inline_batch.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/discovery/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/discovery/scanner.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/agent_wiring.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/config_builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/errors.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/existing_config.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/profiler.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/prompt_builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/response_parser.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/generation/service.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/models.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/cached_provider.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/ollama.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/providers/registry.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/schema_resolver.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/suggestion_utils.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/validate_executor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/llm/yaml_io.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/preview/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/preview/loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/preview/path_validation.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/project_loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/schema_runtime_builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/dag/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/dag/builder.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/dag/executor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/dag/sorter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/data_loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/engine.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/extractors.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/history.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/json_payload.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/loader.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/memory_monitor.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/postprocess.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/progress.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/service.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/types.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/validators/__init__.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/validators/adapter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/validators/base.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/validators/composite.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/shared/services/validation/validators/date_logic.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/app/start_server.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/precis_cli.egg-info/dependency_links.txt +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/precis_cli.egg-info/entry_points.txt +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/precis_cli.egg-info/requires.txt +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/precis_cli.egg-info/top_level.txt +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/setup.cfg +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/tests/test_local_file_reporter.py +0 -0
- {precis_cli-0.1.4 → precis_cli-0.1.6}/tests/test_schema_builder.py +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: precis-cli
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: Precis - 可配置数据校验工具 CLI(校验 CSV/Excel/JSON 数据质量)
|
|
5
|
+
Author: Precis Team
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/AirSaiga/Precis
|
|
8
|
+
Project-URL: Repository, https://github.com/AirSaiga/Precis
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/AirSaiga/Precis/issues
|
|
10
|
+
Project-URL: Discussions, https://github.com/AirSaiga/Precis/discussions
|
|
11
|
+
Keywords: data-quality,data-validation,csv,excel,json,yaml,cli,mcp
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: <3.14,>=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: pydantic>=2.12.0
|
|
23
|
+
Requires-Dist: pandas<3.0,>=2.2.0
|
|
24
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
25
|
+
Requires-Dist: xlrd>=2.0.1
|
|
26
|
+
Requires-Dist: SQLAlchemy>=2.0.48
|
|
27
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
28
|
+
Requires-Dist: ruamel.yaml>=0.19.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: readchar>=4.2.0
|
|
31
|
+
Requires-Dist: cryptography>=44.0.0
|
|
32
|
+
Requires-Dist: simpleeval>=1.0.0
|
|
33
|
+
Requires-Dist: filelock>=3.12.0
|
|
34
|
+
Provides-Extra: api
|
|
35
|
+
Requires-Dist: fastapi!=0.136.3,>=0.135.0; extra == "api"
|
|
36
|
+
Requires-Dist: uvicorn>=0.42.0; extra == "api"
|
|
37
|
+
Requires-Dist: email-validator>=2.3.0; extra == "api"
|
|
38
|
+
Requires-Dist: uritemplate>=4.2.0; extra == "api"
|
|
39
|
+
Requires-Dist: python-multipart>=0.0.26; extra == "api"
|
|
40
|
+
Requires-Dist: cryptography>=44.0.0; extra == "api"
|
|
41
|
+
Provides-Extra: ai
|
|
42
|
+
Requires-Dist: openai>=1.6.0; extra == "ai"
|
|
43
|
+
Requires-Dist: aiohttp>=3.9.0; extra == "ai"
|
|
44
|
+
Requires-Dist: psutil>=5.9.6; extra == "ai"
|
|
45
|
+
Provides-Extra: mcp
|
|
46
|
+
Requires-Dist: mcp<2,>=1.30.0; extra == "mcp"
|
|
47
|
+
Provides-Extra: completion
|
|
48
|
+
Requires-Dist: pyreadline3>=0.1.8; sys_platform == "win32" and extra == "completion"
|
|
49
|
+
Provides-Extra: full
|
|
50
|
+
Requires-Dist: precis-cli[ai,api,completion,mcp]; extra == "full"
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: precis-cli[ai,api,mcp]; extra == "dev"
|
|
53
|
+
Requires-Dist: debugpy>=1.8.0; extra == "dev"
|
|
54
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
55
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
56
|
+
Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
|
|
57
|
+
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
58
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
59
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
60
|
+
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
61
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
62
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
63
|
+
|
|
64
|
+
# Precis CLI
|
|
65
|
+
|
|
66
|
+
> Local-first data validation engine — define schemas and constraints in YAML, validate CSV / Excel / JSON with a single command.
|
|
67
|
+
> 本地优先的数据校验引擎:用 YAML 定义表结构与约束规则,一条命令校验 CSV / Excel / JSON。
|
|
68
|
+
|
|
69
|
+
**Alpha** — 核心功能稳定,配置格式有版本保障(V2)。
|
|
70
|
+
|
|
71
|
+
## 安装
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install precis-cli # 需要 Python >= 3.12
|
|
75
|
+
|
|
76
|
+
# 或免安装直接运行(需要 uv)
|
|
77
|
+
uvx --from precis-cli precis --version
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 快速上手
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# 1. 从数据文件推断 schema 草稿
|
|
84
|
+
precis infer-schema orders.csv > schemas/orders.schema.yaml
|
|
85
|
+
|
|
86
|
+
# 2. 在项目清单 project.precis.yaml 中登记 schema 与约束文件
|
|
87
|
+
# (格式见下方示例)
|
|
88
|
+
|
|
89
|
+
# 3. 执行校验
|
|
90
|
+
precis validate --manifest project.precis.yaml --format json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
退出码契约(CI / AI agent 友好):
|
|
94
|
+
|
|
95
|
+
| 退出码 | 含义 |
|
|
96
|
+
|--------|------|
|
|
97
|
+
| `0` | 校验通过 |
|
|
98
|
+
| `1` | 校验完成,发现数据违规(详情见 JSON 输出的 `errors`) |
|
|
99
|
+
| `2` | 工具自身错误(参数错误、文件不存在、异常崩溃) |
|
|
100
|
+
|
|
101
|
+
## 配置示例(V2 YAML)
|
|
102
|
+
|
|
103
|
+
`project.precis.yaml`:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
version: 2
|
|
107
|
+
project:
|
|
108
|
+
id: my-project
|
|
109
|
+
name: 订单数据校验
|
|
110
|
+
schemas:
|
|
111
|
+
- id: orders
|
|
112
|
+
path: schemas/orders.schema.yaml
|
|
113
|
+
constraints:
|
|
114
|
+
- id: orders_amount_range
|
|
115
|
+
path: constraints/orders_amount_range.constraint.yaml
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`constraints/orders_amount_range.constraint.yaml`:
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
version: 2
|
|
122
|
+
id: orders_amount_range
|
|
123
|
+
type: Range
|
|
124
|
+
enabled: true
|
|
125
|
+
refs:
|
|
126
|
+
table_id: orders
|
|
127
|
+
column_id: amount
|
|
128
|
+
params:
|
|
129
|
+
min: 0
|
|
130
|
+
max: 1000000
|
|
131
|
+
boundary_mode: inclusive
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 10 种约束类型
|
|
135
|
+
|
|
136
|
+
`NotNull` 非空 · `Unique` 唯一 · `AllowedValues` 枚举 · `Range` 数值区间 ·
|
|
137
|
+
`ForeignKey` 跨表引用 · `Conditional` 条件约束 · `Scripted` 脚本表达式 ·
|
|
138
|
+
`Charset` 字符集 · `DateLogic` 日期逻辑 · `Composite` 组合约束
|
|
139
|
+
|
|
140
|
+
数据类型:`string` / `integer` / `float` / `decimal` / `boolean` / `date`。
|
|
141
|
+
大文件(>500MB)自动分块加载。
|
|
142
|
+
|
|
143
|
+
## 与 AI Agent 集成
|
|
144
|
+
|
|
145
|
+
`--format json` 输出为机器可读契约(含行级错误定位与约束来源文件回溯),
|
|
146
|
+
可直接被 AI 编程助手消费:
|
|
147
|
+
|
|
148
|
+
- **MCP server**:`pip install "precis-cli[mcp]"` 后运行 `precis-mcp`(stdio),
|
|
149
|
+
提供 `validate_data` / `infer_schema` / `check_config` / `describe_constraints` 四个工具
|
|
150
|
+
- **Kimi Code 插件**:`/plugins install https://github.com/AirSaiga/Precis`
|
|
151
|
+
- **CLI 自带 AI 命令**(自然语言生成/修改校验配置,独立于宿主 agent):
|
|
152
|
+
`pip install "precis-cli[ai]"`,然后 `precis ai chat` 或 `precis ai ask "..."`
|
|
153
|
+
|
|
154
|
+
## 链接
|
|
155
|
+
|
|
156
|
+
- 仓库与文档:<https://github.com/AirSaiga/Precis>
|
|
157
|
+
- 问题反馈:<https://github.com/AirSaiga/Precis/issues>
|
|
158
|
+
- 桌面 GUI 版本(画布式可视化编辑)见仓库 releases
|
|
159
|
+
|
|
160
|
+
License: Apache-2.0
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Precis CLI
|
|
2
|
+
|
|
3
|
+
> Local-first data validation engine — define schemas and constraints in YAML, validate CSV / Excel / JSON with a single command.
|
|
4
|
+
> 本地优先的数据校验引擎:用 YAML 定义表结构与约束规则,一条命令校验 CSV / Excel / JSON。
|
|
5
|
+
|
|
6
|
+
**Alpha** — 核心功能稳定,配置格式有版本保障(V2)。
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install precis-cli # 需要 Python >= 3.12
|
|
12
|
+
|
|
13
|
+
# 或免安装直接运行(需要 uv)
|
|
14
|
+
uvx --from precis-cli precis --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 快速上手
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 1. 从数据文件推断 schema 草稿
|
|
21
|
+
precis infer-schema orders.csv > schemas/orders.schema.yaml
|
|
22
|
+
|
|
23
|
+
# 2. 在项目清单 project.precis.yaml 中登记 schema 与约束文件
|
|
24
|
+
# (格式见下方示例)
|
|
25
|
+
|
|
26
|
+
# 3. 执行校验
|
|
27
|
+
precis validate --manifest project.precis.yaml --format json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
退出码契约(CI / AI agent 友好):
|
|
31
|
+
|
|
32
|
+
| 退出码 | 含义 |
|
|
33
|
+
|--------|------|
|
|
34
|
+
| `0` | 校验通过 |
|
|
35
|
+
| `1` | 校验完成,发现数据违规(详情见 JSON 输出的 `errors`) |
|
|
36
|
+
| `2` | 工具自身错误(参数错误、文件不存在、异常崩溃) |
|
|
37
|
+
|
|
38
|
+
## 配置示例(V2 YAML)
|
|
39
|
+
|
|
40
|
+
`project.precis.yaml`:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
version: 2
|
|
44
|
+
project:
|
|
45
|
+
id: my-project
|
|
46
|
+
name: 订单数据校验
|
|
47
|
+
schemas:
|
|
48
|
+
- id: orders
|
|
49
|
+
path: schemas/orders.schema.yaml
|
|
50
|
+
constraints:
|
|
51
|
+
- id: orders_amount_range
|
|
52
|
+
path: constraints/orders_amount_range.constraint.yaml
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`constraints/orders_amount_range.constraint.yaml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
version: 2
|
|
59
|
+
id: orders_amount_range
|
|
60
|
+
type: Range
|
|
61
|
+
enabled: true
|
|
62
|
+
refs:
|
|
63
|
+
table_id: orders
|
|
64
|
+
column_id: amount
|
|
65
|
+
params:
|
|
66
|
+
min: 0
|
|
67
|
+
max: 1000000
|
|
68
|
+
boundary_mode: inclusive
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 10 种约束类型
|
|
72
|
+
|
|
73
|
+
`NotNull` 非空 · `Unique` 唯一 · `AllowedValues` 枚举 · `Range` 数值区间 ·
|
|
74
|
+
`ForeignKey` 跨表引用 · `Conditional` 条件约束 · `Scripted` 脚本表达式 ·
|
|
75
|
+
`Charset` 字符集 · `DateLogic` 日期逻辑 · `Composite` 组合约束
|
|
76
|
+
|
|
77
|
+
数据类型:`string` / `integer` / `float` / `decimal` / `boolean` / `date`。
|
|
78
|
+
大文件(>500MB)自动分块加载。
|
|
79
|
+
|
|
80
|
+
## 与 AI Agent 集成
|
|
81
|
+
|
|
82
|
+
`--format json` 输出为机器可读契约(含行级错误定位与约束来源文件回溯),
|
|
83
|
+
可直接被 AI 编程助手消费:
|
|
84
|
+
|
|
85
|
+
- **MCP server**:`pip install "precis-cli[mcp]"` 后运行 `precis-mcp`(stdio),
|
|
86
|
+
提供 `validate_data` / `infer_schema` / `check_config` / `describe_constraints` 四个工具
|
|
87
|
+
- **Kimi Code 插件**:`/plugins install https://github.com/AirSaiga/Precis`
|
|
88
|
+
- **CLI 自带 AI 命令**(自然语言生成/修改校验配置,独立于宿主 agent):
|
|
89
|
+
`pip install "precis-cli[ai]"`,然后 `precis ai chat` 或 `precis ai ask "..."`
|
|
90
|
+
|
|
91
|
+
## 链接
|
|
92
|
+
|
|
93
|
+
- 仓库与文档:<https://github.com/AirSaiga/Precis>
|
|
94
|
+
- 问题反馈:<https://github.com/AirSaiga/Precis/issues>
|
|
95
|
+
- 桌面 GUI 版本(画布式可视化编辑)见仓库 releases
|
|
96
|
+
|
|
97
|
+
License: Apache-2.0
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
from __future__ import annotations
|
|
42
42
|
|
|
43
43
|
import logging
|
|
44
|
+
import math
|
|
44
45
|
import os
|
|
45
46
|
import tempfile
|
|
46
47
|
from collections.abc import Iterator
|
|
@@ -207,6 +208,10 @@ def resolve_config_file(project_path: str, filename: str | None) -> tuple[str |
|
|
|
207
208
|
def get_by_dotpath(data: dict, key_path: str) -> tuple[bool, Any]:
|
|
208
209
|
"""按点号路径从字典中查找值。
|
|
209
210
|
|
|
211
|
+
YAML 键可为非字符串(如 ``2024:`` 解析为 int、``true:`` 解析为 bool)——
|
|
212
|
+
路径段除精确匹配外也按字符串形态匹配非字符串键,防止"键存在却查不到"
|
|
213
|
+
进而被 set 侧写出同形态影子键。
|
|
214
|
+
|
|
210
215
|
Args:
|
|
211
216
|
data: 配置字典
|
|
212
217
|
key_path: 点号路径,如 "project.name"
|
|
@@ -217,8 +222,16 @@ def get_by_dotpath(data: dict, key_path: str) -> tuple[bool, Any]:
|
|
|
217
222
|
keys = key_path.split(".")
|
|
218
223
|
value: Any = data
|
|
219
224
|
for key in keys:
|
|
220
|
-
if isinstance(value, dict)
|
|
221
|
-
|
|
225
|
+
if isinstance(value, dict):
|
|
226
|
+
if key in value:
|
|
227
|
+
value = value[key]
|
|
228
|
+
continue
|
|
229
|
+
for k, v in value.items():
|
|
230
|
+
if not isinstance(k, str) and str(k) == key:
|
|
231
|
+
value = v
|
|
232
|
+
break
|
|
233
|
+
else:
|
|
234
|
+
return False, None
|
|
222
235
|
else:
|
|
223
236
|
return False, None
|
|
224
237
|
return True, value
|
|
@@ -227,7 +240,8 @@ def get_by_dotpath(data: dict, key_path: str) -> tuple[bool, Any]:
|
|
|
227
240
|
def set_by_dotpath(data: dict, key_path: str, value: Any) -> dict:
|
|
228
241
|
"""按点号路径写入值,返回新 data(不改原 dict)。
|
|
229
242
|
|
|
230
|
-
|
|
243
|
+
中间层级不存在时自动创建空字典;路径段与既有非字符串键(如 int ``2024``)
|
|
244
|
+
字符串形态相同时复用原键,防止写出同形态影子键并存。
|
|
231
245
|
|
|
232
246
|
Args:
|
|
233
247
|
data: 原配置字典(不会被修改)
|
|
@@ -244,10 +258,21 @@ def set_by_dotpath(data: dict, key_path: str, value: Any) -> dict:
|
|
|
244
258
|
keys = key_path.split(".")
|
|
245
259
|
current = new_data
|
|
246
260
|
for key in keys[:-1]:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
261
|
+
existing = current.get(key)
|
|
262
|
+
if isinstance(existing, dict):
|
|
263
|
+
current = existing
|
|
264
|
+
continue
|
|
265
|
+
# 同形态非字符串键复用(写原键而非另建字符串影子键)
|
|
266
|
+
non_str_key = next((k for k in current if not isinstance(k, str) and str(k) == key), None)
|
|
267
|
+
if non_str_key is not None and isinstance(current[non_str_key], dict):
|
|
268
|
+
current = current[non_str_key]
|
|
269
|
+
continue
|
|
270
|
+
target = non_str_key if non_str_key is not None else key
|
|
271
|
+
current[target] = {}
|
|
272
|
+
current = current[target]
|
|
273
|
+
leaf = keys[-1]
|
|
274
|
+
leaf_key = next((k for k in current if not isinstance(k, str) and str(k) == leaf), leaf)
|
|
275
|
+
current[leaf_key] = value
|
|
251
276
|
return new_data
|
|
252
277
|
|
|
253
278
|
|
|
@@ -267,8 +292,9 @@ def parse_config_value(value_str: str) -> tuple[bool, Any, str]:
|
|
|
267
292
|
value_str: 原始字符串值
|
|
268
293
|
|
|
269
294
|
Returns:
|
|
270
|
-
(成功, 转换后的值, 错误信息)
|
|
271
|
-
|
|
295
|
+
(成功, 转换后的值, 错误信息);毒化字面量(inf/nan/下划线整数等
|
|
296
|
+
无法被下游安全消费的数值形态)返回 (False, None, 错误信息),
|
|
297
|
+
调用方应拒绝落盘。
|
|
272
298
|
"""
|
|
273
299
|
# 尝试布尔值
|
|
274
300
|
if value_str.lower() == "true":
|
|
@@ -278,17 +304,24 @@ def parse_config_value(value_str: str) -> tuple[bool, Any, str]:
|
|
|
278
304
|
if value_str.lower() == "null" or value_str.lower() == "none":
|
|
279
305
|
return True, None, ""
|
|
280
306
|
|
|
281
|
-
#
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
307
|
+
# 尝试整数:拒绝 Python 下划线字面量('1_0' 静默变 10 会毒化下游数值读取)
|
|
308
|
+
if "_" not in value_str:
|
|
309
|
+
try:
|
|
310
|
+
return True, int(value_str), ""
|
|
311
|
+
except ValueError:
|
|
312
|
+
pass
|
|
286
313
|
|
|
287
|
-
#
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
314
|
+
# 尝试浮点数:拒绝非有限值(inf/nan/溢出 1e999)与下划线字面量——
|
|
315
|
+
# timeout_seconds: .inf 落盘后 validate 侧 int(inf) 抛 OverflowError,项目从此无法校验
|
|
316
|
+
if "_" not in value_str:
|
|
317
|
+
try:
|
|
318
|
+
parsed_float = float(value_str)
|
|
319
|
+
except ValueError:
|
|
320
|
+
parsed_float = None
|
|
321
|
+
if parsed_float is not None:
|
|
322
|
+
if not math.isfinite(parsed_float):
|
|
323
|
+
return False, None, f"不接受非有限数值: {value_str}(inf/nan 落盘会使项目无法校验)"
|
|
324
|
+
return True, parsed_float, ""
|
|
292
325
|
|
|
293
326
|
# 去除引号
|
|
294
327
|
if (value_str.startswith('"') and value_str.endswith('"')) or (
|
|
@@ -36,6 +36,7 @@ from __future__ import annotations
|
|
|
36
36
|
import glob
|
|
37
37
|
import logging
|
|
38
38
|
import os
|
|
39
|
+
import re
|
|
39
40
|
from pathlib import Path
|
|
40
41
|
from typing import Any
|
|
41
42
|
|
|
@@ -46,6 +47,25 @@ logger = logging.getLogger(__name__)
|
|
|
46
47
|
# 支持的文件扩展名(generate/migrate 共用)
|
|
47
48
|
SUPPORTED_EXTENSIONS = (".xlsx", ".xls", ".csv", ".json", ".jsonl")
|
|
48
49
|
|
|
50
|
+
# 实体 id 安全白名单:字母/数字/下划线/连字符(H10——LLM 生成的 id 未消毒即拼盘
|
|
51
|
+
# 路径时,`../../evil` 类路径成分可越项目写文件/建目录,写盘前须拒绝)
|
|
52
|
+
_SAFE_ENTITY_ID_RE = re.compile(r"[A-Za-z0-9_-]+")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _filter_safe_entity_ids(entities: dict[str, Any], kind: str) -> dict[str, Any]:
|
|
56
|
+
"""过滤掉含路径成分或非法字符的实体 id,记录错误日志(H10)。
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
仅保留安全 id 的实体字典;非法 id 整体跳过(不写盘、不进 manifest 引用)
|
|
60
|
+
"""
|
|
61
|
+
safe: dict[str, Any] = {}
|
|
62
|
+
for eid, body in entities.items():
|
|
63
|
+
if isinstance(eid, str) and _SAFE_ENTITY_ID_RE.fullmatch(eid):
|
|
64
|
+
safe[eid] = body
|
|
65
|
+
else:
|
|
66
|
+
logger.error("跳过非法实体 id(含路径成分或非法字符,kind=%s): %r", kind, eid)
|
|
67
|
+
return safe
|
|
68
|
+
|
|
49
69
|
|
|
50
70
|
def scan_data_files(patterns: list[str], project_path: str) -> list[str]:
|
|
51
71
|
"""展开数据文件路径(支持通配符、相对路径、data/ 目录扫描)。
|
|
@@ -91,8 +111,9 @@ def scan_data_files(patterns: list[str], project_path: str) -> list[str]:
|
|
|
91
111
|
def apply_generated_config(result: dict[str, Any], project_path: str) -> list[str]:
|
|
92
112
|
"""将生成的配置写入项目目录。
|
|
93
113
|
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
保留策略为并集(R6):生成 manifest 未携带的既有顶层键(project/settings/
|
|
115
|
+
templates/data_sources 等)一律保留,防止空/局部 manifest 清空既有配置;
|
|
116
|
+
schemas、constraints、regex_nodes 引用则始终按生成结果覆盖写入。
|
|
96
117
|
|
|
97
118
|
Args:
|
|
98
119
|
result: ConfigGenerationService / ConfigMigrationService 返回的配置字典
|
|
@@ -111,16 +132,24 @@ def apply_generated_config(result: dict[str, Any], project_path: str) -> list[st
|
|
|
111
132
|
except Exception:
|
|
112
133
|
logger.warning("读取现有 manifest 失败,将覆盖写入", exc_info=True)
|
|
113
134
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
generated_manifest = result.get("manifest")
|
|
136
|
+
if generated_manifest:
|
|
137
|
+
manifest = dict(generated_manifest)
|
|
138
|
+
else:
|
|
139
|
+
# LLM 未返回 manifest:以现有 manifest 为底仅补最小骨架,
|
|
140
|
+
# 不再制造空 project 兜底清空既有 project.id/settings(R6)
|
|
141
|
+
manifest = dict(existing_manifest)
|
|
142
|
+
manifest.setdefault("version", 2)
|
|
143
|
+
manifest.setdefault("project", {"id": "", "name": ""})
|
|
144
|
+
|
|
145
|
+
# 保留策略并集(R6):生成 manifest 未携带的既有键一律保留
|
|
146
|
+
for key, value in existing_manifest.items():
|
|
147
|
+
if key not in manifest:
|
|
148
|
+
manifest[key] = value
|
|
149
|
+
|
|
150
|
+
schemas = _filter_safe_entity_ids(result.get("schemas", {}) or {}, "schema")
|
|
151
|
+
constraints = _filter_safe_entity_ids(result.get("constraints", {}) or {}, "constraint")
|
|
152
|
+
regex_nodes = _filter_safe_entity_ids(result.get("regex_nodes", {}) or {}, "regex")
|
|
124
153
|
|
|
125
154
|
# 确保目录存在
|
|
126
155
|
(Path(project_path) / "schemas").mkdir(exist_ok=True)
|
|
@@ -51,10 +51,25 @@ HISTORY_FILE = os.path.expanduser("~/.precis_project_history")
|
|
|
51
51
|
MAX_HISTORY = 10
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def _backup_corrupt_history() -> None:
|
|
55
|
+
"""把损坏的历史文件改名备份(.corrupt.bak),失败仅记日志。
|
|
56
|
+
|
|
57
|
+
原实现在 JSON 损坏后静默按空历史继续——下次保存直接覆盖,
|
|
58
|
+
用户历史不可恢复;备份保留现场供手工抢救。
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
backup = f"{HISTORY_FILE}.corrupt.bak"
|
|
62
|
+
os.replace(HISTORY_FILE, backup)
|
|
63
|
+
logger.warning("项目历史文件损坏,已备份到 %s 并从空历史继续", backup)
|
|
64
|
+
except OSError:
|
|
65
|
+
logger.error("备份损坏的项目历史文件失败: %s", HISTORY_FILE, exc_info=True)
|
|
66
|
+
|
|
67
|
+
|
|
54
68
|
def load_history() -> list[dict]:
|
|
55
69
|
"""加载项目打开历史。
|
|
56
70
|
|
|
57
71
|
从 HISTORY_FILE 读取历史记录。文件不存在、格式损坏或权限不足时返回空列表。
|
|
72
|
+
JSON 损坏时先把原文件备份为 ``*.corrupt.bak`` 再按空历史继续(不静默清零)。
|
|
58
73
|
|
|
59
74
|
Returns:
|
|
60
75
|
历史记录列表,每个元素为包含 path 和 last_opened 的字典
|
|
@@ -78,7 +93,8 @@ def load_history() -> list[dict]:
|
|
|
78
93
|
return valid
|
|
79
94
|
return []
|
|
80
95
|
except json.JSONDecodeError:
|
|
81
|
-
# JSON
|
|
96
|
+
# JSON 格式损坏:备份后按空历史继续
|
|
97
|
+
_backup_corrupt_history()
|
|
82
98
|
return []
|
|
83
99
|
except PermissionError:
|
|
84
100
|
# 无权限读取,返回空列表
|
|
@@ -91,7 +107,8 @@ def load_history() -> list[dict]:
|
|
|
91
107
|
def _save_history(history: list[dict]) -> None:
|
|
92
108
|
"""保存项目打开历史。
|
|
93
109
|
|
|
94
|
-
|
|
110
|
+
原子写(临时文件 + os.replace):进程中断不会留下半截 JSON——
|
|
111
|
+
半截文件在下次 load 即被判损坏。保存失败仅记录日志,不阻断主流程。
|
|
95
112
|
|
|
96
113
|
Args:
|
|
97
114
|
history: 历史记录列表
|
|
@@ -99,8 +116,10 @@ def _save_history(history: list[dict]) -> None:
|
|
|
99
116
|
try:
|
|
100
117
|
# 确保目录存在(用户主目录)
|
|
101
118
|
os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
|
|
102
|
-
|
|
119
|
+
tmp_file = f"{HISTORY_FILE}.tmp"
|
|
120
|
+
with open(tmp_file, "w", encoding="utf-8") as f:
|
|
103
121
|
json.dump(history, f, ensure_ascii=False, indent=2)
|
|
122
|
+
os.replace(tmp_file, HISTORY_FILE)
|
|
104
123
|
except (PermissionError, OSError):
|
|
105
124
|
# 保存失败时记录错误日志,但不阻断主流程
|
|
106
125
|
logger.error("保存项目历史记录失败", exc_info=True)
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
- 支持直接通过自然语言执行 AI 指令
|
|
24
24
|
|
|
25
25
|
架构设计:
|
|
26
|
-
- AICommand 作为聚合命令,管理 chat、status、switch、delete、
|
|
26
|
+
- AICommand 作为聚合命令,管理 chat、status、switch、delete、provider 等子命令
|
|
27
|
+
- 裸装(无 [ai] extra)时入口门控:help 标注缺依赖,execute 直接给安装指引
|
|
27
28
|
- 无参数时显示交互式菜单(支持方向键导航)
|
|
28
29
|
- 第一个参数如果不是子命令,则将其视为直接询问的消息
|
|
29
30
|
- 使用 execute_ai_chat 统一执行 AI 对话
|
|
@@ -52,6 +53,31 @@ from app.cli.shell.config_storage import get_cli_config
|
|
|
52
53
|
from app.cli.shell.formatter import Formatter
|
|
53
54
|
from app.cli.shell.interactive_menu import InteractiveMenu
|
|
54
55
|
|
|
56
|
+
# [ai] 扩展依赖探测结果缓存(失败导入不会被 import 机制负缓存,help 每次渲染
|
|
57
|
+
# 都会读 description,缓存避免重复文件系统扫描;测试可经 monkeypatch 覆盖)
|
|
58
|
+
_AI_DEPS_AVAILABLE: bool | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def ai_dependencies_available() -> bool:
|
|
62
|
+
"""探测 AI 功能的可选依赖是否已安装(裸装 precis-cli 不含 [ai] extra)。
|
|
63
|
+
|
|
64
|
+
openai 是核心门控导入(OpenAI 兼容 provider 全系走它);aiohttp/psutil
|
|
65
|
+
随同一 extra 安装,openai 可导入即视为 extra 已装。
|
|
66
|
+
"""
|
|
67
|
+
global _AI_DEPS_AVAILABLE # noqa: PLW0603
|
|
68
|
+
if _AI_DEPS_AVAILABLE is None:
|
|
69
|
+
try:
|
|
70
|
+
import openai # noqa: F401
|
|
71
|
+
except ImportError:
|
|
72
|
+
_AI_DEPS_AVAILABLE = False
|
|
73
|
+
else:
|
|
74
|
+
_AI_DEPS_AVAILABLE = True
|
|
75
|
+
return _AI_DEPS_AVAILABLE
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# 依赖缺失时 ai 入口的统一提示(help 标注与 execute 门控共用同一安装指引)
|
|
79
|
+
_AI_INSTALL_HINT = 'pip install "precis-cli[ai]"(源码安装用 pip install -e ".[ai]")'
|
|
80
|
+
|
|
55
81
|
|
|
56
82
|
class AICommand(Command):
|
|
57
83
|
"""AI 助手命令。
|
|
@@ -82,6 +108,10 @@ class AICommand(Command):
|
|
|
82
108
|
|
|
83
109
|
@property
|
|
84
110
|
def description(self) -> str:
|
|
111
|
+
# help 列表动态标注:裸装(无 [ai] extra)不伪装成可用功能,
|
|
112
|
+
# 让用户在进菜单之前就知道差什么、怎么补
|
|
113
|
+
if not ai_dependencies_available():
|
|
114
|
+
return f"AI 助手 - 未安装依赖({_AI_INSTALL_HINT})"
|
|
85
115
|
return "AI 助手 - 使用自然语言修改项目配置"
|
|
86
116
|
|
|
87
117
|
@property
|
|
@@ -139,6 +169,14 @@ class AICommand(Command):
|
|
|
139
169
|
Returns:
|
|
140
170
|
子命令执行结果或 AI 对话结果
|
|
141
171
|
"""
|
|
172
|
+
if not ai_dependencies_available():
|
|
173
|
+
# 门控在菜单/子命令分发之前:缺依赖时不进交互菜单(菜单里每个
|
|
174
|
+
# 选项最终都会失败),直接给安装指引;provider 预配置不受影响
|
|
175
|
+
# (顶层 provider 命令不依赖 LLM 库)
|
|
176
|
+
return CommandResult.error(
|
|
177
|
+
f"AI 功能需要可选依赖,当前未安装。安装命令: {_AI_INSTALL_HINT}\n"
|
|
178
|
+
"如仅需预配置 AI Provider,可先运行 'provider add'(无需依赖)。"
|
|
179
|
+
)
|
|
142
180
|
if not args:
|
|
143
181
|
# 无参数时显示交互式菜单
|
|
144
182
|
return self._show_interactive_menu(context)
|
|
@@ -100,7 +100,7 @@ class AIChatCommand(Command):
|
|
|
100
100
|
# 检查 AI 配置(使用 CLI 配置)
|
|
101
101
|
provider = self._cli_config.get_active_provider()
|
|
102
102
|
if not provider:
|
|
103
|
-
return CommandResult.error("没有可用的 LLM Provider 配置\n请先运行 '
|
|
103
|
+
return CommandResult.error("没有可用的 LLM Provider 配置\n请先运行 'provider add' 命令配置 API Key")
|
|
104
104
|
|
|
105
105
|
# 解析命令行参数(--stream/--no-stream 已随 execute_ai_chat 的死参数一并移除)
|
|
106
106
|
agent_mode = True # 默认启用 Agent 深度模式
|
|
@@ -258,8 +258,6 @@ AI 助手帮助
|
|
|
258
258
|
qq - 直接退出整个程序(全局可用)
|
|
259
259
|
|
|
260
260
|
启动选项:
|
|
261
|
-
--stream - 启用流式输出
|
|
262
|
-
--no-stream - 禁用流式输出(默认,显示 loading spinner)
|
|
263
261
|
--no-agent-mode - 关闭 Agent 深度模式(降级为旧 JSON actions 路径)
|
|
264
262
|
|
|
265
263
|
你可以用自然语言描述你的需求,例如:
|
|
@@ -80,7 +80,7 @@ def execute_ai_chat(
|
|
|
80
80
|
# 获取用于显示的 Provider 配置(脱敏,用于日志或展示)
|
|
81
81
|
provider_config_display = _get_provider_display()
|
|
82
82
|
if not provider_config_display:
|
|
83
|
-
return CommandResult.error("没有可用的 LLM Provider 配置,请先运行 '
|
|
83
|
+
return CommandResult.error("没有可用的 LLM Provider 配置,请先运行 'provider add' 命令配置 AI Provider")
|
|
84
84
|
|
|
85
85
|
# 获取包含完整 API Key 的 Provider 配置(用于实际调用 API)
|
|
86
86
|
provider_config = _get_provider_with_key()
|
|
@@ -53,6 +53,7 @@ from app.cli.shared_services.generation_ops import (
|
|
|
53
53
|
)
|
|
54
54
|
from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
|
|
55
55
|
from app.cli.shell.formatter import Formatter
|
|
56
|
+
from app.shared.core.utils.path_utils import display_relpath
|
|
56
57
|
from app.shared.services.llm.generation import (
|
|
57
58
|
ConfigGenerationService,
|
|
58
59
|
GenerationOptions,
|
|
@@ -200,7 +201,7 @@ class AIGenerateCommand(Command):
|
|
|
200
201
|
print(Formatter.info(f"迭代次数: {max_iterations}"))
|
|
201
202
|
print(Formatter.info("数据文件:"))
|
|
202
203
|
for p in file_paths:
|
|
203
|
-
print(f" - {
|
|
204
|
+
print(f" - {display_relpath(p, project_path)}")
|
|
204
205
|
|
|
205
206
|
def progress_callback(stage: str, progress: float, extra: dict[str, Any] | None = None) -> None:
|
|
206
207
|
"""终端进度回调。"""
|
|
@@ -51,6 +51,7 @@ from app.cli.shared_services.generation_ops import (
|
|
|
51
51
|
)
|
|
52
52
|
from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
|
|
53
53
|
from app.cli.shell.formatter import Formatter
|
|
54
|
+
from app.shared.core.utils.path_utils import display_relpath
|
|
54
55
|
from app.shared.services.ai.migrate_service import ConfigMigrationService
|
|
55
56
|
from app.shared.services.llm.generation import (
|
|
56
57
|
GenerationOptions,
|
|
@@ -72,6 +73,10 @@ def _infer_language(file_path: str) -> str:
|
|
|
72
73
|
return "natural_language"
|
|
73
74
|
|
|
74
75
|
|
|
76
|
+
# --language 可选值(与 usage 声明一致)
|
|
77
|
+
_SUPPORTED_LANGUAGES = ("python", "sql", "excel_formula", "natural_language")
|
|
78
|
+
|
|
79
|
+
|
|
75
80
|
class AIMigrateCommand(Command):
|
|
76
81
|
"""AI 配置迁移命令。
|
|
77
82
|
|
|
@@ -171,6 +176,9 @@ class AIMigrateCommand(Command):
|
|
|
171
176
|
|
|
172
177
|
if language is None:
|
|
173
178
|
language = _infer_language(script_path)
|
|
179
|
+
elif language not in _SUPPORTED_LANGUAGES:
|
|
180
|
+
# 枚举校验:非法值前置拒绝(usage 已声明可选值),不透传下游晦涩报错
|
|
181
|
+
return CommandResult.error(f"--language 仅支持 {'|'.join(_SUPPORTED_LANGUAGES)},收到: {language}")
|
|
174
182
|
|
|
175
183
|
try:
|
|
176
184
|
with open(script_path, encoding="utf-8") as f:
|
|
@@ -202,10 +210,10 @@ class AIMigrateCommand(Command):
|
|
|
202
210
|
|
|
203
211
|
print(Formatter.header("\nAI 配置迁移"))
|
|
204
212
|
print(Formatter.info(f"项目: {project_name}"))
|
|
205
|
-
print(Formatter.info(f"脚本: {
|
|
213
|
+
print(Formatter.info(f"脚本: {display_relpath(script_path, project_path)} ({language})"))
|
|
206
214
|
print(Formatter.info("数据文件:"))
|
|
207
215
|
for p in file_paths:
|
|
208
|
-
print(f" - {
|
|
216
|
+
print(f" - {display_relpath(p, project_path)}")
|
|
209
217
|
|
|
210
218
|
def progress_callback(stage: str, progress: float, extra: dict[str, Any] | None = None) -> None:
|
|
211
219
|
"""终端进度回调。"""
|