|
| 1 | +<!-- Generic guidance for all coding agents (Claude Code, Zed, Cursor, etc.) --> |
| 2 | + |
| 3 | +# Kolibri Development Guide for AI Coding Agents |
| 4 | + |
| 5 | +**Project:** Kolibri - Offline learning platform for low-resource communities |
| 6 | +**Stack:** Python/Django backend, Vue.js 2.7 frontend, pytest/Jest testing |
| 7 | +**Platforms:** Linux, Windows, Mac, Android (via python-for-android) |
| 8 | + |
| 9 | +## Quick Start |
| 10 | + |
| 11 | +```bash |
| 12 | +pip install -r requirements/dev.txt # Python deps |
| 13 | +pnpm install # Node deps |
| 14 | +pre-commit install # Required — commits fail without this |
| 15 | +export KOLIBRI_RUN_MODE=dev |
| 16 | +kolibri manage migrate # Database migrations |
| 17 | +``` |
| 18 | + |
| 19 | +Dev servers (run in separate terminals): |
| 20 | +```bash |
| 21 | +pnpm run python-devserver # Django on port 8000 |
| 22 | +pnpm run watch # Webpack watcher |
| 23 | +``` |
| 24 | + |
| 25 | +→ Full setup: `docs/getting_started.rst` | Architecture: `docs/stack.rst` |
| 26 | + |
| 27 | +## Critical Gotchas |
| 28 | + |
| 29 | +### ⚠️ BEFORE Writing Any Vue Component, Search for Existing Ones |
| 30 | +Do not create a new component without first searching for an existing solution: |
| 31 | +1. **Kolibri Design System** ([docs](https://design-system.learningequality.org/)) — `KButton`, `KCircularLoader`, `KTextbox`, `KSelect`, `KModal`, `KCheckbox`, `KIcon`, etc. |
| 32 | +2. **`packages/kolibri/components/`** — `CoreTable`, `AuthMessage`, `BottomAppBar`, `AppBar`, etc. |
| 33 | +3. **`packages/kolibri-common/components/`** — `AccordionContainer`, `BaseToolbar`, etc. |
| 34 | + |
| 35 | +Use existing components (e.g., `CoreTable` for tabular data, `KCircularLoader` for loading states). If one does 80% of what you need, wrap it — do not rewrite. |
| 36 | + |
| 37 | +### ⚠️ Use Theme Tokens, Not Hard-Coded Colors |
| 38 | +Never use raw color values. Access theme colors via `$themeTokens` and `$themePalette`: |
| 39 | +```vue |
| 40 | +<template> |
| 41 | + <div :style="{ color: $themeTokens.text, backgroundColor: $themeTokens.surface }"> |
| 42 | + <span :style="{ color: $themeTokens.annotation }">secondary text</span> |
| 43 | + </div> |
| 44 | +</template> |
| 45 | +``` |
| 46 | +For computed dynamic styles, use `$computedClass`. See `docs/frontend_architecture/core.rst`. |
| 47 | + |
| 48 | +### ⚠️ Style Blocks, Not Inline — RTL Depends On It |
| 49 | +Non-dynamic styles go in `<style>` blocks. RTLCSS auto-flips directional properties (`padding-left` → `padding-right`) in style blocks but **cannot flip inline styles**. Dynamic directional styles must check `isRtl`. → `docs/i18n.rst` |
| 50 | + |
| 51 | +### ⚠️ Composition API, Not Options API |
| 52 | +New components must use `setup()`. Do not use Options API (`data()`, `computed:`, `methods:`). |
| 53 | + |
| 54 | +### ⚠️ No New Vuex — Use Composables |
| 55 | +Vuex is deprecated. Use Vue composables for state. → `docs/frontend_architecture/composables.rst`, `docs/frontend_architecture/vuex.rst` |
| 56 | + |
| 57 | +### ⚠️ Use `responsive-window` / `responsive-element`, Not Media Queries |
| 58 | +Do not use CSS `@media` queries. Kolibri runs on Android and varied screen sizes. Use the `responsive-window` or `responsive-element` system for responsive layouts. |
| 59 | + |
| 60 | +### ⚠️ Internationalize All User-Visible Text |
| 61 | +Use `createTranslator` — never hard-code strings in templates: |
| 62 | +```javascript |
| 63 | +const strings = createTranslator('QuizStrings', { |
| 64 | + title: { message: 'Quiz Results', context: 'Page heading' }, |
| 65 | +}); |
| 66 | +// In setup(), destructure with $ suffix: |
| 67 | +const { title$ } = strings; // title$() returns translated string |
| 68 | +``` |
| 69 | + |
| 70 | +### ⚠️ API Calls via Resource Classes Only |
| 71 | +Use `Resource` from `kolibri/apiResource`. Define in `apiResources.js`. Never use raw `fetch` or `axios`. |
| 72 | + |
| 73 | +### ⚠️ Backend APIs: Use ValuesViewset |
| 74 | +Use `ValuesViewset` (or `ReadOnlyValuesViewset`) from `kolibri.core.api` for new API endpoints — not `ModelViewSet`, `ViewSet`, or `GenericViewSet`: |
| 75 | +```python |
| 76 | +from kolibri.core.api import ReadOnlyValuesViewset |
| 77 | + |
| 78 | +class MyViewSet(ReadOnlyValuesViewset): |
| 79 | + values = ("id", "title", "description") |
| 80 | + # Define values tuple and annotate_queryset for computed fields |
| 81 | +``` |
| 82 | +Viewset permissions use `KolibriAuthPermissions` from `kolibri.core.auth.api`. See `docs/backend_architecture/api_patterns.rst`. |
| 83 | + |
| 84 | +### ⚠️ Testing is Required |
| 85 | +- **Python:** pytest is the test runner. Django API tests extend `APITestCase` from `rest_framework.test`. Other Django tests extend `django.test.TestCase`. Only use bare pytest-style function tests for non-Django code. |
| 86 | +- **Frontend:** Jest runner + Vue Testing Library. Do NOT import from `vitest` or `@vue/test-utils`. `describe`/`it`/`expect` are Jest globals (no import needed). Use `jest.fn()` and `jest.mock()`: |
| 87 | + ```javascript |
| 88 | + import { render, screen } from '@testing-library/vue'; |
| 89 | + // describe, it, expect are Jest globals — do NOT import them |
| 90 | + describe('MyComponent', () => { |
| 91 | + it('renders', () => { |
| 92 | + render(MyComponent, { props: { title: 'Hello' } }); |
| 93 | + expect(screen.getByText('Hello')).toBeTruthy(); |
| 94 | + }); |
| 95 | + }); |
| 96 | + ``` |
| 97 | +- **TDD:** Write a failing test first, then make it pass. This is especially important for bug fixes — always write a test that reproduces the bug before fixing it. |
| 98 | + |
| 99 | +### ⚠️ Pre-commit Auto-fixes Files |
| 100 | +When a commit fails: pre-commit auto-fixes files → **`git add` the fixed files** → re-commit. |
| 101 | + |
| 102 | +## Project Structure |
| 103 | + |
| 104 | +``` |
| 105 | +kolibri/ |
| 106 | +├── kolibri/core/ # Core modules: auth/, content/, device/, lessons/, exams/, logger/, tasks/ |
| 107 | +├── kolibri/plugins/ # Frontend plugins: learn/, coach/, facility/, ... |
| 108 | +│ └── <plugin>/ # api_urls.py, viewsets.py, kolibri_plugin.py, test/ |
| 109 | +│ └── frontend/ # app.js, views/, composables/, routes/, __tests__/ |
| 110 | +├── packages/ # JS packages: kolibri/, kolibri-common/, kolibri-tools/ |
| 111 | +├── docs/ # Developer docs (architecture, testing, i18n, etc.) |
| 112 | +├── requirements/ # Python deps |
| 113 | +└── test/ # Test utilities and fixtures |
| 114 | +``` |
| 115 | + |
| 116 | +→ See `docs/backend_architecture/plugins.rst` for plugin layout and core-vs-plugins decision guide |
| 117 | + |
| 118 | +## Code Quality |
| 119 | + |
| 120 | +→ See `docs/code_quality.rst` for detailed principles. Key: tests assert behavior not implementation, composition over inheritance, let errors propagate, don't weaken existing tests, compute don't store, tell don't ask. |
| 121 | + |
| 122 | +## Key Conventions |
| 123 | + |
| 124 | +**Python:** F-strings preferred. One import per line. `DateTimeTzField` for timestamps (not Django's `DateTimeField`). `UUIDField` from morango for syncable models. Descriptive migration names (no `_auto_`). |
| 125 | + |
| 126 | +**Vue:** PascalCase filenames. Component `name` must match filename. Use `computed()` for derived values. |
| 127 | + |
| 128 | +**Git:** Imperative commit messages, no conventional-commit prefixes. Logical commit ordering for review. Black/Prettier enforced by pre-commit. |
| 129 | + |
| 130 | +**Don't guess — look at existing code** for patterns: `docs/backend_architecture/api_patterns.rst`, `docs/frontend_architecture/`, existing test files in `__tests__/` or `test/`. |
| 131 | + |
| 132 | +## Running Tests |
| 133 | + |
| 134 | +```bash |
| 135 | +pytest kolibri/path/to/test/ # Python (directory) |
| 136 | +pytest kolibri/core/auth/test/ -k test_login # Python (filter by name) |
| 137 | +pnpm run test-jest -- path/to/file.spec.js # Frontend (single file) |
| 138 | +pnpm run test-jest -- --testPathPattern learn # Frontend (filter by pattern) |
| 139 | +pre-commit run --all-files # Lint |
| 140 | +``` |
| 141 | + |
| 142 | +## Docs Reference |
| 143 | + |
| 144 | +Testing: `docs/testing.rst`, `docs/frontend_architecture/unit_testing.rst`, `docs/backend_architecture/testing.rst` | Frontend arch: `docs/frontend_architecture/` | Backend arch: `docs/backend_architecture/` | i18n: `docs/i18n.rst` | Code quality: `docs/code_quality.rst` | How-tos: `docs/howtos/` | Workflow: `docs/development_workflow.rst` | Multi-agent setup: `docs/howtos/multi_agent_setup.md` | User docs: https://kolibri.readthedocs.io/ |
0 commit comments