Nora is a personal research workflow system for maintaining project context, recovering stale research projects, and tracking progress across agent sessions — plus a set of optional modules for citation auditing, literature tracking, and writing assistance.
Status: alpha. Core project-state management, citation auditing, literature tracking, and writing assistance all exist and are usable; none have seen extensive real-world mileage yet (see Current limitations below).
- Global Nora skills teach agents how to work; project-local state lives in
.nora/. nora-project-managerowns project state, lifecycle, and session continuity. It is the only required skill.- Citation, literature, and writing support are separate, optional modules — each its own skill, each with its own
.nora/<module>/state, none required to use Nora at all. - Agents should read project state at session start and propose (not silently apply) state updates at session end.
- Prefer Markdown/YAML files over databases; prefer review queues and proposed updates over silent mutation.
- Human review is required before overwriting important project or module state. Agent proposals that change research state go through the decision gate (
.nora/decisions/decisions.yaml): agents appendpendingentries; only the user approves. - Nora state stays local by default:
nora newwrites a.nora/.gitignorethat keeps everything under.nora/out of git. Sharing happens by explicit export, not by committing state. - One directory = one workspace, resolved like git resolves
.git: the nearest ancestor with.nora/wins (nora root). Sibling workspaces of the same research project are normal, not conflicts; nested ones are conflicts.
- Project management (
nora-project-manager) — bootstrap a new project, recover a stale one, summarize a session and propose state updates, and keepAGENTS.mdin sync with the current template. This is the core of Nora; everything else is optional on top of it. - Citation auditing (
nora citationCLI +nora-citation-auditor) — the mechanical checks are scripted (nora citation lint: duplicate keys/DOIs, missing fields, missing/unused citations, malformed commands, cite clusters, uncited-claim heuristic) with safe hygiene fixes behind an explicitfix --apply(dry-run + backup first); the skill supplies judgment: fuzzy duplicate-title and arXiv/journal-duplicate candidates, false-positive filtering, and a bounded human-in-the-loop first pass at whether a citation appears to support a specific claim. The skill never edits.bib/.tex; nothing ever edits.tex. - Literature management (
nora-literature-manager+nora literatureCLI) — a structured backend (papers.yamlas the single source of truth) with CLI-enforced bookkeeping: ingest candidates from BibTeX/title lists/manual entry with built-in dedup, a conservative status machine (candidate → queued → reading → read → proposed_cite → cited/rejected) whose promotions are mechanically gated on user-approved decisions, role-based coverage reports, and generated reading-queue/related-work views. The skill supplies judgment (triage, notes, gap analysis); the CLI owns the state. - Writing assistance (
nora writingCLI +nora-writing-assistant) — mechanical prose guardrails are scripted (nora writing lint: placeholder commands, draft-markup leftovers, hand-written references vs\cref, straight quotes, emphasis overuse, long sentences, non-ASCII punctuation — read-only, no auto-apply); the skill polishes, restructures, and overclaim-checks prose while preserving technical meaning and citation placement, diagnoses paragraphs, and maintains the project's writing style. Works with or without a Nora project. - Current limitations — no figure-generation tooling, no Zotero (or any external) write-back, no database backend, no background daemon or web dashboard, no full paper generation, no automatic manuscript rewriting, no automatic BibTeX edits, and no unsupervised claim verification.
Requires Python 3.8+ (standard library only — no packages to install).
nora install-skills # symlink all four skills into Claude Code / Codex
nora new # scaffold core .nora/ project state + AGENTS.md
nora doctor # check global install + core project state + optional modules
# enable whichever optional modules you actually need — none are required:
nora citation init
nora literature init
nora writing initModules are opt-in. nora new only creates the core project state; nora doctor will not fail just because citation/literature/writing modules are absent — it prints an INFO line for each uninitialized module and suggests the relevant nora <module> init.
bin/nora provides:
nora new(aliasnora init) — scaffold core.nora/project state andAGENTS.mdin the current directory: the five core state files plus.nora/.gitignore(state stays out of git),.nora/config.yaml(workspace identity:project_id/workspace_id/workspace_type), and the.nora/decisions/decision gate. Does not touch any optional module. Refuses to run inside an existing workspace (nested.noradirectories conflict).nora root— resolve the active workspace: the nearest ancestor directory (including the current one) containing.nora/, stopping at$HOME/filesystem root. Prints the workspace path and, whenconfig.yamlexists, its identity. Agents run this before reading state instead of assuming./.nora.nora doctor— global install check (Nora home, required skills present withSKILL.md, skill symlinks not broken) + core project-state check against the resolved workspace (ERRORif a core file is missing) + workspace layout (nested.norainside the workspace =WARNING; sibling workspaces next to it =INFO, not a conflict; missingconfig.yaml/.gitignore/decisions/in older workspaces =INFO) + optional-module status (INFOif not initialized,WARNINGif initialized but incomplete). Exits non-zero only onERROR.nora install-skills(aliasnora install-skill) — symlink all four skills (nora-project-manager,nora-citation-auditor,nora-literature-manager,nora-writing-assistant) into Claude Code (~/.claude/skills) and Codex (~/.codex/skills), with short aliasesnora,nora-citation,nora-literature,nora-writing.nora update— pull the latest Nora CLI and skills from git (refuses if$NORA_HOMEhas uncommitted changes); if theAGENTS.mdtemplate changed, prints a note to run/nora sync-agentsin projects you want to sync.nora citation <subcommand>— deterministic citation-hygiene backend:lint(aliascheck) runs all mechanical checks over.bib/.tex(duplicate keys/DOIs with proxy-host normalization, missing fields, undefined/unused keys with\nocite{*}honored, malformed cite commands, oversized cite clusters, and a clearly-labeled heuristic for uncited technical claims;--writesavesLINT_REPORT.md);normalizepreviews the hygiene diff (proxy DOIs →https://doi.org/, page-range--, unambiguous author names →Lastname, Firstname);fixapplies those SAFE_FIX changes (dry-run by default,--applywrites after backing up to.nora/citation/backups/);keygenproposes convention-conforming keys (report-only — renames touch.tex, which the CLI never edits); plusinit/doctor. Judgment work stays innora-citation-auditorbelow.nora literature <subcommand>(aliasnora lit) — structured literature backend around.nora/literature/papers.yaml:init,search(query arXiv/OpenAlex/Semantic Scholar/Crossref/DBLP — keyless, 10s timeouts, per-source degradation, permanently cached with--refreshto re-fetch; merged hits entercandidatestate only),expand(citation-graph expansion from a tracked seed — its references and its citers; Semantic Scholar primary, OpenAlex fallback; same cache/degradation rules; summary inCITATION_GRAPH_SUMMARY.md),ingest(--bibtex FILE/--titles FILE/ manual--title ..., with DOI/arXiv/normalized-title dedup on entry),dedup(reports likely duplicates, never merges),queue,mark <id> [status] [--role ...] [--note ...](the only status-transition entry point — promotions toread/proposed_cite/cited/rejectedare refused without--decision <approved id>from.nora/decisions/decisions.yaml),coverage(role × status report with gaps;--writeforCOVERAGE_REPORT.md),render(regenerate the markdown views incl. the signal-rankedTOP_CANDIDATES.md), anddoctor(parses papers.yaml and cross-checks the decision gate). Seenora-literature-managerbelow for the judgment side.nora writing <subcommand>— writing guardrails:lintruns mechanical prose checks over.tex(read-only, no auto-apply path at all):\work[...]placeholder commands (BLOCKED— agent-misuse artifacts, removed and never formalized), leftover draft markup/markers (\hl,TODO,??, …), hand-writtenFigure~\ref{...}/hard-coded "Figure 3" that should be\cref, straight"..."quotes that should be...'' `` (verbatim/\verb/`\lstinline`/`\texttt` exempt), bold/italic overuse, overlong sentences (heuristic), and non-ASCII punctuation with suggested replacements; `--write` saves `LINT_REPORT.md`. Plus `init`/`doctor`. Judgment work stays in `nora-writing-assistant` below.
Each <module> doctor fails with a clear error and an init suggestion if that module's .nora/<module>/ directory doesn't exist — unlike the default nora doctor, which only warns.
Four workflows:
new— bootstrap a brand-new project with no prior statereboot— recover a stale or long-inactive projectsession-update— summarize the current session and propose project state updatessync-agents— check the project'sAGENTS.mdagainst the current template and propose an update
Invoke with /nora <workflow> (e.g. /nora reboot). A bare /nora, or one followed by text that doesn't match a workflow name, loads the project's .nora/ context and continues the conversation normally — it does not guess and trigger one of the four workflows on its own.
Mechanical and semi-mechanical BibTeX/LaTeX citation auditing, plus a bounded claim-support review pass. It never edits .bib or .tex files, never renames keys, and never claims to fully verify scientific correctness.
Four workflows:
check-bibtex— duplicate keys, duplicate DOIs, missing important fields, possible duplicate titles (conservative fuzzy match), possible arXiv/journal duplicate candidatescheck-latex-citations— citation keys used in.texbut missing from.bib, and.bibentries never citedreport— consolidate the above into.nora/citation/CITATION_AUDIT_REPORT.mdand.nora/citation/CITATION_REVIEW_QUEUE.yamlaudit-claim-support— for a specific claim–citation pair, a bounded read of the cited source's available material, classified asSUPPORTED/PARTIALLY_SUPPORTED/WEAK_OR_INDIRECT/UNSUPPORTED/REVIEW_REQUIRED/BLOCKED, written to.nora/citation/CLAIM_SUPPORT_AUDIT.md. This is a reading aid for a human reviewer, not automated verification.
Invoke with /nora-citation-auditor <workflow> or the short alias /nora-citation <workflow> (e.g. /nora-citation check-bibtex).
Unlike nora-project-manager, this skill does not require .nora/ to exist. If it's missing, the skill runs in standalone citation-audit mode and says so explicitly, writing output only to .nora/citation/. If a full Nora project exists, it also reads .nora/PROJECT_STATE.yaml for context and may propose (never silently apply) a pointer in .nora/OPEN_LOOPS.md.
Mechanical findings are tagged AUTO_SAFE, REVIEW_REQUIRED, BLOCKED, or DO_NOT_APPLY — these describe confidence in the finding, not permission to auto-fix; nothing is ever applied automatically. audit-claim-support uses its own label set (above) since "how confident is this mechanical finding" and "does this source support this claim" are different kinds of judgment.
Track, triage, and organize research literature on top of the nora literature CLI backend — the skill judges, the CLI keeps the books.
Five workflows:
search— collect candidate papers (web/search tools if available, or user-supplied titles/DOIs/BibTeX/abstracts) and ingest them vianora literature ingesttriage— judge candidates: assign roles (mark --role) and relevance notes (mark --note), queue what's worth reading; rejections go through the decision gatereading-queue— view/advance the queue through the status machine; gated promotions are proposed indecisions.yamland applied only after user approvalpaper-note— create/update a structured per-paper note under.nora/literature/PAPER_NOTES/<id>.mdrelated-work-map— interpret thecoveragereport (which gaps matter and why) and regenerate the views viarender
Invoke with /nora-literature-manager <workflow> or the short alias /nora-literature <workflow>.
Paper statuses: candidate → queued → reading → read → proposed_cite → cited (or rejected, gated, from anywhere). Free transitions among candidate/queued/reading are agent-safe bookkeeping; every promotion to read/proposed_cite/cited/rejected requires a user-approved decision — and the CLI enforces that mechanically rather than trusting the agent. The skill never writes to .bib/.tex, and never edits papers.yaml by hand.
Works standalone (no .nora/) the same way nora-citation-auditor does: it says so explicitly and writes only to .nora/literature/. In a full Nora project, it also reads .nora/CONTEXT_BRIEF.md/PROJECT_STATE.yaml to ground relevance judgments.
Academic writing improvement — polishing, restructuring, overclaim-checking, paragraph diagnosis, and style-profile setup — while preserving technical meaning, citation placement, and authorial caution. Does not generate full papers or invent claims/results/citations/mechanisms/novelty.
Five workflows:
polish— improve wording/clarity/flow without changing technical content or claim strength (any strength change is called out explicitly)restructure— reorganize a passage/section (ordering, topic sentences, transitions) without changing technical contentoverclaim-check— flag overly strong/promotional language against what the evidence/citations actually support, and propose more cautious phrasing; marks uncertain citation support asREVIEW_REQUIREDparagraph-diagnosis— diagnose structural problems in a paragraph without necessarily rewriting itstyle-profile— establish/update the project's writing style (WRITING_STYLE.md) and phrase bank (PHRASE_BANK.md)
Invoke with /nora-writing-assistant <workflow> or the short alias /nora-writing <workflow>.
This skill is primarily skill-driven rather than CLI-driven — the CLI only scaffolds/checks .nora/writing/*. It explicitly supports two modes: project-aware (.nora/ exists — reads CONTEXT_BRIEF.md, PROJECT_STATE.yaml, NEXT_ACTIONS.md, OPEN_LOOPS.md, and .nora/writing/WRITING_STYLE.md if present) and standalone (.nora/ absent — uses default academic writing rules and says explicitly that no Nora project context was detected). A writing workspace doesn't need to contain project code or artifacts at all.
Each project may contain:
.nora/
.gitignore # '*' — Nora state stays out of git by default
config.yaml # workspace identity (project_id / workspace_id / workspace_type)
PROJECT_STATE.yaml
CONTEXT_BRIEF.md
SESSION_LOG.md
NEXT_ACTIONS.md
OPEN_LOOPS.md
decisions/
decisions.yaml # decision gate: agent proposals pending user approval
citation/ # optional — enabled via `nora citation init`
CITATION_AUDIT_REPORT.md
CITATION_REVIEW_QUEUE.yaml
CLAIM_SUPPORT_AUDIT.md
literature/ # optional — enabled via `nora literature init`
papers.yaml # single source of truth — written only by the nora CLI
READING_QUEUE.md # generated view (`nora literature render`)
RELATED_WORK_MAP.md # generated view (`nora literature render`)
TOP_CANDIDATES.md # generated view — candidates ranked by mechanical signals
CITATION_GRAPH_SUMMARY.md # generated view — expansion results per seed, multi-seed overlaps
COVERAGE_REPORT.md # generated report (`nora literature coverage --write`)
cache/ # permanent external-source response cache (`search --refresh` bypasses)
PAPER_NOTES/ # one note per paper, named <id>.md
writing/ # optional — enabled via `nora writing init`
WRITING_STYLE.md
STYLE_NOTES.md
PHRASE_BANK.md
LATEX_CONVENTIONS.md # project LaTeX conventions (lint checks the mechanical ones)
REVISION_CHECKLIST.md # pre-submission pass: mechanical steps, then judgment steps
LINT_REPORT.md # generated report (`nora writing lint --write`)
nora new creates only the core state (the five state files, .gitignore, config.yaml, decisions/) plus AGENTS.md — none of citation/, literature/, or writing/. Each module directory is created only by its own nora <module> init, and only when the user explicitly enables that module. A Nora-managed project is never forced to initialize any module it doesn't need. Module init commands resolve the workspace root first, so running them from a subdirectory targets the workspace's .nora/, never a new nested one. Workspaces created before 0.3.0 lack .gitignore/config.yaml/decisions/; nora doctor reports those as INFO, not errors.
nora new also copies an AGENTS.md into the project root, pointing any agent (Claude Code, Codex, or otherwise) at the Nora startup protocol, the four available skills, and the human-review policy, even if that agent doesn't use nora-project-manager directly.
Three of the four skills work without .nora/PROJECT_STATE.yaml ever existing — they say so explicitly and scope their output to just their own module directory:
- Citation auditing in a plain LaTeX workspace: run
nora citation initin a directory that has.tex/.bibfiles but no other Nora state. The skill creates just.nora/citation/and runs its checks against the.tex/.bibfiles it finds — no project stage, goals, or other Nora files are required. - Writing assistance without
.nora/: paste a passage or point at a single file;nora-writing-assistantstates that no Nora project context was detected, applies default academic writing rules instead of a project-specific style, and doesn't require any project artifacts. - Literature triage from manually supplied metadata: if no web/search tools are available (or the workspace has no project at all),
nora-literature-managerstill organizes whatever the user pastes in — titles, DOIs, BibTeX entries, PDF text, notes — into the same reading-queue/triage/notes structure it would use with tool-assisted search.
Agents propose updates to .nora/* files (core and module) and to AGENTS.md; they do not overwrite any of them silently. Low-stakes, purely additive actions (e.g. appending a new literature candidate found during search, or writing a brand-new per-paper note) may be written directly and then summarized — nothing existing is being changed. Anything that changes or removes existing content goes through a propose-then-confirm step.