precis-cli 0.1.8__tar.gz → 0.1.10__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.
Files changed (463) hide show
  1. {precis_cli-0.1.8/precis_cli.egg-info → precis_cli-0.1.10}/PKG-INFO +1 -1
  2. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/projects.py +12 -0
  3. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/models.py +4 -2
  4. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/stream.py +64 -19
  5. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/projects/__init__.py +2 -1
  6. precis_cli-0.1.10/app/api/routers/projects/check.py +62 -0
  7. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/agent_interaction.py +84 -24
  8. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/chat.py +8 -2
  9. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/executor.py +48 -31
  10. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/executor_utils.py +51 -39
  11. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/interaction.py +6 -77
  12. precis_cli-0.1.10/app/cli/shell/commands/ai/markdown_stream.py +527 -0
  13. precis_cli-0.1.10/app/cli/shell/commands/ai/stream_renderer.py +242 -0
  14. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/mcp_server.py +10 -33
  15. precis_cli-0.1.10/app/shared/core/app_version.py +100 -0
  16. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/json_source.py +1 -2
  17. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/embedded_constraints.py +87 -41
  18. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/main.py +3 -3
  19. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/column_utils.py +37 -0
  20. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/source.py +2 -2
  21. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/source_options.py +3 -4
  22. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/range.py +2 -2
  23. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/__init__.py +14 -1
  24. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/apply_actions.py +199 -57
  25. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/ask_user.py +1 -1
  26. precis_cli-0.1.10/app/shared/services/ai/agent/chat_tools/infer_schema.py +161 -0
  27. precis_cli-0.1.10/app/shared/services/ai/agent/chat_tools/list_data_files.py +222 -0
  28. precis_cli-0.1.10/app/shared/services/ai/agent/chat_tools/path_guard.py +57 -0
  29. precis_cli-0.1.10/app/shared/services/ai/agent/chat_tools/read_config_file.py +285 -0
  30. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/read_project.py +18 -1
  31. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/schemas.py +34 -0
  32. precis_cli-0.1.10/app/shared/services/ai/agent/chat_tools/validate_table.py +221 -0
  33. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/executor.py +12 -2
  34. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/memory.py +42 -0
  35. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tool_registry.py +57 -2
  36. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/config_validate.py +68 -5
  37. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/types.py +3 -0
  38. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/chat_agent_runner.py +297 -99
  39. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/chat_orchestrator.py +90 -40
  40. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/orchestrator.py +11 -5
  41. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/pending_interaction_store.py +113 -20
  42. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/utils.py +185 -8
  43. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/action_handlers.py +111 -33
  44. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/action_processor.py +37 -11
  45. precis_cli-0.1.10/app/shared/services/llm/actions/action_summaries.py +220 -0
  46. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/action_validator.py +28 -2
  47. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/diff_compute.py +14 -10
  48. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/regex_handlers.py +5 -2
  49. precis_cli-0.1.10/app/shared/services/llm/actions/registry.py +697 -0
  50. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/schema_handlers.py +156 -10
  51. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/specs.py +24 -2
  52. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/transform_handlers.py +14 -4
  53. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/chat/chat_system_prompt.py +85 -29
  54. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config/models.py +29 -0
  55. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/constraints/__init__.py +3 -3
  56. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/constraints/constraint_builder.py +10 -4
  57. precis_cli-0.1.10/app/shared/services/llm/constraints/constraint_deletion.py +111 -0
  58. precis_cli-0.1.10/app/shared/services/llm/constraints/constraint_lookup.py +214 -0
  59. precis_cli-0.1.10/app/shared/services/llm/constraints/frontend_instructions.py +383 -0
  60. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/constraints/inline_batch.py +35 -14
  61. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/agent_wiring.py +1 -0
  62. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/config_builder.py +298 -18
  63. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/prompt_builder.py +13 -8
  64. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/service.py +1 -0
  65. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/openai.py +30 -15
  66. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/schema_resolver.py +32 -6
  67. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/validate_executor.py +37 -3
  68. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/yaml_io.py +33 -0
  69. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/schema_inference.py +13 -1
  70. {precis_cli-0.1.8 → precis_cli-0.1.10/precis_cli.egg-info}/PKG-INFO +1 -1
  71. {precis_cli-0.1.8 → precis_cli-0.1.10}/precis_cli.egg-info/SOURCES.txt +9 -1
  72. {precis_cli-0.1.8 → precis_cli-0.1.10}/pyproject.toml +1 -1
  73. precis_cli-0.1.8/app/shared/core/app_version.py +0 -59
  74. precis_cli-0.1.8/app/shared/services/ai/agent/chat_tools/validate_table.py +0 -131
  75. precis_cli-0.1.8/app/shared/services/llm/actions/registry.py +0 -340
  76. precis_cli-0.1.8/app/shared/services/llm/constraints/constraint_deletion.py +0 -84
  77. precis_cli-0.1.8/app/shared/services/llm/constraints/constraint_id.py +0 -174
  78. precis_cli-0.1.8/app/shared/services/llm/constraints/frontend_instructions.py +0 -333
  79. {precis_cli-0.1.8 → precis_cli-0.1.10}/MANIFEST.in +0 -0
  80. {precis_cli-0.1.8 → precis_cli-0.1.10}/PYPI_README.md +0 -0
  81. {precis_cli-0.1.8 → precis_cli-0.1.10}/README.md +0 -0
  82. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/__init__.py +0 -0
  83. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/__init__.py +0 -0
  84. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/dependencies.py +0 -0
  85. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/main.py +0 -0
  86. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/middleware/__init__.py +0 -0
  87. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/middleware/exception_handler.py +0 -0
  88. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/middleware/request_logging.py +0 -0
  89. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/middleware/token_auth.py +0 -0
  90. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/__init__.py +0 -0
  91. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/connection_rules.py +0 -0
  92. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/files.py +0 -0
  93. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/full_validation.py +0 -0
  94. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/project.py +0 -0
  95. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/schema.py +0 -0
  96. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/v2_responses.py +0 -0
  97. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/validation.py +0 -0
  98. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/models/workspace.py +0 -0
  99. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/__init__.py +0 -0
  100. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/__init__.py +0 -0
  101. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/chat.py +0 -0
  102. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/generate.py +0 -0
  103. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/hardware.py +0 -0
  104. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/jobs.py +0 -0
  105. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/migrate.py +0 -0
  106. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/ollama.py +0 -0
  107. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/providers.py +0 -0
  108. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/router.py +0 -0
  109. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/ai/utils.py +0 -0
  110. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/core/__init__.py +0 -0
  111. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/core/connection_rules.py +0 -0
  112. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/core/data_sources.py +0 -0
  113. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/core/regex.py +0 -0
  114. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/core/reporting.py +0 -0
  115. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/files/__init__.py +0 -0
  116. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/files/ops.py +0 -0
  117. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/files/transfer.py +0 -0
  118. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/__init__.py +0 -0
  119. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/content_mode.py +0 -0
  120. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/header_row.py +0 -0
  121. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/models.py +0 -0
  122. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/path_mode.py +0 -0
  123. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/preview/router.py +0 -0
  124. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/__init__.py +0 -0
  125. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/base.py +0 -0
  126. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/constraint.py +0 -0
  127. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/full_config.py +0 -0
  128. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/full_config_writer.py +0 -0
  129. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/helpers.py +0 -0
  130. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/inspection_fix.py +0 -0
  131. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/manifest.py +0 -0
  132. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/models.py +0 -0
  133. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/pattern.py +0 -0
  134. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/regex.py +0 -0
  135. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/scanner.py +0 -0
  136. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/schema.py +0 -0
  137. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/schema_helpers.py +0 -0
  138. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/settings.py +0 -0
  139. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/template.py +0 -0
  140. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/validation.py +0 -0
  141. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/view.py +0 -0
  142. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/project/workspaces.py +0 -0
  143. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/projects/create.py +0 -0
  144. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/projects/open.py +0 -0
  145. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/projects/scan.py +0 -0
  146. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/__init__.py +0 -0
  147. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/common.py +0 -0
  148. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/content_mode.py +0 -0
  149. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/history.py +0 -0
  150. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/inline_mode.py +0 -0
  151. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/path_mode.py +0 -0
  152. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/routers/validation/router.py +0 -0
  153. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/services/full_validation_response_builder.py +0 -0
  154. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/services/io_error_messages.py +0 -0
  155. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/api/services/preview_service.py +0 -0
  156. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/__init__.py +0 -0
  157. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/__main__.py +0 -0
  158. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shared_services/__init__.py +0 -0
  159. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shared_services/config_ops.py +0 -0
  160. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shared_services/generation_ops.py +0 -0
  161. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shared_services/project_ops.py +0 -0
  162. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/__init__.py +0 -0
  163. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/__init__.py +0 -0
  164. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/__init__.py +0 -0
  165. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/delete.py +0 -0
  166. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/diff.py +0 -0
  167. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/display.py +0 -0
  168. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/generate.py +0 -0
  169. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/migrate.py +0 -0
  170. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/resolver.py +0 -0
  171. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/status.py +0 -0
  172. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/switch.py +0 -0
  173. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/ai/utils.py +0 -0
  174. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/base.py +0 -0
  175. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/__init__.py +0 -0
  176. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/base.py +0 -0
  177. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/check.py +0 -0
  178. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/edit.py +0 -0
  179. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/get.py +0 -0
  180. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/init.py +0 -0
  181. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/inspect.py +0 -0
  182. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/list.py +0 -0
  183. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/set.py +0 -0
  184. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/config/show.py +0 -0
  185. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/exit.py +0 -0
  186. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/help.py +0 -0
  187. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/infer_schema.py +0 -0
  188. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/open.py +0 -0
  189. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/project.py +0 -0
  190. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/provider.py +0 -0
  191. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/system.py +0 -0
  192. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/commands/validate.py +0 -0
  193. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/completer.py +0 -0
  194. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/config_storage.py +0 -0
  195. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/exceptions.py +0 -0
  196. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/formatter.py +0 -0
  197. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/interactive_menu.py +0 -0
  198. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/main.py +0 -0
  199. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/shell/parser.py +0 -0
  200. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/cli/start.py +0 -0
  201. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/__init__.py +0 -0
  202. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/config/__init__.py +0 -0
  203. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/config/server.py +0 -0
  204. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/__init__.py +0 -0
  205. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loader.py +0 -0
  206. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/__init__.py +0 -0
  207. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/base.py +0 -0
  208. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/converter.py +0 -0
  209. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/csv_loader.py +0 -0
  210. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/excel_loader.py +0 -0
  211. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/extractor.py +0 -0
  212. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/json_loader.py +0 -0
  213. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/registry.py +0 -0
  214. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/sql_loader.py +0 -0
  215. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/strategies/__init__.py +0 -0
  216. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/strategies/array_parser.py +0 -0
  217. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/strategies/lines_parser.py +0 -0
  218. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/loaders/strategies/object_parser.py +0 -0
  219. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/schema_info.py +0 -0
  220. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/__init__.py +0 -0
  221. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/base.py +0 -0
  222. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/csv_source.py +0 -0
  223. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/excel_source.py +0 -0
  224. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/file_base.py +0 -0
  225. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/data_source/specs/sql_source.py +0 -0
  226. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/encoding.py +0 -0
  227. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/io/__init__.py +0 -0
  228. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/io/yaml.py +0 -0
  229. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/manifest_schema/__init__.py +0 -0
  230. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/manifest_schema/version.py +0 -0
  231. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/patterns/__init__.py +0 -0
  232. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/patterns/loader.py +0 -0
  233. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/patterns/writer.py +0 -0
  234. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/__init__.py +0 -0
  235. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/__init__.py +0 -0
  236. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/base.py +0 -0
  237. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/composite.py +0 -0
  238. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/conditional.py +0 -0
  239. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/foreign_key.py +0 -0
  240. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/registry.py +0 -0
  241. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/scripted.py +0 -0
  242. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/single_column.py +0 -0
  243. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/builders/unique.py +0 -0
  244. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/factory.py +0 -0
  245. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/reader.py +0 -0
  246. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/registry.py +0 -0
  247. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/types/__init__.py +0 -0
  248. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/types/constraint_file.py +0 -0
  249. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/types/refs.py +0 -0
  250. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/types.py +0 -0
  251. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/constraint/writer.py +0 -0
  252. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/__init__.py +0 -0
  253. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader.py +0 -0
  254. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/config_inspector.py +0 -0
  255. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/file_loaders.py +0 -0
  256. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/inspection_ids.py +0 -0
  257. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/inspector_helpers.py +0 -0
  258. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/inspector_id_checks.py +0 -0
  259. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +0 -0
  260. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +0 -0
  261. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/loading_error_messages.py +0 -0
  262. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/path_validation.py +0 -0
  263. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/loader_parts/runtime.py +0 -0
  264. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/loader/types.py +0 -0
  265. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/coverage.py +0 -0
  266. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/reader.py +0 -0
  267. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types.py +0 -0
  268. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/__init__.py +0 -0
  269. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/constants.py +0 -0
  270. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/data_source.py +0 -0
  271. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/info.py +0 -0
  272. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/manifest.py +0 -0
  273. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/refs.py +0 -0
  274. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/settings.py +0 -0
  275. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/settings_file_processing.py +0 -0
  276. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/settings_script_security.py +0 -0
  277. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/settings_validation.py +0 -0
  278. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/types_parts/template.py +0 -0
  279. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manifest/writer.py +0 -0
  280. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manual_data/__init__.py +0 -0
  281. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/manual_data/types.py +0 -0
  282. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/regex/reader.py +0 -0
  283. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/regex/types.py +0 -0
  284. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/regex/writer.py +0 -0
  285. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/reader.py +0 -0
  286. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types.py +0 -0
  287. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/__init__.py +0 -0
  288. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/column.py +0 -0
  289. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/constraint.py +0 -0
  290. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/schema_id.py +0 -0
  291. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/types_parts/table.py +0 -0
  292. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema/writer.py +0 -0
  293. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/schema_ref_check.py +0 -0
  294. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/template/__init__.py +0 -0
  295. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/template/expander.py +0 -0
  296. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/template/reader.py +0 -0
  297. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/template/types.py +0 -0
  298. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/transform/reader.py +0 -0
  299. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/transform/types.py +0 -0
  300. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/project/transform/writer.py +0 -0
  301. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/pydantic_messages.py +0 -0
  302. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/__init__.py +0 -0
  303. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporter.py +0 -0
  304. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/__init__.py +0 -0
  305. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/base.py +0 -0
  306. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/dingtalk_app_reporter.py +0 -0
  307. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/email_reporter.py +0 -0
  308. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/feishu_app_reporter.py +0 -0
  309. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/local_file_reporter.py +0 -0
  310. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/reporter/reporters/wecom_app_reporter.py +0 -0
  311. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/utils/__init__.py +0 -0
  312. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/utils/path_utils.py +0 -0
  313. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/core/utils/regex_extract.py +0 -0
  314. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/__init__.py +0 -0
  315. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/__init__.py +0 -0
  316. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/allowed_values.py +0 -0
  317. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/base.py +0 -0
  318. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/charset.py +0 -0
  319. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/composite.py +0 -0
  320. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/condition_registry.py +0 -0
  321. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/conditional.py +0 -0
  322. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/date_logic.py +0 -0
  323. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/foreign_key.py +0 -0
  324. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/key_normalization.py +0 -0
  325. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/not_null.py +0 -0
  326. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/regex.py +0 -0
  327. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/scripted.py +0 -0
  328. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/constraints/unique.py +0 -0
  329. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_engine.py +0 -0
  330. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types.py +0 -0
  331. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/__init__.py +0 -0
  332. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/base.py +0 -0
  333. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/composite.py +0 -0
  334. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/expression.py +0 -0
  335. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/extracted.py +0 -0
  336. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/json_types.py +0 -0
  337. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/scalars.py +0 -0
  338. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/data_types_parts/sequence.py +0 -0
  339. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/dataset_schema.py +0 -0
  340. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/eval_sandbox.py +0 -0
  341. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/expression_system.py +0 -0
  342. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/regex_flags.py +0 -0
  343. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/schema/builder.py +0 -0
  344. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/schema/models.py +0 -0
  345. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/__init__.py +0 -0
  346. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/aggregate.py +0 -0
  347. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/base.py +0 -0
  348. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/cast_type.py +0 -0
  349. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/concat.py +0 -0
  350. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/conditional_assign.py +0 -0
  351. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/date_format.py +0 -0
  352. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/digits.py +0 -0
  353. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/drop_duplicates.py +0 -0
  354. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/fill_na.py +0 -0
  355. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/filter_rows.py +0 -0
  356. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/lookup.py +0 -0
  357. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/lower_case.py +0 -0
  358. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/map_value.py +0 -0
  359. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/math_expr.py +0 -0
  360. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/modulo.py +0 -0
  361. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/regex_extract.py +0 -0
  362. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/registry.py +0 -0
  363. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/replace.py +0 -0
  364. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/sort_rows.py +0 -0
  365. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/string_split.py +0 -0
  366. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/strip.py +0 -0
  367. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/substring.py +0 -0
  368. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/upper_case.py +0 -0
  369. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/transforms/weighted_sum.py +0 -0
  370. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/domain/validation_constraints.py +0 -0
  371. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/__init__.py +0 -0
  372. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/__init__.py +0 -0
  373. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/__init__.py +0 -0
  374. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/read_canvas.py +0 -0
  375. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/chat_tools/read_table.py +0 -0
  376. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/planner.py +0 -0
  377. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/__init__.py +0 -0
  378. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/config_generate.py +0 -0
  379. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/config_refine.py +0 -0
  380. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/merge_results.py +0 -0
  381. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/plan_chunks.py +0 -0
  382. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/agent/tools/script_parse.py +0 -0
  383. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/failure_messages.py +0 -0
  384. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/job_storage.py +0 -0
  385. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/migrate_service.py +0 -0
  386. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/__init__.py +0 -0
  387. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/event_journal.py +0 -0
  388. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/sse_response.py +0 -0
  389. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/ai/streaming/types.py +0 -0
  390. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/diff/config_diff.py +0 -0
  391. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/hardware.py +0 -0
  392. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/__init__.py +0 -0
  393. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/__init__.py +0 -0
  394. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_canvas_validator.py +0 -0
  395. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_constraint_validator.py +0 -0
  396. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_regex_validator.py +0 -0
  397. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_schema_validator.py +0 -0
  398. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_settings_validator.py +0 -0
  399. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/_transform_validator.py +0 -0
  400. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/action_parser.py +0 -0
  401. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/settings_handlers.py +0 -0
  402. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/actions/validation_types.py +0 -0
  403. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/cache/__init__.py +0 -0
  404. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/cache/response_cache.py +0 -0
  405. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/chat/__init__.py +0 -0
  406. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/chat/response_parser.py +0 -0
  407. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config/__init__.py +0 -0
  408. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config/crypto.py +0 -0
  409. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config/loader.py +0 -0
  410. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config/presets.py +0 -0
  411. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/config_generator.py +0 -0
  412. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/discovery/__init__.py +0 -0
  413. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/discovery/scanner.py +0 -0
  414. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/__init__.py +0 -0
  415. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/errors.py +0 -0
  416. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/existing_config.py +0 -0
  417. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/profiler.py +0 -0
  418. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/generation/response_parser.py +0 -0
  419. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/__init__.py +0 -0
  420. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/base.py +0 -0
  421. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/cached_provider.py +0 -0
  422. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/fake.py +0 -0
  423. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/ollama.py +0 -0
  424. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/providers/registry.py +0 -0
  425. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/llm/suggestion_utils.py +0 -0
  426. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/preview/__init__.py +0 -0
  427. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/preview/loader.py +0 -0
  428. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/preview/path_validation.py +0 -0
  429. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/project_loader.py +0 -0
  430. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/schema_runtime_builder.py +0 -0
  431. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/__init__.py +0 -0
  432. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/chunked_loader.py +0 -0
  433. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/dag/__init__.py +0 -0
  434. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/dag/builder.py +0 -0
  435. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/dag/executor.py +0 -0
  436. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/dag/sorter.py +0 -0
  437. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/data_loader.py +0 -0
  438. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/engine.py +0 -0
  439. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/executor.py +0 -0
  440. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/extractors.py +0 -0
  441. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/history.py +0 -0
  442. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/json_payload.py +0 -0
  443. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/loader.py +0 -0
  444. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/memory_monitor.py +0 -0
  445. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/postprocess.py +0 -0
  446. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/progress.py +0 -0
  447. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/report_export.py +0 -0
  448. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/resolver.py +0 -0
  449. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/service.py +0 -0
  450. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/types.py +0 -0
  451. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/validators/__init__.py +0 -0
  452. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/validators/adapter.py +0 -0
  453. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/validators/base.py +0 -0
  454. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/validators/composite.py +0 -0
  455. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/shared/services/validation/validators/date_logic.py +0 -0
  456. {precis_cli-0.1.8 → precis_cli-0.1.10}/app/start_server.py +0 -0
  457. {precis_cli-0.1.8 → precis_cli-0.1.10}/precis_cli.egg-info/dependency_links.txt +0 -0
  458. {precis_cli-0.1.8 → precis_cli-0.1.10}/precis_cli.egg-info/entry_points.txt +0 -0
  459. {precis_cli-0.1.8 → precis_cli-0.1.10}/precis_cli.egg-info/requires.txt +0 -0
  460. {precis_cli-0.1.8 → precis_cli-0.1.10}/precis_cli.egg-info/top_level.txt +0 -0
  461. {precis_cli-0.1.8 → precis_cli-0.1.10}/setup.cfg +0 -0
  462. {precis_cli-0.1.8 → precis_cli-0.1.10}/tests/test_local_file_reporter.py +0 -0
  463. {precis_cli-0.1.8 → precis_cli-0.1.10}/tests/test_schema_builder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: precis-cli
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Precis - 可配置数据校验工具 CLI(校验 CSV/Excel/JSON 数据质量)
5
5
  Author: Precis Team
6
6
  License: Apache-2.0
@@ -78,3 +78,15 @@ class CloseProjectResponse(BaseModel):
78
78
  """关闭项目的响应。"""
79
79
 
80
80
  success: bool
81
+
82
+
83
+ class CheckProjectResponse(BaseModel):
84
+ """项目探测端点(GET /projects/check)的响应。
85
+
86
+ 设计约束:对合法输入一律 200,探测结果用布尔字段表达——前端「智能打开」
87
+ 用它预判目录是否为项目根,避免探测请求在浏览器控制台留下 404 红字。
88
+ """
89
+
90
+ path: str
91
+ dir_exists: bool
92
+ is_project: bool
@@ -39,7 +39,7 @@
39
39
  manifest: Optional[dict[str, Any]] = None
40
40
  """
41
41
 
42
- from typing import Any
42
+ from typing import Any, Literal
43
43
 
44
44
  from pydantic import BaseModel, ConfigDict, Field
45
45
 
@@ -110,7 +110,9 @@ class AiChatRequest(BaseModel):
110
110
  class AiChatConfirmRequest(BaseModel):
111
111
  """AI Chat apply_actions 确认/拒绝请求"""
112
112
 
113
- decision: str = Field(..., description="confirm(确认并落盘)或 reject(拒绝,不落盘)")
113
+ # Literal 枚举:非法值(如 "yes"/"ok")由 Pydantic 自动 422 并回显合法值集合,
114
+ # 不再被静默当作 reject 处理
115
+ decision: Literal["confirm", "reject"] = Field(..., description="confirm(确认并落盘)或 reject(拒绝,不落盘)")
114
116
  apply_id: str | None = Field(
115
117
  default=None,
116
118
  description="本次 apply 的 ID({job_id}#{seq})。为空时回退到该 job 当前唯一挂起项。",
@@ -27,7 +27,12 @@
27
27
  - 取消信号: 内存中 _cancel_events 字典(job_id → asyncio.Event)
28
28
  - Last-Event-ID: 仅作为本连接内 journal 回放的起始游标(§2.8);不支持跨连接断线续传——
29
29
  每次请求都新建 job_id 与 journal,重连等价于重新发起会话(真续传需 job 持久化+按 job_id 重连端点,属未来 feature)
30
- - 资源清理: orchestrator 结束后注销 cancel_event,防止内存泄漏
30
+ - SSE 断开宽限期: 客户端断开不取消后台任务(网络抖动/休眠/组件卸载等瞬断不应软取消
31
+ 整个 job);任务继续运行至终态事件或挂起确认自然超时,EventJournal 持续落盘。
32
+ 断开仅登记"客户端失联",让等待中的 apply/ask 超时兜底按"连接中断"回灌文案;
33
+ 显式取消走 /jobs/{job_id}/cancel 端点(行为不变)
34
+ - 资源清理: orchestrator 结束后注销 cancel_event,防止内存泄漏——
35
+ cancel_event 的存续期等于后台 task 生命周期(宽限期内 task 未结束则保留,属在用而非泄漏)
31
36
  """
32
37
 
33
38
  from __future__ import annotations
@@ -67,6 +72,12 @@ _cancel_events_lock = threading.Lock()
67
72
  # task 完成后通过 add_done_callback 自动从集合移除
68
73
  _background_tasks: set[asyncio.Task[None]] = set()
69
74
 
75
+ # 实时事件队列容量上限:journal 是权威全量记录(终止事件携带完整快照兜底),
76
+ # 丢实时帧只影响投递即时性、无正确性影响;而 SSE 断开宽限期内(挂起确认 300s × N +
77
+ # LLM 流式)队列无人消费,无界队列会让 delta/tool 事件持续内存积压。
78
+ # 有界后 orchestrator.emit 的 QueueFull 丢弃守卫被激活:满员丢实时帧、journal 照常落盘
79
+ _EVENT_QUEUE_MAXSIZE = 1000
80
+
70
81
  # journal 存储目录基础(按项目 config_path 分片),复用 AgentJobStorage 的 .precis 约定
71
82
  # 实际路径在端点中按 project_path 拼接
72
83
 
@@ -89,6 +100,41 @@ def _journal_dir_for(project_path: str | None) -> str:
89
100
  return os.path.join(os.path.expanduser("~"), ".precis", "stream_jobs")
90
101
 
91
102
 
103
+ async def _sse_frames_with_disconnect_grace(
104
+ job_id: str,
105
+ journal: EventJournal,
106
+ last_event_id: int,
107
+ event_queue: asyncio.Queue[dict[str, Any]],
108
+ ) -> AsyncIterator[str]:
109
+ """@methoddesc SSE 帧生成器(断开宽限期语义)
110
+
111
+ 与旧实现的差异:客户端断开时不再 cancel_event.set()——瞬断(网络抖动/系统休眠/
112
+ IDE↔Agent 模式切换卸载组件)不应软取消整个后台任务。任务继续运行至终态事件
113
+ (completed/error/cancelled)或挂起确认自然超时(300s),期间 EventJournal 持续
114
+ 落盘(重连回放/事后审计的前提),confirm/respond 端点仍可决议挂起交互。
115
+
116
+ 断开时只做一件事:登记该 job 客户端失联(mark_job_client_gone),使等待中的
117
+ apply 确认/ask 提问在超时兜底时按"连接中断"(decision=disconnected)而非
118
+ "用户拒绝/跳过"回灌文案。显式取消走 /jobs/{job_id}/cancel 端点,行为不变。
119
+ """
120
+ completed_normally = False
121
+ try:
122
+ async for frame in sse_event_stream(
123
+ journal=journal,
124
+ last_event_id=last_event_id,
125
+ event_queue=event_queue,
126
+ ):
127
+ yield frame
128
+ # 流自然耗尽 == 已投递终态事件(sse_event_stream 仅在终态后结束):
129
+ # 任务已结束、挂起交互已决议,无需登记失联
130
+ completed_normally = True
131
+ finally:
132
+ # 仅异常终止(客户端断开触发 GeneratorExit/CancelledError、流内部异常)
133
+ # 才登记失联;正常收尾不登记,避免给每个已完成 job 留下失联残影
134
+ if not completed_normally:
135
+ get_global_pending_interaction_store().mark_job_client_gone(job_id)
136
+
137
+
92
138
  @router.post("/chat/stream", summary="AI 聊天流式接口(SSE)")
93
139
  async def chat_stream(
94
140
  request: AiChatRequest,
@@ -148,8 +194,10 @@ async def chat_stream(
148
194
  canvas_nodes = [node.model_dump() for node in request.context.canvasNodes]
149
195
  history = [{"role": h.role, "content": h.content} for h in (request.history or [])]
150
196
 
151
- # 实时事件队列: orchestrator emit 时推入, sse_event_stream 消费
152
- event_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
197
+ # 实时事件队列: orchestrator emit 时推入, sse_event_stream 消费。
198
+ # 有界(_EVENT_QUEUE_MAXSIZE):宽限期内无人消费时不内存积压,
199
+ # 满员由 emit 侧丢弃实时帧(journal 仍是权威全量记录)
200
+ event_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue(maxsize=_EVENT_QUEUE_MAXSIZE)
153
201
 
154
202
  orchestrator = StreamingOrchestrator(
155
203
  job_id=job_id,
@@ -177,21 +225,17 @@ async def chat_stream(
177
225
  _background_tasks.add(task)
178
226
  task.add_done_callback(_background_tasks.discard)
179
227
 
180
- # 返回 SSE 流: 先回放 journal(本连接内,自 last_event_id 起), 再实时推送队列
181
- async def _sse_generator() -> AsyncIterator[str]:
182
- try:
183
- async for frame in sse_event_stream(
184
- journal=journal,
185
- last_event_id=last_event_id,
186
- event_queue=event_queue,
187
- ):
188
- yield frame
189
- finally:
190
- # 客户端断连时(StreamingResponse 被取消),通知后台 task 停止
191
- # 这让 executor 在下一个取消检查点中断,避免后台 task 泄漏
192
- cancel_event.set()
193
-
194
- return StreamingResponse(_sse_generator(), media_type="text/event-stream")
228
+ # 返回 SSE 流: 先回放 journal(本连接内,自 last_event_id 起), 再实时推送队列。
229
+ # 生成器含断开宽限期语义:断开不取消后台任务(见 _sse_frames_with_disconnect_grace)
230
+ return StreamingResponse(
231
+ _sse_frames_with_disconnect_grace(
232
+ job_id=job_id,
233
+ journal=journal,
234
+ last_event_id=last_event_id,
235
+ event_queue=event_queue,
236
+ ),
237
+ media_type="text/event-stream",
238
+ )
195
239
 
196
240
 
197
241
  @router.post("/jobs/{job_id}/cancel", summary="取消正在运行的 AI job(软取消)")
@@ -234,7 +278,8 @@ async def confirm_apply(job_id: str, request: AiChatConfirmRequest) -> dict[str,
234
278
 
235
279
  参数:
236
280
  job_id: 任务 ID
237
- request: 含 decision 字段("confirm" 或 "reject")
281
+ request: 含 decision 字段("confirm" 或 "reject";非法值由 Pydantic 校验
282
+ 返回 422 并回显合法值集合)
238
283
 
239
284
  返回:
240
285
  {"status": "resolved", "decision": "confirm"} 或 404
@@ -17,9 +17,10 @@
17
17
 
18
18
  from fastapi import APIRouter
19
19
 
20
- from . import create, open, scan
20
+ from . import check, create, open, scan
21
21
 
22
22
  router = APIRouter(prefix="/api/latest/projects", tags=["Projects"])
23
23
  router.include_router(scan.router)
24
24
  router.include_router(open.router)
25
25
  router.include_router(create.router)
26
+ router.include_router(check.router)
@@ -0,0 +1,62 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview 项目探测路由:只读判断目录是否为合法 Precis 项目根
18
+
19
+ 功能概述:
20
+ - GET /api/latest/projects/check?path=<绝对路径>
21
+ - 供前端「智能打开」(useSmartProjectOpen)在引导就地新建前探测目录
22
+
23
+ 架构设计:
24
+ - 与 open.py/create.py 对称,但**永不 404**:空目录/不存在的目录都用布尔
25
+ 字段表达(dir_exists/is_project),浏览器控制台不会因探测留下 404 红字;
26
+ 仅非法输入(相对路径/路径穿越)返回 400
27
+ - 路径经 query 参数传递(UTF-8 自动编解码),无 X-Project-Config-Path
28
+ header 的 latin-1 编码问题
29
+ - 不经 get_project_config_path 依赖(该依赖对缺 manifest 的目录抛 404,
30
+ 正是本端点要避免的语义)
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import os
36
+
37
+ from fastapi import APIRouter, HTTPException, Query
38
+
39
+ from app.api.models.projects import CheckProjectResponse
40
+
41
+ router = APIRouter(prefix="", tags=["Projects-Check"])
42
+
43
+
44
+ @router.get(
45
+ "/check",
46
+ response_model=CheckProjectResponse,
47
+ summary="探测目录是否为 Precis 项目(永不 404)",
48
+ )
49
+ def check_project(path: str = Query(..., description="待探测的目录绝对路径")) -> CheckProjectResponse:
50
+ """只读探测目录状态,不创建、不修改任何文件,不改变"当前项目"。"""
51
+ # 输入校验与 get_project_config_path 同规:先拒绝相对路径(Windows 下
52
+ # abspath 会把相对路径解析成绝对路径,必须先判 isabs),再防路径穿越
53
+ if not os.path.isabs(path):
54
+ raise HTTPException(status_code=400, detail="path 必须是一个绝对路径。")
55
+
56
+ normalized = os.path.abspath(os.path.normpath(path))
57
+ if ".." in normalized.split(os.sep):
58
+ raise HTTPException(status_code=400, detail="path 不允许包含 .. 目录穿越。")
59
+
60
+ dir_exists = os.path.isdir(normalized)
61
+ is_project = os.path.isfile(os.path.join(normalized, "project.precis.yaml"))
62
+ return CheckProjectResponse(path=normalized, dir_exists=dir_exists, is_project=is_project)
@@ -38,8 +38,9 @@ from app.shared.services.ai.agent.chat_tools.ask_user import AskCallbacks
38
38
 
39
39
  logger = logging.getLogger(__name__)
40
40
 
41
- # 终端 diff 展示上限:超长 diff 只显示前 N 行,避免刷屏(完整内容在 dry-run 文件里)
42
- _MAX_DIFF_LINES = 30
41
+ # 确认清单(动作/文件)的展示上限:超长清单只显示前 N 项 + 总数,避免刷屏;
42
+ # diff 正文不在此处展示,用户按 [d] 可查看全量内容
43
+ _MAX_LIST_ITEMS = 10
43
44
 
44
45
 
45
46
  def build_agent_interaction(spinner: SpinnerController | None) -> tuple[ApplyCallbacks, AskCallbacks]:
@@ -61,7 +62,7 @@ def build_agent_interaction(spinner: SpinnerController | None) -> tuple[ApplyCal
61
62
  _print_apply_summary(payload)
62
63
 
63
64
  def _worker() -> None:
64
- decision = _read_apply_decision()
65
+ decision = _read_apply_decision(payload)
65
66
  try:
66
67
  future = asyncio.run_coroutine_threadsafe(_resolve_apply(apply_id, decision), loop)
67
68
  future.result(timeout=10)
@@ -123,33 +124,75 @@ async def _resolve_ask(ask_id: str, response: dict[str, Any]) -> None:
123
124
 
124
125
 
125
126
  def _print_apply_summary(payload: dict[str, Any]) -> None:
126
- """打印写盘计划摘要:文件清单 + 状态 + 截断 diff + 汇总行。"""
127
+ """打印写盘计划摘要:动作语义清单 + 文件状态清单(不打印 diff 正文)。
128
+
129
+ 摘要优先——用户先确认"AI 要干什么"(动作清单),再看"动了哪些文件";
130
+ diff 正文体积大(新建文件即全文),改为在决策提示中按 [d] 按需查看全量。
131
+ """
132
+ actions = payload.get("actions") or []
127
133
  files = payload.get("files") or []
128
- print(Formatter.warning(f"\nAI 请求写入 {len(files)} 个文件:"))
134
+
135
+ # 动作语义清单:一行一个动作(如 "添加约束:users.email — NotNull")
136
+ if actions:
137
+ print(Formatter.warning(f"\n将执行 {len(actions)} 个动作:"))
138
+ for i, action in enumerate(actions[:_MAX_LIST_ITEMS], start=1):
139
+ description = str(action.get("description") or action.get("action_type") or "")
140
+ print(f" {i}. {description}")
141
+ if len(actions) > _MAX_LIST_ITEMS:
142
+ print(f" ... 共 {len(actions)} 个")
143
+
144
+ # 文件清单:状态 + 路径(无 diff 正文)
145
+ counts = {"created": 0, "modified": 0, "deleted": 0}
129
146
  for f in files:
130
- status = f.get("status", "?")
131
- path = f.get("path", "?")
132
- print(f" [{status}] {path}")
133
- diff = (f.get("diff") or "").strip()
134
- if diff:
135
- diff_lines = diff.splitlines()
136
- shown = diff_lines[:_MAX_DIFF_LINES]
137
- for line in shown:
138
- print(f" {line}")
139
- if len(diff_lines) > _MAX_DIFF_LINES:
140
- print(f" ...(共 {len(diff_lines)} 行,已截断)")
147
+ status = str(f.get("status", ""))
148
+ if status in counts:
149
+ counts[status] += 1
150
+ print(
151
+ Formatter.warning(
152
+ f"\nAI 请求写入 {len(files)} 个文件"
153
+ f"(新增 {counts['created']},修改 {counts['modified']},删除 {counts['deleted']}):"
154
+ )
155
+ )
156
+ for f in files[:_MAX_LIST_ITEMS]:
157
+ print(f" [{f.get('status', '?')}] {f.get('path', '?')}")
158
+ if len(files) > _MAX_LIST_ITEMS:
159
+ print(f" ... 共 {len(files)} 个")
160
+
141
161
  summary = payload.get("summary")
142
162
  if summary:
143
163
  print(Formatter.info(f" 摘要: {summary}"))
144
164
 
145
165
 
146
- def _read_apply_decision() -> str:
147
- """读取 y/n 决策;空输入与非 y 输入一律 reject(写盘默认保守)。"""
148
- try:
149
- raw = input(Formatter.warning("确认写入以上变更?[y/N]: ")).strip().lower()
150
- except EOFError:
166
+ def _print_full_diff(payload: dict[str, Any]) -> None:
167
+ """打印全量 diff([d] 按需查看):payload 携带的就是完整内容,不做截断。"""
168
+ files = payload.get("files") or []
169
+ if not files:
170
+ print(Formatter.info(" (无文件变更)"))
171
+ return
172
+ for f in files:
173
+ print(Formatter.info(f"\n--- {f.get('path', '?')}({f.get('status', '?')})---"))
174
+ diff = (f.get("diff") or "").strip()
175
+ print(diff if diff else " (无 diff 内容)")
176
+
177
+
178
+ def _read_apply_decision(payload: dict[str, Any]) -> str:
179
+ """三态决策循环:y 确认写入 / d 查看全量 diff 后回到提示 / 其余(含空输入)拒绝。
180
+
181
+ 空输入与非 y 输入一律 reject(写盘默认保守),与旧版语义一致。
182
+ """
183
+ while True:
184
+ try:
185
+ raw = (
186
+ input(Formatter.warning("确认写入以上变更?[y]确认写入 / [d]查看详细 diff / [N]拒绝: ")).strip().lower()
187
+ )
188
+ except EOFError:
189
+ return "reject"
190
+ if raw in ("y", "yes", "是"):
191
+ return "confirm"
192
+ if raw in ("d", "diff", "查看"):
193
+ _print_full_diff(payload)
194
+ continue
151
195
  return "reject"
152
- return "confirm" if raw in ("y", "yes", "是") else "reject"
153
196
 
154
197
 
155
198
  def _print_ask_question(payload: dict[str, Any]) -> None:
@@ -157,11 +200,27 @@ def _print_ask_question(payload: dict[str, Any]) -> None:
157
200
  options = payload.get("options")
158
201
  if isinstance(options, list) and options:
159
202
  for idx, opt in enumerate(options, start=1):
160
- print(f" {idx}. {opt}")
203
+ _, text = _split_option(opt)
204
+ print(f" {idx}. {text}")
161
205
  if payload.get("optional"):
162
206
  print(Formatter.info(" (直接回车跳过此问题)"))
163
207
 
164
208
 
209
+ def _split_option(opt: Any) -> tuple[str, str]:
210
+ """解析单个选项为 (value, 展示文本)。
211
+
212
+ ask_user 工具 schema 声明 options 为 {label, value, description?} 对象;
213
+ 纯字符串形态(历史/测试用例)兼容处理,value 即字符串本身。
214
+ """
215
+ if isinstance(opt, dict):
216
+ value = str(opt.get("value", opt.get("label", "")))
217
+ label = str(opt.get("label", value))
218
+ desc = str(opt.get("description") or "")
219
+ text = f"{label} — {desc}" if desc else label
220
+ return value, text
221
+ return str(opt), str(opt)
222
+
223
+
165
224
  def _read_ask_response(payload: dict[str, Any]) -> dict[str, Any]:
166
225
  """按 question_type 读取终端回答,构造 AskResponseBody 形态。
167
226
 
@@ -182,7 +241,8 @@ def _read_ask_response(payload: dict[str, Any]) -> dict[str, Any]:
182
241
  picked: list[str] = []
183
242
  for part in indexes:
184
243
  if part.isdigit() and 1 <= int(part) <= len(options):
185
- picked.append(str(options[int(part) - 1]))
244
+ # 按编号选中:回灌选项的 value 字段(与前端 AskUserCard 契约一致)
245
+ picked.append(_split_option(options[int(part) - 1])[0])
186
246
  else:
187
247
  picked.append(part)
188
248
  if payload.get("multiple"):
@@ -80,7 +80,7 @@ class AIChatCommand(Command):
80
80
 
81
81
  @property
82
82
  def usage(self) -> str:
83
- return "ai chat [--no-agent-mode]"
83
+ return "ai chat [--no-agent-mode] [--no-pretty]"
84
84
 
85
85
  def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
86
86
  """执行 AI 交互式对话命令。
@@ -88,7 +88,7 @@ class AIChatCommand(Command):
88
88
  检查项目和 AI 配置,解析命令行参数,然后进入交互式输入循环。
89
89
 
90
90
  Args:
91
- args: 命令参数列表,可能包含 --no-agent-mode
91
+ args: 命令参数列表,可能包含 --no-agent-mode / --no-pretty
92
92
  context: 命令上下文,必须包含已打开的项目
93
93
 
94
94
  Returns:
@@ -104,10 +104,14 @@ class AIChatCommand(Command):
104
104
 
105
105
  # 解析命令行参数(--stream/--no-stream 已随 execute_ai_chat 的死参数一并移除)
106
106
  agent_mode = True # 默认启用 Agent 深度模式
107
+ pretty: bool | None = None # markdown 流式渲染:None 自动(TTY 检测)
107
108
  filtered_args = []
108
109
  for arg in args:
109
110
  if arg == "--no-agent-mode":
110
111
  agent_mode = False
112
+ elif arg == "--no-pretty":
113
+ # 关闭流式 markdown 渲染,回退原文直出(与非 TTY 管道路径一致)
114
+ pretty = False
111
115
  else:
112
116
  filtered_args.append(arg)
113
117
 
@@ -209,6 +213,7 @@ class AIChatCommand(Command):
209
213
  interactive=True,
210
214
  history=chat_history,
211
215
  agent_mode=agent_mode,
216
+ pretty=pretty,
212
217
  )
213
218
 
214
219
  if result.success and result.data:
@@ -259,6 +264,7 @@ AI 助手帮助
259
264
 
260
265
  启动选项:
261
266
  --no-agent-mode - 关闭 Agent 深度模式(降级为旧 JSON actions 路径)
267
+ --no-pretty - 关闭流式 markdown 渲染(标题/粗体/表格重排),回退原文直出
262
268
 
263
269
  你可以用自然语言描述你的需求,例如:
264
270
  - "在 users 表的 email 列上添加非空约束"
@@ -18,6 +18,7 @@
18
18
  功能概述:
19
19
  - 执行 AI 聊天命令的主入口
20
20
  - 辅助函数已拆分到 executor_utils.py / diff.py / display.py
21
+ - agent 模式经 ChatStreamRenderer(stream_renderer.py)接终端流式渲染
21
22
  """
22
23
 
23
24
  from __future__ import annotations
@@ -33,12 +34,12 @@ from .display import _display_execution_results, _display_tool_trail
33
34
  from .executor_utils import (
34
35
  SpinnerController,
35
36
  _collect_all_config_files,
36
- _collect_declared_new_files,
37
37
  _get_provider_display,
38
38
  _get_provider_with_key,
39
39
  )
40
40
  from .interaction import confirm_actions as base_confirm_actions
41
41
  from .resolver import resolve_ambiguities as base_resolve_ambiguities
42
+ from .stream_renderer import ChatStreamRenderer
42
43
 
43
44
  # 为模型回复预留的 token 预算
44
45
  RESERVED_OUTPUT_TOKENS = 8000
@@ -52,6 +53,7 @@ def execute_ai_chat(
52
53
  interactive: bool = False,
53
54
  history: list[dict[str, str]] | None = None,
54
55
  agent_mode: bool = True,
56
+ pretty: bool | None = None,
55
57
  ) -> CommandResult:
56
58
  """执行 AI 对话(统一版本)。
57
59
 
@@ -68,6 +70,8 @@ def execute_ai_chat(
68
70
  interactive: 是否为交互模式(显示 spinner、提示确认等)
69
71
  history: 对话历史记录列表,每个元素包含 role 和 content
70
72
  agent_mode: 是否启用 Agent 深度模式(默认 True),CLI 无画布时仍走 Agent 工具循环
73
+ pretty: 流式 markdown 渲染开关(True/False 强制,None 自动按 stdout TTY 检测;
74
+ 非 TTY 管道场景自动回退原文直出)
71
75
 
72
76
  Returns:
73
77
  命令执行结果,成功时 data 包含 reply、actions 和 frontend_instructions
@@ -134,6 +138,12 @@ def execute_ai_chat(
134
138
 
135
139
  agent_apply_callbacks, agent_ask_callbacks = build_agent_interaction(spinner)
136
140
 
141
+ # 流式渲染器:agent 模式把 runner 的 on_chunk/on_tool_* 回调接到终端,
142
+ # LLM 逐字输出与工具过程实时可见;legacy JSON 路径无流式管道,保持原 spinner 体验
143
+ stream_renderer = (
144
+ ChatStreamRenderer(spinner=spinner, interactive=interactive, pretty=pretty) if agent_mode else None
145
+ )
146
+
137
147
  # 配置对话选项
138
148
  options = ChatOptions(
139
149
  history=history or [],
@@ -144,21 +154,23 @@ def execute_ai_chat(
144
154
  confirm_callback=confirm_actions_wrapper if interactive else None,
145
155
  ambiguity_resolver=ambiguity_resolver_wrapper if interactive else None,
146
156
  agent_mode=agent_mode,
147
- max_agent_iterations=5,
148
157
  canvas_nodes=[],
158
+ # CLI 终端没有画布:隔离画布能力——read_canvas 工具不注册、
159
+ # ADD_TO_CANVAS 动作拦截、系统提示词剔除画布话术(agent/legacy 两路径共用)
160
+ canvas_enabled=False,
149
161
  apply_callbacks=agent_apply_callbacks,
150
162
  ask_callbacks=agent_ask_callbacks,
151
163
  dry_run_enabled=agent_apply_callbacks is not None,
164
+ agent_stream_callbacks=stream_renderer.callbacks() if stream_renderer else None,
152
165
  )
153
166
 
154
167
  try:
155
168
  import asyncio
156
169
 
157
- # 收集原始文件内容(仅在交互模式下,用于 diff 对比)。
158
- # 4.25: AI 本次新建的文件纳入执行前缓存(登记为空内容)——执行后它们
159
- # 存在而缓存里没有,diff 生成器会跳过,新建文件的变更在摘要里永不显示
170
+ # 收集原始文件内容(仅 legacy 交互路径使用——agent 模式的 diff 由
171
+ # 确认框(apply 回调)基于 dry-run 计算,不需要会话前快照)
160
172
  original_files_cache = {}
161
- if interactive and project_path:
173
+ if interactive and not agent_mode and project_path:
162
174
  original_files_cache = _collect_all_config_files(project_path)
163
175
 
164
176
  # 启动 spinner 动画,提示用户 AI 正在处理
@@ -166,19 +178,26 @@ def execute_ai_chat(
166
178
  spinner.start()
167
179
 
168
180
  # 执行 AI 对话(CLI 是同步环境:asyncio.run 创建一次性事件循环并在结束时确保关闭,
169
- # 不再使用已弃用的 get_event_loop + run_until_complete,避免循环泄漏)
170
- result = asyncio.run(
171
- orchestrator.execute_chat(
172
- message=message,
173
- project_path=project_path,
174
- context_nodes=context_nodes,
175
- options=options,
181
+ # 不再使用已弃用的 get_event_loop + run_until_complete,避免循环泄漏)。
182
+ # 收尾走 finally:流式中途 Ctrl+C 等任何退出路径(含 BaseException)都先 finish
183
+ # 再 stop——spinner.stop 的清行序列从行首覆盖,必须保证光标已落在干净行首
184
+ try:
185
+ result = asyncio.run(
186
+ orchestrator.execute_chat(
187
+ message=message,
188
+ project_path=project_path,
189
+ context_nodes=context_nodes,
190
+ options=options,
191
+ )
176
192
  )
177
- )
193
+ finally:
194
+ # 收尾流式文本行(先于 spinner.stop,顺序不可颠倒)
195
+ if stream_renderer:
196
+ stream_renderer.finish()
178
197
 
179
- # 停止 spinner 动画
180
- if spinner:
181
- spinner.stop()
198
+ # 停止 spinner 动画
199
+ if spinner:
200
+ spinner.stop()
182
201
 
183
202
  # 处理结果:如果失败则返回错误
184
203
  if not result.success:
@@ -189,26 +208,27 @@ def execute_ai_chat(
189
208
 
190
209
  reply = result.reply
191
210
  actions = result.actions
211
+ # 流式已把最终回复逐字上屏时不再整段重复打印(多轮中间文本已由 on_turn 重置排除)
212
+ reply_streamed = bool(stream_renderer and stream_renderer.final_reply_already_shown(reply))
192
213
 
193
214
  # 在交互模式下显示 AI 的文本回复
194
- if interactive:
195
- if reply:
196
- print(reply)
215
+ if interactive and reply and not reply_streamed:
216
+ print(reply)
197
217
 
198
218
  # 在交互模式下显示 Agent 工具轨迹(仅 Agent 模式有 tool_steps)
199
219
  if interactive and agent_mode and result.tool_steps:
200
220
  _display_tool_trail(result.tool_steps)
201
221
 
202
- # 在交互模式下显示执行结果和 diff。
203
- # 4.25: AI 本次新建的文件先登记进缓存(空内容)——执行后它们存在而缓存
204
- # 没有,diff 生成器会跳过,新建文件的变更在摘要里永不显示
205
- if interactive and actions and project_path is not None:
206
- for declared in _collect_declared_new_files(result, project_path):
207
- original_files_cache.setdefault(declared, "")
222
+ # 在交互 + legacy(非 agent)模式下显示执行结果和 diff 摘要。
223
+ # agent 模式不再弹事后摘要:确认框(apply 回调)已展示完整 dry-run diff,
224
+ # 且这里的数据源是会话前快照(漏掉本会话新建的文件),input() 还会
225
+ # 吞掉用户的下一条命令
226
+ if interactive and not agent_mode and actions and project_path is not None:
208
227
  _display_execution_results(result, project_path, original_files_cache)
209
228
 
210
229
  return CommandResult.ok(
211
- reply if not interactive else "",
230
+ # 非交互(ai ask)单发模式由 main 打印 message:流式已输出过回复时置空避免重复
231
+ "" if interactive or reply_streamed else reply,
212
232
  data={
213
233
  "reply": reply,
214
234
  "actions": actions,
@@ -217,10 +237,7 @@ def execute_ai_chat(
217
237
  )
218
238
 
219
239
  except Exception as e:
220
- # 发生异常时确保 spinner 停止,避免动画残留
221
- if spinner:
222
- spinner.stop()
223
-
240
+ # 流式收尾与 spinner 停止已由内层 finally 覆盖(任何退出路径),此处只做错误呈现
224
241
  logger.error(f"AI 对话失败: {e}", exc_info=True)
225
242
  error_msg = f"AI 对话失败: {str(e)}"
226
243