precis-cli 0.1.3__py3-none-any.whl

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 (442) hide show
  1. app/__init__.py +16 -0
  2. app/api/__init__.py +32 -0
  3. app/api/dependencies.py +185 -0
  4. app/api/main.py +301 -0
  5. app/api/middleware/__init__.py +34 -0
  6. app/api/middleware/exception_handler.py +126 -0
  7. app/api/middleware/request_logging.py +97 -0
  8. app/api/middleware/token_auth.py +215 -0
  9. app/api/models/__init__.py +151 -0
  10. app/api/models/connection_rules.py +98 -0
  11. app/api/models/files.py +99 -0
  12. app/api/models/full_validation.py +554 -0
  13. app/api/models/project.py +117 -0
  14. app/api/models/projects.py +80 -0
  15. app/api/models/schema.py +121 -0
  16. app/api/models/v2_responses.py +46 -0
  17. app/api/models/validation.py +303 -0
  18. app/api/models/workspace.py +118 -0
  19. app/api/routers/__init__.py +56 -0
  20. app/api/routers/ai/__init__.py +36 -0
  21. app/api/routers/ai/chat.py +243 -0
  22. app/api/routers/ai/generate.py +156 -0
  23. app/api/routers/ai/hardware.py +199 -0
  24. app/api/routers/ai/jobs.py +544 -0
  25. app/api/routers/ai/migrate.py +471 -0
  26. app/api/routers/ai/models.py +352 -0
  27. app/api/routers/ai/ollama.py +108 -0
  28. app/api/routers/ai/providers.py +491 -0
  29. app/api/routers/ai/router.py +44 -0
  30. app/api/routers/ai/stream.py +281 -0
  31. app/api/routers/ai/utils.py +182 -0
  32. app/api/routers/core/__init__.py +34 -0
  33. app/api/routers/core/connection_rules.py +188 -0
  34. app/api/routers/core/data_sources.py +374 -0
  35. app/api/routers/core/regex.py +333 -0
  36. app/api/routers/core/reporting.py +263 -0
  37. app/api/routers/files/__init__.py +24 -0
  38. app/api/routers/files/ops.py +179 -0
  39. app/api/routers/files/transfer.py +101 -0
  40. app/api/routers/preview/__init__.py +35 -0
  41. app/api/routers/preview/content_mode.py +264 -0
  42. app/api/routers/preview/header_row.py +154 -0
  43. app/api/routers/preview/models.py +81 -0
  44. app/api/routers/preview/path_mode.py +440 -0
  45. app/api/routers/preview/router.py +30 -0
  46. app/api/routers/project/__init__.py +61 -0
  47. app/api/routers/project/base.py +111 -0
  48. app/api/routers/project/constraint.py +345 -0
  49. app/api/routers/project/full_config.py +411 -0
  50. app/api/routers/project/full_config_writer.py +327 -0
  51. app/api/routers/project/helpers.py +167 -0
  52. app/api/routers/project/inspection_fix.py +330 -0
  53. app/api/routers/project/manifest.py +605 -0
  54. app/api/routers/project/models.py +177 -0
  55. app/api/routers/project/pattern.py +268 -0
  56. app/api/routers/project/regex.py +358 -0
  57. app/api/routers/project/scanner.py +157 -0
  58. app/api/routers/project/schema.py +567 -0
  59. app/api/routers/project/schema_helpers.py +149 -0
  60. app/api/routers/project/settings.py +399 -0
  61. app/api/routers/project/template.py +325 -0
  62. app/api/routers/project/validation.py +227 -0
  63. app/api/routers/project/view.py +177 -0
  64. app/api/routers/project/workspaces.py +167 -0
  65. app/api/routers/projects/__init__.py +25 -0
  66. app/api/routers/projects/create.py +121 -0
  67. app/api/routers/projects/open.py +103 -0
  68. app/api/routers/projects/scan.py +100 -0
  69. app/api/routers/validation/__init__.py +39 -0
  70. app/api/routers/validation/common.py +263 -0
  71. app/api/routers/validation/content_mode.py +401 -0
  72. app/api/routers/validation/history.py +235 -0
  73. app/api/routers/validation/inline_mode.py +196 -0
  74. app/api/routers/validation/path_mode.py +314 -0
  75. app/api/routers/validation/router.py +51 -0
  76. app/api/services/full_validation_response_builder.py +412 -0
  77. app/api/services/io_error_messages.py +41 -0
  78. app/api/services/preview_service.py +248 -0
  79. app/cli/__init__.py +18 -0
  80. app/cli/__main__.py +30 -0
  81. app/cli/shared_services/__init__.py +42 -0
  82. app/cli/shared_services/config_ops.py +581 -0
  83. app/cli/shared_services/generation_ops.py +160 -0
  84. app/cli/shared_services/project_ops.py +288 -0
  85. app/cli/shell/__init__.py +48 -0
  86. app/cli/shell/commands/__init__.py +63 -0
  87. app/cli/shell/commands/ai/__init__.py +260 -0
  88. app/cli/shell/commands/ai/chat.py +331 -0
  89. app/cli/shell/commands/ai/delete.py +165 -0
  90. app/cli/shell/commands/ai/diff.py +70 -0
  91. app/cli/shell/commands/ai/display.py +428 -0
  92. app/cli/shell/commands/ai/executor.py +225 -0
  93. app/cli/shell/commands/ai/executor_utils.py +271 -0
  94. app/cli/shell/commands/ai/generate.py +285 -0
  95. app/cli/shell/commands/ai/interaction.py +262 -0
  96. app/cli/shell/commands/ai/migrate.py +281 -0
  97. app/cli/shell/commands/ai/resolver.py +224 -0
  98. app/cli/shell/commands/ai/status.py +101 -0
  99. app/cli/shell/commands/ai/switch.py +158 -0
  100. app/cli/shell/commands/ai/utils.py +52 -0
  101. app/cli/shell/commands/base.py +343 -0
  102. app/cli/shell/commands/config/__init__.py +131 -0
  103. app/cli/shell/commands/config/base.py +80 -0
  104. app/cli/shell/commands/config/check.py +246 -0
  105. app/cli/shell/commands/config/edit.py +148 -0
  106. app/cli/shell/commands/config/get.py +111 -0
  107. app/cli/shell/commands/config/init.py +123 -0
  108. app/cli/shell/commands/config/inspect.py +164 -0
  109. app/cli/shell/commands/config/list.py +104 -0
  110. app/cli/shell/commands/config/set.py +119 -0
  111. app/cli/shell/commands/config/show.py +150 -0
  112. app/cli/shell/commands/exit.py +67 -0
  113. app/cli/shell/commands/help.py +104 -0
  114. app/cli/shell/commands/infer_schema.py +156 -0
  115. app/cli/shell/commands/open.py +201 -0
  116. app/cli/shell/commands/project.py +215 -0
  117. app/cli/shell/commands/provider.py +619 -0
  118. app/cli/shell/commands/system.py +170 -0
  119. app/cli/shell/commands/validate.py +455 -0
  120. app/cli/shell/completer.py +231 -0
  121. app/cli/shell/config_storage.py +191 -0
  122. app/cli/shell/exceptions.py +90 -0
  123. app/cli/shell/formatter.py +371 -0
  124. app/cli/shell/interactive_menu.py +348 -0
  125. app/cli/shell/main.py +286 -0
  126. app/cli/shell/parser.py +266 -0
  127. app/cli/start.py +180 -0
  128. app/cli_main.py +52 -0
  129. app/mcp_server.py +311 -0
  130. app/shared/core/__init__.py +18 -0
  131. app/shared/core/app_version.py +59 -0
  132. app/shared/core/config/__init__.py +119 -0
  133. app/shared/core/config/server.py +193 -0
  134. app/shared/core/data_source/__init__.py +106 -0
  135. app/shared/core/data_source/loader.py +348 -0
  136. app/shared/core/data_source/loaders/__init__.py +213 -0
  137. app/shared/core/data_source/loaders/base.py +215 -0
  138. app/shared/core/data_source/loaders/converter.py +337 -0
  139. app/shared/core/data_source/loaders/csv_loader.py +252 -0
  140. app/shared/core/data_source/loaders/excel_loader.py +666 -0
  141. app/shared/core/data_source/loaders/extractor.py +268 -0
  142. app/shared/core/data_source/loaders/json_loader.py +354 -0
  143. app/shared/core/data_source/loaders/registry.py +88 -0
  144. app/shared/core/data_source/loaders/sql_loader.py +313 -0
  145. app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
  146. app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
  147. app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
  148. app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
  149. app/shared/core/data_source/schema_info.py +50 -0
  150. app/shared/core/data_source/specs/__init__.py +128 -0
  151. app/shared/core/data_source/specs/base.py +258 -0
  152. app/shared/core/data_source/specs/csv_source.py +137 -0
  153. app/shared/core/data_source/specs/excel_source.py +141 -0
  154. app/shared/core/data_source/specs/file_base.py +252 -0
  155. app/shared/core/data_source/specs/json_source.py +226 -0
  156. app/shared/core/data_source/specs/sql_source.py +161 -0
  157. app/shared/core/encoding.py +36 -0
  158. app/shared/core/io/__init__.py +24 -0
  159. app/shared/core/io/yaml.py +379 -0
  160. app/shared/core/manifest_schema/__init__.py +52 -0
  161. app/shared/core/manifest_schema/types.py +99 -0
  162. app/shared/core/manifest_schema/version.py +175 -0
  163. app/shared/core/patterns/__init__.py +24 -0
  164. app/shared/core/patterns/loader.py +134 -0
  165. app/shared/core/patterns/writer.py +244 -0
  166. app/shared/core/project/__init__.py +47 -0
  167. app/shared/core/project/constraint/builders/__init__.py +50 -0
  168. app/shared/core/project/constraint/builders/base.py +100 -0
  169. app/shared/core/project/constraint/builders/composite.py +77 -0
  170. app/shared/core/project/constraint/builders/conditional.py +67 -0
  171. app/shared/core/project/constraint/builders/foreign_key.py +53 -0
  172. app/shared/core/project/constraint/builders/registry.py +64 -0
  173. app/shared/core/project/constraint/builders/scripted.py +51 -0
  174. app/shared/core/project/constraint/builders/single_column.py +86 -0
  175. app/shared/core/project/constraint/builders/unique.py +53 -0
  176. app/shared/core/project/constraint/factory.py +170 -0
  177. app/shared/core/project/constraint/reader.py +214 -0
  178. app/shared/core/project/constraint/registry.py +233 -0
  179. app/shared/core/project/constraint/types/__init__.py +63 -0
  180. app/shared/core/project/constraint/types/constraint_file.py +261 -0
  181. app/shared/core/project/constraint/types/refs.py +460 -0
  182. app/shared/core/project/constraint/types.py +28 -0
  183. app/shared/core/project/constraint/writer.py +181 -0
  184. app/shared/core/project/loader/__init__.py +56 -0
  185. app/shared/core/project/loader/loader.py +30 -0
  186. app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
  187. app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
  188. app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
  189. app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
  190. app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
  191. app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
  192. app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
  193. app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
  194. app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
  195. app/shared/core/project/loader/loader_parts/main.py +432 -0
  196. app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
  197. app/shared/core/project/loader/loader_parts/runtime.py +125 -0
  198. app/shared/core/project/loader/types.py +246 -0
  199. app/shared/core/project/manifest/coverage.py +391 -0
  200. app/shared/core/project/manifest/reader.py +260 -0
  201. app/shared/core/project/manifest/types.py +91 -0
  202. app/shared/core/project/manifest/types_parts/__init__.py +60 -0
  203. app/shared/core/project/manifest/types_parts/constants.py +40 -0
  204. app/shared/core/project/manifest/types_parts/data_source.py +68 -0
  205. app/shared/core/project/manifest/types_parts/info.py +59 -0
  206. app/shared/core/project/manifest/types_parts/manifest.py +294 -0
  207. app/shared/core/project/manifest/types_parts/refs.py +153 -0
  208. app/shared/core/project/manifest/types_parts/settings.py +92 -0
  209. app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
  210. app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
  211. app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
  212. app/shared/core/project/manifest/types_parts/template.py +66 -0
  213. app/shared/core/project/manifest/writer.py +262 -0
  214. app/shared/core/project/manual_data/__init__.py +27 -0
  215. app/shared/core/project/manual_data/types.py +75 -0
  216. app/shared/core/project/regex/reader.py +197 -0
  217. app/shared/core/project/regex/types.py +405 -0
  218. app/shared/core/project/regex/writer.py +123 -0
  219. app/shared/core/project/schema/reader.py +170 -0
  220. app/shared/core/project/schema/types.py +47 -0
  221. app/shared/core/project/schema/types_parts/__init__.py +36 -0
  222. app/shared/core/project/schema/types_parts/column.py +174 -0
  223. app/shared/core/project/schema/types_parts/column_utils.py +72 -0
  224. app/shared/core/project/schema/types_parts/constraint.py +165 -0
  225. app/shared/core/project/schema/types_parts/schema_id.py +66 -0
  226. app/shared/core/project/schema/types_parts/source.py +255 -0
  227. app/shared/core/project/schema/types_parts/source_options.py +347 -0
  228. app/shared/core/project/schema/types_parts/table.py +230 -0
  229. app/shared/core/project/schema/writer.py +139 -0
  230. app/shared/core/project/schema_ref_check.py +95 -0
  231. app/shared/core/project/template/__init__.py +27 -0
  232. app/shared/core/project/template/expander.py +263 -0
  233. app/shared/core/project/template/reader.py +120 -0
  234. app/shared/core/project/template/types.py +114 -0
  235. app/shared/core/project/transform/reader.py +76 -0
  236. app/shared/core/project/transform/types.py +116 -0
  237. app/shared/core/project/transform/writer.py +84 -0
  238. app/shared/core/pydantic_messages.py +59 -0
  239. app/shared/core/reporter/__init__.py +46 -0
  240. app/shared/core/reporter/reporter.py +220 -0
  241. app/shared/core/reporter/reporters/__init__.py +65 -0
  242. app/shared/core/reporter/reporters/base.py +188 -0
  243. app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
  244. app/shared/core/reporter/reporters/email_reporter.py +271 -0
  245. app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
  246. app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
  247. app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
  248. app/shared/core/utils/__init__.py +18 -0
  249. app/shared/core/utils/path_utils.py +60 -0
  250. app/shared/core/utils/regex_extract.py +113 -0
  251. app/shared/domain/__init__.py +114 -0
  252. app/shared/domain/constraints/__init__.py +76 -0
  253. app/shared/domain/constraints/allowed_values.py +211 -0
  254. app/shared/domain/constraints/base.py +141 -0
  255. app/shared/domain/constraints/charset.py +337 -0
  256. app/shared/domain/constraints/composite.py +174 -0
  257. app/shared/domain/constraints/condition_registry.py +130 -0
  258. app/shared/domain/constraints/conditional.py +629 -0
  259. app/shared/domain/constraints/date_logic.py +731 -0
  260. app/shared/domain/constraints/foreign_key.py +261 -0
  261. app/shared/domain/constraints/key_normalization.py +56 -0
  262. app/shared/domain/constraints/not_null.py +185 -0
  263. app/shared/domain/constraints/range.py +360 -0
  264. app/shared/domain/constraints/regex.py +218 -0
  265. app/shared/domain/constraints/scripted.py +276 -0
  266. app/shared/domain/constraints/unique.py +233 -0
  267. app/shared/domain/data_engine.py +384 -0
  268. app/shared/domain/data_types.py +113 -0
  269. app/shared/domain/data_types_parts/__init__.py +67 -0
  270. app/shared/domain/data_types_parts/base.py +204 -0
  271. app/shared/domain/data_types_parts/composite.py +250 -0
  272. app/shared/domain/data_types_parts/expression.py +202 -0
  273. app/shared/domain/data_types_parts/extracted.py +106 -0
  274. app/shared/domain/data_types_parts/json_types.py +234 -0
  275. app/shared/domain/data_types_parts/scalars.py +719 -0
  276. app/shared/domain/data_types_parts/sequence.py +129 -0
  277. app/shared/domain/dataset_schema.py +48 -0
  278. app/shared/domain/eval_sandbox.py +63 -0
  279. app/shared/domain/expression_system.py +366 -0
  280. app/shared/domain/regex_flags.py +53 -0
  281. app/shared/domain/schema/builder.py +223 -0
  282. app/shared/domain/schema/models.py +339 -0
  283. app/shared/domain/transforms/__init__.py +28 -0
  284. app/shared/domain/transforms/aggregate.py +141 -0
  285. app/shared/domain/transforms/base.py +171 -0
  286. app/shared/domain/transforms/cast_type.py +120 -0
  287. app/shared/domain/transforms/concat.py +103 -0
  288. app/shared/domain/transforms/conditional_assign.py +114 -0
  289. app/shared/domain/transforms/date_format.py +81 -0
  290. app/shared/domain/transforms/digits.py +77 -0
  291. app/shared/domain/transforms/drop_duplicates.py +94 -0
  292. app/shared/domain/transforms/fill_na.py +94 -0
  293. app/shared/domain/transforms/filter_rows.py +97 -0
  294. app/shared/domain/transforms/lookup.py +78 -0
  295. app/shared/domain/transforms/lower_case.py +70 -0
  296. app/shared/domain/transforms/map_value.py +90 -0
  297. app/shared/domain/transforms/math_expr.py +112 -0
  298. app/shared/domain/transforms/modulo.py +82 -0
  299. app/shared/domain/transforms/regex_extract.py +103 -0
  300. app/shared/domain/transforms/registry.py +95 -0
  301. app/shared/domain/transforms/replace.py +88 -0
  302. app/shared/domain/transforms/sort_rows.py +94 -0
  303. app/shared/domain/transforms/string_split.py +84 -0
  304. app/shared/domain/transforms/strip.py +73 -0
  305. app/shared/domain/transforms/substring.py +92 -0
  306. app/shared/domain/transforms/upper_case.py +70 -0
  307. app/shared/domain/transforms/weighted_sum.py +104 -0
  308. app/shared/domain/validation_constraints.py +69 -0
  309. app/shared/services/__init__.py +62 -0
  310. app/shared/services/ai/__init__.py +48 -0
  311. app/shared/services/ai/agent/__init__.py +40 -0
  312. app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
  313. app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
  314. app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
  315. app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
  316. app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
  317. app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
  318. app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
  319. app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
  320. app/shared/services/ai/agent/executor.py +554 -0
  321. app/shared/services/ai/agent/memory.py +256 -0
  322. app/shared/services/ai/agent/planner.py +282 -0
  323. app/shared/services/ai/agent/tool_registry.py +296 -0
  324. app/shared/services/ai/agent/tools/__init__.py +32 -0
  325. app/shared/services/ai/agent/tools/config_generate.py +133 -0
  326. app/shared/services/ai/agent/tools/config_refine.py +109 -0
  327. app/shared/services/ai/agent/tools/config_validate.py +403 -0
  328. app/shared/services/ai/agent/tools/merge_results.py +136 -0
  329. app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
  330. app/shared/services/ai/agent/tools/script_parse.py +359 -0
  331. app/shared/services/ai/agent/types.py +138 -0
  332. app/shared/services/ai/chat_agent_runner.py +596 -0
  333. app/shared/services/ai/chat_orchestrator.py +725 -0
  334. app/shared/services/ai/failure_messages.py +65 -0
  335. app/shared/services/ai/job_storage.py +274 -0
  336. app/shared/services/ai/migrate_service.py +550 -0
  337. app/shared/services/ai/streaming/__init__.py +65 -0
  338. app/shared/services/ai/streaming/event_journal.py +197 -0
  339. app/shared/services/ai/streaming/orchestrator.py +262 -0
  340. app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
  341. app/shared/services/ai/streaming/sse_response.py +148 -0
  342. app/shared/services/ai/streaming/types.py +73 -0
  343. app/shared/services/ai/types.py +213 -0
  344. app/shared/services/ai/utils.py +322 -0
  345. app/shared/services/diff/config_diff.py +320 -0
  346. app/shared/services/hardware.py +237 -0
  347. app/shared/services/llm/__init__.py +62 -0
  348. app/shared/services/llm/actions/__init__.py +42 -0
  349. app/shared/services/llm/actions/_canvas_validator.py +147 -0
  350. app/shared/services/llm/actions/_constraint_validator.py +401 -0
  351. app/shared/services/llm/actions/_regex_validator.py +85 -0
  352. app/shared/services/llm/actions/_schema_validator.py +82 -0
  353. app/shared/services/llm/actions/_settings_validator.py +76 -0
  354. app/shared/services/llm/actions/_transform_validator.py +78 -0
  355. app/shared/services/llm/actions/action_handlers.py +400 -0
  356. app/shared/services/llm/actions/action_parser.py +63 -0
  357. app/shared/services/llm/actions/action_processor.py +468 -0
  358. app/shared/services/llm/actions/action_validator.py +311 -0
  359. app/shared/services/llm/actions/diff_compute.py +195 -0
  360. app/shared/services/llm/actions/regex_handlers.py +284 -0
  361. app/shared/services/llm/actions/registry.py +336 -0
  362. app/shared/services/llm/actions/schema_handlers.py +335 -0
  363. app/shared/services/llm/actions/settings_handlers.py +180 -0
  364. app/shared/services/llm/actions/specs.py +259 -0
  365. app/shared/services/llm/actions/transform_handlers.py +279 -0
  366. app/shared/services/llm/actions/validation_types.py +147 -0
  367. app/shared/services/llm/cache/__init__.py +18 -0
  368. app/shared/services/llm/cache/response_cache.py +80 -0
  369. app/shared/services/llm/chat/__init__.py +35 -0
  370. app/shared/services/llm/chat/chat_system_prompt.py +561 -0
  371. app/shared/services/llm/chat/response_parser.py +296 -0
  372. app/shared/services/llm/config/__init__.py +45 -0
  373. app/shared/services/llm/config/crypto.py +135 -0
  374. app/shared/services/llm/config/loader.py +258 -0
  375. app/shared/services/llm/config/models.py +202 -0
  376. app/shared/services/llm/config/presets.py +128 -0
  377. app/shared/services/llm/config_generator.py +163 -0
  378. app/shared/services/llm/constraints/__init__.py +41 -0
  379. app/shared/services/llm/constraints/constraint_builder.py +177 -0
  380. app/shared/services/llm/constraints/constraint_deletion.py +84 -0
  381. app/shared/services/llm/constraints/constraint_id.py +174 -0
  382. app/shared/services/llm/constraints/frontend_instructions.py +333 -0
  383. app/shared/services/llm/constraints/inline_batch.py +279 -0
  384. app/shared/services/llm/discovery/__init__.py +35 -0
  385. app/shared/services/llm/discovery/scanner.py +181 -0
  386. app/shared/services/llm/generation/__init__.py +42 -0
  387. app/shared/services/llm/generation/agent_wiring.py +156 -0
  388. app/shared/services/llm/generation/config_builder.py +386 -0
  389. app/shared/services/llm/generation/errors.py +36 -0
  390. app/shared/services/llm/generation/existing_config.py +99 -0
  391. app/shared/services/llm/generation/profiler.py +279 -0
  392. app/shared/services/llm/generation/prompt_builder.py +199 -0
  393. app/shared/services/llm/generation/response_parser.py +144 -0
  394. app/shared/services/llm/generation/service.py +622 -0
  395. app/shared/services/llm/models.py +104 -0
  396. app/shared/services/llm/providers/__init__.py +48 -0
  397. app/shared/services/llm/providers/base.py +310 -0
  398. app/shared/services/llm/providers/cached_provider.py +90 -0
  399. app/shared/services/llm/providers/ollama.py +498 -0
  400. app/shared/services/llm/providers/openai.py +334 -0
  401. app/shared/services/llm/providers/registry.py +108 -0
  402. app/shared/services/llm/schema_resolver.py +141 -0
  403. app/shared/services/llm/suggestion_utils.py +219 -0
  404. app/shared/services/llm/validate_executor.py +163 -0
  405. app/shared/services/llm/yaml_io.py +406 -0
  406. app/shared/services/preview/__init__.py +18 -0
  407. app/shared/services/preview/loader.py +145 -0
  408. app/shared/services/preview/path_validation.py +138 -0
  409. app/shared/services/project_loader.py +57 -0
  410. app/shared/services/schema_inference.py +260 -0
  411. app/shared/services/schema_runtime_builder.py +120 -0
  412. app/shared/services/validation/__init__.py +52 -0
  413. app/shared/services/validation/chunked_loader.py +509 -0
  414. app/shared/services/validation/dag/__init__.py +35 -0
  415. app/shared/services/validation/dag/builder.py +141 -0
  416. app/shared/services/validation/dag/executor.py +225 -0
  417. app/shared/services/validation/dag/sorter.py +77 -0
  418. app/shared/services/validation/data_loader.py +231 -0
  419. app/shared/services/validation/engine.py +446 -0
  420. app/shared/services/validation/executor.py +1030 -0
  421. app/shared/services/validation/extractors.py +266 -0
  422. app/shared/services/validation/history.py +209 -0
  423. app/shared/services/validation/json_payload.py +136 -0
  424. app/shared/services/validation/loader.py +152 -0
  425. app/shared/services/validation/memory_monitor.py +179 -0
  426. app/shared/services/validation/postprocess.py +208 -0
  427. app/shared/services/validation/progress.py +64 -0
  428. app/shared/services/validation/report_export.py +222 -0
  429. app/shared/services/validation/resolver.py +180 -0
  430. app/shared/services/validation/service.py +418 -0
  431. app/shared/services/validation/types.py +238 -0
  432. app/shared/services/validation/validators/__init__.py +36 -0
  433. app/shared/services/validation/validators/adapter.py +182 -0
  434. app/shared/services/validation/validators/base.py +332 -0
  435. app/shared/services/validation/validators/composite.py +233 -0
  436. app/shared/services/validation/validators/date_logic.py +276 -0
  437. app/start_server.py +133 -0
  438. precis_cli-0.1.3.dist-info/METADATA +180 -0
  439. precis_cli-0.1.3.dist-info/RECORD +442 -0
  440. precis_cli-0.1.3.dist-info/WHEEL +5 -0
  441. precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
  442. precis_cli-0.1.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,80 @@
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
+ # backend/app/cli/shell/commands/config/base.py
17
+ """
18
+ @fileoverview Config 命令共享工具模块
19
+
20
+ 功能概述:
21
+ - 提供配置命令的共享模板常量
22
+ - 内置 project、constraint、pattern 配置模板
23
+
24
+ 架构设计:
25
+ - PROJECT_TEMPLATE/CONSTRAINT_TEMPLATE/PATTERNS_TEMPLATE: 字符串模板常量
26
+ """
27
+
28
+ # 配置模板
29
+ # 注意:模板中的 {project_name} 由 init.py 以字符串 replace 方式填充,
30
+ # 不要使用 str.format(正则模板含 {2,} 等花括号字面量会被误当替换字段)。
31
+ PROJECT_TEMPLATE = """# Precis 项目清单(V2 格式)
32
+ # 项目基本信息:id 为项目标识符,name 为显示名称
33
+ version: 2
34
+ project:
35
+ id: "{project_name}"
36
+ name: "{project_name}"
37
+
38
+ # Schema/约束/正则等资源按需登记在下列列表(path 相对项目根):
39
+ # schemas:
40
+ # - id: <uuid>
41
+ # path: schemas/<uuid>.schema.yaml
42
+ # constraints: []
43
+ # regex_nodes: []
44
+ # transforms: []
45
+ # manual_data: []
46
+ """
47
+
48
+ CONSTRAINT_TEMPLATE = """# Precis 约束配置文件
49
+ # 定义数据校验规则
50
+
51
+ constraints:
52
+ - name: unique_id
53
+ type: unique
54
+ columns:
55
+ - id
56
+ message: "ID 必须唯一"
57
+
58
+ - name: not_null_name
59
+ type: not_null
60
+ columns:
61
+ - name
62
+ message: "名称不能为空"
63
+ """
64
+
65
+ PATTERNS_TEMPLATE = """# Precis 正则模式配置文件
66
+ # 定义可复用的正则表达式
67
+
68
+ patterns:
69
+ - name: email
70
+ pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
71
+ description: "邮箱地址格式"
72
+
73
+ - name: phone
74
+ pattern: "^1[3-9]\\d{9}$"
75
+ description: "中国手机号码"
76
+
77
+ - name: id_card
78
+ pattern: "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)"
79
+ description: "身份证号码"
80
+ """
@@ -0,0 +1,246 @@
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
+ # backend/app/cli/shell/commands/config/check.py
17
+ """
18
+ @fileoverview 配置检查命令模块
19
+
20
+ 功能概述:
21
+ - 提供 config check 子命令检查项目 YAML 配置文件语法
22
+ - 支持单文件检查与全项目扫描
23
+ - 格式化 YAML 语法错误为友好提示(显示行号、代码片段、建议)
24
+
25
+ 架构设计:
26
+ - ConfigCheckCommand 继承 Command 基类
27
+ - execute() 根据参数决定单文件检查或全项目扫描
28
+ - _check_file(): 使用 yaml.safe_load 验证文件,返回 (是否有效, 错误信息)
29
+ - _format_yaml_error(): 将 YAML 解析错误转换为友好的中文提示
30
+
31
+ 输入示例:
32
+ config check
33
+ config check project.precis.yaml
34
+ config check --all
35
+
36
+ 输出示例:
37
+ CommandResult.ok("✓ 所有 5 个配置文件格式正确")
38
+ CommandResult.error("发现 2 个配置文件格式错误: ...")
39
+ """
40
+
41
+ import os
42
+
43
+ from rich.console import Console
44
+ from rich.syntax import Syntax
45
+
46
+ from app.cli.shared_services.config_ops import YamlCheckResult, check_yaml_syntax, find_config_file
47
+ from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
48
+ from app.cli.shell.formatter import _supports_unicode
49
+
50
+ _CHECK_MARK = "\u2713" if _supports_unicode() else "[OK]"
51
+ _CROSS_MARK = "\u2717" if _supports_unicode() else "[FAIL]"
52
+
53
+ _console = Console()
54
+
55
+
56
+ class ConfigCheckCommand(Command):
57
+ """检查配置命令。
58
+
59
+ 验证项目中的 YAML 配置文件语法是否正确。
60
+ """
61
+
62
+ def __init__(self) -> None:
63
+ super().__init__("check")
64
+
65
+ @property
66
+ def description(self) -> str:
67
+ return "检查配置文件的语法格式"
68
+
69
+ @property
70
+ def usage(self) -> str:
71
+ return "config check [config_file] [--all]"
72
+
73
+ def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
74
+ """执行配置检查命令。
75
+
76
+ Args:
77
+ args: 命令参数列表,可能包含文件名或 --all 标志
78
+ context: 项目上下文
79
+
80
+ Returns:
81
+ 检查结果,成功表示所有文件格式正确
82
+ """
83
+ project_path = context.project_path
84
+ if project_path is None:
85
+ return CommandResult.error("未打开项目,请先使用 'open <path>' 命令打开项目")
86
+
87
+ # 解析参数
88
+ show_all = "--all" in args
89
+ if show_all:
90
+ args = [a for a in args if a != "--all"]
91
+
92
+ # 如果指定了文件,只验证该文件
93
+ if args:
94
+ config_file = args[0]
95
+ config_path = find_config_file(project_path, config_file)
96
+
97
+ if not config_path:
98
+ return CommandResult.error(f"配置文件不存在: {config_file}")
99
+
100
+ # 显示相对路径
101
+ rel_path = os.path.relpath(config_path, project_path)
102
+ return self._check_single_file(config_path, rel_path)
103
+
104
+ # 否则验证所有 YAML 文件
105
+ all_files = []
106
+ for root, _, files in os.walk(project_path):
107
+ # 跳过隐藏目录
108
+ if any(part.startswith(".") for part in root.split(os.sep)):
109
+ continue
110
+ for f in files:
111
+ if f.endswith((".yaml", ".yml")):
112
+ rel_path = os.path.relpath(os.path.join(root, f), project_path)
113
+ all_files.append(rel_path)
114
+
115
+ results = []
116
+ valid_count = 0
117
+ invalid_count = 0
118
+
119
+ for config_file in sorted(all_files):
120
+ config_path = os.path.join(project_path, config_file)
121
+ result, error_msg = self._check_file(config_path)
122
+ if result:
123
+ valid_count += 1
124
+ if show_all:
125
+ results.append(f"✓ {config_file}")
126
+ else:
127
+ invalid_count += 1
128
+ results.append(f"✗ {config_file}")
129
+ if error_msg:
130
+ # 显示简化的错误信息(位置和问题)
131
+ error_lines = error_msg.split("\n")
132
+ # 提取位置行、代码片段、问题行
133
+ for line in error_lines:
134
+ if line.startswith("位置:") or line.startswith("问题:") or line.startswith("提示:"):
135
+ results.append(f" {line}")
136
+ # 显示代码片段的指示行(包含 >>> 的那一行)
137
+ elif ">>>" in line:
138
+ results.append(f" {line}")
139
+ # 也显示下一行(箭头)
140
+ idx = error_lines.index(line)
141
+ if idx + 1 < len(error_lines) and "^" in error_lines[idx + 1]:
142
+ results.append(f" {error_lines[idx + 1]}")
143
+
144
+ if invalid_count == 0:
145
+ return CommandResult.ok(f"✓ 所有 {valid_count} 个配置文件格式正确")
146
+
147
+ # 有错误时只显示错误文件(除非指定 --all)
148
+ output_lines = [f"[bold]\n发现 {invalid_count} 个配置文件格式错误:[/bold]"]
149
+ output_lines.extend(results)
150
+ if show_all:
151
+ output_lines.append(f"\n总计: {valid_count} 个有效, {invalid_count} 个无效")
152
+ else:
153
+ output_lines.append(f"\n(共检查 {valid_count + invalid_count} 个文件,使用 --all 查看全部)")
154
+
155
+ return CommandResult.error("\n".join(output_lines))
156
+
157
+ def _check_single_file(self, config_path: str, config_file: str) -> CommandResult:
158
+ result, error_msg = self._check_file(config_path)
159
+ if result:
160
+ return CommandResult.ok(f"{_CHECK_MARK} {config_file} 格式正确")
161
+ else:
162
+ output = f"{_CROSS_MARK} {config_file} 格式错误"
163
+ if error_msg:
164
+ output += f"\n\n错误详情:\n{error_msg}"
165
+ return CommandResult.error(output)
166
+
167
+ def _check_file(self, config_path: str) -> tuple[bool, str | None]:
168
+ """检查文件,返回 (是否有效, 错误信息)。
169
+
170
+ 语法检查与错误收集委托 shared_services.check_yaml_syntax(纯逻辑),
171
+ rich.Syntax 高亮等渲染留在本类 _render_yaml_error。
172
+
173
+ Args:
174
+ config_path: 文件的完整路径
175
+
176
+ Returns:
177
+ tuple: (bool, str|None) - 是否有效,错误信息(如果无效)
178
+ """
179
+ filename = os.path.basename(config_path)
180
+
181
+ try:
182
+ with open(config_path, encoding="utf-8") as f:
183
+ content = f.read()
184
+ except Exception as e:
185
+ return False, f"读取文件失败: {e}"
186
+
187
+ # 语法检查 + 错误收集委托 shared_services(CLI/TUI 同源)
188
+ result = check_yaml_syntax(content, filename)
189
+ if result.valid:
190
+ return True, None
191
+ # 渲染部分留在 CLI 层(rich.Syntax 高亮是 CLI 专属 UI)
192
+ return False, self._render_yaml_error(result, content)
193
+
194
+ def _render_yaml_error(self, result: YamlCheckResult, content: str) -> str:
195
+ """渲染 YAML 检查结果为友好提示(CLI 专属 UI)。
196
+
197
+ 纯逻辑(错误位置/问题/建议收集)已由 shared_services.check_yaml_syntax 完成,
198
+ 本方法只负责 rich.Syntax 高亮与文本拼接。
199
+
200
+ Args:
201
+ result: check_yaml_syntax 返回的纯逻辑结果
202
+ content: 文件原始内容(用于 rich.Syntax 高亮取行)
203
+
204
+ Returns:
205
+ 格式化的错误提示字符串
206
+ """
207
+ lines: list[str] = []
208
+ content_lines = content.split("\n")
209
+
210
+ # 位置
211
+ if result.line_no is not None:
212
+ lines.append(f"位置: 第 {result.line_no} 行")
213
+
214
+ # 使用 rich.syntax 高亮代码片段(行号 0-based 用于 highlight_lines)
215
+ error_line_no = result.line_no - 1 # 转回 0-based
216
+ context_start = max(0, error_line_no - 2)
217
+ context_end = min(len(content_lines), error_line_no + 3)
218
+ snippet = "\n".join(content_lines[context_start:context_end])
219
+ start_line = context_start + 1
220
+
221
+ lines.append("")
222
+ lines.append("代码片段:")
223
+ syntax = Syntax(
224
+ snippet,
225
+ "yaml",
226
+ start_line=start_line,
227
+ line_numbers=True,
228
+ highlight_lines={error_line_no + 1},
229
+ theme="monokai",
230
+ )
231
+ with _console.capture() as capture:
232
+ _console.print(syntax)
233
+ lines.append(capture.get())
234
+ else:
235
+ lines.append("位置: 未知")
236
+
237
+ # 问题(已由 check_yaml_syntax 简化,可能含上下文)
238
+ if result.problem:
239
+ lines.append(f"问题: {result.problem}")
240
+
241
+ # 修复建议
242
+ if result.hint:
243
+ lines.append("")
244
+ lines.append(f"提示: {result.hint}")
245
+
246
+ return "\n".join(lines)
@@ -0,0 +1,148 @@
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
+ # backend/app/cli/shell/commands/config/edit.py
17
+ """
18
+ @fileoverview 配置编辑命令模块
19
+
20
+ 功能概述:
21
+ - 提供 config edit 子命令调用系统编辑器修改配置文件
22
+ - 自动检测 EDITOR/VISUAL 环境变量,支持跨平台编辑器
23
+ - 未指定文件时默认编辑 project.precis.yaml
24
+
25
+ 架构设计:
26
+ - ConfigEditCommand 继承 Command 基类
27
+ - _get_editor(): 按优先级查找合适的编辑器
28
+ - 使用 subprocess.run 启动编辑器进程,等待用户关闭
29
+
30
+ 输入示例:
31
+ config edit
32
+ config edit constraints.yaml
33
+
34
+ 输出示例:
35
+ CommandResult.ok("已保存配置文件: project.precis.yaml")
36
+ CommandResult.error("未找到编辑器: notepad")
37
+ """
38
+
39
+ import os
40
+ import subprocess
41
+ import sys
42
+
43
+ from app.cli.shared_services.config_ops import find_config_file
44
+ from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
45
+ from app.cli.shell.exceptions import EditorError
46
+
47
+
48
+ class ConfigEditCommand(Command):
49
+ """编辑配置命令。
50
+
51
+ 调用系统默认编辑器打开项目配置文件。
52
+ """
53
+
54
+ def __init__(self) -> None:
55
+ super().__init__("edit")
56
+
57
+ @property
58
+ def description(self) -> str:
59
+ return "使用编辑器编辑项目的配置文件"
60
+
61
+ @property
62
+ def usage(self) -> str:
63
+ return "config edit [config_file]"
64
+
65
+ def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
66
+ """执行编辑命令。
67
+
68
+ Args:
69
+ args: 命令参数列表,可能包含要编辑的文件名
70
+ context: 项目上下文
71
+
72
+ Returns:
73
+ 编辑成功或失败的结果
74
+
75
+ Raises:
76
+ EditorError: 当编辑器发生异常时抛出
77
+ """
78
+ project_path = context.project_path
79
+ if project_path is None:
80
+ return CommandResult.error("未打开项目,请先使用 'open <path>' 命令打开项目")
81
+
82
+ config_file = args[0] if args else "project.precis.yaml"
83
+ config_path = find_config_file(project_path, config_file)
84
+
85
+ if not config_path:
86
+ # 列出可用的配置文件
87
+ available = []
88
+ for root, _, files in os.walk(project_path):
89
+ # 跳过隐藏目录
90
+ if any(part.startswith(".") for part in root.split(os.sep)):
91
+ continue
92
+ for f in files:
93
+ if f.endswith((".yaml", ".yml")):
94
+ rel_path = os.path.relpath(os.path.join(root, f), project_path)
95
+ available.append(rel_path)
96
+
97
+ return CommandResult.error(
98
+ f"配置文件不存在: {config_file}\n"
99
+ f"可用配置文件: {', '.join(available[:10]) if available else '无'}"
100
+ f"{'...' if len(available) > 10 else ''}"
101
+ )
102
+
103
+ editor = self._get_editor()
104
+
105
+ try:
106
+ result = subprocess.run(
107
+ [editor, config_path],
108
+ cwd=project_path,
109
+ )
110
+
111
+ if result.returncode != 0:
112
+ return CommandResult.error(f"编辑器退出码: {result.returncode}")
113
+
114
+ return CommandResult.ok(f"已保存配置文件: {config_file}")
115
+
116
+ except FileNotFoundError:
117
+ return CommandResult.error(f"未找到编辑器: {editor}")
118
+ except Exception as e:
119
+ raise EditorError(str(e))
120
+
121
+ def _get_editor(self) -> str:
122
+ """获取编辑器路径。
123
+
124
+ 按以下优先级查找:
125
+ 1. EDITOR 环境变量
126
+ 2. VISUAL 环境变量
127
+ 3. Windows: notepad
128
+ 4. Linux/macOS: vim, nano, vi
129
+
130
+ Returns:
131
+ 编辑器可执行文件路径或名称
132
+ """
133
+ for env_var in ["EDITOR", "VISUAL"]:
134
+ editor = os.environ.get(env_var)
135
+ if editor:
136
+ return editor
137
+
138
+ if sys.platform == "win32":
139
+ return "notepad"
140
+
141
+ for editor in ["vim", "nano", "vi"]:
142
+ try:
143
+ subprocess.run(["which", editor], capture_output=True, check=True)
144
+ return editor
145
+ except subprocess.CalledProcessError:
146
+ continue
147
+
148
+ return "vim"
@@ -0,0 +1,111 @@
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
+ # backend/app/cli/shell/commands/config/get.py
17
+ """
18
+ @fileoverview 配置获取命令模块
19
+
20
+ 功能概述:
21
+ - 提供 config get 子命令按点号路径获取配置项值
22
+ - 支持字典与列表的 YAML 格式化输出
23
+ - 使用点号路径访问嵌套配置,如 project.name
24
+
25
+ 架构设计:
26
+ - ConfigGetCommand 继承 Command 基类
27
+ - 按点号分割路径,逐级访问字典,遇到不存在的键则报错
28
+
29
+ 输入示例:
30
+ config get project.precis.yaml project.name
31
+
32
+ 输出示例:
33
+ CommandResult.ok("project.name = My Project")
34
+ CommandResult.error("配置项不存在: project.unknown")
35
+ """
36
+
37
+ import os
38
+
39
+ import yaml
40
+
41
+ from app.cli.shared_services.config_ops import get_by_dotpath
42
+ from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
43
+
44
+
45
+ class ConfigGetCommand(Command):
46
+ """获取配置项命令。
47
+
48
+ 按点号路径从 YAML 配置文件中读取指定项的值。
49
+ """
50
+
51
+ def __init__(self) -> None:
52
+ super().__init__("get")
53
+
54
+ @property
55
+ def description(self) -> str:
56
+ return "获取配置项的值(支持点号路径)"
57
+
58
+ @property
59
+ def usage(self) -> str:
60
+ return "config get <config_file> <key_path>"
61
+
62
+ def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
63
+ """执行获取配置项命令。
64
+
65
+ Args:
66
+ args: 命令参数列表,需要包含配置文件名和点号路径
67
+ context: 项目上下文
68
+
69
+ Returns:
70
+ 配置项值或错误提示
71
+ """
72
+ project_path = context.project_path
73
+ if project_path is None:
74
+ return CommandResult.error("未打开项目,请先使用 'open <path>' 命令打开项目")
75
+
76
+ if len(args) < 2:
77
+ return CommandResult.error(
78
+ "用法: config get <config_file> <key_path>\n示例: config get project.precis.yaml project.name"
79
+ )
80
+
81
+ config_file = args[0]
82
+ key_path = args[1]
83
+ config_path = os.path.join(project_path, config_file)
84
+
85
+ if not os.path.isfile(config_path):
86
+ return CommandResult.error(f"配置文件不存在: {config_file}")
87
+
88
+ try:
89
+ with open(config_path, encoding="utf-8") as f:
90
+ data = yaml.safe_load(f)
91
+
92
+ if data is None:
93
+ return CommandResult.error("配置文件为空")
94
+
95
+ # 按点号路径查找值(委托 shared_services 纯逻辑,CLI/TUI 同源)
96
+ found, value = get_by_dotpath(data, key_path)
97
+ if not found:
98
+ return CommandResult.error(f"配置项不存在: {key_path}")
99
+
100
+ # 格式化输出
101
+ if isinstance(value, (dict, list)):
102
+ formatted = yaml.dump(value, allow_unicode=True, default_flow_style=False)
103
+ else:
104
+ formatted = str(value)
105
+
106
+ return CommandResult.ok(f"{key_path} = {formatted}")
107
+
108
+ except yaml.YAMLError as e:
109
+ return CommandResult.error(f"YAML 解析失败: {e}")
110
+ except Exception as e:
111
+ return CommandResult.error(f"读取失败: {e}")