karni-cli 0.3.1__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 (256) hide show
  1. {karni_cli-0.3.1 → karni_cli-0.4.0}/.gitignore +1 -0
  2. {karni_cli-0.3.1 → karni_cli-0.4.0}/PKG-INFO +61 -53
  3. {karni_cli-0.3.1 → karni_cli-0.4.0}/README.md +59 -51
  4. {karni_cli-0.3.1 → karni_cli-0.4.0}/hatch_build.py +13 -1
  5. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/__init__.py +1 -1
  6. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/check.py +6 -6
  7. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/history.py +1 -1
  8. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/history_chronicle.py +2 -1
  9. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/overrides.py +1 -1
  10. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/rules.py +299 -93
  11. karni_cli-0.4.0/karni_cli/_changelog/0.4.0.toml +101 -0
  12. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_errorcodes/__init__.py +579 -115
  13. karni_cli-0.4.0/karni_cli/_errorcodes/failures.py +1282 -0
  14. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_errorcodes/links.py +1 -1
  15. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/cookiecutter.json +1 -1
  16. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/.dockerignore +3 -0
  17. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/.env.example +6 -4
  18. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/.gitignore +4 -0
  19. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/Caddyfile +1 -1
  20. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/Dockerfile +3 -3
  21. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/README.md +31 -24
  22. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/settings.py +2 -2
  23. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/urls.py +2 -2
  24. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/docker-compose.yml +6 -2
  25. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/Dockerfile.dev +1 -1
  26. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/eslint.config.js +1 -1
  27. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/package.json +4 -8
  28. karni_cli-0.4.0/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/App.tsx +21 -0
  29. karni_cli-0.4.0/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/index.css +15 -0
  30. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/layout.tsx +4 -3
  31. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/routes.tsx +1 -1
  32. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/tailwind.config.ts +1 -1
  33. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/vite.config.ts +4 -1
  34. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/pyproject.toml +10 -1
  35. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/tests/test_api_schema.py +1 -1
  36. karni_cli-0.4.0/karni_cli/_ui/styles.css +248 -0
  37. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/adapters.py +11 -10
  38. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/agent_adapters.toml +2 -2
  39. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/agent_data.py +1 -1
  40. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/clidoc.py +20 -7
  41. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/add.py +5 -5
  42. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/beat.py +2 -1
  43. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/check.py +14 -7
  44. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/config.py +4 -4
  45. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/context.py +5 -4
  46. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/createsuperuser.py +2 -1
  47. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/doctor.py +5 -5
  48. karni_cli-0.4.0/karni_cli/commands/explain.py +119 -0
  49. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/make.py +1 -1
  50. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/manage_passthrough.py +10 -2
  51. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/migrate.py +7 -2
  52. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/new.py +14 -6
  53. karni_cli-0.4.0/karni_cli/commands/queries.py +99 -0
  54. karni_cli-0.4.0/karni_cli/commands/replay.py +53 -0
  55. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/routes.py +4 -1
  56. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/rules.py +6 -6
  57. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/run_tests.py +2 -1
  58. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/schedule.py +9 -3
  59. karni_cli-0.4.0/karni_cli/commands/schema.py +61 -0
  60. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/seed.py +11 -6
  61. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/serve.py +7 -4
  62. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/shell.py +1 -1
  63. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/sync_roles.py +9 -2
  64. karni_cli-0.4.0/karni_cli/commands/trace.py +31 -0
  65. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/types.py +3 -3
  66. karni_cli-0.4.0/karni_cli/commands/ui.py +384 -0
  67. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/upgrade.py +3 -3
  68. karni_cli-0.4.0/karni_cli/commands/why.py +203 -0
  69. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/worker.py +2 -1
  70. karni_cli-0.4.0/karni_cli/database.py +176 -0
  71. karni_cli-0.4.0/karni_cli/db_probe.py +89 -0
  72. karni_cli-0.4.0/karni_cli/debugging.py +113 -0
  73. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/errorcodes.py +244 -18
  74. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/errordoc.py +7 -5
  75. karni_cli-0.4.0/karni_cli/explain_text.py +62 -0
  76. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/apitest.py +16 -14
  77. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/app.py +4 -4
  78. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/command.py +1 -1
  79. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/component.py +2 -2
  80. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/core.py +11 -13
  81. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/event.py +1 -1
  82. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/factory.py +1 -1
  83. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/fields.py +248 -20
  84. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/introspect.py +2 -2
  85. karni_cli-0.4.0/karni_cli/generators/lifecycle_fields.py +207 -0
  86. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/listener.py +9 -8
  87. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/middleware.py +2 -2
  88. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/migration.py +5 -5
  89. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/model.py +10 -8
  90. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/module.py +4 -4
  91. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/page.py +12 -9
  92. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/pysource.py +1 -1
  93. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/schedule.py +9 -9
  94. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/seeder.py +3 -3
  95. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/serializer.py +4 -4
  96. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/service.py +3 -3
  97. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/task.py +2 -2
  98. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/viewset.py +7 -7
  99. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/local_state.py +1 -1
  100. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/main.py +27 -2
  101. karni_cli-0.4.0/karni_cli/mcp_server.py +272 -0
  102. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/project.py +61 -16
  103. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/rules_render.py +1 -1
  104. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/scaffold.py +17 -5
  105. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/settings_edit.py +2 -2
  106. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/tips.py +5 -3
  107. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/typegen.py +1 -1
  108. karni_cli-0.4.0/karni_cli/uitheme/__init__.py +23 -0
  109. karni_cli-0.4.0/karni_cli/uitheme/css.py +190 -0
  110. karni_cli-0.4.0/karni_cli/uitheme/defaults.py +91 -0
  111. karni_cli-0.4.0/karni_cli/uitheme/designsystems.py +699 -0
  112. karni_cli-0.4.0/karni_cli/uitheme/frontend.py +236 -0
  113. karni_cli-0.4.0/karni_cli/uitheme/jsobject.py +317 -0
  114. karni_cli-0.4.0/karni_cli/uitheme/model.py +173 -0
  115. karni_cli-0.4.0/karni_cli/uitheme/primitives.py +192 -0
  116. karni_cli-0.4.0/karni_cli/uitheme/render.py +201 -0
  117. karni_cli-0.4.0/karni_cli/uitheme/shadcn.py +259 -0
  118. karni_cli-0.4.0/karni_cli/uitheme/tailwind.py +707 -0
  119. karni_cli-0.4.0/karni_cli/uitheme/wiring.py +143 -0
  120. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/engine.py +2 -2
  121. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/python.py +5 -5
  122. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/typescript.py +1 -1
  123. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v0_2/agent_guidance.py +3 -3
  124. {karni_cli-0.3.1 → karni_cli-0.4.0}/pyproject.toml +5 -3
  125. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/conftest.py +45 -0
  126. karni_cli-0.4.0/tests/fixtures/ui/chakra/package.json +9 -0
  127. karni_cli-0.4.0/tests/fixtures/ui/chakra/src/main.tsx +7 -0
  128. karni_cli-0.4.0/tests/fixtures/ui/chakra/src/theme.ts +35 -0
  129. karni_cli-0.4.0/tests/fixtures/ui/chakra3/package.json +9 -0
  130. karni_cli-0.4.0/tests/fixtures/ui/chakra3/src/components/ui/theme.ts +29 -0
  131. karni_cli-0.4.0/tests/fixtures/ui/chakra3/src/main.tsx +7 -0
  132. karni_cli-0.4.0/tests/fixtures/ui/mantine/package.json +9 -0
  133. karni_cli-0.4.0/tests/fixtures/ui/mantine/src/main.tsx +7 -0
  134. karni_cli-0.4.0/tests/fixtures/ui/mantine/src/theme.ts +41 -0
  135. karni_cli-0.4.0/tests/fixtures/ui/mui/package.json +11 -0
  136. karni_cli-0.4.0/tests/fixtures/ui/mui/src/main.tsx +7 -0
  137. karni_cli-0.4.0/tests/fixtures/ui/mui/src/theme/index.ts +29 -0
  138. karni_cli-0.4.0/tests/fixtures/ui/plain/package.json +5 -0
  139. karni_cli-0.4.0/tests/fixtures/ui/plain/src/index.css +4 -0
  140. karni_cli-0.4.0/tests/fixtures/ui/plain/src/main.tsx +7 -0
  141. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/components.json +18 -0
  142. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/package.json +16 -0
  143. {karni_cli-0.3.1/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend → karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3}/src/App.tsx +0 -2
  144. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/components/ui/button.tsx +29 -0
  145. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/components/ui/checkbox.tsx +14 -0
  146. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/components/ui/input.tsx +12 -0
  147. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/components/ui/label.tsx +16 -0
  148. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/components/ui/select.tsx +69 -0
  149. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/index.css +49 -0
  150. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/src/main.tsx +11 -0
  151. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/tailwind.config.js +18 -0
  152. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw3/tsconfig.json +10 -0
  153. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/components.json +21 -0
  154. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/package.json +11 -0
  155. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/App.tsx +5 -0
  156. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/components/ui/button.tsx +23 -0
  157. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/components/ui/select.tsx +42 -0
  158. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/components/ui/switch.tsx +10 -0
  159. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/components/ui/textarea.tsx +9 -0
  160. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/index.css +60 -0
  161. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/src/main.tsx +10 -0
  162. karni_cli-0.4.0/tests/fixtures/ui/shadcn-tw4/tsconfig.json +9 -0
  163. karni_cli-0.4.0/tests/fixtures/ui/tailwind3/package.json +6 -0
  164. karni_cli-0.4.0/tests/fixtures/ui/tailwind3/src/index.css +3 -0
  165. karni_cli-0.4.0/tests/fixtures/ui/tailwind3/src/main.tsx +7 -0
  166. karni_cli-0.4.0/tests/fixtures/ui/tailwind3/tailwind.config.ts +32 -0
  167. karni_cli-0.4.0/tests/fixtures/ui/tailwind4/package.json +7 -0
  168. karni_cli-0.4.0/tests/fixtures/ui/tailwind4/src/index.css +8 -0
  169. karni_cli-0.4.0/tests/fixtures/ui/tailwind4/src/main.tsx +7 -0
  170. karni_cli-0.4.0/tests/fixtures/ui/tailwind4/src/theme.css +27 -0
  171. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_add.py +2 -2
  172. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_audit_fixes.py +2 -2
  173. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_check.py +12 -2
  174. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_clidoc.py +1 -1
  175. karni_cli-0.4.0/tests/test_command_states.py +267 -0
  176. karni_cli-0.4.0/tests/test_database.py +308 -0
  177. karni_cli-0.4.0/tests/test_debugging.py +176 -0
  178. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_doctor.py +6 -4
  179. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_errorcodes_cli.py +11 -3
  180. karni_cli-0.4.0/tests/test_explain_text.py +233 -0
  181. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_history.py +4 -3
  182. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_make.py +5 -5
  183. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_make_gate.py +45 -1
  184. karni_cli-0.4.0/tests/test_make_lifecycle.py +114 -0
  185. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_make_units.py +69 -1
  186. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_mcp.py +21 -2
  187. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_new.py +72 -4
  188. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_rules.py +4 -2
  189. karni_cli-0.4.0/tests/test_schema_cli.py +83 -0
  190. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_types.py +1 -1
  191. karni_cli-0.4.0/tests/test_ui_detect.py +703 -0
  192. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_upgrade.py +11 -6
  193. karni_cli-0.4.0/tests/test_why.py +191 -0
  194. karni_cli-0.3.1/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/postcss.config.js +0 -6
  195. karni_cli-0.3.1/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/index.css +0 -11
  196. karni_cli-0.3.1/karni_cli/commands/explain.py +0 -60
  197. karni_cli-0.3.1/karni_cli/mcp_server.py +0 -103
  198. {karni_cli-0.3.1 → karni_cli-0.4.0}/LICENSE +0 -0
  199. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/__init__.py +0 -0
  200. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_agent/__main__.py +0 -0
  201. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_changelog/0.2.0.toml +0 -0
  202. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_changelog/0.3.0.toml +0 -0
  203. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_changelog/__init__.py +0 -0
  204. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_errorcodes/__main__.py +0 -0
  205. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/Makefile +0 -0
  206. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/__init__.py +0 -0
  207. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/asgi.py +0 -0
  208. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/celery.py +0 -0
  209. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/config/wsgi.py +0 -0
  210. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/.dockerignore +0 -0
  211. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/.jscpd.json +0 -0
  212. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/.npmrc +0 -0
  213. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/index.html +0 -0
  214. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/public/favicon.svg +0 -0
  215. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/api/health.ts +0 -0
  216. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/main.tsx +0 -0
  217. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/pages/home.tsx +0 -0
  218. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/src/vite-env.d.ts +0 -0
  219. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/frontend/tsconfig.json +0 -0
  220. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/manage.py +0 -0
  221. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/tests/__init__.py +0 -0
  222. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/_templates/project/{{cookiecutter.project_slug}}/tests/test_smoke.py +0 -0
  223. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/brand.py +0 -0
  224. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/__init__.py +0 -0
  225. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/commands/mcp.py +0 -0
  226. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/config.py +0 -0
  227. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/environment.py +0 -0
  228. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/__init__.py +0 -0
  229. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/generators/naming.py +0 -0
  230. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/history.py +0 -0
  231. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/__init__.py +0 -0
  232. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/changelog.py +0 -0
  233. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/codemod.py +0 -0
  234. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/legacy_agent_files.py +0 -0
  235. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/transforms/rename-export.cjs +0 -0
  236. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v0_2/__init__.py +0 -0
  237. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/__init__.py +0 -0
  238. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/all_objects_to_without.py +0 -0
  239. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/all_with_deleted_to_without.py +0 -0
  240. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/deleted_only_to_without_dead.py +0 -0
  241. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/object_permission_classes_to_access.py +0 -0
  242. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/related_lookups_to_optimize.py +0 -0
  243. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/scoped_related_lookups_to_optimize.py +0 -0
  244. {karni_cli-0.3.1 → karni_cli-0.4.0}/karni_cli/upgrades/v1_0/soft_delete_manager_mixin_to_scoped.py +0 -0
  245. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/__init__.py +0 -0
  246. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/fixtures/schema.fixture.json +0 -0
  247. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/fixtures/upgrade_changelog/0.9.0.toml +0 -0
  248. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_adapters.py +0 -0
  249. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_agent_upgrade.py +0 -0
  250. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_brand.py +0 -0
  251. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_context.py +0 -0
  252. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_docs_tutorial.py +0 -0
  253. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_project.py +0 -0
  254. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_runners.py +0 -0
  255. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_upgrade_codemods.py +0 -0
  256. {karni_cli-0.3.1 → karni_cli-0.4.0}/tests/test_upgrade_typescript.py +0 -0
@@ -19,6 +19,7 @@ packages/karni-cli/karni_cli/_templates/
19
19
  packages/karni-cli/karni_cli/_agent/
20
20
  packages/karni-cli/karni_cli/_changelog/
21
21
  packages/karni-cli/karni_cli/_errorcodes/
22
+ packages/karni-cli/karni_cli/_ui/
22
23
 
23
24
  # Node
24
25
  node_modules/
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: karni-cli
3
- Version: 0.3.1
4
- Summary: The karni command for the Karni Django + React framework: scaffold a project, generate models, viewsets and pages, run it, upgrade it across releases, and give AI coding agents its context, rules and error explanations.
3
+ Version: 0.4.0
4
+ Summary: The karni command: scaffold, generate, run and upgrade Karni projects. Karni is an open-source Django + React framework for building with AI coding agents. Your agent writes only your domain. The API, admin, permissions and React screens come from a package you upgrade, not code it rewrites in each project. One rule set covers Claude Code, Cursor and Copilot. The build fails when an agent hand-writes what Karni ships.
5
5
  Project-URL: Homepage, https://github.com/cbsshekhawat18-lab/karni
6
6
  Project-URL: Documentation, https://cbsshekhawat18-lab.github.io/karni/
7
7
  Project-URL: Repository, https://github.com/cbsshekhawat18-lab/karni
@@ -38,66 +38,73 @@ Description-Content-Type: text/markdown
38
38
 
39
39
  # karni-cli
40
40
 
41
- The `karni` command. Orchestrates a Karni project's own `manage.py` via
42
- subprocess — the CLI itself never imports Django.
41
+ The `karni` command. It runs a Karni project's own `manage.py` as a
42
+ subprocess, so the CLI itself never imports Django.
43
43
 
44
44
  ## Commands
45
- - `karni new <name>` — scaffold a project from the bundled cookiecutter
46
- template (monorepo checkouts use the live `templates/project`; override with
47
- `KARNI_TEMPLATES`).
48
- - `karni add <module>` — add `karni-<module>` to `[project].dependencies` and
45
+ - `karni new <name>`: scaffold a project from the bundled cookiecutter
46
+ template. Monorepo checkouts use the live `templates/project`, and
47
+ `KARNI_TEMPLATES` overrides it.
48
+ - `karni add <module>`: add `karni-<module>` to `[project].dependencies` and
49
49
  wire `karni_<module>` into the `# karni:apps` block in config/settings.py.
50
- Only that marker block is managed; any other layout gets a clear error and
50
+ Only that marker block is managed. Any other layout gets a clear error, and
51
51
  you edit settings manually.
52
52
  Once the module is installed it also records the module's
53
53
  `AGENTS.fragment.md` as `.karni/modules/<module>.md` and refreshes the agent
54
54
  adapters.
55
- - `karni types` — dump the OpenAPI schema (`manage.py spectacular`), generate
55
+ - `karni types`: dump the OpenAPI schema (`manage.py spectacular`), generate
56
56
  `types.ts` (openapi-typescript, project-pinned binary preferred, else
57
57
  `npx openapi-typescript@^7`) and typed hooks (`hooks.ts`) over @karni/ui.
58
- - `karni context` — compact machine-readable inventory (< 200 tokens for a
59
- typical project): APPS (from the `# karni:apps` block) and PAGES (from
60
- `frontend/src/pages/`) read statically, MODELS/VIEWSETS from `manage.py karni_context`,
61
- COMPONENTS/HOOKS from @karni/ui's build-generated `dist/manifest.json`
62
- (omitted when absent — run `npm i @karni/ui` in `frontend/` on the host to
63
- enable them; `--manifest PATH` overrides). Nothing is hand-maintained.
64
- - `karni context --with-history [PATH]` — the same inventory plus a
58
+ - `karni ui detect`: map the frontend's design system (shadcn/ui, a Tailwind
59
+ theme, Material UI, Chakra UI or Mantine) onto @karni/ui's `--karni-*`
60
+ tokens in `karni-theme.css`, plus `karni-primitives.tsx` for shadcn/ui.
61
+ `--dry-run` previews, `--json` reports for agents, and edited files are kept
62
+ unless `--force`.
63
+ - `karni context`: a compact machine-readable inventory (< 200 tokens for a
64
+ typical project). APPS (from the `# karni:apps` block) and PAGES (from
65
+ `frontend/src/pages/`) are read statically, MODELS/VIEWSETS come from `manage.py karni_context`,
66
+ and COMPONENTS/HOOKS from @karni/ui's build-generated `dist/manifest.json`.
67
+ Those two are omitted when the manifest is absent. Run `npm i @karni/ui` in
68
+ `frontend/` on the host to enable them, or override with `--manifest PATH`.
69
+ Nothing is hand-maintained.
70
+ - `karni context --with-history [PATH]`: the same inventory plus a
65
71
  `HISTORY:` brief of what was already asked and decided about PATH (without
66
72
  PATH: the project's recorded decisions) from any history provider that
67
73
  detects a store, e.g. Gigai Chronicle's `.chronicle/`. Capped at ~300
68
74
  tokens (characters ÷ 4), cut at line boundaries with a last line naming the
69
75
  full-brief command. With no store the output is exactly `karni context`'s.
70
76
  See `docs/concepts/history-providers.md`.
71
- - `karni check [-q]` — Django system checks plus `karni.checks` entry-point hooks
77
+ - `karni check [-q]`: Django system checks plus `karni.checks` entry-point hooks
72
78
  (the architecture-test extension surface). A clean run outside CI may end
73
- with one tip on stderr, at most once per project; `-q` suppresses it.
74
- - `karni doctor [-q]` — human-readable environment report: project,
75
- interpreter (`.venv` / compose service), Node/npm, the @karni/ui manifest,
76
- adapter freshness, history providers. May end with the same one-time tip.
77
- - `karni config get|set|list [--global] KEY [VALUE]` — settings
78
- (`suggest_tools`): `.karni/config.toml` in the project (committed) or
79
- `$XDG_CONFIG_HOME/karni/config.toml` with `--global`; the project wins.
79
+ with one tip on stderr, at most once per project. `-q` suppresses it.
80
+ - `karni doctor [-q]`: a human-readable environment report covering the
81
+ project, interpreter (`.venv` / compose service), Node/npm, the @karni/ui
82
+ manifest, adapter freshness and history providers. It may end with the same
83
+ one-time tip.
84
+ - `karni config get|set|list [--global] KEY [VALUE]`: settings
85
+ (`suggest_tools`), kept in `.karni/config.toml` in the project (committed) or
86
+ in `$XDG_CONFIG_HOME/karni/config.toml` with `--global`. The project wins.
80
87
  Machine-local state (e.g. "tip shown") lives in the self-ignoring
81
88
  `.karni/local/`.
82
- - `karni rules [--explain CODE | --procedure NAME | --check FILE... | --json]`
83
- — Karni's conventions from `karni.agent.rules` (no Django needed), and
89
+ - `karni rules [--explain CODE | --procedure NAME | --check FILE... | --json]`:
90
+ Karni's conventions from `karni.agent.rules` (no Django needed), and
84
91
  static single-file checks (exit 1 on violations).
85
- - `karni rules sync [--check] [--force]` — regenerate the thin agent adapters
92
+ - `karni rules sync [--check] [--force]`: regenerate the thin agent adapters
86
93
  (AGENTS.md, CLAUDE.md, Cursor, Windsurf, Copilot, Cline, Aider, Junie) and
87
- slash commands from `karni_cli/agent_adapters.toml`; `--check` is the CI
94
+ slash commands from `karni_cli/agent_adapters.toml`. `--check` is the CI
88
95
  staleness gate.
89
- - `karni mcp` — context, rules and check as MCP tools
96
+ - `karni mcp`: context, rules and check as MCP tools
90
97
  (`pip install 'karni-cli[mcp]'`).
91
98
  - `karni upgrade [--to VERSION] [--from VERSION] [--dry-run] [--allow-dirty] [--list]`
92
99
  moves the project across Karni versions. It reads the karni installed in
93
100
  the project (`--from` overrides), takes every breaking change between that
94
101
  and `--to` from Karni's machine-readable changelog
95
102
  (`packages/karni/karni/changelog/*.toml`, bundled into karni-cli), and runs
96
- their codemods in release order. Python codemods use libcst; TypeScript
97
- codemods use a pinned jscodeshift through `npx` and are skipped, with the
98
- reason, without Node.js. The codemods run on a scratch copy, and the result
103
+ their codemods in release order. Python codemods use libcst. TypeScript
104
+ codemods use a pinned jscodeshift through `npx`, and without Node.js they
105
+ are skipped with the reason. The codemods run on a scratch copy, and the result
99
106
  is written back all at once, or not at all if one fails. `--dry-run` prints
100
- a unified diff and writes nothing. Writing needs a clean git tree;
107
+ a unified diff and writes nothing. Writing needs a clean git tree.
101
108
  `--allow-dirty` overrides that, and is also required outside git. The
102
109
  report lists what was applied (each file and its rewrite count), what could
103
110
  not be fixed automatically (the sites found, with each migration-note link)
@@ -110,7 +117,7 @@ See `docs/agents.md` for the agent surface in full.
110
117
  Artisan-style shortcuts. Each runner runs one project command through the
111
118
  project interpreter (see *Execution environment* below), streams its output
112
119
  and exits with its exit code. `--help` on each names the command it wraps.
113
- Extra arguments pass straight through; put `--` before any that clash with
120
+ Extra arguments pass straight through. Put `--` before any that clash with
114
121
  karni's own options (`--docker`, `--service`, `--help`).
115
122
 
116
123
  | Command | Runs |
@@ -138,25 +145,25 @@ With the compose stack up, `worker` and `beat` already run as services, so
138
145
  ## Generators
139
146
  `karni make <type>` writes the project code a task needs, so neither a person
140
147
  nor an AI agent hand-creates a file a generator makes. `karni make --list`
141
- prints them all; every one is also a hidden Laravel-style alias
148
+ prints them all. Each one is also available as a hidden Laravel-style alias,
142
149
  `karni make:<type>`. The full reference (every option, the field DSL) is
143
150
  [docs/cli.md](https://cbsshekhawat18-lab.github.io/karni/cli/), generated from this CLI.
144
151
 
145
152
  | Command | Writes |
146
153
  |---|---|
147
- | `karni make app NAME` | `NAME/{__init__,apps,models,viewsets}.py`, `migrations/`, `tests/`; wires `# karni:apps` |
148
- | `karni make model NAME [FIELD...]` | `class NAME(BaseModel)` in `<app>/models.py`; `--with viewset,page` / `-a` chains; `--migrate` |
154
+ | `karni make app NAME` | `NAME/{__init__,apps,models,viewsets}.py`, `migrations/`, `tests/`, and wires `# karni:apps` |
155
+ | `karni make model NAME [FIELD...]` | `class NAME(BaseModel)` in `<app>/models.py`, with `--with viewset,page` / `-a` chains and `--migrate` |
149
156
  | `karni make viewset MODEL` | `<Model>ViewSet` with its `access` (`--access authenticated\|public-read\|owned:<field>\|staff\|role:<name>`), `--search` |
150
157
  | `karni make serializer MODEL --reason ...` | `<app>/serializers.py` + `serializer_class` on the viewset |
151
158
  | `karni make migration [NAME]` | runs `manage.py makemigrations [--app] [--name] [--empty]` through the project interpreter |
152
159
  | `karni make task NAME` | `@shared_task` in `<app>/tasks.py` (`--retry N`) |
153
160
  | `karni make schedule NAME` | `every(...)`/`cron(...)` in `<app>/schedules.py` (`--every 15m` / `--cron "0 3 * * *"`, `--task`) |
154
161
  | `karni make event NAME` | a `Signal` in `<app>/events.py` |
155
- | `karni make listener NAME --on ...` | `<app>/listeners.py`: `Model.created\|updated\|deleted` or a custom event; `--queued` |
162
+ | `karni make listener NAME --on ...` | `<app>/listeners.py`: `Model.created\|updated\|deleted` or a custom event, with `--queued` |
156
163
  | `karni make command NAME` | `<app>/management/commands/NAME.py` |
157
164
  | `karni make service NAME` | `<app>/services/NAME.py` (where HTTP clients are allowed) |
158
165
  | `karni make middleware NAME` | `<app>/middleware.py`, wired through `# karni:middleware` |
159
- | `karni make page NAME` | `frontend/src/pages/NAME.tsx` (≤ 50 lines; `--resource R --kind list\|detail\|form`), route in `frontend/src/routes.tsx` |
166
+ | `karni make page NAME` | `frontend/src/pages/NAME.tsx` (≤ 50 lines, `--resource R --kind list\|detail\|form`), route in `frontend/src/routes.tsx` |
160
167
  | `karni make component NAME` | `frontend/src/components/NAME.tsx` claiming a role (`--role`) |
161
168
  | `karni make module NAME` | `modules/karni-NAME/` per docs/modules.md |
162
169
 
@@ -169,23 +176,24 @@ karni make listener notify_organizer --on Event.created --queued
169
176
  karni make schedule nightly-report --cron "0 3 * * *"
170
177
  ```
171
178
 
172
- Every generator: never overwrites without `--force`; `--dry-run` prints the
173
- files and diff and writes nothing; prints each created/modified file plus
174
- notes (the chosen `access`, the route, the next command); resolves the app
175
- from `--app` or the project's only local app; edits existing files only
176
- through marker comments or AST-located appends (an unfamiliar layout is an
177
- error naming the manual step); never imports Django. Generated Python is
178
- clean under ruff (lint and format) and `karni rules --check`, and generated
179
- pages parse and stay within 50 lines — `tests/test_make_gate.py` generates
180
- every type into a fresh scaffold and asserts it (plus `tsc` and the
181
- frontend's ESLint config when `KARNI_TEST_NODE_MODULES` points at an
182
- installed `frontend/node_modules`), then runs the generated backend for real
183
- (makemigrations, migrate, check, requests through the Django test client).
179
+ Every generator follows the same rules. It never overwrites without
180
+ `--force`, and `--dry-run` prints the files and diff and writes nothing. It
181
+ prints each created/modified file plus notes (the chosen `access`, the route,
182
+ the next command). It resolves the app from `--app` or the project's only
183
+ local app. It edits existing files only through marker comments or
184
+ AST-located appends, and an unfamiliar layout is an error naming the manual
185
+ step. It never imports Django. Generated Python is clean under ruff (lint and
186
+ format) and `karni rules --check`, and generated pages parse and stay within
187
+ 50 lines. `tests/test_make_gate.py` generates every type into a fresh scaffold
188
+ and asserts this (plus `tsc` and the frontend's ESLint config when
189
+ `KARNI_TEST_NODE_MODULES` points at an installed `frontend/node_modules`).
190
+ Then it runs the generated backend for real (makemigrations, migrate, check,
191
+ requests through the Django test client).
184
192
 
185
193
  Quote fields that end in `?` in zsh (`'venue:fk:Venue?'`) and choice values
186
194
  joined by `|`.
187
195
 
188
196
  ## Execution environment
189
197
  Commands that run the project resolve, in order: `--docker/--no-docker` flag →
190
- `<project>/.venv` → a running compose service (default `django`; change with
198
+ `<project>/.venv` → a running compose service (default `django`, changed with
191
199
  `--service` or `KARNI_COMPOSE_SERVICE`) → an error naming both remedies.
@@ -1,65 +1,72 @@
1
1
  # karni-cli
2
2
 
3
- The `karni` command. Orchestrates a Karni project's own `manage.py` via
4
- subprocess — the CLI itself never imports Django.
3
+ The `karni` command. It runs a Karni project's own `manage.py` as a
4
+ subprocess, so the CLI itself never imports Django.
5
5
 
6
6
  ## Commands
7
- - `karni new <name>` — scaffold a project from the bundled cookiecutter
8
- template (monorepo checkouts use the live `templates/project`; override with
9
- `KARNI_TEMPLATES`).
10
- - `karni add <module>` — add `karni-<module>` to `[project].dependencies` and
7
+ - `karni new <name>`: scaffold a project from the bundled cookiecutter
8
+ template. Monorepo checkouts use the live `templates/project`, and
9
+ `KARNI_TEMPLATES` overrides it.
10
+ - `karni add <module>`: add `karni-<module>` to `[project].dependencies` and
11
11
  wire `karni_<module>` into the `# karni:apps` block in config/settings.py.
12
- Only that marker block is managed; any other layout gets a clear error and
12
+ Only that marker block is managed. Any other layout gets a clear error, and
13
13
  you edit settings manually.
14
14
  Once the module is installed it also records the module's
15
15
  `AGENTS.fragment.md` as `.karni/modules/<module>.md` and refreshes the agent
16
16
  adapters.
17
- - `karni types` — dump the OpenAPI schema (`manage.py spectacular`), generate
17
+ - `karni types`: dump the OpenAPI schema (`manage.py spectacular`), generate
18
18
  `types.ts` (openapi-typescript, project-pinned binary preferred, else
19
19
  `npx openapi-typescript@^7`) and typed hooks (`hooks.ts`) over @karni/ui.
20
- - `karni context` — compact machine-readable inventory (< 200 tokens for a
21
- typical project): APPS (from the `# karni:apps` block) and PAGES (from
22
- `frontend/src/pages/`) read statically, MODELS/VIEWSETS from `manage.py karni_context`,
23
- COMPONENTS/HOOKS from @karni/ui's build-generated `dist/manifest.json`
24
- (omitted when absent — run `npm i @karni/ui` in `frontend/` on the host to
25
- enable them; `--manifest PATH` overrides). Nothing is hand-maintained.
26
- - `karni context --with-history [PATH]` — the same inventory plus a
20
+ - `karni ui detect`: map the frontend's design system (shadcn/ui, a Tailwind
21
+ theme, Material UI, Chakra UI or Mantine) onto @karni/ui's `--karni-*`
22
+ tokens in `karni-theme.css`, plus `karni-primitives.tsx` for shadcn/ui.
23
+ `--dry-run` previews, `--json` reports for agents, and edited files are kept
24
+ unless `--force`.
25
+ - `karni context`: a compact machine-readable inventory (< 200 tokens for a
26
+ typical project). APPS (from the `# karni:apps` block) and PAGES (from
27
+ `frontend/src/pages/`) are read statically, MODELS/VIEWSETS come from `manage.py karni_context`,
28
+ and COMPONENTS/HOOKS from @karni/ui's build-generated `dist/manifest.json`.
29
+ Those two are omitted when the manifest is absent. Run `npm i @karni/ui` in
30
+ `frontend/` on the host to enable them, or override with `--manifest PATH`.
31
+ Nothing is hand-maintained.
32
+ - `karni context --with-history [PATH]`: the same inventory plus a
27
33
  `HISTORY:` brief of what was already asked and decided about PATH (without
28
34
  PATH: the project's recorded decisions) from any history provider that
29
35
  detects a store, e.g. Gigai Chronicle's `.chronicle/`. Capped at ~300
30
36
  tokens (characters ÷ 4), cut at line boundaries with a last line naming the
31
37
  full-brief command. With no store the output is exactly `karni context`'s.
32
38
  See `docs/concepts/history-providers.md`.
33
- - `karni check [-q]` — Django system checks plus `karni.checks` entry-point hooks
39
+ - `karni check [-q]`: Django system checks plus `karni.checks` entry-point hooks
34
40
  (the architecture-test extension surface). A clean run outside CI may end
35
- with one tip on stderr, at most once per project; `-q` suppresses it.
36
- - `karni doctor [-q]` — human-readable environment report: project,
37
- interpreter (`.venv` / compose service), Node/npm, the @karni/ui manifest,
38
- adapter freshness, history providers. May end with the same one-time tip.
39
- - `karni config get|set|list [--global] KEY [VALUE]` — settings
40
- (`suggest_tools`): `.karni/config.toml` in the project (committed) or
41
- `$XDG_CONFIG_HOME/karni/config.toml` with `--global`; the project wins.
41
+ with one tip on stderr, at most once per project. `-q` suppresses it.
42
+ - `karni doctor [-q]`: a human-readable environment report covering the
43
+ project, interpreter (`.venv` / compose service), Node/npm, the @karni/ui
44
+ manifest, adapter freshness and history providers. It may end with the same
45
+ one-time tip.
46
+ - `karni config get|set|list [--global] KEY [VALUE]`: settings
47
+ (`suggest_tools`), kept in `.karni/config.toml` in the project (committed) or
48
+ in `$XDG_CONFIG_HOME/karni/config.toml` with `--global`. The project wins.
42
49
  Machine-local state (e.g. "tip shown") lives in the self-ignoring
43
50
  `.karni/local/`.
44
- - `karni rules [--explain CODE | --procedure NAME | --check FILE... | --json]`
45
- — Karni's conventions from `karni.agent.rules` (no Django needed), and
51
+ - `karni rules [--explain CODE | --procedure NAME | --check FILE... | --json]`:
52
+ Karni's conventions from `karni.agent.rules` (no Django needed), and
46
53
  static single-file checks (exit 1 on violations).
47
- - `karni rules sync [--check] [--force]` — regenerate the thin agent adapters
54
+ - `karni rules sync [--check] [--force]`: regenerate the thin agent adapters
48
55
  (AGENTS.md, CLAUDE.md, Cursor, Windsurf, Copilot, Cline, Aider, Junie) and
49
- slash commands from `karni_cli/agent_adapters.toml`; `--check` is the CI
56
+ slash commands from `karni_cli/agent_adapters.toml`. `--check` is the CI
50
57
  staleness gate.
51
- - `karni mcp` — context, rules and check as MCP tools
58
+ - `karni mcp`: context, rules and check as MCP tools
52
59
  (`pip install 'karni-cli[mcp]'`).
53
60
  - `karni upgrade [--to VERSION] [--from VERSION] [--dry-run] [--allow-dirty] [--list]`
54
61
  moves the project across Karni versions. It reads the karni installed in
55
62
  the project (`--from` overrides), takes every breaking change between that
56
63
  and `--to` from Karni's machine-readable changelog
57
64
  (`packages/karni/karni/changelog/*.toml`, bundled into karni-cli), and runs
58
- their codemods in release order. Python codemods use libcst; TypeScript
59
- codemods use a pinned jscodeshift through `npx` and are skipped, with the
60
- reason, without Node.js. The codemods run on a scratch copy, and the result
65
+ their codemods in release order. Python codemods use libcst. TypeScript
66
+ codemods use a pinned jscodeshift through `npx`, and without Node.js they
67
+ are skipped with the reason. The codemods run on a scratch copy, and the result
61
68
  is written back all at once, or not at all if one fails. `--dry-run` prints
62
- a unified diff and writes nothing. Writing needs a clean git tree;
69
+ a unified diff and writes nothing. Writing needs a clean git tree.
63
70
  `--allow-dirty` overrides that, and is also required outside git. The
64
71
  report lists what was applied (each file and its rewrite count), what could
65
72
  not be fixed automatically (the sites found, with each migration-note link)
@@ -72,7 +79,7 @@ See `docs/agents.md` for the agent surface in full.
72
79
  Artisan-style shortcuts. Each runner runs one project command through the
73
80
  project interpreter (see *Execution environment* below), streams its output
74
81
  and exits with its exit code. `--help` on each names the command it wraps.
75
- Extra arguments pass straight through; put `--` before any that clash with
82
+ Extra arguments pass straight through. Put `--` before any that clash with
76
83
  karni's own options (`--docker`, `--service`, `--help`).
77
84
 
78
85
  | Command | Runs |
@@ -100,25 +107,25 @@ With the compose stack up, `worker` and `beat` already run as services, so
100
107
  ## Generators
101
108
  `karni make <type>` writes the project code a task needs, so neither a person
102
109
  nor an AI agent hand-creates a file a generator makes. `karni make --list`
103
- prints them all; every one is also a hidden Laravel-style alias
110
+ prints them all. Each one is also available as a hidden Laravel-style alias,
104
111
  `karni make:<type>`. The full reference (every option, the field DSL) is
105
112
  [docs/cli.md](https://cbsshekhawat18-lab.github.io/karni/cli/), generated from this CLI.
106
113
 
107
114
  | Command | Writes |
108
115
  |---|---|
109
- | `karni make app NAME` | `NAME/{__init__,apps,models,viewsets}.py`, `migrations/`, `tests/`; wires `# karni:apps` |
110
- | `karni make model NAME [FIELD...]` | `class NAME(BaseModel)` in `<app>/models.py`; `--with viewset,page` / `-a` chains; `--migrate` |
116
+ | `karni make app NAME` | `NAME/{__init__,apps,models,viewsets}.py`, `migrations/`, `tests/`, and wires `# karni:apps` |
117
+ | `karni make model NAME [FIELD...]` | `class NAME(BaseModel)` in `<app>/models.py`, with `--with viewset,page` / `-a` chains and `--migrate` |
111
118
  | `karni make viewset MODEL` | `<Model>ViewSet` with its `access` (`--access authenticated\|public-read\|owned:<field>\|staff\|role:<name>`), `--search` |
112
119
  | `karni make serializer MODEL --reason ...` | `<app>/serializers.py` + `serializer_class` on the viewset |
113
120
  | `karni make migration [NAME]` | runs `manage.py makemigrations [--app] [--name] [--empty]` through the project interpreter |
114
121
  | `karni make task NAME` | `@shared_task` in `<app>/tasks.py` (`--retry N`) |
115
122
  | `karni make schedule NAME` | `every(...)`/`cron(...)` in `<app>/schedules.py` (`--every 15m` / `--cron "0 3 * * *"`, `--task`) |
116
123
  | `karni make event NAME` | a `Signal` in `<app>/events.py` |
117
- | `karni make listener NAME --on ...` | `<app>/listeners.py`: `Model.created\|updated\|deleted` or a custom event; `--queued` |
124
+ | `karni make listener NAME --on ...` | `<app>/listeners.py`: `Model.created\|updated\|deleted` or a custom event, with `--queued` |
118
125
  | `karni make command NAME` | `<app>/management/commands/NAME.py` |
119
126
  | `karni make service NAME` | `<app>/services/NAME.py` (where HTTP clients are allowed) |
120
127
  | `karni make middleware NAME` | `<app>/middleware.py`, wired through `# karni:middleware` |
121
- | `karni make page NAME` | `frontend/src/pages/NAME.tsx` (≤ 50 lines; `--resource R --kind list\|detail\|form`), route in `frontend/src/routes.tsx` |
128
+ | `karni make page NAME` | `frontend/src/pages/NAME.tsx` (≤ 50 lines, `--resource R --kind list\|detail\|form`), route in `frontend/src/routes.tsx` |
122
129
  | `karni make component NAME` | `frontend/src/components/NAME.tsx` claiming a role (`--role`) |
123
130
  | `karni make module NAME` | `modules/karni-NAME/` per docs/modules.md |
124
131
 
@@ -131,23 +138,24 @@ karni make listener notify_organizer --on Event.created --queued
131
138
  karni make schedule nightly-report --cron "0 3 * * *"
132
139
  ```
133
140
 
134
- Every generator: never overwrites without `--force`; `--dry-run` prints the
135
- files and diff and writes nothing; prints each created/modified file plus
136
- notes (the chosen `access`, the route, the next command); resolves the app
137
- from `--app` or the project's only local app; edits existing files only
138
- through marker comments or AST-located appends (an unfamiliar layout is an
139
- error naming the manual step); never imports Django. Generated Python is
140
- clean under ruff (lint and format) and `karni rules --check`, and generated
141
- pages parse and stay within 50 lines — `tests/test_make_gate.py` generates
142
- every type into a fresh scaffold and asserts it (plus `tsc` and the
143
- frontend's ESLint config when `KARNI_TEST_NODE_MODULES` points at an
144
- installed `frontend/node_modules`), then runs the generated backend for real
145
- (makemigrations, migrate, check, requests through the Django test client).
141
+ Every generator follows the same rules. It never overwrites without
142
+ `--force`, and `--dry-run` prints the files and diff and writes nothing. It
143
+ prints each created/modified file plus notes (the chosen `access`, the route,
144
+ the next command). It resolves the app from `--app` or the project's only
145
+ local app. It edits existing files only through marker comments or
146
+ AST-located appends, and an unfamiliar layout is an error naming the manual
147
+ step. It never imports Django. Generated Python is clean under ruff (lint and
148
+ format) and `karni rules --check`, and generated pages parse and stay within
149
+ 50 lines. `tests/test_make_gate.py` generates every type into a fresh scaffold
150
+ and asserts this (plus `tsc` and the frontend's ESLint config when
151
+ `KARNI_TEST_NODE_MODULES` points at an installed `frontend/node_modules`).
152
+ Then it runs the generated backend for real (makemigrations, migrate, check,
153
+ requests through the Django test client).
146
154
 
147
155
  Quote fields that end in `?` in zsh (`'venue:fk:Venue?'`) and choice values
148
156
  joined by `|`.
149
157
 
150
158
  ## Execution environment
151
159
  Commands that run the project resolve, in order: `--docker/--no-docker` flag →
152
- `<project>/.venv` → a running compose service (default `django`; change with
160
+ `<project>/.venv` → a running compose service (default `django`, changed with
153
161
  `--service` or `KARNI_COMPOSE_SERVICE`) → an error naming both remedies.
@@ -11,7 +11,10 @@ to Karni's machine-readable changelog (packages/karni/karni/changelog ->
11
11
  karni_cli/_changelog), which `karni upgrade` reads: the CLI must know every
12
12
  change up to its own release, whatever karni the project has installed.
13
13
  Likewise Karni's error code registry (packages/karni/karni/errorcodes ->
14
- karni_cli/_errorcodes), which `karni explain` and error messages read.
14
+ karni_cli/_errorcodes), which `karni explain` and error messages read, and
15
+ @karni/ui's token stylesheet (packages/karni-ui/src/styles.css ->
16
+ karni_cli/_ui/styles.css), whose defaults `karni ui detect` lists when a
17
+ project has neither @karni/ui installed nor a monorepo checkout.
15
18
  """
16
19
 
17
20
  from __future__ import annotations
@@ -58,6 +61,15 @@ class TemplateBundleHook(BuildHookInterface):
58
61
  elif not bundled_links.is_file():
59
62
  raise RuntimeError("No karni/links.py to bundle into karni_cli/_errorcodes.")
60
63
 
64
+ styles = pkg_root.parent / "karni-ui" / "src" / "styles.css"
65
+ bundled_styles = pkg_root / "karni_cli" / "_ui" / "styles.css"
66
+ if styles.is_file():
67
+ bundled_styles.parent.mkdir(parents=True, exist_ok=True)
68
+ shutil.copy2(styles, bundled_styles)
69
+ elif not bundled_styles.is_file():
70
+ raise RuntimeError("No @karni/ui styles.css to bundle into karni_cli/_ui.")
71
+ build_data["artifacts"].append("karni_cli/_ui/")
72
+
61
73
  @staticmethod
62
74
  def _bundle_karni(
63
75
  pkg_root: Path, package: str, bundle: str, what: str, build_data: dict[str, Any]
@@ -1,3 +1,3 @@
1
1
  """karni-cli — the ``karni`` command."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.4.0"
@@ -290,7 +290,7 @@ def _python(path: str, text: str) -> list[tuple[Finding, list[int]]]:
290
290
  _finding(
291
291
  "viewset-access",
292
292
  node.lineno,
293
- f"{node.name} declares no `access` — every request is denied",
293
+ f"{node.name} declares no `access`, so every request is denied",
294
294
  ),
295
295
  _class_lines(node),
296
296
  )
@@ -301,7 +301,7 @@ def _python(path: str, text: str) -> list[tuple[Finding, list[int]]]:
301
301
  _finding(
302
302
  "redundant-serializer",
303
303
  node.lineno,
304
- f"{node.name} is a Meta-only ModelSerializer — "
304
+ f"{node.name} is a Meta-only ModelSerializer, and "
305
305
  "BaseViewSet(model=X) already generates it",
306
306
  ),
307
307
  _class_lines(node),
@@ -329,7 +329,7 @@ def _python(path: str, text: str) -> list[tuple[Finding, list[int]]]:
329
329
  _finding(
330
330
  "access-override",
331
331
  lineno,
332
- "filter_queryset_for_user overridden — express row rules in `access`",
332
+ "filter_queryset_for_user overridden: express row rules in `access`",
333
333
  ),
334
334
  candidates,
335
335
  )
@@ -562,7 +562,7 @@ def _ts(path: str, text: str) -> list[Finding]:
562
562
  report(
563
563
  "karni/no-raw-elements",
564
564
  number,
565
- f"raw <{element}> — compose {_REPLACEMENTS[element]} from @karni/ui",
565
+ f"raw <{element}>: compose {_REPLACEMENTS[element]} from @karni/ui",
566
566
  )
567
567
  if "pages" in parts[:-1]:
568
568
  physical = text.split("\n")
@@ -587,7 +587,7 @@ def _ts(path: str, text: str) -> list[Finding]:
587
587
  if _FETCH.search(bare):
588
588
  report("karni/no-direct-http", number, "direct fetch() outside src/api/")
589
589
  if _AXIOS.search(code):
590
- report("karni/no-direct-http", number, "axios import — Karni ships the HTTP layer")
590
+ report("karni/no-direct-http", number, "axios import: Karni ships the HTTP layer")
591
591
  for m in _NEW_TRANSPORT.finditer(bare):
592
592
  report("karni/no-direct-http", number, f"new {m.group(1)}() outside src/api/")
593
593
  return findings
@@ -620,7 +620,7 @@ def check_source(path: str | Path, text: str) -> list[Finding]:
620
620
  except SyntaxError as exc:
621
621
  raise SourceParseError(
622
622
  f"{path}:{exc.lineno}: cannot parse: {exc.msg}. karni check reads code "
623
- "statically; fix the syntax error and run it again."
623
+ "statically. Fix the syntax error and run it again."
624
624
  ) from None
625
625
  findings = _python_optouts(text.splitlines(), found)
626
626
  elif suffix in TS_SUFFIXES:
@@ -288,7 +288,7 @@ def _truncation_line(
288
288
  full_command = getattr(provider, "full_command", None)
289
289
  command = _safe(lambda: full_command(root, path)) if callable(full_command) else None
290
290
  if isinstance(command, str) and command.strip():
291
- return f"… truncated at ~{max_tokens} tokens; full brief: {command.strip()}"
291
+ return f"… truncated at ~{max_tokens} tokens. Full brief: {command.strip()}"
292
292
  return f"… truncated at ~{max_tokens} tokens; {provider.name} holds the full brief"
293
293
 
294
294
 
@@ -221,7 +221,8 @@ def brief_from_pack(prompts: object, knowledge: object) -> str:
221
221
  for i, prompt in enumerate(shaped, start=1):
222
222
  text = prompt.get("text")
223
223
  quoted = '"' + " ".join(text.split()) + '"' if isinstance(text, str) else "(no text)"
224
- lines.append(f"{i}. {_when(prompt.get('ts'))} {prompt.get('churn', '')} — {quoted}")
224
+ label = f"{_when(prompt.get('ts'))} {prompt.get('churn', '')}".strip()
225
+ lines.append(f"{i}. {label}: {quoted}" if label else f"{i}. {quoted}")
225
226
  return "\n".join(_SPACES.sub(" ", line) for line in lines)
226
227
 
227
228
 
@@ -64,7 +64,7 @@ def _literal(node: ast.expr, name: str, path: Path) -> Any:
64
64
  return ast.literal_eval(node)
65
65
  except ValueError:
66
66
  raise OverrideError(
67
- f"{path}:{node.lineno}: {name} must be a literal (strings, lists, dicts) — "
67
+ f"{path}:{node.lineno}: {name} must be a literal (strings, lists, dicts). "
68
68
  "karni.rules.py is read statically and never executed."
69
69
  ) from None
70
70