尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Personal study hub: a Next.js roadmap over the NUS SoC course shortlist, a file-backed tutoring loop for Claude Code, and narrated Manim explainers.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Gnosia is a personal study workspace built around the NUS School of Computing AY2026/27 course shortlist. A Next.js hub renders each course as a docs-style roadmap: topic checklists, notes, curated resources, and short Socratic tutoring sessions whose conclusions are saved back against the topic.
Behind the hub sits a tutoring loop that runs inside Claude Code. It measures what you already know, plans a path from there to a goal, teaches one reasoning step at a time, and schedules what you will forget. Everything it writes lands in the catalog, a SQLite file, each change a revision with an author and a reason that catalog history shows, and learn vault pull hands the notes back as plain Markdown you can read and edit.
A third part, the tools/manimkc/ submodule, renders narrated animations for concepts that text and SVG cannot carry.
/learn <anything> in Claude Code: probe, plan, teach, close
/review spaced repetition over what is due
Knowing what is worth learning and actually learning it are two different problems, and most tools solve only one. The roadmap answers the first: a shortlist of courses broken into topics you can tick off. The pipeline answers the second, by keeping a real model of what you hold rather than a list of what you have read. Asset generation is the third piece, for the ideas that only make sense in motion, so the loop can end in a video rather than a paragraph.
| Part | Where | What it does |
|---|---|---|
| Roadmap | apps/web/ | Next.js hub over the AY2026/27 shortlist: progress, notes, resources, tutoring sessions, topic recaps |
| Learning pipeline | scripts/learn/, services/catalog/src/learn/ | Probe → Plan → Teach → Close loop over a knowledge base in the catalog |
| Asset generation | tools/manimkc/ (submodule) | Narrated manim animations for concepts with moving parts |
| Asset generation | tools/remotionkc/ (submodule) | Narrated Remotion explainer videos, indexed like manimkc's and labelled "remotion" |
| Asset generation | tools/revideokc/ (submodule) | Narrated Revideo explainer videos, indexed like manimkc's and labelled "revideo" |
| Asset generation | tools/blenderkc/ (submodule) | Narrated photoreal 3D explainer videos rendered by Blender's Cycles, indexed like manimkc's and labelled "blender" |
| Diagrams | tools/tldrawkc/ (submodule) | Teaching diagrams drawn on a real tldraw canvas, exported as self-contained SVG with the editable .tldr committed beside it |
| Catalog | packages/schema/, services/catalog/ | The SQLite database (one file, ~/.gnosia/catalog.db by default) that owns the content and the learner state, and the catalog CLI that writes and reads it |
Ten courses are covered (CS2111, CS4263, CP4281 through CP4285, CS3216, CS3227, CS3249). Three have inferred syllabi and say so in a banner until official NUSMods data lands. Learner state — ticks, notes, recaps, preferences — lives in the same database as the catalog; there are no accounts yet, just one learner row behind one owner password (/sign-in). A browser that used the old localStorage document imports it once, automatically, on first load.
| Surface | Purpose |
|---|---|
/ | The public landing page; a signed-in owner is sent to /home |
/sign-in | The owner password (docs/DEPLOYMENT.md, "Signing in"); everything below needs the session |
/home | Home: pick up where you left off (course modules, unfinished videos, studio drafts, concepts), what that opens up next, a few cards worth a quick recall, and an Ask box that finds a concept, course or video by title or opens a tutor chat on anything else. /today redirects here |
/courses | The course catalog and overall progress |
/courses/[slug] | The course path: units of topic nodes coloured by mastery, with each topic's checklist, notes, recaps and resources in its panel |
/learn | Knowledge base: mastery per concept, the frontier, the dependency graph |
/learn/[concept] | One concept's workspace: its note and sources, a tutor grounded in the note, checks, assets |
/learn/[concept]/session | Focus-mode session: probe, teach, check, close, each graded answer recorded as evidence |
/review | Spaced-repetition review of what is due |
/library, /library/[id] | Every indexed asset (videos, diagrams, notes), searchable, with the concepts each explains |
/settings | Theme (palette Classic or Gnosia, appearance Light, Dark or System), tutor style; export and reset |
/courses/[slug]/[module]/session?topic=… | The older Socratic micro-session for one topic |
POST /api/agent/chat, POST /api/agent/summarize | Server proxy to the free provider cascade (system prompt injected server-side), and transcript to structured topic recap |
apps/web/ The Next.js app: src/app · src/components · src/data · src/lib · public/
packages/content/ One reader for the export format's content/, and the content types
packages/schema/ Drizzle tables: the single source of the catalog's row shapes
services/catalog/ Migrations, the database connection and the catalog CLI
scripts/learn/ Pipeline CLI: bkt · graph · schedule · markdown · store · render
seed/ A small slice of the content in the export format: a fresh clone's data
docs/ Long-form docs, the pipeline's among them (docs/LEARN.md)
tools/manimkc/ Private submodule: narrated manim videos
tools/remotionkc/ Private submodule: narrated Remotion videos
tools/revideokc/ Private submodule: narrated Revideo videos
tools/blenderkc/ Submodule (public): narrated photoreal 3D videos (Blender)
tools/tldrawkc/ Submodule (public): tldraw diagrams
.claude/ launch.json, the four skills symlinked from the submodules, and video-router
Three of those are npm workspaces (apps/*, packages/*, services/*), so
one npm ci at the root installs the lot and npm run lint, typecheck and
test cover all of them from there. tools/ is not a workspace: the
submodules carry their own lockfiles and toolchains.
The catalog is the join the content never had: one topic vocabulary every
asset points at, typed link tables with foreign keys at both ends, and an
append-only event log the learner state is replayed from. It owns the
courses, topics, tags, notes, session logs and diagrams outright; every change
goes through a catalog or learn command with a reason, and catalog export --out <dir> writes them out as files whenever you want a copy. npm run catalog -- import <dir> reads such a tree into a database, and a bare
import refreshes what it reads from manimkc topics --json,
remotionkc topics --json, revideokc topics --json and blenderkc topics --json. Agents read it
back in layers with ls, show, path and search, every one of them
--json and bounded. The design is in
services/catalog/SCHEMA.md, the export format in
seed/README.md.
.claude/ holds no learning skills. /learn, /review and the three subagents live in the globally installed learning plugin, so they work in every project; see AGENTS.md for why copying them back here would be a mistake.
tools/manimkc/ is its own repo with its own venvs and build system. It currently holds 28 topics across 15 domains, filed as topics/<domain>/<topic>/ and mirrored into out/. Narration syncs TTS to the animation through a voiceover() context manager with six backends (local, soprano, kokoro, chatterbox, elevenlabs, sarvam), defaulting to the offline local voice, with clip caching so re-renders cost no API quota. Agents drive it through tools/manimkc/tools/cli/manimkc and the manimkc-video skill; read tools/manimkc/AGENTS.md before working in there.
git clone https://github.com/c-wenlong/gnosia-learn
cd gnosia-learn
npm ci
cp .env.example apps/web/.env.local # Next reads it from apps/web/, never the root; set OWNER_PASSWORD and AUTH_SECRET in it
npm run dev
Do not add --recursive: three video-tool submodules under tools/
(manimkc, remotionkc, revideokc) are private repositories, so a
recursive clone fails on them. Nothing above needs them, and CI skips them
the same way. tools/tldrawkc and tools/blenderkc are public; only the diagram
tooling at the end of this section fetches one of them.
Open http://localhost:3000. Node 22 or newer is required.
The diagram studio at /studio needs its runner beside the app. npm run dev:up starts both in one tmux session, a pane each, after checking Node, node_modules, your sign-in variables and the tldrawkc build; npm run dev:down stops them, the runner first so it can finish what it is writing. Without tmux it runs both in the one terminal, as npm run dev:all does. scripts/dev.sh --help lists the options. It also checks that the catalog file below exists and opens, and stops the start, naming the step to run, when it does not; --no-checks skips every check.
The app reads its courses, topics, notes and diagrams from the catalog, and nothing else. The catalog is one SQLite file, $GNOSIA_HOME/catalog.db, else ~/.gnosia/catalog.db, so there is no database server to install or start (SQLITE-MIGRATION.md). The repository holds no content of its own beyond seed/, a small real slice in the export format, so a fresh clone builds its database from that. The first time:
npm run catalog -- migrate # creates the file, or brings its tables up to date
MANIMKC_ROOT=/nonexistent REMOTIONKC_ROOT=/nonexistent REVIDEOKC_ROOT=/nonexistent BLENDERKC_ROOT=/nonexistent npm run catalog -- import seed # the seed, leaving the video tools out
for t in topics tags concepts sessions courses diagrams; do npm run catalog -- flip $t; done # the database owns each type
The flips run in that order, the one scripts/lib/flip-order.mjs keeps. MANIMKC_ROOT=/nonexistent leaves manimkc out, since no seed topic maps a video folder and the import would refuse the renders it found, and REMOTIONKC_ROOT=/nonexistent, REVIDEOKC_ROOT=/nonexistent and BLENDERKC_ROOT=/nonexistent do the same for remotionkc, revideokc and blenderkc, whose folders name topics the seed does not hold; the seed's README says more. With your own content, import an export of it (catalog import <dir>) in place of seed. Once topics map their folders (catalog topic map), a bare npm run catalog -- import takes in the submodules' videos and npm run catalog -- asset upload --source manimkc (and --source remotionkc, --source revideokc) copies them, their captions and posters into the blob store. npm run e2e builds exactly this database on its own, under a throwaway name, so the smoke test needs none of these steps.
A type that has not been flipped is refused by name rather than read from the files: the page says it could not read its content, and the server log names the catalog flip to run (CONTENT-MIGRATION.md has the flip-day details). When the app cannot open the catalog it shows a banner saying nothing you change is saved, and each page that shows content says it could not read it.
Both the app and the catalog CLI default to that file, and CATALOG_DB names another (services/catalog/src/env.ts). A postgres:// value in DATABASE_URL is refused (databasePath() in env.ts). To point the catalog anywhere else, set CATALOG_DB in services/catalog/.env (copy services/catalog/.env.example), which the CLI and the app both read: set in apps/web/.env.local alone, the app opens one database while the CLI migrates another. A database from before the move is carried over with services/catalog/scripts/pg-to-sqlite.mjs (SQLITE-MIGRATION.md).
Three video-tool submodules are private, so a clone leaves tools/manimkc/, tools/remotionkc/ and tools/revideokc/ empty, and the app, the tests and npm run catalog -- import seed all work without them. On a machine with access, git submodule update --init tools/manimkc fetches one (add --recursive for manimkc, which nests a manim submodule of its own). A clone that predates the tools/ move needs git submodule sync --recursive first, because the submodule paths changed.
The diagram tool in tools/tldrawkc/ needs one extra step, because npm run canvas runs a compiled CLI that is not committed:
git submodule update --init tools/tldrawkc
(cd tools/tldrawkc && npm ci && npm run build)
npm run canvas -- doctor # says which Chromium it found
Skip it if you are not drawing diagrams; nothing else in the repo depends on it.
Four phases, driven by a dependency-free Node CLI. Probe ranks concepts by expected information gain, so it binary-searches the edge of your understanding instead of marching through a syllabus; being asked does not count as being taught. Plan returns a topologically ordered path from what you hold to the goal, with mastered prerequisites dropped and claims fact-checked before anything is taught. Teach hands back one concept at a time at the frontier, with a check after each step. Close writes the concept notes, stores the checks, logs the session, and schedules the reviews.
Mastery uses Bayesian Knowledge Tracing; review scheduling uses SM-2. Both are written from scratch in under 200 lines each with no dependencies. The event table is the source of truth, an append-only record of every answer with its before and after belief (append-only enforced by a trigger, not by convention), so the mastery model and the schedule are both projections that can be thrown away and replayed, and the model parameters are safe to change. Concept notes and session logs are rows too, one revision per write; learn vault pull writes them out as a folder of Markdown for Obsidian, and learn map prints the map.
npm run learn -- status # where you are
npm run learn -- doctor # validate the concept graph
npm run learn -- help # the full primitive surface
The /learn and /review skills are not in this repo. They live in the globally installed learning plugin, and both open by running npm run learn -- status so they refuse to improvise a session that records nothing. Full architecture, formulas, and an honest list of what these models get wrong: docs/LEARN.md.
tools/tldrawkc/ draws teaching diagrams on a real tldraw canvas: a snippet places boxes and arrows, the agent looks at a screenshot and fixes what reads wrong, then exports a self-contained SVG. Both the editable .tldr and the exported .svg are stored in the catalog as one revision (catalog diagram commit, or Accept in the studio at /studio); the PNGs used to look at a diagram mid-edit are scratch and never stored.
| Entry point | How | When |
|---|---|---|
/learn's Teach phase | Spawns the diagram-maker subagent, which drives the tool and falls back to a hand-written SVG if doctor fails | Automatic, nothing to type |
The tldrawkc-diagram skill | Symlinked into .claude/skills/ | You ask an agent to draw a diagram or lift a mermaid flowchart onto the canvas |
| The CLI | npm run --silent canvas -- <verb> | You, or any script |
| The library | A relative import, e.g. import { run } from "../tools/tldrawkc/dist/lib/index.js" from scripts/. tools/ is not an npm workspace, so there is no bare "tldrawkc" specifier | Node code that wants the verbs without a shell |
serve | A real browser tab, open until Ctrl+C | A human watching and nudging shapes by hand |
The deployed web app is not an entry point. It reads the diagrams from the catalog and serves the stored SVGs; it never runs the tool, because a Vercel build has no local Chromium and no business writing local files.
A CLI session, start to finish (needs the submodule built first, see Install):
npm run --silent canvas -- doctor
npm run --silent catalog -- diagram checkout probe-and-frontier --dir /tmp/diagrams # a new slug: a record, no file yet
npm run --silent canvas -- run /tmp/diagrams/probe-and-frontier.tldr --create \
--shot /tmp/probe-and-frontier.png --code - <<'JS'
helpers.box('probe', 'probe', { x: 60, y: 60, w: 160, h: 64 })
helpers.box('teach', 'teach', { after: 'probe', gap: 120, w: 160, h: 64 })
helpers.connect('probe', 'teach', { label: 'frontier' })
return helpers.describe().lints
JS
# look: the "frontier" label wraps, gap is too small. fix with a second snippet:
npm run --silent canvas -- run /tmp/diagrams/probe-and-frontier.tldr \
--shot /tmp/probe-and-frontier.png --code - <<'JS'
helpers.box('teach', 'teach', { after: 'probe', gap: 160, w: 160, h: 64 })
return helpers.describe().lints
JS
# look again: fixed. file it, export, and store both in the catalog:
npm run --silent canvas -- meta set /tmp/diagrams/probe-and-frontier.tldr \
--topic vector-and-linear-algebra-basics
npm run --silent canvas -- export /tmp/diagrams/probe-and-frontier.tldr \
--svg /tmp/diagrams/probe-and-frontier.svg
npm run --silent catalog -- diagram commit probe-and-frontier --dir /tmp/diagrams --reason "probe feeds teach"
| Verb | What |
|---|---|
doctor | check node, the page bundle, Chromium and the page |
api | what a snippet can call, from the helpers' own JSDoc |
new | create an empty document, refuses to overwrite |
run | load, run a snippet, save, export |
shot | screenshot without running anything |
inspect | print every shape, binding, lint and the metadata. Exits 3 on lints |
export | write the files that get committed |
from-mermaid | build a document from a mermaid flowchart |
list | every .tldr in a directory, no browser (its default, learn/assets, no longer exists here, so name one) |
meta set | stamp metadata on an existing document, no browser |
serve | mirror the document in a real browser tab until Ctrl+C |
Three things that bite:
npm run canvas without --silent prints npm's own banner ahead of the tool's stdout, which breaks --json parsing. Same for npm run learn.--allow-lints. Exit 2 means the snippet threw and nothing was written..tldr needs meta set --topic <slug> (or new --topic <slug>) with a slug from the vocabulary (catalog topic ls) before the catalog will store it.flowchart LR
subgraph PLUGIN["learning plugin (global)"]
SK["/learn · /review"]
AG["concept-mapper<br/>fact-checker<br/>diagram-maker"]
end
subgraph CODE["the learn CLI (this repo)"]
C["17 commands"]
LIB["bkt · graph<br/>schedule"]
ST["the learner store"]
end
subgraph KB["the notes (rows in the catalog)"]
REC["concept · session_log_entry<br/>(authored during Close)"]
MAP["learn map<br/>(derived, printed)"]
end
subgraph PG["the catalog (SQLite)"]
EV[("event<br/>source of truth, append-only")]
DER["mastery · review<br/>(projections, replayable)"]
STA["progress · recap · preference<br/>(the web hub's state)"]
end
WEB["/learn page · course pages"]
SK --> AG
SK --> C
C --> LIB
C --> REC
C --> MAP
C --> ST
ST --> EV
EV --> DER
WEB --> ST
ST --> STA
DER --> WEB
| Path | Purpose | Shipped? |
|---|---|---|
concept, session_log_entry, diagram (the catalog) | Atomic concept notes (Obsidian-compatible and wikilinked once pulled), one log per session written live, and the diagrams drawn while teaching. catalog export or the nightly export is the copy | not in git |
seed/ | A small slice of the content in the export format, for a fresh clone and the tests | tracked |
~/.gnosia/ (GNOSIA_HOME) | The blob store (blobs/), the Obsidian vault (vault/) and the nightly export (archive/, npm run export:nightly) | outside the repo |
event, mastery, review (the catalog) | The trajectory and its projections. event is the only copy | not in git |
progress, recap, preference (the catalog) | The web hub's ticks, notes and recaps. Also the only copy | not in git |
apps/web/.env.local | OWNER_PASSWORD, AUTH_SECRET, provider keys; read from apps/web/, never the root. The catalog's file is set in services/catalog/.env, if anywhere | gitignored |
tools/manimkc/out/, tools/manimkc/cache/ | Rendered videos and the TTS cache | gitignored |
Tutoring chat tries three free providers in order and falls through on errors and rate limits. Every variable is optional: with no keys the site still works, and Launch Agent falls back to Copy system prompt and Copy terminal launch pack.
| Variable | Default | Purpose |
|---|---|---|
NEXT_PUBLIC_SITE_URL | Vercel domain in production, http://localhost:3000 locally | Canonical origin that social preview images resolve against |
NVIDIA_API_KEY | unset | Provider 1, NVIDIA NIM |
GEMINI_API_KEY | unset | Provider 2, Google AI Studio free tier |
OPENROUTER_API_KEY | unset | Provider 3, OpenRouter free models |
NVIDIA_MODEL · GEMINI_MODEL · OPENROUTER_MODEL | google/gemma-4-31b-it · gemini-2.5-flash · openrouter/free | Model override per provider |
OPENROUTER_HTTP_REFERER, OPENROUTER_APP_TITLE | unset | Attribution headers sent to OpenRouter |
Keys are read server-side only (packages/tutor/src/providers.ts); the browser never sees them. Production also needs them set in the Vercel project's environment variables. Deployment details are in docs/DEPLOYMENT.md, and editing the curriculum is covered in docs/CURRICULUM.md.
| Command | Description |
|---|---|
npm run dev | Dev server |
npm run dev:up / npm run dev:down | The dev server and the studio runner together in one tmux session, with Studio on; stop both. -- --detach starts without attaching, -- --no-studio runs the app alone. See Install |
npm run dev:all | The same two processes in the current terminal, with no checks |
npm run build | Production build |
npm run start | Serve a production build |
npm run lint / npm run typecheck | ESLint / tsc --noEmit |
npm run test | Vitest, single run |
npm run test:watch | Vitest watch mode |
npm run test:coverage | Vitest with v8 coverage, lcov for Codecov |
npm run e2e | Headless smoke test of a production build against a throwaway database it builds from seed/. Needs npm run build first; see CONTRIBUTING.md |
npm run learn -- <cmd> | The learning pipeline CLI |
npm run canvas -- <cmd> | The tools/tldrawkc/ diagram CLI. Needs npm ci && npm run build inside the submodule first |
npm run catalog -- <cmd> | The catalog CLI: migrate, import (index is its old name), status, ls, show, path, search. Works on the catalog's SQLite file, created by migrate on first use |
Thirteen Vitest files cover the three halves. On the web side: curriculum integrity, progress counting, learner-state reducers, topic recaps and the v1 to v2 migration, the recap summariser, and agent prompt/provider/objective selection. On the pipeline side: BKT update formulas and information-gain ordering, DAG cycle detection and frontier advance, the SM-2 interval ladder and leech handling, frontmatter and wikilink parsing, and scripts/learn/cli.test.mjs over the command surface itself. On the catalog side: migrations applying from empty and staying idempotent, and the constraints that make the database worth having — an unknown foreign key, a prerequisite kind outside requires|assumes, a collection_item with zero or two targets, and an UPDATE on event. Each runs against a scratch SQLite file of its own under the OS temp directory (services/catalog/test/scratch-database.ts), so they need no database server and run everywhere.
.github/workflows/ci.yml runs lint → typecheck → test:coverage on every pull request (not on the push that merges it, which would test the same code twice), with two test files at a time, then uploads the lcov report to Codecov. node_modules and ESLint's per-file results come from a cache, which .github/workflows/cache.yml keeps on main. Production deploys only when a GitHub release is published (.github/workflows/deploy.yml), and that workflow runs build before it deploys; pull requests get no preview and a merge to main deploys nothing. See docs/DEPLOYMENT.md.
Start with CONTRIBUTING.md for the branch, test and PR conventions, and ARCHITECTURE.md for how the code is shaped. Found a security problem? SECURITY.md says how to report it. AGENTS.md is the source of truth for coding agents working here, including a gotchas log of traps not worth rediscovering; CLAUDE.md just imports it.
MIT. See LICENSE.