@z29k/notabene 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 z29k
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # notabene
2
+
3
+ **Comment your docs like Google Docs — then let your AI coding agent apply the
4
+ feedback, resolve the threads, and journal *what changed and why*.**
5
+
6
+ notabene renders your repo's Markdown/MDX as a navigable site with
7
+ **Google-Docs-style commenting**, and ships the **human↔agent review protocol**
8
+ that turns those comments into edits. The viewer is the support; **the protocol is
9
+ the product.**
10
+
11
+ > *nota bene* — the margin mark that means "note this." A comment.
12
+
13
+ **MIT** · Node ≥ 18.20 · MDX **and** CommonMark/GFM · dev-local, zero backend ·
14
+ _pre-1.0, dogfooded on a real multi-service platform._
15
+
16
+ ---
17
+
18
+ ## Why
19
+
20
+ Doc review today is scattered across PR line-comments, chat threads, and "can you
21
+ fix the wording in section 3." The feedback is disconnected from the doc, and
22
+ applying it is manual and lossy.
23
+
24
+ notabene puts the comments **on the rendered doc**, stores them **in your git**
25
+ (no SaaS, no database), and closes the loop: your agent reads the comments, edits
26
+ the docs, marks them resolved, and writes a journal entry linking *what changed* to
27
+ *why*.
28
+
29
+ - **Stateless tool, data in your git.** Comments and journal are JSON files under
30
+ `.notabene/`. They travel with your repo, diff in PRs, and are readable by your
31
+ agent. No account, no server to deploy, no central state.
32
+ - **Agent-native.** The review loop ships as a Claude Code skill — and as a
33
+ plain-text protocol any agent can follow.
34
+ - **MDX *and* CommonMark/GFM.** Point it at `.md` (lenient) or `.mdx` (strict), or
35
+ mix them — selectable via config.
36
+ - **Dev-local & safe by default.** The write API only runs under `notabene dev`,
37
+ binds loopback (`127.0.0.1`) by default, and never ships in a build.
38
+
39
+ ## How it works (30 seconds)
40
+
41
+ 1. `npx notabene dev` → open the site, **select any text → leave a comment** (or
42
+ comment a whole page). Threads, resolve, hold, a global `/comments` view.
43
+ 2. Tell your agent: **"address the doc comments."**
44
+ 3. The agent reads `.notabene/`, edits the docs faithfully, marks each comment
45
+ **resolved**, and appends a **journal** entry (what / why / which comments).
46
+ 4. Read the trail at `/journal`.
47
+
48
+ > 📽️ _Live in 30s — run `npx notabene dev` and select some text. (Demo GIF coming.)_
49
+
50
+ ## Install
51
+
52
+ notabene is **two installable pieces**: the **renderer** (an npm package + CLI) and
53
+ the **review skill** (a Claude Code plugin). Install one or both.
54
+
55
+ ### 1 · The renderer — npm package
56
+
57
+ ```bash
58
+ npm install -D @z29k/notabene # or: pnpm add -D @z29k/notabene · bun add -d @z29k/notabene
59
+ npx notabene init # writes notabene.config.mjs + creates the .notabene store
60
+ npx notabene dev # → http://localhost:3009
61
+ ```
62
+
63
+ > The npm package is scoped (`@z29k/notabene`); the CLI command it installs is
64
+ > just **`notabene`**, so `npx notabene …` works as-is.
65
+
66
+ `init` is the **only** thing that touches your repo — it writes `notabene.config.mjs`
67
+ and creates the `.notabene/` store. The renderer itself **runs from the package**
68
+ (nothing is scaffolded or copied into your repo; upgrades are just `npm update`).
69
+
70
+ CLI:
71
+
72
+ | Command | What it does |
73
+ | --- | --- |
74
+ | `notabene init` | Write `notabene.config.mjs` + create the store (no-op if present) |
75
+ | `notabene dev` | Start the review server over this repo's docs (live-reload) |
76
+ | `notabene build` | Build the static site (no write API in the artifact) |
77
+ | `notabene preview` | Serve the built site |
78
+
79
+ Flags: `--port <n>` · `--config <path>` · `--root <path>` · `--host` (expose on the
80
+ LAN — trusted networks only).
81
+
82
+ ### 2 · The review skill — Claude Code plugin
83
+
84
+ In Claude Code:
85
+
86
+ ```
87
+ /plugin marketplace add z29k/notabene
88
+ /plugin install notabene@z29k
89
+ ```
90
+
91
+ Then just say **"address the doc comments"** (or *"review the docs"*, *"apply the
92
+ review feedback"*). The skill reads your `notabene.config.mjs`, processes the `open`
93
+ (non-held) comments, edits the docs, marks them resolved, appends the journal, and
94
+ runs your `verify` checks — never committing without asking.
95
+
96
+ Prefer manual install? Copy `packages/plugin/skills/notabene/` into your project's
97
+ `.claude/skills/`. Using another agent? The skill file **is** the protocol spec —
98
+ point your agent at it.
99
+
100
+ ## Configure
101
+
102
+ `notabene.config.mjs` at your repo root is the only wiring. Paths are repo-relative.
103
+
104
+ ```js
105
+ // notabene.config.mjs
106
+ export default {
107
+ siteName: "My Project",
108
+ tagline: "docs",
109
+ locale: "en",
110
+
111
+ // Input format. "mdx": .mdx STRICT + .md CommonMark/GFM lenient (mix by extension).
112
+ // "commonmark": everything CommonMark/GFM, no MDX dependency/strictness.
113
+ format: "commonmark",
114
+
115
+ // Doc spaces. `key` = url slug + store space; `path` = repo-relative folder.
116
+ roots: [
117
+ { key: "docs", label: "Docs", path: "docs", exclude: [".notabene/**"] },
118
+ ],
119
+
120
+ store: "docs/.notabene", // comments + journal (commit this folder)
121
+ port: 3009,
122
+ host: false, // loopback only — the write API edits your git
123
+ verify: [], // consumer checks your agent runs after editing
124
+ };
125
+ ```
126
+
127
+ | Key | Default | Meaning |
128
+ | --- | --- | --- |
129
+ | `siteName` / `tagline` | `"Docs"` / `"docs"` | Header brand |
130
+ | `locale` | `"en"` | UI language + nav sort collation |
131
+ | `format` | `"mdx"` | `"mdx"` or `"commonmark"` (see below) |
132
+ | `roots[]` | `[{docs}]` | Doc spaces: `{ key, label, path, exclude, description }` |
133
+ | `store` | `"docs/.notabene"` | Comments + journal folder |
134
+ | `port` | `3009` | `astro dev` port |
135
+ | `host` | `false` | `true`/`NOTABENE_HOST=1`/`--host` exposes the write API to the LAN |
136
+ | `verify[]` | `[]` | Post-edit checks the agent runs (the renderer build always runs) |
137
+
138
+ ## MDX and CommonMark/GFM
139
+
140
+ The renderer picks the processor **by file extension**:
141
+
142
+ - **`.md`** → CommonMark/GFM, **lenient**. `<email@x>`, `Promise<T>`, `{var}`, raw
143
+ HTML and GFM tables all render without a crash.
144
+ - **`.mdx`** → **strict** MDX (JSX/expressions) — importable components, but `<`/`{`
145
+ outside code fences must be escaped.
146
+
147
+ `format: "mdx"` (default) enables both, mixable in one repo. `format: "commonmark"`
148
+ drops the MDX dependency entirely — best for a plain-Markdown repo.
149
+
150
+ ## The `.notabene` contract
151
+
152
+ The store is a **versioned contract** (`<store>/meta.json` → `schemaVersion`), so
153
+ your data stays portable and diffable. A comment:
154
+
155
+ ```jsonc
156
+ { "id", "space", "page", "scope",
157
+ "anchor": { "quote", "prefix", "suffix", "section" } | null, // text-quote anchor
158
+ "thread": [{ "author", "body", "ts" }],
159
+ "status": "open" | "addressed" | "resolved",
160
+ "hold": false, // agent skips held comments
161
+ "resolution": { "note", "journalEntryId"? } | null,
162
+ "createdAt", "updatedAt" }
163
+ ```
164
+
165
+ A journal entry: `{ id, date, title, summary, changes[] { page, commentIds[], what, why } }`.
166
+
167
+ ## Why not Starlight / Docusaurus / Google Docs?
168
+
169
+ - **Starlight / Docusaurus** render docs beautifully — but there's no commenting and
170
+ no review loop.
171
+ - **Google Docs** has commenting — but your docs aren't in Google Docs. They're in
172
+ your repo, in Markdown, next to your code, in your PRs.
173
+ - **notabene** is the missing middle: review your **repo's** docs in the browser,
174
+ keep **everything in git**, and let your **agent** close the loop.
175
+
176
+ ## Safety
177
+
178
+ The comments API writes into your git. So:
179
+
180
+ - It **only runs under `notabene dev`** — it is not part of a build artifact
181
+ (writes return `403` outside dev).
182
+ - It **binds loopback by default** — not reachable from your network unless you opt
183
+ in with `--host` / `NOTABENE_HOST=1` on a trusted network.
184
+ - The agent skill **never commits without asking** and **never bulk-deletes** the
185
+ store.
186
+
187
+ ## Repo layout
188
+
189
+ - **`packages/renderer`** — the `notabene` npm package (Astro renderer + CLI).
190
+ - **`packages/plugin`** — the Claude Code plugin (the review skill).
191
+
192
+ ## Status & roadmap
193
+
194
+ Pre-1.0 and **dogfooded on a real multi-service platform** before this release.
195
+ Positioned as a **Claude Code companion** today; the protocol is agent-agnostic by
196
+ design. Feedback and issues welcome.
197
+
198
+ ## License
199
+
200
+ [MIT](./LICENSE).
@@ -0,0 +1,37 @@
1
+ // @ts-check
2
+ import { defineConfig } from "astro/config";
3
+ import mdx from "@astrojs/mdx";
4
+ import node from "@astrojs/node";
5
+ import { remarkRewriteLinks } from "./src/remark/rewrite-links.mjs";
6
+ import { REPO_ROOT, host, mdxEnabled, port, roots } from "./src/config.mjs";
7
+
8
+ // notabene renderer — a navigable site over a repo's docs + a human↔agent review
9
+ // loop. DEV-LOCAL tool, not deployed. Plain Astro (not Starlight): content lives
10
+ // OUTSIDE the app (glob `base` derived from notabene.config `roots[]`), and we keep
11
+ // full control of the 3-column layout for the comments right-rail. Run-from-package:
12
+ // the app renders the consumer's repo (NOTABENE_ROOT), only data lives there.
13
+ export default defineConfig({
14
+ // SAFETY (§3): the comments API writes into the consumer's git. Default bind is
15
+ // LOOPBACK (host: false = 127.0.0.1) — the write endpoint is NOT reachable from
16
+ // the LAN. Explicit opt-in only (config `host: true` or NOTABENE_HOST=1), trusted
17
+ // networks only.
18
+ server: { host, port },
19
+ // Doc pages are static (prerendered); /api/comments is `prerender = false`
20
+ // (on-demand) → the Node adapter serves the write API. Dev-only.
21
+ adapter: node({ mode: "standalone" }),
22
+ // MDX only in "mdx" format (§10.bis). In "commonmark", .md files go through
23
+ // Astro's native markdown pipeline (lenient CommonMark/GFM).
24
+ integrations: mdxEnabled ? [mdx()] : [],
25
+ markdown: {
26
+ // GFM on by default. Shiki syntax highlighting.
27
+ shikiConfig: { theme: "github-dark", wrap: true },
28
+ // Rewrite inter-doc .md links → site routes (see src/remark/). Tuple form
29
+ // [attacher, options]: unified calls remarkRewriteLinks(roots).
30
+ remarkPlugins: [[remarkRewriteLinks, roots]],
31
+ },
32
+ vite: {
33
+ // Consumer content (docs, notabene.config) lives outside the Astro root
34
+ // (the app is in node_modules) → let Vite serve it.
35
+ server: { fs: { allow: [REPO_ROOT] } },
36
+ },
37
+ });
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ // notabene CLI — run-from-package. Renders a consumer repo's docs from this
3
+ // package (no app is copied into the consumer). Only DATA lives in the consumer:
4
+ // notabene.config.mjs + the .notabene store.
5
+ //
6
+ // notabene init write notabene.config.mjs + create the store (no-op if present)
7
+ // notabene dev start the review server (astro dev) over the current repo
8
+ // notabene build build the static site (no write API in the artifact)
9
+ // notabene preview serve the built site
10
+ //
11
+ // Flags: --config <path> (default <cwd>/notabene.config.mjs), --root <path>
12
+ // (consumer repo root, default cwd), --host (expose on the LAN — trusted only).
13
+ import { spawn } from "node:child_process";
14
+ import { createRequire } from "node:module";
15
+ import { fileURLToPath } from "node:url";
16
+ import path from "node:path";
17
+ import fs from "node:fs";
18
+
19
+ const require = createRequire(import.meta.url);
20
+ // bin/ is at <package>/bin → the Astro app root is the package root.
21
+ const APP_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
+
23
+ const argv = process.argv.slice(2);
24
+ const cmd = argv[0];
25
+ const flag = (name) => {
26
+ const i = argv.indexOf(name);
27
+ return i !== -1 ? (argv[i + 1] ?? true) : undefined;
28
+ };
29
+
30
+ const repoRoot = path.resolve(flag("--root") || process.cwd());
31
+ const configPath = path.resolve(flag("--config") || path.join(repoRoot, "notabene.config.mjs"));
32
+ const exposeHost = argv.includes("--host");
33
+
34
+ function fail(msg) {
35
+ console.error(`notabene: ${msg}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ const TEMPLATE_CONFIG = path.join(APP_DIR, "templates", "notabene.config.mjs");
40
+
41
+ function readStore(cfgPath) {
42
+ // Best-effort: read the `store` value from an existing config to create it.
43
+ try {
44
+ const txt = fs.readFileSync(cfgPath, "utf8");
45
+ const m = txt.match(/store\s*:\s*["'`]([^"'`]+)["'`]/);
46
+ return m ? m[1] : "docs/.notabene";
47
+ } catch {
48
+ return "docs/.notabene";
49
+ }
50
+ }
51
+
52
+ function doInit() {
53
+ if (fs.existsSync(configPath)) {
54
+ console.log(`notabene: ${path.relative(repoRoot, configPath)} already exists — leaving it.`);
55
+ } else {
56
+ fs.copyFileSync(TEMPLATE_CONFIG, configPath);
57
+ console.log(`notabene: wrote ${path.relative(repoRoot, configPath)}`);
58
+ }
59
+ const store = path.resolve(repoRoot, readStore(configPath));
60
+ fs.mkdirSync(store, { recursive: true });
61
+ const meta = path.join(store, "meta.json");
62
+ if (!fs.existsSync(meta)) fs.writeFileSync(meta, `${JSON.stringify({ schemaVersion: 1 }, null, 2)}\n`);
63
+ console.log(`notabene: store ready at ${path.relative(repoRoot, store)}/`);
64
+ console.log("notabene: run `notabene dev` to start reviewing.");
65
+ }
66
+
67
+ function runAstro(astroCmd) {
68
+ if (!fs.existsSync(configPath)) {
69
+ fail(`no config at ${configPath}. Run \`notabene init\` first (or pass --config).`);
70
+ }
71
+ // Astro CLI entry, resolved from this package's deps. Astro's `exports` blocks
72
+ // deep subpaths, so resolve via package.json + its `bin` field.
73
+ const astroPkgPath = require.resolve("astro/package.json");
74
+ const astroPkg = JSON.parse(fs.readFileSync(astroPkgPath, "utf8"));
75
+ const binRel = typeof astroPkg.bin === "string" ? astroPkg.bin : astroPkg.bin.astro;
76
+ const astroBin = path.join(path.dirname(astroPkgPath), binRel);
77
+ // Note: build output goes to <APP_DIR>/dist (next to the renderer's deps, so
78
+ // Astro's prerender step resolves them). It's a throwaway verification artifact,
79
+ // gitignored, inside the installed package — not the consumer's source.
80
+ const args = [astroBin, astroCmd, "--root", APP_DIR];
81
+ const port = flag("--port");
82
+ if (port && port !== true) args.push("--port", String(port));
83
+ if (exposeHost) args.push("--host");
84
+ const child = spawn(process.execPath, args, {
85
+ stdio: "inherit",
86
+ cwd: repoRoot,
87
+ env: {
88
+ ...process.env,
89
+ NOTABENE_ROOT: repoRoot,
90
+ NOTABENE_CONFIG: configPath,
91
+ ...(exposeHost ? { NOTABENE_HOST: "1" } : {}),
92
+ },
93
+ });
94
+ child.on("exit", (code) => process.exit(code ?? 0));
95
+ }
96
+
97
+ switch (cmd) {
98
+ case "init":
99
+ doInit();
100
+ break;
101
+ case "dev":
102
+ case "build":
103
+ case "preview":
104
+ runAstro(cmd);
105
+ break;
106
+ default:
107
+ console.log(
108
+ "notabene — docs review tool\n\n" +
109
+ " notabene init write notabene.config.mjs + create the store\n" +
110
+ " notabene dev start the review server over this repo's docs\n" +
111
+ " notabene build build the static site\n" +
112
+ " notabene preview serve the built site\n\n" +
113
+ "Flags: --config <path> --root <path> --host",
114
+ );
115
+ process.exit(cmd ? 1 : 0);
116
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@z29k/notabene",
3
+ "version": "0.1.0",
4
+ "description": "Navigable docs renderer + human↔agent review loop. Comment your docs like Google Docs; an agent reads .notabene/, applies the feedback, and journals it.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "bin": {
11
+ "notabene": "bin/notabene.mjs"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "templates",
17
+ "astro.config.mjs",
18
+ "tsconfig.json"
19
+ ],
20
+ "scripts": {
21
+ "dev": "node bin/notabene.mjs dev",
22
+ "build": "node bin/notabene.mjs build"
23
+ },
24
+ "dependencies": {
25
+ "@astrojs/mdx": "5.0.6",
26
+ "@astrojs/node": "10.0.6",
27
+ "astro": "^6.0.0"
28
+ },
29
+ "keywords": ["docs", "documentation", "review", "comments", "mdx", "markdown", "astro", "ai", "agent"],
30
+ "engines": {
31
+ "node": ">=18.20.8"
32
+ }
33
+ }