bc-cli 0.2.0__tar.gz → 0.4.0__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 (262) hide show
  1. {bc_cli-0.2.0 → bc_cli-0.4.0}/.gitignore +3 -0
  2. {bc_cli-0.2.0 → bc_cli-0.4.0}/AGENTS.md +118 -7
  3. bc_cli-0.4.0/CHANGELOG.md +458 -0
  4. {bc_cli-0.2.0 → bc_cli-0.4.0}/PKG-INFO +14 -1
  5. bc_cli-0.4.0/docs/extraction.md +236 -0
  6. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/mcp-server.md +54 -31
  7. bc_cli-0.4.0/docs/plans/team-deployment.md +309 -0
  8. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/saved-queries.md +74 -1
  9. bc_cli-0.4.0/examples/extract/purchase_invoice_lines.yaml +61 -0
  10. {bc_cli-0.2.0 → bc_cli-0.4.0}/pyproject.toml +15 -2
  11. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/__init__.py +2 -0
  12. bc_cli-0.4.0/src/bcli/batch/__init__.py +9 -0
  13. bc_cli-0.4.0/src/bcli/batch/ledger.py +563 -0
  14. bc_cli-0.4.0/src/bcli/bundle/__init__.py +47 -0
  15. bc_cli-0.4.0/src/bcli/bundle/_apply.py +323 -0
  16. bc_cli-0.4.0/src/bcli/bundle/_fetch.py +197 -0
  17. bc_cli-0.4.0/src/bcli/bundle/_manifest.py +127 -0
  18. bc_cli-0.4.0/src/bcli/bundle/_publish.py +110 -0
  19. bc_cli-0.4.0/src/bcli/bundle/_verify.py +166 -0
  20. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/client/_async.py +36 -4
  21. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/client/_transport.py +40 -6
  22. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/config/_model.py +48 -0
  23. bc_cli-0.4.0/src/bcli/diagnostics/__init__.py +26 -0
  24. bc_cli-0.4.0/src/bcli/diagnostics/_checks.py +510 -0
  25. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/errors.py +4 -0
  26. bc_cli-0.4.0/src/bcli/exit_codes.py +69 -0
  27. bc_cli-0.4.0/src/bcli/extract/__init__.py +43 -0
  28. bc_cli-0.4.0/src/bcli/extract/_claude.py +248 -0
  29. bc_cli-0.4.0/src/bcli/extract/_factory.py +106 -0
  30. bc_cli-0.4.0/src/bcli/extract/_openai.py +318 -0
  31. bc_cli-0.4.0/src/bcli/extract/_pdf.py +68 -0
  32. bc_cli-0.4.0/src/bcli/extract/_protocol.py +97 -0
  33. bc_cli-0.4.0/src/bcli/extract/_schema.py +208 -0
  34. bc_cli-0.4.0/src/bcli/extract/_yaml_writer.py +173 -0
  35. bc_cli-0.4.0/src/bcli/result_envelope.py +140 -0
  36. bc_cli-0.4.0/src/bcli/workflow/_query_search.py +154 -0
  37. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/workflow/_resolver.py +4 -2
  38. bc_cli-0.4.0/src/bcli_cli/_envelope_wrap.py +344 -0
  39. bc_cli-0.4.0/src/bcli_cli/_error_handler.py +140 -0
  40. bc_cli-0.4.0/src/bcli_cli/_progress.py +92 -0
  41. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/_safety.py +6 -5
  42. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/_state.py +5 -0
  43. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/app.py +95 -2
  44. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/attach_cmd.py +81 -45
  45. bc_cli-0.4.0/src/bcli_cli/commands/batch_cmd.py +1089 -0
  46. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/config_cmd.py +10 -0
  47. bc_cli-0.4.0/src/bcli_cli/commands/delete_cmd.py +119 -0
  48. bc_cli-0.4.0/src/bcli_cli/commands/describe_cmd.py +562 -0
  49. bc_cli-0.4.0/src/bcli_cli/commands/doctor_cmd.py +171 -0
  50. bc_cli-0.4.0/src/bcli_cli/commands/extract_cmd.py +240 -0
  51. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/get_cmd.py +2 -1
  52. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/patch_cmd.py +61 -19
  53. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/post_cmd.py +63 -16
  54. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/query_cmd.py +260 -20
  55. bc_cli-0.4.0/src/bcli_cli/commands/refresh_cmd.py +298 -0
  56. bc_cli-0.4.0/src/bcli_cli/commands/skill_cmd.py +599 -0
  57. bc_cli-0.4.0/src/bcli_cli/commands/skill_init_cmd.py +755 -0
  58. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/output/_formatters.py +121 -12
  59. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_mcp/__main__.py +2 -2
  60. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_mcp/_runner.py +38 -32
  61. bc_cli-0.4.0/src/bcli_mcp/_server.py +243 -0
  62. bc_cli-0.4.0/src/bcli_mcp/_tool_generator.py +195 -0
  63. bc_cli-0.4.0/tests/test_batch_ledger/test_batch_cmd_ledger.py +265 -0
  64. bc_cli-0.4.0/tests/test_batch_ledger/test_idempotency_replay.py +225 -0
  65. bc_cli-0.4.0/tests/test_batch_ledger/test_ledger_idempotency.py +214 -0
  66. bc_cli-0.4.0/tests/test_batch_ledger/test_ledger_schema.py +340 -0
  67. bc_cli-0.4.0/tests/test_batch_ledger/test_rollback_cmd.py +299 -0
  68. bc_cli-0.4.0/tests/test_batch_ledger/test_state_list_cmds.py +124 -0
  69. bc_cli-0.4.0/tests/test_bundle/test_bundle_roundtrip.py +394 -0
  70. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_batch_safety.py +13 -1
  71. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_output_format.py +9 -2
  72. bc_cli-0.4.0/tests/test_cli/test_pipe_handling.py +65 -0
  73. bc_cli-0.4.0/tests/test_cli/test_records_format.py +137 -0
  74. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_safety.py +2 -2
  75. bc_cli-0.4.0/tests/test_describe/__init__.py +1 -0
  76. bc_cli-0.4.0/tests/test_describe/test_describe_cmd.py +493 -0
  77. bc_cli-0.4.0/tests/test_describe/test_describe_positionals_limits.py +119 -0
  78. bc_cli-0.4.0/tests/test_diagnostics/test_checks.py +306 -0
  79. bc_cli-0.4.0/tests/test_envelope/conftest.py +143 -0
  80. bc_cli-0.4.0/tests/test_envelope/test_batch_envelope_with_ledger.py +286 -0
  81. bc_cli-0.4.0/tests/test_envelope/test_envelope_other_verbs.py +245 -0
  82. bc_cli-0.4.0/tests/test_envelope/test_envelope_policy_violation.py +163 -0
  83. bc_cli-0.4.0/tests/test_envelope/test_envelope_post.py +289 -0
  84. bc_cli-0.4.0/tests/test_envelope/test_result_envelope.py +165 -0
  85. bc_cli-0.4.0/tests/test_errors/__init__.py +1 -0
  86. bc_cli-0.4.0/tests/test_errors/test_did_you_mean.py +116 -0
  87. bc_cli-0.4.0/tests/test_exit_codes/__init__.py +1 -0
  88. bc_cli-0.4.0/tests/test_exit_codes/test_taxonomy.py +57 -0
  89. bc_cli-0.4.0/tests/test_extract/test_claude.py +224 -0
  90. bc_cli-0.4.0/tests/test_extract/test_factory.py +131 -0
  91. bc_cli-0.4.0/tests/test_extract/test_openai.py +303 -0
  92. bc_cli-0.4.0/tests/test_extract/test_pdf.py +53 -0
  93. bc_cli-0.4.0/tests/test_extract/test_schema.py +192 -0
  94. bc_cli-0.4.0/tests/test_extract/test_yaml_writer.py +149 -0
  95. bc_cli-0.4.0/tests/test_idempotency/__init__.py +1 -0
  96. bc_cli-0.4.0/tests/test_idempotency/conftest.py +64 -0
  97. bc_cli-0.4.0/tests/test_idempotency/test_cli_flags.py +135 -0
  98. bc_cli-0.4.0/tests/test_idempotency/test_idempotency_key.py +117 -0
  99. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_mcp/test_runner.py +66 -33
  100. bc_cli-0.4.0/tests/test_mcp/test_server_tools.py +328 -0
  101. bc_cli-0.4.0/tests/test_mcp/test_tool_generator.py +395 -0
  102. bc_cli-0.4.0/tests/test_odata/__init__.py +0 -0
  103. bc_cli-0.4.0/tests/test_output/__init__.py +1 -0
  104. bc_cli-0.4.0/tests/test_output/test_json_on_pipe.py +85 -0
  105. bc_cli-0.4.0/tests/test_progress/__init__.py +1 -0
  106. bc_cli-0.4.0/tests/test_progress/test_progress_events.py +157 -0
  107. bc_cli-0.4.0/tests/test_registry/__init__.py +0 -0
  108. bc_cli-0.4.0/tests/test_skill_init/__init__.py +0 -0
  109. bc_cli-0.4.0/tests/test_skill_init/conftest.py +147 -0
  110. bc_cli-0.4.0/tests/test_skill_init/test_skill_init_wizard.py +555 -0
  111. bc_cli-0.4.0/tests/test_skill_install/__init__.py +1 -0
  112. bc_cli-0.4.0/tests/test_skill_install/test_skill_install.py +484 -0
  113. bc_cli-0.4.0/tests/test_telemetry/__init__.py +0 -0
  114. bc_cli-0.4.0/tests/test_url/__init__.py +0 -0
  115. bc_cli-0.4.0/tests/test_workflow/__init__.py +0 -0
  116. bc_cli-0.4.0/tests/test_workflow/test_query_search.py +139 -0
  117. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_workflow/test_resolver.py +10 -0
  118. {bc_cli-0.2.0 → bc_cli-0.4.0}/uv.lock +201 -2
  119. bc_cli-0.2.0/CHANGELOG.md +0 -237
  120. bc_cli-0.2.0/src/bcli_cli/commands/batch_cmd.py +0 -416
  121. bc_cli-0.2.0/src/bcli_cli/commands/delete_cmd.py +0 -76
  122. bc_cli-0.2.0/src/bcli_mcp/_server.py +0 -162
  123. bc_cli-0.2.0/tests/test_mcp/test_server_tools.py +0 -215
  124. {bc_cli-0.2.0 → bc_cli-0.4.0}/.env.example +0 -0
  125. {bc_cli-0.2.0 → bc_cli-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  126. {bc_cli-0.2.0 → bc_cli-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  127. {bc_cli-0.2.0 → bc_cli-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  128. {bc_cli-0.2.0 → bc_cli-0.4.0}/.github/workflows/publish.yml +0 -0
  129. {bc_cli-0.2.0 → bc_cli-0.4.0}/.github/workflows/tests.yml +0 -0
  130. {bc_cli-0.2.0 → bc_cli-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  131. {bc_cli-0.2.0 → bc_cli-0.4.0}/CONTRIBUTING.md +0 -0
  132. {bc_cli-0.2.0 → bc_cli-0.4.0}/LICENSE +0 -0
  133. {bc_cli-0.2.0 → bc_cli-0.4.0}/NOTICE +0 -0
  134. {bc_cli-0.2.0 → bc_cli-0.4.0}/README.md +0 -0
  135. {bc_cli-0.2.0 → bc_cli-0.4.0}/SECURITY.md +0 -0
  136. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/authentication.md +0 -0
  137. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/batch-operations.md +0 -0
  138. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/business-central-admin-setup.md +0 -0
  139. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/command-reference.md +0 -0
  140. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/configuration.md +0 -0
  141. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/contributing.md +0 -0
  142. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/custom-apis.md +0 -0
  143. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/demo-setup.md +0 -0
  144. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/getting-started.md +0 -0
  145. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/multi-company.md +0 -0
  146. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/querying.md +0 -0
  147. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/sdk-usage.md +0 -0
  148. {bc_cli-0.2.0 → bc_cli-0.4.0}/docs/write-operations.md +0 -0
  149. {bc_cli-0.2.0 → bc_cli-0.4.0}/examples/ap-monthly-review.yaml +0 -0
  150. {bc_cli-0.2.0 → bc_cli-0.4.0}/examples/attach-purchase-invoice-pdf.yaml +0 -0
  151. {bc_cli-0.2.0 → bc_cli-0.4.0}/examples/create-purchase-invoice.yaml +0 -0
  152. {bc_cli-0.2.0 → bc_cli-0.4.0}/examples/month-end-cronus.yaml +0 -0
  153. {bc_cli-0.2.0 → bc_cli-0.4.0}/examples/queries/sample.yaml +0 -0
  154. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/_url.py +0 -0
  155. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/_version.py +0 -0
  156. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/audit/__init__.py +0 -0
  157. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/audit/_factory.py +0 -0
  158. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/audit/_protocol.py +0 -0
  159. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/audit/_redact.py +0 -0
  160. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/__init__.py +0 -0
  161. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_base.py +0 -0
  162. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_browser.py +0 -0
  163. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_credentials.py +0 -0
  164. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_device_code.py +0 -0
  165. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_secure_io.py +0 -0
  166. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/auth/_token_cache.py +0 -0
  167. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/client/__init__.py +0 -0
  168. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/client/_safety.py +0 -0
  169. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/client/_sync.py +0 -0
  170. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/config/__init__.py +0 -0
  171. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/config/_defaults.py +0 -0
  172. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/config/_loader.py +0 -0
  173. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/__init__.py +0 -0
  174. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_auth.py +0 -0
  175. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_bridge.py +0 -0
  176. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_client.py +0 -0
  177. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_generic.py +0 -0
  178. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_polaris.py +0 -0
  179. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/etl/_stampers.py +0 -0
  180. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/__init__.py +0 -0
  181. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/_escape.py +0 -0
  182. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/_filter_fields.py +0 -0
  183. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/_pagination.py +0 -0
  184. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/_query.py +0 -0
  185. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/odata/_response.py +0 -0
  186. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/py.typed +0 -0
  187. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/registry/__init__.py +0 -0
  188. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/registry/_importers.py +0 -0
  189. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/registry/_registry.py +0 -0
  190. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/registry/_schema.py +0 -0
  191. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/registry/standard_v2.json +0 -0
  192. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/telemetry/__init__.py +0 -0
  193. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/telemetry/_azure_monitor.py +0 -0
  194. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/telemetry/_factory.py +0 -0
  195. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/telemetry/_protocol.py +0 -0
  196. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/telemetry/events.py +0 -0
  197. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/workflow/__init__.py +0 -0
  198. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/workflow/_loader.py +0 -0
  199. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli/workflow/_models.py +0 -0
  200. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/__init__.py +0 -0
  201. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/_audit_wrap.py +0 -0
  202. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/_dry_run.py +0 -0
  203. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/_url_resolve.py +0 -0
  204. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/__init__.py +0 -0
  205. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/auth_cmd.py +0 -0
  206. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/company_cmd.py +0 -0
  207. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/context_cmd.py +0 -0
  208. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/endpoint_cmd.py +0 -0
  209. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/env_cmd.py +0 -0
  210. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/etl_cmd.py +0 -0
  211. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/registry_cmd.py +0 -0
  212. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/commands/test_cmd.py +0 -0
  213. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/output/__init__.py +0 -0
  214. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_cli/output/_display.py +0 -0
  215. {bc_cli-0.2.0 → bc_cli-0.4.0}/src/bcli_mcp/__init__.py +0 -0
  216. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/conftest.py +0 -0
  217. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/fixtures/sample_postman_collection.json +0 -0
  218. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_audit/__init__.py +0 -0
  219. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_audit/test_factory.py +0 -0
  220. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_audit/test_redact.py +0 -0
  221. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_audit/test_sink.py +0 -0
  222. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_auth/__init__.py +0 -0
  223. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_auth/test_browser_auth.py +0 -0
  224. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_auth/test_secure_io.py +0 -0
  225. {bc_cli-0.2.0/tests/test_cli → bc_cli-0.4.0/tests/test_batch_ledger}/__init__.py +0 -0
  226. {bc_cli-0.2.0/tests/test_client → bc_cli-0.4.0/tests/test_bundle}/__init__.py +0 -0
  227. {bc_cli-0.2.0/tests/test_config → bc_cli-0.4.0/tests/test_cli}/__init__.py +0 -0
  228. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_audit_wrap.py +0 -0
  229. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_company_cmd.py +0 -0
  230. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_config_cmd.py +0 -0
  231. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_dry_run.py +0 -0
  232. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_query_cmd.py +0 -0
  233. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_cli/test_state.py +0 -0
  234. {bc_cli-0.2.0/tests/test_etl → bc_cli-0.4.0/tests/test_client}/__init__.py +0 -0
  235. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_client/test_resolve_url.py +0 -0
  236. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_client/test_safety.py +0 -0
  237. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_client/test_transport.py +0 -0
  238. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_client/test_upload_attachment.py +0 -0
  239. {bc_cli-0.2.0/tests/test_mcp → bc_cli-0.4.0/tests/test_config}/__init__.py +0 -0
  240. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_config/test_config.py +0 -0
  241. {bc_cli-0.2.0/tests/test_odata → bc_cli-0.4.0/tests/test_diagnostics}/__init__.py +0 -0
  242. {bc_cli-0.2.0/tests/test_registry → bc_cli-0.4.0/tests/test_envelope}/__init__.py +0 -0
  243. {bc_cli-0.2.0/tests/test_telemetry → bc_cli-0.4.0/tests/test_etl}/__init__.py +0 -0
  244. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_etl/test_bridge.py +0 -0
  245. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_etl/test_generic.py +0 -0
  246. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_etl/test_stampers.py +0 -0
  247. {bc_cli-0.2.0/tests/test_url → bc_cli-0.4.0/tests/test_extract}/__init__.py +0 -0
  248. {bc_cli-0.2.0/tests/test_workflow → bc_cli-0.4.0/tests/test_mcp}/__init__.py +0 -0
  249. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_odata/test_escape.py +0 -0
  250. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_odata/test_filter_fields.py +0 -0
  251. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_odata/test_query.py +0 -0
  252. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_registry/test_caution.py +0 -0
  253. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_registry/test_importers.py +0 -0
  254. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_registry/test_metadata_fields.py +0 -0
  255. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_registry/test_registry.py +0 -0
  256. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_telemetry/test_events.py +0 -0
  257. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_telemetry/test_sink.py +0 -0
  258. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_url/test_origin_allowlist.py +0 -0
  259. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_url/test_url_builder.py +0 -0
  260. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_workflow/test_batch_integration.py +0 -0
  261. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_workflow/test_loader.py +0 -0
  262. {bc_cli-0.2.0 → bc_cli-0.4.0}/tests/test_workflow/test_models.py +0 -0
@@ -33,3 +33,6 @@ PRD-*.md
33
33
  TODOS.md
34
34
  bcapi_cli_prd.md
35
35
  .planning/
36
+
37
+ # Codex CLI session metadata — local-only, not part of the repo
38
+ .context/
@@ -161,6 +161,106 @@ user, don't loop.
161
161
 
162
162
  ---
163
163
 
164
+ ## Mutation result envelope — read this, not stdout
165
+
166
+ For real writes (not dry-run), pass `--result-out PATH` (or
167
+ `--result-fd N` on Unix harnesses) and parse the JSON envelope written
168
+ there. The envelope is the canonical record of what happened — stdout
169
+ is for human consumers; agents shouldn't scrape it.
170
+
171
+ ```bash
172
+ bcli --profile p post vendors --data '{"...": "..."}' --result-out /tmp/r.json
173
+ # then jq < /tmp/r.json
174
+ ```
175
+
176
+ The envelope is an 18-field JSON object: `invocation_id`,
177
+ `tool_version`, `profile`, `environment`, `company`, `method`,
178
+ `endpoint`, `resolved_url`, `record_id`, `dry_run`, `status`
179
+ (`succeeded` / `failed`), `exit_code`, `bc_correlation_id`,
180
+ `telemetry_event_id`, `audit_log_offset`, `started_at`,
181
+ `duration_ms`, plus `version` of the envelope schema. Atomic write
182
+ (tmp + `os.replace` + `fsync`) — the file appears whole or not at all.
183
+
184
+ **Failed envelopes** keep the same shape with `status="failed"`,
185
+ `exit_code` per the taxonomy below, and `bc_correlation_id` set when
186
+ BC returned one. Use it to diagnose without scraping a Python
187
+ traceback off stderr.
188
+
189
+ For `bcli batch run`, the envelope's `record_id` IS the ledger run id.
190
+ Pivot from there to `bcli batch state <run-id>` for per-step detail.
191
+
192
+ ## Batch operation state
193
+
194
+ `bcli batch run` writes a durable SQLite ledger that survives SIGKILL.
195
+ Three new commands let you inspect and undo runs:
196
+
197
+ ```bash
198
+ bcli batch list --format json # recent runs, newest first
199
+ bcli batch state <run-id> --format json # per-step detail for one run
200
+ bcli batch rollback <run-id> --dry-run # preview undo
201
+ bcli batch rollback <run-id> # apply undo (POST→DELETE only)
202
+ ```
203
+
204
+ `bcli batch list` filters with `--state STATE` (`completed`, `failed`,
205
+ `partially_committed`, `rolled_back`, `running`, `cancelled`). Use
206
+ `partially_committed` to find runs that died mid-way — those are where
207
+ ledger-based recovery is worth the cost.
208
+
209
+ Rollback issues `DELETE` for committed POSTs only. PATCH and DELETE
210
+ steps are marked `rollback_skipped` because there's no clean inverse
211
+ without a pre-image snapshot. `disable_writes` profiles refuse rollback
212
+ outright (no `--yes` bypass) — that's by design.
213
+
214
+ ## Exit code taxonomy
215
+
216
+ Don't treat all non-zero as "generic error." The taxonomy is
217
+ documented in `bcli describe`'s `exit_codes` field:
218
+
219
+ | Code | Meaning |
220
+ |---|---|
221
+ | 0 | success |
222
+ | 1 | generic crash / unhandled exception |
223
+ | 2 | usage error (bad flag, missing arg) |
224
+ | 3 | auth (token expired, login required) |
225
+ | 4 | not found (endpoint, record, profile) |
226
+ | 5 | validation (filter, param schema) |
227
+ | 6 | remote 4xx (BC rejected the request) |
228
+ | 7 | remote 5xx (BC server error) |
229
+ | 8 | policy refusal (`disable_writes` triggered without `--yes`) |
230
+
231
+ Key off the specific code. Exit `8` means "the profile is read-only";
232
+ prompting for `--yes` and retrying is the right move. Exit `3` means
233
+ "run `bcli auth login --profile X`." Exit `1` is the only one that
234
+ warrants "report to user and stop."
235
+
236
+ ## Idempotency keys for retries
237
+
238
+ For mutations you might retry, pass `--idempotency-key K`. The IETF
239
+ `Idempotency-Key` HTTP header goes out on the first call so any
240
+ gateway-level dedup applies; subsequent retries within the same `bcli
241
+ batch run` short-circuit through the ledger (no second HTTP, no
242
+ duplicate row).
243
+
244
+ ```bash
245
+ KEY=$(uuidgen)
246
+ bcli post vendors --data '{"...":"..."}' --idempotency-key "$KEY" --result-out r.json
247
+ # If you need to retry, reuse the same KEY — same-run replay is safe.
248
+ ```
249
+
250
+ Inside a batch YAML, declare per-step:
251
+
252
+ ```yaml
253
+ steps:
254
+ - name: create_vendor
255
+ action: post
256
+ endpoint: vendors
257
+ idempotency_key: "${{ params.vendor_no }}-2026Q2"
258
+ body: { ... }
259
+ ```
260
+
261
+ Cross-run replay is deferred (would mean scanning every ledger DB); the
262
+ header is sent on every retry so gateway-level dedup remains in play.
263
+
164
264
  ## Dry-run before writes
165
265
 
166
266
  Before any `post` / `patch` / `delete` / `attach upload`, run with `--dry-run`
@@ -226,15 +326,26 @@ on, and the CLI exit code is the answer when it's off.
226
326
 
227
327
  If the user has mounted `bcli-mcp` (see [`docs/mcp-server.md`](docs/mcp-server.md)),
228
328
  prefer those tools — they collapse discovery + query into single calls
229
- with structured results:
230
-
231
- - `list_endpoints()` full registry as JSON
232
- - `describe_endpoint(name, discover_fields=True)` metadata + field
233
- discovery in one call
234
- - `query(endpoint, filter, ...)` — the same as `bcli get`, but typed
329
+ with structured results. As of 0.4.0 the MCP server generates 23 tools
330
+ dynamically from `bcli describe`, including five new mutating verbs
331
+ (`bcli_post`, `bcli_patch`, `bcli_delete`, `bcli_attach_upload`,
332
+ `bcli_batch_run`) that internally pass `--result-out` and return the
333
+ envelope as the tool result.
334
+
335
+ **Tool names match the CLI command path** (`bcli_get`,
336
+ `bcli_endpoint_list`, `bcli_endpoint_info`, `bcli_endpoint_fields`,
337
+ `bcli_company_list`, …). The pre-0.4.0 names (`query`,
338
+ `list_endpoints`, `describe_endpoint`, `list_companies`) are gone — see
339
+ the migration table in [`docs/mcp-server.md`](docs/mcp-server.md) if
340
+ your client config references the old names.
341
+
342
+ For mutating tools, a `status="failed"` envelope surfaces as MCP
343
+ `ToolError` with the BC correlation id quoted in the error message —
344
+ you don't need to read the envelope separately for failures.
235
345
 
236
346
  The CLI recipes above still work fine if the MCP server isn't
237
- available; this is purely an "if you've got it, use it" optimization.
347
+ available; the MCP tools are an "if you've got them, use them"
348
+ optimization.
238
349
 
239
350
  ---
240
351
 
@@ -0,0 +1,458 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] — 2026-05-18 — Agent Interface Profile v0.1
11
+
12
+ The Agent Interface Profile (AIP) v0.1 lands: a small kernel of CLI
13
+ primitives that any agent runtime can drive deterministically, without
14
+ parallel schemas or hand-written MCP tools.
15
+
16
+ ### Added
17
+
18
+ - **`bcli describe --format json`** — canonical machine-readable
19
+ projection of the live Typer surface + endpoint registry + active
20
+ profile. One command MCP, completions, and docs all consume; new CLI
21
+ commands light up automatically. Cached at
22
+ `~/.config/bcli/describe/<profile>.<hash>.json` with mtime
23
+ invalidation. Subtree mode (`bcli describe get`, `bcli describe batch
24
+ run`) returns narrow output for token-constrained agents. Includes
25
+ forward-compat declarations: `emits_result_envelope`,
26
+ `emits_operation_state`, `requires_confirmation: "production"`, plus
27
+ the new `exit_codes` taxonomy and per-command `positionals` /
28
+ `required` / `limits` extensions.
29
+ - **Mutation result envelope (`--result-out PATH` / `--result-fd N`)**
30
+ on every mutating verb (`post`, `patch`, `delete`, `attach upload`,
31
+ `batch run`). Frozen 18-field JSON envelope written atomically
32
+ (`os.replace` + `fsync`); contains profile, environment, company,
33
+ method, endpoint, resolved URL, record id, status, exit code, BC
34
+ correlation id, started_at, duration_ms. Failed envelopes carry the
35
+ exit code (4 = not found, 6 = remote 4xx, 7 = remote 5xx, 8 = policy
36
+ refusal, etc.) so an agent can read it side-channel and act without
37
+ scraping stdout. For `batch run` the envelope's `record_id` is the
38
+ ledger run id — pivot directly to `bcli batch state <run-id>` for
39
+ per-step detail.
40
+ - **Batch operation ledger (SQLite)** — one
41
+ `~/.config/bcli/batch/<run-id>.db` per `bcli batch run` invocation.
42
+ WAL + `synchronous=NORMAL`, intent row written before each HTTP call
43
+ (survives SIGKILL); outcome row after. Derived run state
44
+ distinguishes `partially_committed` from a stale `running` stamp. New
45
+ commands: `bcli batch state <run-id>`, `bcli batch list [--state
46
+ STATE] [--limit N]`, `bcli batch rollback <run-id> [--dry-run]
47
+ [--yes]`. Rollback issues `DELETE` for committed POSTs only; PATCH /
48
+ DELETE marked `rollback_skipped` (no clean inverse without pre-image
49
+ snapshots). `disable_writes` is a hard refusal on rollback (no
50
+ `--yes` bypass).
51
+ - **Exit code taxonomy** — `bcli.exit_codes` defines 0/1/2/3/4/5/6/7/8
52
+ with short labels; `bcli describe` projects the map; centralized
53
+ error handler maps `BCLIError` subclasses (`AuthError → 3`,
54
+ `RegistryError → 4`, `ValidationError → 5`, `ConfigError → 2`,
55
+ `SafetyError → 8`).
56
+ - **"Did you mean" remediation hints** on `BCLIError` paths: auth →
57
+ `Run 'bcli auth login --profile X'`, config (no profiles) →
58
+ `Run 'bcli config init'`, config (unknown profile) → fuzzy match,
59
+ registry (no fuzzy) → `Run 'bcli registry import …'`.
60
+ - **JSON on pipe by default** — when stdout isn't a TTY and no
61
+ `--format` was passed, emit JSON. Pipelines, redirects, CI steps,
62
+ agent runtimes all get the canonical machine-readable shape with no
63
+ flag dance. The `CLAUDECODE` and `BCLI_AGENT` env hints keep their
64
+ markdown semantics (explicit user opt-in); legacy Windows console
65
+ host stays on markdown for the mojibake reason. `BCLI_FORMAT` and
66
+ explicit `--format` always win.
67
+ - **`--idempotency-key KEY`** on `post`, `patch`, `delete`, `attach
68
+ upload`. IETF `Idempotency-Key` HTTP header sent on the first call
69
+ (gateway-level dedup remains in play). Same-run replay protection in
70
+ `bcli batch run`: if two mutating steps share an `idempotency_key:`
71
+ in the YAML, the second is replayed (no second HTTP, no duplicate
72
+ ledger row), and the result entry carries `prior_seq`,
73
+ `prior_step_id`, `prior_bc_correlation_id`. Ledger schema migrates
74
+ v1 → v2 non-destructively via `ALTER TABLE step ADD COLUMN
75
+ idempotency_key`.
76
+ - **Progress events (`--progress-fd N`)** on `bcli batch run` and
77
+ `bcli extract run`. JSON-lines `step_started` / `step_completed`
78
+ written to a dedicated fd (separate from `--result-fd`). Stderr
79
+ stays human-readable; the fd channel is structured and stable for
80
+ agents to demux. Replayed steps emit a synthetic pair with
81
+ `status="replayed"` so the progress stream tells the truth.
82
+ - **23 dynamically-generated MCP tools** in `bcli_mcp` (was 4
83
+ hand-written). Server subprocesses `bcli describe` once on startup
84
+ and registers one tool per command; new CLI commands light up as
85
+ MCP tools automatically. Five new mutating tools (`bcli_post`,
86
+ `bcli_patch`, `bcli_delete`, `bcli_attach_upload`, `bcli_batch_run`)
87
+ pass `--result-out` and return the envelope as their tool result.
88
+ `status="failed"` envelopes surface as MCP `ToolError` with the BC
89
+ correlation id quoted.
90
+ - **`AsyncBCClient.delete_url(url, *, etag="*")`** — new SDK method
91
+ for absolute-URL deletes (used by the rollback path; avoids
92
+ re-resolving the registry at undo time).
93
+ - **`bcli skill install`** — generates `.claude/commands/bcli-<name>.md`
94
+ per saved query and per batch template (`~/.config/bcli/batches/
95
+ <profile>/*.yaml`). Generates a top-level
96
+ `.claude/skills/bcli/SKILL.md` index grouped by `categories:`.
97
+ SHA-256 content hash embedded in the provenance comment for
98
+ byte-stable idempotency — no `generated_at` timestamp, so re-runs on
99
+ unchanged sources are mtime-preserving no-ops. `manual: true` in a
100
+ file's YAML frontmatter protects it from regeneration. `--dry-run`
101
+ previews; `--target` resolves to explicit path > CWD with `.claude/`
102
+ > `$HOME`. Stdlib only — no jinja2; atomic writes via
103
+ `tempfile.mkstemp` + `os.replace`.
104
+ - **`bcli skill init`** — interactive wizard that reads `bcli describe
105
+ --format json` via subprocess, runs 4 Rich prompts (role / top-three
106
+ daily questions / slash-command style / generate-new-queries y/N),
107
+ fuzzy-matches existing saved queries against the top-three free
108
+ text (stdlib `difflib`), and proposes new role-tailored queries via
109
+ entry-point providers — each with a per-query `[y/N]` approval gate.
110
+ Generates `~/.claude/skills/bcli-<user>/SKILL.md` with YAML
111
+ provenance frontmatter. Atomic commit phase: snapshots existing
112
+ content, writes all targets, restores on first failure. Guardrails
113
+ via `_assert_writable` restrict writes to `~/.config/bcli/queries/`,
114
+ `~/.claude/skills/bcli-<user>/`, and `~/.config/bcli/skills/`;
115
+ symlink-safe via `Path.resolve(strict=False)` + `is_relative_to`.
116
+ - **`bcli skill update`** — idempotent re-run via state cache at
117
+ `~/.config/bcli/skills/.last-init.json`. The cache persists both
118
+ the interview answers AND the approved-query bodies so a later
119
+ `--non-interactive` replay re-writes the same queries verbatim
120
+ (without re-asking the operator). Describe-payload-hash mismatch
121
+ refuses silent replay and asks for a re-interview when the describe
122
+ surface changed under the user's feet.
123
+ - **Saved-query YAML schema extension** — three additive fields
124
+ (`description`, `categories`, `args`). Existing saved-query bundles
125
+ without these still work: when `args:` is omitted, `bcli skill
126
+ install` derives it from `params:` keys (required first, optional
127
+ second, both in YAML insertion order). Documented in
128
+ `docs/saved-queries.md`'s new "Slash-command projection" section.
129
+ - **Entry-point group `bcli.skill_init.role_templates`** — OSS bcli
130
+ ships with an opinion-free default proposer (returns `[]` for every
131
+ role). Downstream packages plug in role templates by registering
132
+ callables under this entry-point group via standard Python
133
+ packaging. Discovered at wizard time via
134
+ `importlib.metadata.entry_points`. The provider signature is
135
+ `(interview, payload) -> list[ProposedQuery]`; downstream
136
+ integrators publish their own integration documentation.
137
+
138
+ ### Changed
139
+
140
+ - **Policy refusal exit code 1 → 8.** Scripts that grep `if exit==1`
141
+ for "the read-only profile blocked me" need updating. Agents
142
+ consuming `bcli describe`'s new `exit_codes` field pick up the new
143
+ code automatically.
144
+ - **MCP tool renames** (breaking for existing MCP clients — Claude
145
+ Desktop, MCP Inspector configs referencing the old names need an
146
+ update):
147
+ - `query` → `bcli_get`
148
+ - `list_endpoints` → `bcli_endpoint_list`
149
+ - `describe_endpoint` → `bcli_endpoint_info` (with
150
+ `bcli_endpoint_fields` split out for field discovery)
151
+ - `list_companies` → `bcli_company_list`
152
+
153
+ Migration table in `docs/mcp-server.md`. Tool names now consistently
154
+ match the CLI command path.
155
+ - **MCP `bcli_get --top` cap remains 50 default / 1000 max** — parity
156
+ with the pre-rewrite hand-written `query` tool. CLI shell users can
157
+ still pass `--top 100000` directly; the cap is enforced only at the
158
+ MCP schema level (advisory for agent runtimes).
159
+ - **Stderr routing for `bcli batch run` metadata** — the ledger path
160
+ and run id print to stderr, not stdout. Stdout matches legacy batch
161
+ output byte-for-byte (required by the additive constraint).
162
+
163
+ ### Fixed
164
+
165
+ - **Clean SIGPIPE handling for piped output** — `bcli <cmd> | head`,
166
+ `| grep -m 1`, and similar pipe-truncating consumers now terminate
167
+ the CLI silently, matching `cat` and `grep` conventions, instead of
168
+ emitting a `BrokenPipeError: [Errno 32] Broken pipe` traceback at
169
+ interpreter shutdown. Implemented as a new `bcli_cli.app:main`
170
+ console-script entry point that installs `SIGPIPE -> SIG_DFL` on
171
+ POSIX with a `BrokenPipeError` safety net for Windows.
172
+ - **Hyphenated saved-query param names** — the workflow template
173
+ resolver now accepts hyphens in identifiers, so references like
174
+ `${{ params.vendor-no }}` substitute correctly. Previously the regex
175
+ matched only `[\w.]`, silently leaving the literal `${{ … }}` token
176
+ in the rendered filter (BC then 400'd or, worse, returned mismatched
177
+ rows). Affects both `bcli q` saved queries and `bcli batch`
178
+ workflows.
179
+
180
+ ### Breaking changes
181
+
182
+ The two items above (exit code 1→8 + MCP tool renames) are intentional
183
+ breaking changes called out in the AIP plan. Both have one-line
184
+ migration paths. Skill install / skill init / skill update are
185
+ additive — no breaking changes from the Skills layer.
186
+
187
+ ### Deferred to v0.5
188
+
189
+ - **Cross-run idempotency replay** — would require scanning every
190
+ `*.db` in `~/.config/bcli/batch/` on each mutating call. Same-run
191
+ protection covers the agent-retry case which is the common one;
192
+ gateway-level Idempotency-Key dedup covers the rest.
193
+ - **`batch run --idempotency-key`** as a run-level flag — collides
194
+ across multiple mutating steps. Per-step `idempotency_key:` in the
195
+ batch YAML is the correct surface.
196
+ - **`telemetry_event_id` / `audit_log_offset` on the envelope** —
197
+ currently always `null`. Wiring requires extending the
198
+ `TelemetrySink` and audit protocols to return the emitted event id /
199
+ log offset, touching every backend including the optional Azure
200
+ Monitor extra.
201
+ - **Plan-token binding for single mutations** — `batch run --plan-out`
202
+ works; `bcli post --plan-out` does not. Defer until requested.
203
+ - **Direct FastMCP schema-introspection test** — the `__signature__`
204
+ patch is indirectly covered via tool-list registration tests; a
205
+ future FastMCP upgrade could silently degrade tool input schemas
206
+ without breaking tests.
207
+ - **Etag capture in ledger** — rollback DELETEs use `etag="*"`. A
208
+ future concurrent edit between POST and rollback could clobber.
209
+ - **CWD-relative batch template discovery** for `bcli skill install` —
210
+ today only `~/.config/bcli/batches/<profile>/*.yaml` is scanned;
211
+ project-local batches in `./batches/` would also be useful for the
212
+ per-project `.claude/` workflow.
213
+ - **SKILL.md frontmatter `generated_at` churn cleanup** — the
214
+ timestamp ticks forward on every `bcli skill update
215
+ --non-interactive` replay, so the frontmatter changes even when the
216
+ body is byte-stable. Idempotency tests compare body-only; downstream
217
+ content-hash watchers would see noise. Cosmetic.
218
+ - **`bcli skill update` separated from `init`** — today
219
+ `update_command` delegates to `init_command(...)` verbatim.
220
+ Documented for future evolution.
221
+ - **Public `bcli.skill_init` namespace for the entry-point contract**
222
+ — downstream packages currently couple to
223
+ `bcli_cli.commands.skill_init_cmd` for `InterviewState` /
224
+ `ProposedQuery`. A future release could promote the protocol types
225
+ to a public `bcli.skill_init` namespace.
226
+
227
+ ## [0.2.0] — 2026-05-06
228
+
229
+ ### Added
230
+
231
+ - **Structured `--dry-run` output** — write commands (`post`, `patch`,
232
+ `delete`, `attach upload`) now emit a stable JSON envelope on stdout
233
+ when `--format json` / `ndjson` / `raw` is selected. Includes
234
+ `dry_run`, `method`, `endpoint`, `resolved_url`, `profile`,
235
+ `environment`, `company_id`, `body`, and `record_id` (when applicable).
236
+ Agents can parse the envelope before deciding whether to proceed. The
237
+ human format keeps the same yellow rich panel on stderr but is now
238
+ augmented with the resolved URL and profile context. See
239
+ `docs/write-operations.md`.
240
+ - **Opt-in audit log** — new `[audit]` config section persists every
241
+ write to a per-profile JSONL file. Each entry captures the resolved
242
+ URL, response status, BC `correlation_id`, latency, redacted request
243
+ body, and outcome (`completed` / `failed` / `dry_run`). Bounded disk
244
+ usage via single-backup rotation. SDK (`AsyncBCClient`) does NOT
245
+ auto-emit; this is a CLI-layer ergonomic on top of BC permission sets.
246
+ See `docs/configuration.md#audit-log`.
247
+ - **Endpoint `caution` flag** — `EndpointMetadata` now carries a
248
+ `caution: low | medium | high` level. Importers populate it
249
+ automatically from a verb-name heuristic (entities containing `post`,
250
+ `release`, `cancel`, `void`, `reverse`, `apply`, `unapply` are flagged
251
+ `high`). Surfaced in `bcli endpoint info` and the `list_endpoints` MCP
252
+ tool so agents can require explicit user confirmation before mutating
253
+ posted/closed records.
254
+ - New `AGENTS.md` recipes for dry-run-first writes, caution-level
255
+ interpretation, and audit-log location.
256
+
257
+ ## [0.1.5] — 2026-05-05
258
+
259
+ ### Added
260
+
261
+ - **Business Central admin setup guide** — new
262
+ `docs/business-central-admin-setup.md` walks a zero-knowledge user
263
+ through Entra app registration, localhost redirect setup, delegated BC
264
+ permissions, admin consent, BC user permission sets, first `bcli
265
+ config init`, and verification.
266
+ - **`bcli-mcp` preview server** — an MCP (Model Context Protocol) server
267
+ that lets Claude Desktop and other MCP clients drive bcli. Four
268
+ read-only tools: `query`, `list_endpoints`, `describe_endpoint`,
269
+ `list_companies`. Subprocess-only architecture inherits profile, auth,
270
+ retry, telemetry, and `disable_writes` from the CLI. Install with
271
+ `pip install "bc-cli[mcp]"`. See `docs/mcp-server.md`.
272
+
273
+ ### Changed
274
+
275
+ - `bcli config init` now defaults to browser PKCE auth for local humans
276
+ and agents. New `--automation` and `--headless` shortcuts create
277
+ client-credentials and device-code profiles respectively.
278
+ - CLI runtime dependencies now ship with the base `bc-cli` install, so
279
+ `pip install bc-cli` and `uv tool install bc-cli` provide a working
280
+ `bcli` command without requiring an extra.
281
+ - `bcli company list` accepts `--format` (`json`, `markdown`, `csv`,
282
+ `ndjson`, `table`). Stable JSON shape:
283
+ `[{"id", "name", "alias", "is_default"}]`.
284
+ - `bcli endpoint list` and `bcli endpoint info` accept `--format json`.
285
+ Stable JSON shapes documented inline in each command's help text.
286
+
287
+ ### Removed
288
+
289
+ - Removed WorkOS AuthKit support. Browser PKCE is now the delegated auth
290
+ path, Business Central remains the permission boundary, and
291
+ client-credentials profiles cover automation.
292
+
293
+ ## [0.1.2] — 2026-04-29
294
+
295
+ Security release. Closes four findings from a strix.ai run against the
296
+ repo. No public SDK signature changes; the CLI gains one new flag
297
+ (`bcli batch run --yes`).
298
+
299
+ ### Security
300
+
301
+ - **vuln-0001 (HIGH, CWE-352)** — WorkOS localhost callback now binds a
302
+ per-login high-entropy `state` token. Before this release, any local
303
+ request reaching `127.0.0.1:8401/callback?code=…` during the login
304
+ window would be exchanged for a role-bearing WorkOS identity and
305
+ cached on disk. The handler now rejects callbacks whose path is not
306
+ `/callback` (404) or whose `state` doesn't match the per-login token
307
+ (400), and surfaces invalid callbacks as auth failures rather than
308
+ masking them as timeouts.
309
+ - **vuln-0002 (MEDIUM, CWE-841)** — `bcli batch run` now enforces the
310
+ `disable_writes` profile gate that direct `post`/`patch`/`delete`
311
+ commands already honour. Mutating batch steps on a read-only profile
312
+ prompt for confirmation interactively or abort with exit 1 in
313
+ non-interactive sessions. New `--yes` / `-y` flag opts scripted use
314
+ past the prompt. Pure GET batches are unaffected, and `--dry-run`
315
+ still skips the gate so workflows can be previewed.
316
+ - **vuln-0003 (MEDIUM)** — Browser auth callback listener now binds an
317
+ ephemeral kernel-assigned port instead of a hard-coded 8400, and
318
+ serves continuously until a state-bound callback arrives or the
319
+ timeout expires. Stray requests (e.g. `/favicon.ico`) and
320
+ state-mismatched callbacks no longer consume the only callback slot.
321
+ Microsoft Entra accepts any port for `http://localhost` redirect URIs
322
+ on public clients per RFC 8252, so existing app registrations
323
+ continue to work without changes.
324
+ - **vuln-0004 (HIGH, CWE-841)** — `SafeContext` writes are now bound to
325
+ the explicit `environment` and `company_id` passed to
326
+ `client.safe_write(env, company)`, not the client's profile-bound
327
+ target. Previously the safety gate validated operator intent but the
328
+ underlying URL still resolved against the profile, so writes inside
329
+ `safe_write("Sandbox", "company-SANDBOX")` could still hit
330
+ `Production/company-PROD`. Closes the documentation-vs-behaviour
331
+ mismatch where the README/changelog claimed the gate prevented
332
+ wrong-environment writes.
333
+
334
+ ### Changed
335
+
336
+ - `bcli batch run` accepts a new `--yes` / `-y` flag (see vuln-0002
337
+ above). Existing automation against writable profiles is unaffected;
338
+ CI scripts that run mutating batches against a `disable_writes`
339
+ profile must pass `--yes` or migrate to a writable profile.
340
+
341
+ ## [0.1.1] — 2026-04-29
342
+
343
+ OSS-readiness polish. No SDK behaviour changes; one CLI rename and a
344
+ README/docstring cleanup pass.
345
+
346
+ ### Changed
347
+
348
+ - **Renamed the document-attachment CLI command** to `bcli attach`,
349
+ with subcommands `bcli attach upload` and `bcli attach test`.
350
+ Functionality is unchanged. The previous name was a customer-name
351
+ leftover from internal development; the workflow is generic to any
352
+ Business Central tenant.
353
+ - Genericised customer-named example values in
354
+ `docs/authentication.md`, `examples/attach-purchase-invoice-pdf.yaml`,
355
+ and SDK docstrings.
356
+
357
+ ### Fixed
358
+
359
+ - README license badge said Apache 2.0 but the License section said
360
+ MIT. Now consistent: Apache 2.0 throughout (matches the actual
361
+ `LICENSE` file and `pyproject.toml`).
362
+
363
+ ## [0.1.0] — 2026-04-29
364
+
365
+ First public release on PyPI as **`bc-cli`**.
366
+
367
+ > **Note on the package name.** The PyPI distribution name is `bc-cli`,
368
+ > not `bcli` — the latter is squatted by an unrelated 2018-era
369
+ > "EC2 Cluster Creator" package. The Python import name (`import bcli`)
370
+ > and the CLI binary on PATH (`bcli`) are unaffected.
371
+
372
+ ### Added
373
+
374
+ - **SDK** — `BCClient` (sync) and `AsyncBCClient` (async) for Microsoft
375
+ Dynamics 365 Business Central. Two construction modes: profile-based
376
+ (TOML config files) and programmatic (pass auth params directly).
377
+ - **CLI** — Typer-based `bcli` command with subcommands for query
378
+ (`get`), write (`post`/`patch`/`delete`/`attach`), config, auth,
379
+ registry import, batch operations, saved queries (`q`), and ETL
380
+ pipelines.
381
+ - **Three-tier endpoint resolution.** Custom registry → standard v2.0 →
382
+ fuzzy-match suggestion. Custom APIs imported from Postman v2.1
383
+ collections, raw JSON, or live `$metadata`.
384
+ - **Auth** — Client-credentials, device-code, browser (PKCE), and
385
+ WorkOS AuthKit (role → BC client_id mapping). Token cache with 5-min
386
+ expiry buffer; OS keychain integration via `keyring`.
387
+ - **Write safety** — `SafeContext` gate enforces explicit `environment`
388
+ + `company_id` on writes; production writes require
389
+ `confirm_production=True`. CLI's `disable_writes` profile flag adds
390
+ an interactive confirmation prompt before any mutating call.
391
+ - **Saved queries** (`bcli q`) — Hide OData syntax behind named,
392
+ parameterised aliases stored per-profile. Per-param `type`,
393
+ `pattern`, `min`/`max`, and `enum` validation runs locally before any
394
+ HTTP call. String parameters interpolated into `filter:` are escaped
395
+ using OData v4 single-quote rules so an injection-shaped value cannot
396
+ break the literal.
397
+ - **ETL pipeline** — Built-in [dlt](https://dlthub.com) source for
398
+ incremental backup. Polaris REST catalog integration for Iceberg
399
+ snapshots. Generic + bcli-bridge layers.
400
+ - **Telemetry** — Pluggable backend with `null`, `console`,
401
+ `azure_monitor`, and arbitrary `module:Class` sinks. Privacy-first
402
+ defaults: token redaction in error messages, opt-in capture of filter
403
+ text and signed-in UPN. Every event carries `version`, `os`,
404
+ `os_release`, `arch`, `python_version`, and a stable per-laptop
405
+ `install_id` for downstream slicing.
406
+ - **Structured logging** — JSON request logs to the `bcli.http` logger
407
+ with method/url/status/retry-count/latency/correlation-id.
408
+
409
+ ### Security
410
+
411
+ The 0.1.0 release went through two independent security review passes
412
+ before publish. Findings addressed:
413
+
414
+ - Pinned wheel-version installer scripts; scrubbed registry `supports`
415
+ arrays to `["GET"]` for read-only profiles; removed sensitive field
416
+ metadata from public artefacts shipped by the bootstrap installer.
417
+ - **Critical:** Project-level `.bcli.toml` files cannot override
418
+ `[telemetry] backend`. Closes an arbitrary-Python-import RCE on
419
+ `bcli` invocation in any directory containing a malicious
420
+ `.bcli.toml`.
421
+ - **High:** Token caches and identity caches written with `0o600`
422
+ perms via atomic write; parent dir tightened to `0o700`; one-shot
423
+ warning on insecure existing perms.
424
+ - **High:** WorkOS role cache now expires after 1 hour; revoked roles
425
+ no longer keep mapping to privileged BC apps indefinitely.
426
+ - **Medium:** Absolute-URL paths (`@odata.nextLink`) validated against
427
+ a `*.businesscentral.dynamics.com` / `*.bc.dynamics.com` host
428
+ allowlist before the bearer token is attached. Closes a
429
+ bearer-exfiltration vector via tampered BC responses.
430
+ - **Medium:** CI hardened — third-party actions pinned by full commit
431
+ SHA, default `permissions: contents: read`, `uv sync --locked` for
432
+ reproducible installs from a committed `uv.lock`.
433
+ - **Medium:** Saved-query OData injection prevention (filter-context
434
+ escape + per-parameter `type`/`pattern`/`min`/`max`/`enum`
435
+ validation).
436
+
437
+ ### Known limitations
438
+
439
+ - Alpha software. The SDK and CLI surface may change in 0.x releases;
440
+ track this CHANGELOG for breaking changes.
441
+ - Some BC custom-API edge cases (zero-GUID ids on
442
+ `SourceTableTemporary` pages) require the `--standard` flag to
443
+ bypass the registry — see `bcli attach upload --help`.
444
+
445
+ ## [Pre-0.1.0 history]
446
+
447
+ Earlier development happened under the working name `bcapi`, then
448
+ moved to `bcli` (April 2026), then to the PyPI distribution name
449
+ `bc-cli` (April 2026, after discovering the `bcli` PyPI name was
450
+ squatted by an unrelated 2018 package).
451
+
452
+ [Unreleased]: https://github.com/igor-ctrl/bcli/compare/v0.4.0...HEAD
453
+ [0.4.0]: https://github.com/igor-ctrl/bcli/compare/v0.2.0...v0.4.0
454
+ [0.2.0]: https://github.com/igor-ctrl/bcli/compare/v0.1.5...v0.2.0
455
+ [0.1.5]: https://github.com/igor-ctrl/bcli/compare/v0.1.2...v0.1.5
456
+ [0.1.2]: https://github.com/igor-ctrl/bcli/compare/v0.1.1...v0.1.2
457
+ [0.1.1]: https://github.com/igor-ctrl/bcli/compare/v0.1.0...v0.1.1
458
+ [0.1.0]: https://github.com/igor-ctrl/bcli/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bc-cli
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs
5
5
  Project-URL: Homepage, https://github.com/igor-ctrl/bcli
6
6
  Project-URL: Repository, https://github.com/igor-ctrl/bcli
@@ -32,14 +32,27 @@ Requires-Dist: tomlkit>=0.13
32
32
  Requires-Dist: typer>=0.12
33
33
  Provides-Extra: cli
34
34
  Provides-Extra: dev
35
+ Requires-Dist: anthropic>=0.40; extra == 'dev'
35
36
  Requires-Dist: dlt[filesystem,parquet,s3]>=1.0; extra == 'dev'
36
37
  Requires-Dist: mcp>=1.0; extra == 'dev'
38
+ Requires-Dist: openai>=1.50; extra == 'dev'
39
+ Requires-Dist: pypdf>=4.0; extra == 'dev'
37
40
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
38
41
  Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
39
42
  Requires-Dist: pytest>=8.0; extra == 'dev'
40
43
  Requires-Dist: ruff>=0.5; extra == 'dev'
41
44
  Provides-Extra: etl
42
45
  Requires-Dist: dlt[filesystem,parquet,s3]>=1.0; extra == 'etl'
46
+ Provides-Extra: extract
47
+ Requires-Dist: anthropic>=0.40; extra == 'extract'
48
+ Requires-Dist: openai>=1.50; extra == 'extract'
49
+ Requires-Dist: pypdf>=4.0; extra == 'extract'
50
+ Provides-Extra: extract-claude
51
+ Requires-Dist: anthropic>=0.40; extra == 'extract-claude'
52
+ Requires-Dist: pypdf>=4.0; extra == 'extract-claude'
53
+ Provides-Extra: extract-openai
54
+ Requires-Dist: openai>=1.50; extra == 'extract-openai'
55
+ Requires-Dist: pypdf>=4.0; extra == 'extract-openai'
43
56
  Provides-Extra: mcp
44
57
  Requires-Dist: mcp>=1.0; extra == 'mcp'
45
58
  Provides-Extra: polaris