- Do not remove/skip the
migrateDbIfNeededhook fromSQLiteProviderinapp/_layout.tsx. - Keep
NOTE_TYPE/LIST_TYPEsemantics consistent withgetListItemsByIdand list update queries. - Avoid direct SQL edits outside
lib/dataStorage.ts. - Do not change
journal_modeaway fromDELETE— the Android widget requires it (seeNATIVE_CHANGES.md). - Do not run
expo prebuild --clean— it will wipe the widget's native files.
The android/ folder contains manual modifications on top of expo prebuild output — primarily an Android home-screen widget.
See NATIVE_CHANGES.md for the full list of native files, their purpose, AndroidManifest entries, schema dependencies, and upgrade notes.
Key points for agents:
- Do not run
expo prebuild --clean— useexpo prebuild(no--clean) to preserve widget files. - After any write operation in
lib/dataStorage.ts, callsyncAndroidNoteListWidgetFromApp()(already defined there) so the widget refreshes. All existing CRUD helpers already do this.
This repo is a small Expo + React Native app (using Expo Router and NativeWind/Tailwind) for creating, listing, and editing "notes" and "checklist lists", persisted in a local SQLite database.
When you (or another AI agent) are asked to implement a change, prefer working through the existing route/components structure and the centralized SQLite data layer in lib/dataStorage.ts.
Every change — no matter how small — must follow this exact sequence:
- Pull the latest
master(git checkout master && git pull) before creating any branch — this ensures the version bump targets the correct base and avoids version conflicts in CI. - Create a new branch off
master(never commit directly tomaster) - Make your changes and commit them to the branch
- Bump the version (
npm run bump:patch/minor/major) and commit the version files - Push the branch to remote
- Open a pull request targeting
master
Direct pushes to master are blocked by branch protection. CI enforces the version bump — a PR with the same version as master will fail.
Agent rules:
- "Implement X" means make the code changes only. Stop there and wait. Do not branch, commit, bump the version, push, or open a PR unless the developer explicitly says to (e.g. "commit this", "open a PR", "do the full workflow").
- Before opening a PR, ask whether the new work should go into an existing open branch/PR or a new one. Never assume a new branch.
- Do not push until the developer has tested the changes locally on a dev device and confirmed they are ready.
Choose the bump type based on the nature of the changes:
- patch (
npm run bump:patch) — bug fixes, small tweaks, copy changes - minor (
npm run bump:minor) — new user-visible features, non-breaking additions - major (
npm run bump:major) — breaking changes, major UX overhauls
- Locate the route to change/add under
app/. - If the feature needs persistence, identify/extend the correct helper(s) in
lib/dataStorage.ts. - For DB changes:
- update the schema migration in
migrateDbIfNeeded - bump
DATABASE_VERSION - keep existing migrations compatible (older installs should migrate forward)
- update the schema migration in
- For UI:
- reuse existing components in
components/(especiallyNoteForm,ListForm, andcomponents/ui/*) - use Tailwind/NW class names (via
className)
- reuse existing components in
- Match existing loading/not-found patterns for numeric params and record-type checks.
- If adding a new write operation in
lib/dataStorage.ts, callsyncAndroidNoteListWidgetFromApp()after the DB write (see existing helpers for the pattern).
@lib/dataStorage.ts
Lists store their items inside the note column as a JSON string:
stringifyListItems(items)stores{ checked, text }[]parseListItems(rawContent)validates/filters parsed items
CRUD helpers to use:
- Notes:
addNote,getNoteById,updateNote,deleteNote
- Lists:
addList,getListItemsById,updateList,updateListItems
Important rule: updating list items uses UPDATE content SET note = ? WHERE id = ? AND type = ? (ensures you don't overwrite a note's data by accident).
Routes under app/, reusable UI under components/, non-UI logic under lib/, custom hooks under hooks/.
@README.md
Expo ~54, Expo Router, NativeWind v4, expo-sqlite, TypeScript strict — see @package.json for exact versions.
- Dynamic numeric route params use:
useParsedNumericRouteParam('id')- screens then guard with
isValidId(invalid id -> not found / redirect behavior)
- For typed routing (
expoconfig hasexperiments.typedRoutes: true):- when passing dynamic routes to
router.push, existing code uses casts likeas never.
- when passing dynamic routes to
- Note create/edit uses
components/NoteForm.tsx, which callsonSave(trimmedTitle, noteContent.trim()). - List create/edit uses
components/ListForm.tsx. - Screens typically return:
ScreenLoadingStatewhile fetchingScreenNotFoundStatewhenidis invalid or the record type doesn't match the expected screen
- Hardware back navigation:
- screens use
useHardwareBackHandler(() => router.replace('/'))or redirect to a "backTarget".
- screens use
- Use
useKeyboardOffsetfor bottom padding on screens with inputs (handles Android accessory bar). - Use
HeaderBackButtonfromcomponents/navigation/HeaderBackButton.tsxfor screen header back arrows.
- Use type narrowing (not !) for nullable fields and 'loading' states. Always unwrap numeric route params via useParsedNumericRouteParam — never cast useLocalSearchParams() output directly.
- Run
npx prettier --check .before committing (single quotes,printWidth: 100, Tailwind plugin). CI enforces this. - All SQL lives in lib/dataStorage.ts. No exceptions.
@README.md
Last scanned: 2026-05-18