django-admin-agent 0.1.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 (90) hide show
  1. django_admin_agent-0.1.0/.github/workflows/release.yml +81 -0
  2. django_admin_agent-0.1.0/.github/workflows/tests.yml +95 -0
  3. django_admin_agent-0.1.0/.gitignore +122 -0
  4. django_admin_agent-0.1.0/.pre-commit-config.yaml +32 -0
  5. django_admin_agent-0.1.0/CHANGELOG.md +29 -0
  6. django_admin_agent-0.1.0/CLAUDE.md +146 -0
  7. django_admin_agent-0.1.0/LICENSE +21 -0
  8. django_admin_agent-0.1.0/Makefile +116 -0
  9. django_admin_agent-0.1.0/PKG-INFO +142 -0
  10. django_admin_agent-0.1.0/README.md +104 -0
  11. django_admin_agent-0.1.0/django_admin_agent/__init__.py +19 -0
  12. django_admin_agent-0.1.0/django_admin_agent/admin/__init__.py +0 -0
  13. django_admin_agent-0.1.0/django_admin_agent/admin/build_route_map.py +50 -0
  14. django_admin_agent-0.1.0/django_admin_agent/admin/build_sidebar_context.py +44 -0
  15. django_admin_agent-0.1.0/django_admin_agent/admin/sidebar_admin_site.py +26 -0
  16. django_admin_agent-0.1.0/django_admin_agent/conf.py +43 -0
  17. django_admin_agent-0.1.0/django_admin_agent/py.typed +0 -0
  18. django_admin_agent-0.1.0/django_admin_agent/static/django_admin_agent/admin_agent.js +50 -0
  19. django_admin_agent-0.1.0/django_admin_agent/static/django_admin_agent/admin_tools.js +482 -0
  20. django_admin_agent-0.1.0/django_admin_agent/static/django_admin_agent/ag-ui-web-component.bundle.js +319 -0
  21. django_admin_agent-0.1.0/django_admin_agent/static/django_admin_agent/unfold_shim.js +57 -0
  22. django_admin_agent-0.1.0/django_admin_agent/templates/django_admin_agent/sidebar.html +9 -0
  23. django_admin_agent-0.1.0/django_admin_agent/templatetags/__init__.py +0 -0
  24. django_admin_agent-0.1.0/django_admin_agent/templatetags/django_admin_agent.py +24 -0
  25. django_admin_agent-0.1.0/django_admin_agent/tools/__init__.py +0 -0
  26. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/__init__.py +0 -0
  27. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/get_settings_summary.py +70 -0
  28. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/inspect_modeladmin.py +83 -0
  29. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/list_admin_models.py +44 -0
  30. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/list_installed_apps.py +23 -0
  31. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/list_models.py +35 -0
  32. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/list_signals.py +71 -0
  33. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/list_urls.py +39 -0
  34. django_admin_agent-0.1.0/django_admin_agent/tools/introspect/register.py +25 -0
  35. django_admin_agent-0.1.0/django_admin_agent/tools/register.py +26 -0
  36. django_admin_agent-0.1.0/django_admin_agent/tools/shell/__init__.py +0 -0
  37. django_admin_agent-0.1.0/django_admin_agent/tools/shell/count_model.py +24 -0
  38. django_admin_agent-0.1.0/django_admin_agent/tools/shell/get_model_instance.py +31 -0
  39. django_admin_agent-0.1.0/django_admin_agent/tools/shell/inspect_model_schema.py +56 -0
  40. django_admin_agent-0.1.0/django_admin_agent/tools/shell/query_model.py +49 -0
  41. django_admin_agent-0.1.0/django_admin_agent/tools/shell/register.py +19 -0
  42. django_admin_agent-0.1.0/django_admin_agent/tools/types/__init__.py +0 -0
  43. django_admin_agent-0.1.0/django_admin_agent/tools/utils.py +33 -0
  44. django_admin_agent-0.1.0/django_admin_agent/urls.py +38 -0
  45. django_admin_agent-0.1.0/django_admin_agent/version.py +5 -0
  46. django_admin_agent-0.1.0/docs/admin-wiring.md +92 -0
  47. django_admin_agent-0.1.0/docs/configuration.md +76 -0
  48. django_admin_agent-0.1.0/docs/index.md +63 -0
  49. django_admin_agent-0.1.0/docs/installation.md +138 -0
  50. django_admin_agent-0.1.0/docs/reference.md +57 -0
  51. django_admin_agent-0.1.0/docs/tools.md +153 -0
  52. django_admin_agent-0.1.0/docs/unfold.md +66 -0
  53. django_admin_agent-0.1.0/mkdocs.yml +81 -0
  54. django_admin_agent-0.1.0/pyproject.toml +147 -0
  55. django_admin_agent-0.1.0/scripts/release-publish.sh +211 -0
  56. django_admin_agent-0.1.0/tests/__init__.py +0 -0
  57. django_admin_agent-0.1.0/tests/admin/__init__.py +0 -0
  58. django_admin_agent-0.1.0/tests/admin/test_build_route_map.py +25 -0
  59. django_admin_agent-0.1.0/tests/admin/test_build_sidebar_context.py +28 -0
  60. django_admin_agent-0.1.0/tests/admin/test_sidebar_admin_site.py +30 -0
  61. django_admin_agent-0.1.0/tests/admin/urls_no_admin.py +11 -0
  62. django_admin_agent-0.1.0/tests/conftest.py +0 -0
  63. django_admin_agent-0.1.0/tests/conftest_settings.py +57 -0
  64. django_admin_agent-0.1.0/tests/e2e/__init__.py +0 -0
  65. django_admin_agent-0.1.0/tests/e2e/conftest.py +22 -0
  66. django_admin_agent-0.1.0/tests/e2e/scripted_model.py +87 -0
  67. django_admin_agent-0.1.0/tests/e2e/settings.py +61 -0
  68. django_admin_agent-0.1.0/tests/e2e/templates/admin/base_site.html +7 -0
  69. django_admin_agent-0.1.0/tests/e2e/test_vanilla.py +41 -0
  70. django_admin_agent-0.1.0/tests/e2e/urls.py +12 -0
  71. django_admin_agent-0.1.0/tests/templatetags/__init__.py +0 -0
  72. django_admin_agent-0.1.0/tests/templatetags/test_django_admin_agent.py +28 -0
  73. django_admin_agent-0.1.0/tests/test_conf.py +33 -0
  74. django_admin_agent-0.1.0/tests/test_urls.py +33 -0
  75. django_admin_agent-0.1.0/tests/test_version.py +8 -0
  76. django_admin_agent-0.1.0/tests/testapp/__init__.py +0 -0
  77. django_admin_agent-0.1.0/tests/testapp/admin.py +37 -0
  78. django_admin_agent-0.1.0/tests/testapp/apps.py +9 -0
  79. django_admin_agent-0.1.0/tests/testapp/migrations/0001_initial.py +65 -0
  80. django_admin_agent-0.1.0/tests/testapp/migrations/__init__.py +0 -0
  81. django_admin_agent-0.1.0/tests/testapp/models.py +25 -0
  82. django_admin_agent-0.1.0/tests/testapp/urls.py +8 -0
  83. django_admin_agent-0.1.0/tests/tools/__init__.py +0 -0
  84. django_admin_agent-0.1.0/tests/tools/introspect/__init__.py +0 -0
  85. django_admin_agent-0.1.0/tests/tools/introspect/test_introspect_tools.py +143 -0
  86. django_admin_agent-0.1.0/tests/tools/shell/__init__.py +0 -0
  87. django_admin_agent-0.1.0/tests/tools/shell/test_shell_tools.py +95 -0
  88. django_admin_agent-0.1.0/tests/tools/test_register.py +53 -0
  89. django_admin_agent-0.1.0/tests/tools/test_utils.py +23 -0
  90. django_admin_agent-0.1.0/uv.lock +5046 -0
@@ -0,0 +1,81 @@
1
+ name: release
2
+
3
+ # Main-triggered: every merge to `main` runs `release-publish-prepare`, which
4
+ # short-circuits to a no-op unless the version in source has been bumped past
5
+ # the most recent `vX.Y.Z` tag.
6
+ on:
7
+ push:
8
+ branches: [main]
9
+
10
+ concurrency:
11
+ group: release-main
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ release:
16
+ name: release
17
+ runs-on: ubuntu-latest
18
+ environment:
19
+ name: pypi
20
+ url: https://pypi.org/p/django-admin-agent
21
+ permissions:
22
+ id-token: write # PyPI OIDC trusted publishing
23
+ contents: write # tag push + gh release create
24
+ steps:
25
+ - uses: actions/checkout@v5
26
+ with:
27
+ fetch-depth: 0
28
+ - uses: astral-sh/setup-uv@v7
29
+ with:
30
+ enable-cache: true
31
+ - name: Install
32
+ run: uv sync --all-groups
33
+
34
+ - name: Prepare release (version check, test, build dist)
35
+ id: prepare
36
+ run: make release-publish-prepare
37
+
38
+ - name: Publish to PyPI
39
+ if: steps.prepare.outputs.released == 'true'
40
+ uses: pypa/gh-action-pypi-publish@release/v1
41
+
42
+ - name: Tag + GitHub Release
43
+ if: steps.prepare.outputs.released == 'true'
44
+ env:
45
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+ run: make release-publish-finalize
47
+
48
+ # Embed a coverage-badge JSON into the docs build so `mkdocs gh-deploy`
49
+ # carries it to the gh-pages root, where the README's shields.io endpoint
50
+ # badge reads it. `release-publish-prepare`'s pytest run wrote .coverage
51
+ # to the workspace root.
52
+ - name: Render coverage badge for the docs site
53
+ if: steps.prepare.outputs.released == 'true' && hashFiles('mkdocs.yml') != ''
54
+ run: |
55
+ set -euo pipefail
56
+ uv run coverage xml -o coverage.xml
57
+ python - <<'PY'
58
+ import json
59
+ import xml.etree.ElementTree as ET
60
+
61
+ pct = round(float(ET.parse("coverage.xml").getroot().attrib["line-rate"]) * 100, 1)
62
+ color = (
63
+ "brightgreen" if pct >= 95
64
+ else "green" if pct >= 85
65
+ else "yellow" if pct >= 70
66
+ else "red"
67
+ )
68
+ payload = {"schemaVersion": 1, "label": "coverage", "message": f"{pct}%", "color": color}
69
+ with open("docs/coverage.json", "w") as fh:
70
+ json.dump(payload, fh)
71
+ fh.write("\n")
72
+ print(json.dumps(payload))
73
+ PY
74
+
75
+ - name: Deploy docs + coverage badge to gh-pages
76
+ if: steps.prepare.outputs.released == 'true' && hashFiles('mkdocs.yml') != ''
77
+ run: |
78
+ git config user.name 'github-actions[bot]'
79
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
80
+ DJANGO_SETTINGS_MODULE=tests.conftest_settings \
81
+ uv run mkdocs gh-deploy --force --clean --config-file mkdocs.yml
@@ -0,0 +1,95 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: tests-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint:
14
+ name: lint
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v5
18
+ - uses: astral-sh/setup-uv@v7
19
+ with:
20
+ enable-cache: true
21
+ - name: Install
22
+ run: uv sync --all-groups
23
+ - name: ruff check
24
+ run: uv run ruff check .
25
+ - name: ruff format check
26
+ run: uv run ruff format --check --diff .
27
+ - name: ty
28
+ run: uv run ty check django_admin_agent
29
+
30
+ docs:
31
+ name: docs build
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v5
35
+ - uses: astral-sh/setup-uv@v7
36
+ with:
37
+ enable-cache: true
38
+ - name: Install with docs group
39
+ run: uv sync --all-groups
40
+ - name: mkdocs build --strict
41
+ run: DJANGO_SETTINGS_MODULE=tests.conftest_settings uv run mkdocs build --strict
42
+
43
+ test:
44
+ name: ${{ matrix.python-version }} / Django ${{ matrix.django-version }}
45
+ runs-on: ubuntu-latest
46
+ strategy:
47
+ fail-fast: false
48
+ matrix:
49
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
50
+ django-version: ["4.2", "5.0", "5.1", "5.2", "6.0"]
51
+ exclude:
52
+ # Django 4.2 supports Python 3.8–3.12
53
+ - python-version: "3.13"
54
+ django-version: "4.2"
55
+ - python-version: "3.14"
56
+ django-version: "4.2"
57
+ # Django 5.0 supports Python 3.10–3.12
58
+ - python-version: "3.13"
59
+ django-version: "5.0"
60
+ - python-version: "3.14"
61
+ django-version: "5.0"
62
+ # Django 5.1 supports Python 3.10–3.13
63
+ - python-version: "3.14"
64
+ django-version: "5.1"
65
+ # Django 6.0 requires Python 3.12+
66
+ - python-version: "3.10"
67
+ django-version: "6.0"
68
+ - python-version: "3.11"
69
+ django-version: "6.0"
70
+
71
+ steps:
72
+ - uses: actions/checkout@v5
73
+ - uses: astral-sh/setup-uv@v7
74
+ with:
75
+ enable-cache: true
76
+ - name: Install Python ${{ matrix.python-version }}
77
+ run: uv python install ${{ matrix.python-version }}
78
+ - name: Sync dev deps
79
+ run: uv sync --all-groups --python ${{ matrix.python-version }}
80
+ - name: Pin Django ${{ matrix.django-version }}
81
+ run: uv pip install --reinstall-package django "django~=${{ matrix.django-version }}.0"
82
+ - name: pytest
83
+ run: |
84
+ uv run --no-sync pytest \
85
+ --cov-report=xml:coverage.xml \
86
+ --cov-report=html:htmlcov
87
+ - name: Upload coverage artifact
88
+ if: always()
89
+ uses: actions/upload-artifact@v5
90
+ with:
91
+ name: coverage-py${{ matrix.python-version }}-django${{ matrix.django-version }}
92
+ path: |
93
+ coverage.xml
94
+ htmlcov/
95
+ if-no-files-found: ignore
@@ -0,0 +1,122 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+ coverage*.json
52
+ coverage*.xml
53
+
54
+ # release.yml renders this at workflow-time and drops it under docs/ so
55
+ # mkdocs gh-deploy carries it to the gh-pages root.
56
+ docs/coverage.json
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Sphinx documentation
69
+ docs/_build/
70
+
71
+ # Environments
72
+ .env
73
+ .venv
74
+ env/
75
+ venv/
76
+ ENV/
77
+ env.bak/
78
+ venv.bak/
79
+
80
+ # Virtualenv-style scripts dirs (we re-include the repo-root scripts/ directory).
81
+ [Ss]cripts
82
+ !/scripts/
83
+ pyvenv.cfg
84
+ .Python
85
+ [Bb]in
86
+ [Ii]nclude
87
+ [Ll]ib
88
+ [Ll]ib64
89
+ [Ll]ocal
90
+
91
+ # uv
92
+ .uv-cache/
93
+
94
+ # mkdocs
95
+ /site
96
+
97
+ # Type checkers
98
+ .mypy_cache/
99
+ .dmypy.json
100
+ dmypy.json
101
+ .pyre/
102
+ .pytype/
103
+ .ruff_cache/
104
+ .ty_cache/
105
+
106
+ # IDE
107
+ .vscode/
108
+ .idea/
109
+ *.iml
110
+ *.code-workspace
111
+ .history/
112
+
113
+ # OS
114
+ .DS_Store
115
+ Thumbs.db
116
+
117
+ # Release metadata (rendered by scripts/release-publish.sh during CI)
118
+ .release-metadata/
119
+
120
+ # AI-specific items
121
+ .ai/
122
+ .claude/
@@ -0,0 +1,32 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ args: ["--maxkb=2000"]
10
+
11
+ - repo: local
12
+ hooks:
13
+ - id: ruff-check
14
+ name: "ruff check --fix"
15
+ entry: make lint-fix
16
+ pass_filenames: false
17
+ language: system
18
+ stages: [pre-commit]
19
+
20
+ - id: ruff-format
21
+ name: "ruff format"
22
+ entry: make format
23
+ pass_filenames: false
24
+ language: system
25
+ stages: [pre-commit]
26
+
27
+ - id: backend-type-check
28
+ name: "type check (ty)"
29
+ entry: make type-check
30
+ pass_filenames: false
31
+ language: system
32
+ stages: [pre-commit]
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is 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.1.0] — 2026-06-01
11
+
12
+ ### Added
13
+ - Chat-agent sidebar for the Django admin, built on `django-ag-ui` and the
14
+ vendored `@artooi/ag-ui-web-component` `<ag-ui-chat>` bundle.
15
+ - Default server-side tool registry (`build_default_registry`) of read-only
16
+ `shell.*` (query / count / fetch / inspect-schema) and `introspect.*`
17
+ (model + admin registration) tools, plus `register_*_tools` builders and
18
+ `get_urls()` to mount the agent endpoint.
19
+ - Frontend admin tool layer: `nav.*` navigation and DOM-driving handlers
20
+ (fill field, click, apply changelist filter) with visible animations, and a
21
+ confirmation modal gating `x-destructive` tools.
22
+ - Sidebar wiring: the `{% django_admin_agent_sidebar %}` template tag and a
23
+ `SidebarAdminSite` (`each_context`) path, with an admin route + page map.
24
+ - Django Unfold compatibility shim (runtime JS; lazy, guarded Python).
25
+ - Optional `[mcp]` extra exposing the admin tools as an HTTP MCP server via
26
+ `djangorestframework-mcp-server`.
27
+
28
+ [Unreleased]: https://github.com/Artui/django-admin-agent/compare/v0.1.0...HEAD
29
+ [0.1.0]: https://github.com/Artui/django-admin-agent/releases/tag/v0.1.0
@@ -0,0 +1,146 @@
1
+ # Repo conventions for `django-admin-agent`
2
+
3
+ Single source of truth for how to write code here. Rules are non-negotiable
4
+ unless flagged as a heuristic.
5
+
6
+ ## What this package is
7
+
8
+ An always-present chat-agent sidebar embedded in the Django admin. It depends
9
+ on two sibling packages:
10
+ - **`django-ag-ui`** — the Django ↔ Pydantic-AI ↔ AG-UI bridge. Provides
11
+ `ToolRegistry`, the `@tool` decorator, `DjangoAGUIView`, `get_urls`, the
12
+ audit logger. This package *builds on* those; it does not reimplement them.
13
+ - **`@artooi/ag-ui-web-component`** — the `<ag-ui-chat>` Web Component. We
14
+ vendor its built bundle into `django_admin_agent/static/django_admin_agent/`
15
+ and load it from the admin templates.
16
+
17
+ This package adds: server-side admin tools (`shell.*` / `introspect.*` /
18
+ `nav.*`), the admin-specific frontend tool handlers (DOM driving), the
19
+ `SidebarAdminSite` / `each_context` / template-tag plumbing, and an Unfold
20
+ compatibility shim.
21
+
22
+ The design is at `/Users/arturveres/code/opensource/docs/plans/django-ag-ui-plan.md`.
23
+
24
+ ## Local development
25
+
26
+ `django-ag-ui` is resolved from PyPI via the plain `django-ag-ui>=0.1` pin in
27
+ `pyproject.toml` (so CI, which has no sibling checkout, syncs cleanly). To
28
+ develop both packages together against a local checkout, add a `[tool.uv.sources]`
29
+ override *locally* (do not commit it) and `uv sync`:
30
+
31
+ ```toml
32
+ [tool.uv.sources]
33
+ django-ag-ui = { path = "../django-ag-ui", editable = true }
34
+ ```
35
+
36
+ ### The vendored web-component bundle
37
+
38
+ `django_admin_agent/static/django_admin_agent/ag-ui-web-component.bundle.js` is
39
+ a **build artefact**, not hand-written — it is esbuild's minified output from
40
+ `@artooi/ag-ui-web-component` (TypeScript source), with `@ag-ui/*` inlined.
41
+
42
+ The bundle is **version-pinned** in the `Makefile` (`WEB_COMPONENT_VERSION`).
43
+ That pin is the source of truth:
44
+
45
+ - **Release** re-vendors the pinned published version. `make
46
+ release-publish-prepare` runs `make vendor-bundle-release` (which `npm pack`s
47
+ `@artooi/ag-ui-web-component@$(WEB_COMPONENT_VERSION)` and copies its built
48
+ bundle) *before* building the wheel. So a released wheel always ships exactly
49
+ the pinned bundle — ongoing web-component changes can never retroactively
50
+ break a released `django-admin-agent`, and bumping the bundle is a deliberate
51
+ `WEB_COMPONENT_VERSION` change + release.
52
+ - **Dev** uses `make vendor-bundle` to copy from the sibling
53
+ `../ag-ui-web-component/dist/` checkout. The committed copy is a convenience
54
+ (so `git clone` + `runserver` works) and may drift; the release re-vendor is
55
+ the authoritative refresh.
56
+
57
+ To adopt a new web-component version: bump `WEB_COMPONENT_VERSION`, run `make
58
+ vendor-bundle-release` (or `vendor-bundle` in dev), commit, release.
59
+
60
+ ## Commands
61
+
62
+ | Target | What it does |
63
+ | --- | --- |
64
+ | `make init` | `uv sync --all-groups` + install pre-commit hooks |
65
+ | `make test` | pytest with 100% line+branch coverage gate |
66
+ | `make lint` | `ruff check .` + `ty check django_admin_agent` |
67
+ | `make format` | `ruff format .` |
68
+ | `make docs-build` | `mkdocs build --strict` |
69
+ | `make release-bump VERSION=X.Y.Z` | rewrite `version.py` + promote CHANGELOG |
70
+
71
+ ## Structural rules
72
+
73
+ 1. **One exported class or function per file.** File name = `snake_case` of the
74
+ symbol. `constants.py` is the only multi-export file (enums + constants).
75
+ 2. **Private one-file helpers** stay local with a leading `_`.
76
+ 3. **Cross-file non-exported helpers** go in a sibling `utils.py`.
77
+ 4. **Top-level imports only.** Optional deps (`unfold`, `rest_framework_mcp`)
78
+ import lazily inside function bodies with a clear `ImportError` message.
79
+ 5. **Full type annotations** on every signature. `Any` only at Django ORM /
80
+ admin boundaries where the type genuinely is `Any`.
81
+ 6. **`__init__.py` is the only re-export point.** Internal modules import from
82
+ leaf paths, never from the package `__init__`.
83
+ 7. **Always `from __future__ import annotations`.** Python 3.10+, no PEP 695
84
+ `type` statements, no `match`.
85
+ 8. **Absolute imports only**, ordered stdlib → third-party → first-party
86
+ (`django_admin_agent`). Never relative.
87
+ 9. **Types and functionality in separate sub-packages** (`types/` siblings).
88
+
89
+ ## Reuse, don't reimplement
90
+
91
+ The agent loop, tool registry, AG-UI transport, and audit logger live in
92
+ `django-ag-ui`. Import them:
93
+ - `django_ag_ui.ToolRegistry`, `django_ag_ui.tool` (the `@tool` decorator),
94
+ `django_ag_ui.ToolCategory`
95
+ - `django_ag_ui.DjangoAGUIView`, `django_ag_ui.get_urls`
96
+ - `django_ag_ui.AuditLogger` and friends
97
+
98
+ This package contributes *tools* and *admin wiring*, not transport.
99
+
100
+ ## No module-level or class-level mutable state
101
+
102
+ State lives on instances. No module-level mutable singletons or class-body
103
+ mutables; initialise in `__init__`. A project may mount more than one sidebar.
104
+
105
+ ## Tests
106
+
107
+ - `make test` runs pytest with `--cov=django_admin_agent --cov-fail-under=100`
108
+ (line + branch). Restructure rather than `# pragma: no cover`.
109
+ - Layout mirrors the source tree under `tests/`.
110
+ - `tests/conftest_settings.py` is the Django settings module (admin + auth +
111
+ sessions installed). Fixture admin apps live under `tests/`.
112
+ - Async tests: `async def test_...` (pytest-asyncio auto mode). DB tests:
113
+ `@pytest.mark.django_db`.
114
+
115
+ ## Lint and types
116
+
117
+ - `make lint` runs `ruff check .` + `ty check django_admin_agent`. CI fails on
118
+ either. `ruff format` is the layout source of truth.
119
+ - Pre-commit runs `make lint-fix`, `make format`, `make type-check`. Never
120
+ `--no-verify`.
121
+
122
+ ## Boundaries
123
+
124
+ - Core imports `django.contrib.admin`; that is expected here (unlike
125
+ `django-ag-ui`, which must stay admin-free).
126
+ - `unfold` is never imported at module load — the shim is JS, runtime-detected
127
+ in the browser; any Python Unfold introspection is lazy + guarded.
128
+ - The `[mcp]` extra plugs into `djangorestframework-mcp-server`; import it
129
+ lazily.
130
+
131
+ ## Compatibility floor
132
+
133
+ | Component | Floor | Tested |
134
+ | --- | --- | --- |
135
+ | Python | 3.10 | 3.10–3.14 |
136
+ | Django | 4.2 LTS | 4.2, 5.0, 5.1, 5.2, 6.0 |
137
+ | django-ag-ui | 0.1 | from PyPI (`>=0.1`) |
138
+ | Django Unfold (supported, optional) | 0.40 | latest in matrix |
139
+
140
+ ## Branching & releases
141
+
142
+ Branch-first for every feature/bump; never commit to `main` locally. Releases
143
+ are merge-to-main triggered via `.github/workflows/release.yml` →
144
+ `scripts/release-publish.sh` (version source: `django_admin_agent/version.py`).
145
+ OIDC trusted publishing to PyPI; `mkdocs gh-deploy` to gh-pages. Cut a release
146
+ with `make release-bump VERSION=X.Y.Z`, edit CHANGELOG, PR, merge.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artur Veres
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,116 @@
1
+ .PHONY: help init test lint lint-fix format format-check type-check deps-bump docs-serve docs-build vendor-bundle vendor-bundle-release release-bump release-publish release-publish-prepare release-publish-finalize
2
+
3
+ help:
4
+ @echo "Available targets:"
5
+ @echo " init Sync deps (all groups) and install pre-commit hooks"
6
+ @echo " test Run pytest with coverage (100% required)"
7
+ @echo " lint Run ruff check + ty check"
8
+ @echo " lint-fix Auto-fix lint issues with ruff"
9
+ @echo " format Format with ruff"
10
+ @echo " format-check Verify formatting"
11
+ @echo " type-check Run ty over the package"
12
+ @echo " deps-bump Upgrade pinned dependencies"
13
+ @echo " docs-serve Live-reload docs at http://localhost:8000 (needs mkdocs.yml)"
14
+ @echo " docs-build Build docs into ./site (strict — fails on broken links)"
15
+ @echo " vendor-bundle Copy the web-component bundle from the sibling checkout (dev)"
16
+ @echo " vendor-bundle-release Vendor the PINNED published web-component bundle (release)"
17
+ @echo " release-bump Bump version files + CHANGELOG. Usage: make release-bump VERSION=X.Y.Z"
18
+ @echo " release-publish prepare → uv publish → finalize (workstation release)"
19
+ @echo " release-publish-prepare Run by release.yml on push to main (no-op unless bumped)"
20
+ @echo " release-publish-finalize Tag vX.Y.Z + create GitHub Release after PyPI publish"
21
+
22
+ init:
23
+ uv sync --all-groups
24
+ uv run pre-commit install
25
+
26
+ test:
27
+ uv run pytest
28
+
29
+ # Browser end-to-end suite: Playwright drives a live admin through the sidebar.
30
+ # Separate settings + no coverage gate (it exercises JS + the server over HTTP).
31
+ test-e2e:
32
+ DJANGO_ALLOW_ASYNC_UNSAFE=1 uv run pytest tests/e2e --ds=tests.e2e.settings -o addopts="" -p no:cacheprovider
33
+
34
+ lint:
35
+ uv run ruff check .
36
+ uv run ty check django_admin_agent
37
+
38
+ lint-fix:
39
+ uv run ruff check --fix .
40
+
41
+ format:
42
+ uv run ruff format .
43
+
44
+ format-check:
45
+ uv run ruff format --check --diff .
46
+
47
+ type-check:
48
+ uv run ty check django_admin_agent
49
+
50
+ deps-bump:
51
+ uvx uv-upx upgrade run --profile with_pinned
52
+
53
+ docs-serve:
54
+ uv run --group docs mkdocs serve
55
+
56
+ docs-build:
57
+ uv run --group docs mkdocs build --strict
58
+
59
+ # The web-component bundle is a vendored build artefact. The PINNED version
60
+ # below is the single source of truth: a release re-vendors exactly this
61
+ # version (see release-publish-prepare), so a published wheel never depends on
62
+ # the live web-component source and ongoing component changes cannot
63
+ # retroactively affect a released django-admin-agent.
64
+ WEB_COMPONENT_PKG := @artooi/ag-ui-web-component
65
+ WEB_COMPONENT_VERSION := 0.1.1
66
+ BUNDLE_DEST := django_admin_agent/static/django_admin_agent/ag-ui-web-component.bundle.js
67
+
68
+ # Dev: copy the locally-built bundle from the sibling checkout (run
69
+ # `make build` in ../ag-ui-web-component first). Convenient, may drift — the
70
+ # release re-vendor is the authoritative refresh.
71
+ vendor-bundle:
72
+ cp ../ag-ui-web-component/dist/ag-ui-web-component.bundle.js $(BUNDLE_DEST)
73
+ @echo "Vendored ag-ui-web-component.bundle.js from the sibling checkout (dev)."
74
+
75
+ # Release: fetch the PINNED published version and vendor its built bundle. Run
76
+ # automatically by release-publish-prepare so every wheel ships exactly
77
+ # $(WEB_COMPONENT_PKG)@$(WEB_COMPONENT_VERSION). Fails loudly if the pinned
78
+ # version cannot be fetched — better than shipping a stale bundle.
79
+ vendor-bundle-release:
80
+ @set -e; tmp="$$(mktemp -d)"; \
81
+ ( cd "$$tmp" && npm pack "$(WEB_COMPONENT_PKG)@$(WEB_COMPONENT_VERSION)" >/dev/null ); \
82
+ tar -xzf "$$tmp"/*.tgz -C "$$tmp"; \
83
+ cp "$$tmp/package/dist/ag-ui-web-component.bundle.js" "$(BUNDLE_DEST)"; \
84
+ rm -rf "$$tmp"; \
85
+ echo "Vendored $(WEB_COMPONENT_PKG)@$(WEB_COMPONENT_VERSION) bundle (release)."
86
+
87
+ release-bump:
88
+ @if [ -z "$(VERSION)" ]; then \
89
+ echo "Usage: make release-bump VERSION=X.Y.Z"; exit 1; \
90
+ fi
91
+ uvx bump-my-version bump --new-version "$(VERSION)" patch
92
+ @echo ""
93
+ @echo "Bumped to $(VERSION). Edit CHANGELOG.md to fill the new section,"
94
+ @echo "review with 'git diff', then run 'make release-publish'."
95
+
96
+ # Release pipeline. Version lives in django_admin_agent/version.py
97
+ # (pyproject pulls it in via [tool.hatch.version] dynamic).
98
+ RELEASE_PACKAGE_NAME := django-admin-agent
99
+ RELEASE_VERSION_FILES := django_admin_agent/version.py|^__version__[^=]*= *
100
+
101
+ release-publish: vendor-bundle-release
102
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
103
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
104
+ bash scripts/release-publish.sh all
105
+
106
+ # Re-vendor the pinned bundle BEFORE the script builds the wheel, so the
107
+ # published artefact always carries a fresh, version-pinned bundle.
108
+ release-publish-prepare: vendor-bundle-release
109
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
110
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
111
+ bash scripts/release-publish.sh prepare
112
+
113
+ release-publish-finalize:
114
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
115
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
116
+ bash scripts/release-publish.sh finalize