Thanks for your interest in KnowNote. Bug reports, feature requests, and pull requests are all welcome.
KnowNote is a young project maintained by one person, so the guidance below is mostly about keeping review cheap.
- Search existing issues before opening a new one — duplicates cost triage time.
- Open an issue first for anything larger than a bug fix or a small improvement (a new provider, a new document format, a schema change, anything touching packaging). It is much cheaper to agree on the approach before the code exists than after.
- Small, obviously-correct fixes can go straight to a PR.
Requirements:
- Node.js 24 — the version used by CI (
.github/workflows/verify.yml) and the only versionpackage.json'senginesfield allows..node-versionpins it for version managers (nodenv, asdf, fnm, mise). Do not develop against Node 26 Current; Electron 44's runtime is Node 24 as well. - npm — KnowNote uses npm, not pnpm or yarn. The native-module collector in
electron-builderis built around npm'snode_moduleslayout and silently drops transitive dependencies under pnpm. See the//dependenciescomment inpackage.jsonfor the full story. - No native build toolchain. Every native dependency is N-API and ships
prebuilt binaries, most of them inside the npm package itself
(
better-sqlite3>= 13 carriesprebuilds/for every supported platform/arch), sonpm installcompiles nothing andnpmRebuildis off. See thenpmRebuildnote inelectron-builder.yml.
git clone https://github.com/MrSibe/KnowNote.git
cd KnowNote
npm install
npm run dev| Command | What it does |
|---|---|
npm run dev |
Start the app in development with HMR. |
npm run typecheck |
Typecheck the main/preload, renderer and test projects. |
npm test |
Run the Node test suite (test/**/*.test.ts). |
npm run lint |
ESLint (see the note on the current baseline below). |
npm run check:version |
Fail when package.json disagrees with the newest release tag. |
npm run format |
Prettier over the whole repository. |
npm run build |
Typecheck, then bundle with electron-vite. |
npm run build:unpack |
build, then produce an unpacked app in dist/. |
npm run smoke:packaged |
Launch the packaged app's --smoke-test and check it starts. |
npm run eval:prepare |
One-time, networked: download the pinned eval embedding model. |
npm run eval |
Run the RAG eval harness offline; rewrites docs/eval/. |
npm run db:generate |
Generate a Drizzle migration from src/main/db/schema.ts. |
npm run db:studio |
Inspect the development database. |
npm run typecheck # required — this is a CI gate
npm test # required — this is a CI gate
npm run buildAdditionally, run the packaged-app checks if you touched Electron main or
preload code, dependencies, native addons, packaging, database
initialization, or build configuration:
npm run build:unpack
npm run smoke:packagednpm run build and build:unpack both succeed on an artifact that cannot
start. That is how Cannot find module 'script-loader!sql.js',
ReferenceError: DOMMatrix is not defined, and a lost CJS interop in the Anki
export have all reached a release. smoke:packaged runs the real executable
against the real app.asar, so it is the step that actually catches these.
Two caveats:
-
lintis not a gate.npm run linthas no errors, butmainstill reports ~118 pre-existing@typescript-eslint/no-explicit-anywarnings. Don't add new ones, and don't mass-refactor them in an unrelated PR. -
Don't run
npm run formaton the whole repository. It rewrites every file, and a repo-wide format pulls unrelated changes into your diff. Format only what you touched:npx prettier --write <files>
Tests live in test/**/*.test.ts and run on Node's built-in test runner via
npm test (no test framework dependency). test/fixtures/ additionally
contains sample documents for manual parser checks. For anything not covered by
a test, describe your manual verification in the PR instead — which platform,
which workflow, what you observed.
Branches come off main and are named <type>/<short-description>, e.g.
fix/macos-native-modules, feat/audio-transcription.
Commits follow Conventional Commits:
feat:, fix:, docs:, refactor:, chore:, build:, style:. A scope is
welcome when it is meaningful: fix(mac): ....
Pull request titles use the same prefixes, because the prefix decides two things automatically:
- the label applied by the
Label PRworkflow, and - the section the change appears under in the generated release notes
(
.github/release.yml).
| Title prefix | Label | Release section |
|---|---|---|
feat:, perf: |
enhancement |
Features |
fix: |
bug |
Bug Fixes |
build: |
build |
Build & Packaging |
deps:, chore(deps): |
dependencies |
Dependencies |
docs: |
documentation |
Documentation |
chore:, ci:, style:, test: |
skip-changelog |
(excluded) |
refactor: |
(none) | Other Changes |
refactor: is left unlabelled on purpose: the workflow cannot tell whether a
refactor changes user-visible behaviour. Add enhancement or bug yourself
when it does, and breaking when it breaks compatibility.
Version bumps and releases are done by the maintainer — don't change
version in package.json or add a tag in a PR.
package.json's version is the single source of truth. electron-builder takes
the app version and every artifact name (knownote-${version}-setup.exe, the
.dmg, latest.yml) from it, the updater compares versions against it, and the
About panel reads it back out of the packaged app via app.getVersion(). The
git tag has to agree with it, and it has to be v-prefixed, because
release.yml triggers on tags: v*.*.* — a tag named 1.3.0 would push nothing
and publish no artifacts.
The recommended procedure, which cannot drift because one command does both halves at once:
- On a clean
main, runnpm version <patch|minor|major>. It bumpspackage.jsonandpackage-lock.json, commits, and creates thev<version>tag. git push --follow-tags. Pushing the tag is what startsrelease.yml.
Bumping package.json by hand and tagging the same commit separately also
works, and is how earlier releases here were cut. The mechanism matters less than
the invariant: whatever bumps the version must land on main before the tag is
pushed, and the tag must be v<version>.
scripts/check-version.mjs (npm run check:version) enforces that invariant, and
both workflows run it:
- tag build (
release.yml) — the tag must equalpackage.jsonexactly. This is the release blocker: nothing is built or published on a mismatch, and it runs before the draft release is created, so a bad tag leaves nothing behind. - pull request (
verify.yml) —package.jsonmust not be behind the newest tag. Equality is deliberately not required here:npm versionis merged before the tag is pushed, and during that windowmainis legitimately ahead of the newest tag.
src/main/ Electron main process
db/ Drizzle schema, migrations, queries
services/ Document parsing, RAG, item and note storage
models/ Model Connection resolution and API protocol adapters
src/preload/ IPC bridge
src/renderer/ React UI
src/shared/ Types and utilities used by more than one process
A few project-specific rules worth knowing before you write code:
- Keep
dependenciesminimal. Only packages that cannot be bundled belong there (native addons and a few CommonJS packages that rollup can't inline). Everything else goes indevDependencies. A dependency in the wrong bucket means either a runtimeMODULE_NOT_FOUNDin the packaged app or a bloatedapp.asar. The//dependenciescomment inpackage.jsonexplains the boundary. - Database changes are migrations. Edit
src/main/db/schema.ts, then runnpm run db:generateand commit the generated file undersrc/main/db/migrations/. Never edit a migration that has already shipped. - Add user-facing strings to both locales.
src/renderer/src/locales/en-US/andsrc/renderer/src/locales/zh-CN/are kept in sync, and every namespace is registered insrc/renderer/src/i18n.ts. - Keep PRs focused. No drive-by reformatting, renames, or unrelated cleanups. If you spot something else that needs fixing, open an issue.
The PR template asks for the change, the motivation, the related issue, the scope, and how you tested it. Fill in what applies; delete what does not. Screenshots are expected for meaningful UI changes.
If your PR fixes an issue, use Fixes #123 so it closes automatically.
KnowNote is licensed under GPL-3.0. By contributing, you agree that your contributions are licensed under the same terms.