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,158 @@
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/ai/switch.py
17
+ """
18
+ @fileoverview AI Provider 切换命令模块
19
+
20
+ 功能概述:
21
+ - 提供 ai switch 子命令切换默认 AI Provider
22
+ - 支持交互式菜单选择与直接指定 Provider ID
23
+ - 验证目标 Provider 是否有 API Key,无 Key 则拒绝切换
24
+
25
+ 架构设计:
26
+ - AISwitchCommand 继承 Command 基类
27
+ - execute() 根据是否有参数决定交互式切换或直接切换
28
+ - 通过 CLI 配置管理器设置默认 Provider
29
+
30
+ 输入示例:
31
+ ai switch
32
+ ai switch openai
33
+
34
+ 输出示例:
35
+ CommandResult.ok("已切换 Provider")
36
+ CommandResult.error("Provider 'xxx' 未配置")
37
+ """
38
+
39
+ from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
40
+ from app.cli.shell.config_storage import get_cli_config
41
+ from app.cli.shell.formatter import Formatter
42
+ from app.cli.shell.interactive_menu import InteractiveMenu
43
+
44
+
45
+ class AISwitchCommand(Command):
46
+ """切换默认 AI Provider 命令。
47
+
48
+ 支持交互式菜单或直接指定 Provider ID 切换。
49
+ """
50
+
51
+ def __init__(self) -> None:
52
+ super().__init__("switch")
53
+ self._cli_config = get_cli_config()
54
+
55
+ @property
56
+ def description(self) -> str:
57
+ return "切换默认 AI Provider"
58
+
59
+ @property
60
+ def usage(self) -> str:
61
+ return "ai switch <provider_id>"
62
+
63
+ @property
64
+ def help_text(self) -> str:
65
+ return """
66
+ 用法: ai switch <provider_id>
67
+
68
+ 示例:
69
+ ai switch openai # 切换到 OpenAI
70
+ ai switch kimi # 切换到 Kimi
71
+
72
+ 说明:
73
+ 切换到已配置的 Provider。
74
+ 使用 'ai status' 查看可用的 Provider。
75
+ """.strip()
76
+
77
+ def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
78
+ """执行切换命令。
79
+
80
+ Args:
81
+ args: 命令参数列表,可能包含 Provider ID
82
+ context: 项目上下文
83
+
84
+ Returns:
85
+ 切换成功或失败的结果
86
+ """
87
+ providers = self._cli_config.list_providers()
88
+
89
+ if not providers:
90
+ return CommandResult.error("没有已配置的 Provider\n请先使用 'setup' 命令进行配置")
91
+
92
+ if not args:
93
+ # 无参数时进入交互式选择(支持方向键)
94
+ return self._interactive_switch(providers)
95
+
96
+ # 4.4: 大小写不敏感匹配——原实现把参数强转小写后查存储原名(含大写的
97
+ # provider 恒查不到),交互菜单走原值正常,参数路径自相矛盾
98
+ raw_id = args[0]
99
+ provider_id = next(
100
+ (p.id for p in providers if p.id.lower() == raw_id.lower()),
101
+ raw_id,
102
+ )
103
+ return self._do_switch(provider_id)
104
+
105
+ def _interactive_switch(self, providers: list) -> CommandResult:
106
+ """交互式切换 Provider(支持方向键导航)。
107
+
108
+ 显示菜单让用户选择要切换到的 Provider。
109
+
110
+ Args:
111
+ providers: 已配置的 Provider 列表
112
+
113
+ Returns:
114
+ 切换结果或取消结果
115
+ """
116
+ print(Formatter.header("\n切换 AI Provider"))
117
+
118
+ active = self._cli_config.get_active_provider()
119
+
120
+ # 创建菜单
121
+ menu = InteractiveMenu("请选择要切换的 Provider:")
122
+
123
+ for p in providers:
124
+ status = ""
125
+ if active and active.id == p.id:
126
+ status = " [当前]"
127
+ key_status = "[有密钥]" if p.api_key else "[无密钥]"
128
+ label = f"{p.name} {key_status}{status}"
129
+ menu.add_item(p.id, label, f"模型: {p.model}")
130
+
131
+ provider_id = menu.show()
132
+
133
+ if provider_id is None:
134
+ return CommandResult.ok("已取消")
135
+
136
+ return self._do_switch(provider_id)
137
+
138
+ def _do_switch(self, provider_id: str) -> CommandResult:
139
+ """执行切换操作。
140
+
141
+ 验证 Provider 存在,然后设置为默认。
142
+
143
+ Args:
144
+ provider_id: 要切换到的 Provider ID
145
+
146
+ Returns:
147
+ 切换成功或失败的结果
148
+ """
149
+ provider = self._cli_config.get_provider(provider_id)
150
+ if not provider:
151
+ return CommandResult.error(f"Provider '{provider_id}' 未配置\n请先使用 'setup {provider_id}' 进行配置")
152
+
153
+ if self._cli_config.set_active_provider(provider_id):
154
+ print(Formatter.success(f"\n[*] 已切换到 {provider.name}"))
155
+ print(Formatter.info(f" 模型: {provider.model}"))
156
+ return CommandResult.ok("已切换 Provider")
157
+ else:
158
+ return CommandResult.error("切换失败")
@@ -0,0 +1,52 @@
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/ai/utils.py
17
+ """
18
+ @fileoverview AI 命令共享工具模块
19
+
20
+ 功能概述:
21
+ - 提供 AI 命令的通用工具函数
22
+ - 支持 API Key 脱敏
23
+
24
+ 架构设计:
25
+ - mask_api_key: 对 API Key 进行脱敏处理,保护密钥安全
26
+
27
+ 输入示例:
28
+ mask_api_key("sk-abcdef1234567890")
29
+ resolve_table_name("users", "/path/to/project")
30
+ confirm_actions(actions, reply)
31
+
32
+ 输出示例:
33
+ "sk-abc...7890"
34
+ "users_table_id"
35
+ True/False
36
+ """
37
+
38
+
39
+ def mask_api_key(api_key: str) -> str:
40
+ """脱敏 API Key,只显示前 6 位和后 4 位。
41
+
42
+ 如果密钥过短,则显示掩码 ***。
43
+
44
+ Args:
45
+ api_key: 原始 API Key 字符串
46
+
47
+ Returns:
48
+ 脱敏后的字符串
49
+ """
50
+ if not api_key or len(api_key) < 10:
51
+ return "***"
52
+ return f"{api_key[:6]}...{api_key[-4:]}"
@@ -0,0 +1,343 @@
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/base.py
17
+ """
18
+ @fileoverview CLI Shell 命令基类模块
19
+
20
+ 功能概述:
21
+ - 定义所有 Shell 命令的抽象基类 Command,统一命令的名称、别名、描述与执行接口
22
+ - 提供命令执行结果数据类 CommandResult,支持成功、错误、退出三种结果状态
23
+ - 提供命令执行上下文 CommandContext 与项目上下文 ProjectContext,用于保存运行时状态
24
+
25
+ 架构设计:
26
+ - Command 抽象基类规范命令的基本属性与接口:
27
+ - name: 命令名称
28
+ - aliases: 命令别名列表
29
+ - description: 命令描述(抽象属性,子类必须实现)
30
+ - usage: 命令用法示例
31
+ - help_text: 自动生成完整帮助文本
32
+ - execute(): 执行命令的核心方法(抽象方法,子类必须实现)
33
+ - add_subcommand()/get_subcommand(): 支持子命令注册与查找
34
+ - CommandResult 使用静态工厂方法 ok()/error()/exit() 快速创建结果对象
35
+ - ProjectContext 继承 CommandContext,额外维护当前打开的项目路径和配置
36
+ - 通过 @property.setter 自动同步到父类的状态字典,确保数据一致性
37
+
38
+ 输入示例:
39
+ result = CommandResult.ok("操作成功", data={"key": "value"})
40
+ result = CommandResult.error("文件不存在")
41
+ result = CommandResult.exit("再见")
42
+
43
+ 输出示例:
44
+ CommandResult(success=True, message="操作成功", data={'key': 'value'}, should_exit=False)
45
+ CommandResult(success=False, message="文件不存在", data=None, should_exit=False)
46
+ CommandResult(success=True, message="再见", data=None, should_exit=True)
47
+ """
48
+
49
+ from abc import ABC, abstractmethod
50
+ from dataclasses import dataclass
51
+ from typing import Any, Optional
52
+
53
+
54
+ @dataclass
55
+ class CommandResult:
56
+ """命令执行结果数据类。
57
+
58
+ 封装命令执行后的返回信息,包括是否成功、提示消息、
59
+ 附加数据以及是否需要退出 Shell。
60
+
61
+ Attributes:
62
+ success: 命令是否执行成功
63
+ message: 返回给用户的提示消息
64
+ data: 附加数据字典,可选
65
+ should_exit: 是否触发 Shell 退出,默认为 False
66
+ exit_code: 显式进程退出码(仅单发模式生效),默认 None 表示由调用方
67
+ 按 success 推导(成功 0 / 失败 1)。用于区分"校验发现数据违规"
68
+ (1)与"工具自身错误"(2)等不同失败类别
69
+ """
70
+
71
+ success: bool
72
+ message: str
73
+ data: dict[str, Any] | None = None
74
+ should_exit: bool = False
75
+ exit_code: int | None = None
76
+
77
+ @staticmethod
78
+ def ok(message: str, data: dict[str, Any] | None = None) -> "CommandResult":
79
+ """创建成功结果。
80
+
81
+ Args:
82
+ message: 成功的提示消息
83
+ data: 可选的附加数据
84
+
85
+ Returns:
86
+ 一个表示成功的 CommandResult 实例
87
+ """
88
+ return CommandResult(success=True, message=message, data=data)
89
+
90
+ @staticmethod
91
+ def error(message: str, data: dict[str, Any] | None = None, exit_code: int | None = None) -> "CommandResult":
92
+ """创建错误结果。
93
+
94
+ Args:
95
+ message: 错误的提示消息
96
+ data: 可选的附加数据
97
+ exit_code: 可选的显式进程退出码(仅单发模式生效),
98
+ 如工具自身错误(参数错误/文件不存在)传 2
99
+
100
+ Returns:
101
+ 一个表示失败的 CommandResult 实例
102
+ """
103
+ return CommandResult(success=False, message=message, data=data, exit_code=exit_code)
104
+
105
+ @staticmethod
106
+ def exit(message: str = "再见!") -> "CommandResult":
107
+ """创建退出结果。
108
+
109
+ 当返回此结果时,Shell 主循环检测到 should_exit=True 后会终止运行。
110
+
111
+ Args:
112
+ message: 退出前的告别消息,默认为 "再见!"
113
+
114
+ Returns:
115
+ 一个触发退出的 CommandResult 实例
116
+ """
117
+ return CommandResult(success=True, message=message, should_exit=True)
118
+
119
+
120
+ class Command(ABC):
121
+ """命令抽象基类。
122
+
123
+ 所有 CLI 命令都应继承此类并实现 execute() 方法。
124
+ 子类需要提供 name(命令名)、description(描述)以及 execute(执行逻辑)。
125
+
126
+ Attributes:
127
+ name: 命令名称,用户在 Shell 中输入的标识
128
+ aliases: 命令别名列表,用于简化输入
129
+ _subcommands: 子命令字典,键为子命令名,值为子命令实例
130
+ """
131
+
132
+ def __init__(self, name: str, aliases: list[str] | None = None):
133
+ """初始化命令。
134
+
135
+ Args:
136
+ name: 命令名称
137
+ aliases: 命令别名列表,默认为空列表
138
+ """
139
+ self.name = name
140
+ self.aliases = aliases or []
141
+ # 子命令字典:用于聚合命令模式,如 project 命令包含 open/status 子命令
142
+ self._subcommands: dict[str, Command] = {}
143
+
144
+ @property
145
+ @abstractmethod
146
+ def description(self) -> str:
147
+ """命令描述(抽象属性)。
148
+
149
+ 子类必须实现此属性,返回简短的命令说明文字。
150
+
151
+ Returns:
152
+ 命令描述字符串
153
+ """
154
+ pass
155
+
156
+ @property
157
+ def usage(self) -> str:
158
+ """命令用法示例。
159
+
160
+ Returns:
161
+ 默认返回命令名称,子类可覆盖提供更详细的用法说明
162
+ """
163
+ return self.name
164
+
165
+ @property
166
+ def help_text(self) -> str:
167
+ """完整的帮助文本。
168
+
169
+ 自动生成包含用法、描述和别名的帮助信息。
170
+
171
+ Returns:
172
+ 格式化的多行帮助文本
173
+ """
174
+ lines = [
175
+ f"用法: {self.usage}",
176
+ "",
177
+ self.description,
178
+ ]
179
+ if self.aliases:
180
+ lines.append(f"别名: {', '.join(self.aliases)}")
181
+ return "\n".join(lines)
182
+
183
+ @abstractmethod
184
+ def execute(self, args: list[str], context: "ProjectContext") -> CommandResult:
185
+ """执行命令(抽象方法)。
186
+
187
+ 子类必须实现此方法,包含命令的核心业务逻辑。
188
+
189
+ Args:
190
+ args: 命令参数列表(按空格分割后的字符串数组)
191
+ context: 项目上下文,包含共享状态和项目信息
192
+
193
+ Returns:
194
+ 命令执行结果(CommandResult)
195
+ """
196
+ pass
197
+
198
+ def add_subcommand(self, name: str, command: "Command") -> None:
199
+ """添加子命令。
200
+
201
+ 用于聚合命令模式,例如 project 命令包含 open/status/history 等子命令。
202
+
203
+ Args:
204
+ name: 子命令名称
205
+ command: 子命令实例
206
+ """
207
+ self._subcommands[name] = command
208
+
209
+ def get_subcommand(self, name: str) -> Optional["Command"]:
210
+ """获取子命令。
211
+
212
+ Args:
213
+ name: 子命令名称
214
+
215
+ Returns:
216
+ 子命令实例,如果不存在则返回 None
217
+ """
218
+ return self._subcommands.get(name)
219
+
220
+ def list_subcommands(self) -> list[str]:
221
+ """列出所有已注册的子命令名称。
222
+
223
+ Returns:
224
+ 子命令名称列表
225
+ """
226
+ return list(self._subcommands.keys())
227
+
228
+
229
+ class CommandContext:
230
+ """命令执行上下文。
231
+
232
+ 提供命令执行期间需要访问的共享状态。
233
+ 本质上是一个键值对容器,允许不同命令之间传递数据。
234
+
235
+ 使用示例:
236
+ context.set("username", "admin")
237
+ value = context.get("username") # "admin"
238
+ """
239
+
240
+ def __init__(self) -> None:
241
+ # 内部状态字典,存储所有上下文键值对
242
+ self._state: dict[str, Any] = {}
243
+
244
+ def set(self, key: str, value: Any) -> None:
245
+ """设置上下文值。
246
+
247
+ Args:
248
+ key: 键名
249
+ value: 任意类型的值
250
+ """
251
+ self._state[key] = value
252
+
253
+ def get(self, key: str, default: Any = None) -> Any:
254
+ """获取上下文值。
255
+
256
+ Args:
257
+ key: 键名
258
+ default: 键不存在时返回的默认值
259
+
260
+ Returns:
261
+ 键对应的值,如果键不存在则返回 default
262
+ """
263
+ return self._state.get(key, default)
264
+
265
+ def has(self, key: str) -> bool:
266
+ """检查上下文是否存在指定键。
267
+
268
+ Args:
269
+ key: 键名
270
+
271
+ Returns:
272
+ 如果键存在于上下文中则返回 True,否则返回 False
273
+ """
274
+ return key in self._state
275
+
276
+
277
+ class ProjectContext(CommandContext):
278
+ """项目上下文,扩展命令上下文。
279
+
280
+ 在 CommandContext 的基础上,增加了项目相关的专用属性:
281
+ - project_path: 当前打开的项目目录路径
282
+ - project_config: 当前项目的配置字典(通常从 project.precis.yaml 解析)
283
+
284
+ 通过 @property.setter 设置 project_path 或 project_config 时,
285
+ 会自动同步到父类的状态字典中,确保数据一致性。
286
+ """
287
+
288
+ def __init__(self) -> None:
289
+ super().__init__()
290
+ self._project_path: str | None = None
291
+ self._project_config: dict[str, Any] | None = None
292
+
293
+ @property
294
+ def project_path(self) -> str | None:
295
+ """当前项目路径。
296
+
297
+ Returns:
298
+ 已打开项目的绝对路径,未打开则返回 None
299
+ """
300
+ return self._project_path
301
+
302
+ @project_path.setter
303
+ def project_path(self, path: str | None) -> None:
304
+ """设置当前项目路径。
305
+
306
+ 同时自动同步到上下文字典中,便于统一访问。
307
+
308
+ Args:
309
+ path: 项目目录的绝对路径,或 None 表示关闭项目
310
+ """
311
+ self._project_path = path
312
+ self.set("project_path", path)
313
+
314
+ @property
315
+ def project_config(self) -> dict[str, Any] | None:
316
+ """当前项目配置。
317
+
318
+ Returns:
319
+ 项目配置字典(通常包含 project、settings、schemas 等字段),
320
+ 如果未加载则返回 None
321
+ """
322
+ return self._project_config
323
+
324
+ @project_config.setter
325
+ def project_config(self, config: dict[str, Any] | None) -> None:
326
+ """设置当前项目配置。
327
+
328
+ 同时自动同步到上下文字典中。
329
+
330
+ Args:
331
+ config: 项目配置字典,或 None 表示清空配置
332
+ """
333
+ self._project_config = config
334
+ self.set("project_config", config)
335
+
336
+ @property
337
+ def is_project_open(self) -> bool:
338
+ """是否已打开项目。
339
+
340
+ Returns:
341
+ 如果 project_path 不为 None,说明已打开项目,返回 True
342
+ """
343
+ return self._project_path is not None
@@ -0,0 +1,131 @@
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 CLI Shell 配置管理命令模块入口
18
+
19
+ 功能概述:
20
+ - 提供 config 命令聚合配置相关子命令
21
+ - 支持 show、edit、list、init、get、set、check 子命令分发
22
+
23
+ 架构设计:
24
+ - ConfigCommand 作为聚合命令,包含多个子命令实例
25
+ - 使用字典映射子命令名到实例,实现快速分发
26
+ - execute() 方法负责参数校验和子命令路由
27
+
28
+ 输入示例:
29
+ config show
30
+ config get project.precis.yaml project.name
31
+
32
+ 输出示例:
33
+ 子命令的执行结果
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ from app.cli.shell.commands.base import Command, CommandResult, ProjectContext
39
+ from app.cli.shell.commands.config.check import ConfigCheckCommand
40
+ from app.cli.shell.commands.config.edit import ConfigEditCommand
41
+ from app.cli.shell.commands.config.get import ConfigGetCommand
42
+ from app.cli.shell.commands.config.init import ConfigInitCommand
43
+ from app.cli.shell.commands.config.inspect import ConfigInspectCommand
44
+ from app.cli.shell.commands.config.list import ConfigListCommand
45
+ from app.cli.shell.commands.config.set import ConfigSetCommand
46
+ from app.cli.shell.commands.config.show import ConfigShowCommand
47
+
48
+
49
+ class ConfigCommand(Command):
50
+ """配置管理命令。
51
+
52
+ 聚合所有配置相关的子命令,包括查看、编辑、列表、初始化、获取、设置、检查。
53
+ """
54
+
55
+ def __init__(self) -> None:
56
+ super().__init__("config")
57
+ self._show_cmd = ConfigShowCommand()
58
+ self._edit_cmd = ConfigEditCommand()
59
+ self._list_cmd = ConfigListCommand()
60
+ self._init_cmd = ConfigInitCommand()
61
+ self._get_cmd = ConfigGetCommand()
62
+ self._set_cmd = ConfigSetCommand()
63
+ self._check_cmd = ConfigCheckCommand()
64
+ self._inspect_cmd = ConfigInspectCommand()
65
+
66
+ # 注册子命令
67
+ self.add_subcommand("show", self._show_cmd)
68
+ self.add_subcommand("edit", self._edit_cmd)
69
+ self.add_subcommand("list", self._list_cmd)
70
+ self.add_subcommand("init", self._init_cmd)
71
+ self.add_subcommand("get", self._get_cmd)
72
+ self.add_subcommand("set", self._set_cmd)
73
+ self.add_subcommand("check", self._check_cmd)
74
+ self.add_subcommand("inspect", self._inspect_cmd)
75
+
76
+ @property
77
+ def description(self) -> str:
78
+ return "管理项目配置文件"
79
+
80
+ @property
81
+ def usage(self) -> str:
82
+ return "config <show|edit|list|init|get|set|check|inspect>"
83
+
84
+ def execute(self, args: list[str], context: ProjectContext) -> CommandResult:
85
+ """执行配置管理命令。
86
+
87
+ 校验参数并分发给对应的子命令执行。
88
+
89
+ Args:
90
+ args: 命令参数列表,第一个元素为子命令名
91
+ context: 项目上下文
92
+
93
+ Returns:
94
+ 子命令执行结果或错误提示
95
+ """
96
+ if not args:
97
+ return CommandResult.error(
98
+ "请指定子命令: show, edit, list, init, get, set, check, inspect\n"
99
+ "用法: config <subcommand> [options]\n\n"
100
+ "子命令说明:\n"
101
+ " show - 显示配置文件内容\n"
102
+ " edit - 编辑配置文件\n"
103
+ " list - 列出所有配置文件\n"
104
+ " init - 初始化新配置文件\n"
105
+ " get - 获取配置项值\n"
106
+ " set - 设置配置项值\n"
107
+ " check - 检查配置文件语法格式\n"
108
+ " inspect - 执行配置跨文件一致性自检"
109
+ )
110
+
111
+ subcommand = args[0]
112
+ sub_args = args[1:]
113
+
114
+ subcommands: dict[str, Command] = {
115
+ "show": self._show_cmd,
116
+ "edit": self._edit_cmd,
117
+ "list": self._list_cmd,
118
+ "init": self._init_cmd,
119
+ "get": self._get_cmd,
120
+ "set": self._set_cmd,
121
+ "check": self._check_cmd,
122
+ "inspect": self._inspect_cmd,
123
+ }
124
+
125
+ if subcommand in subcommands:
126
+ return subcommands[subcommand].execute(sub_args, context)
127
+ else:
128
+ return CommandResult.error(f"未知子命令: {subcommand}")
129
+
130
+
131
+ __all__ = ["ConfigCommand"]