claude-second-brain 0.5.0 → 0.6.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/README.md CHANGED
@@ -63,7 +63,7 @@ The CLI will ask:
63
63
  - **qmd index path** — where to store the local search index (default: `~/.cache/qmd/index.sqlite`)
64
64
  - **GitHub repo** — optionally create a private repo and push automatically (requires `gh` CLI)
65
65
 
66
- Then scaffolds the vault, installs `mise` + `bun`, and runs `git init`.
66
+ Then scaffolds the vault, installs `mise` + `node` + `pnpm`, runs `pnpm install`, and `git init`.
67
67
 
68
68
  **Step 2 — Initialize inside Claude Code**
69
69
 
package/bin/create.js CHANGED
@@ -65,6 +65,187 @@ async function patchVault(targetDir, qmdPath, brainName) {
65
65
  }
66
66
  }
67
67
 
68
+ // Resolve the `wrangler` CLI as either an installed binary or `npx wrangler` fallback.
69
+ function resolveWranglerCli() {
70
+ if (commandExists("wrangler")) return { cmd: "wrangler", prefix: [] }
71
+ return { cmd: "npx", prefix: ["wrangler"] }
72
+ }
73
+
74
+ // Strip ANSI escape sequences that wrangler injects into stdout.
75
+ function stripAnsi(s) {
76
+ // eslint-disable-next-line no-control-regex
77
+ return s.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "")
78
+ }
79
+
80
+ // Wrangler prints a decorative banner (⛅, ❅, etc.) alongside the token.
81
+ // Find the actual token line — a long run of Bearer-safe ASCII chars — and ignore the rest.
82
+ function extractBearerToken(stdout) {
83
+ const lines = stripAnsi(stdout).split(/\r?\n/)
84
+ const candidates = lines
85
+ .map(l => l.trim())
86
+ .filter(l => /^[A-Za-z0-9._~+/=-]{20,}$/.test(l))
87
+ // Prefer the longest candidate (real tokens are longer than any accidental match).
88
+ candidates.sort((a, b) => b.length - a.length)
89
+ return candidates[0] || null
90
+ }
91
+
92
+ function truncate(s, n = 80) {
93
+ if (!s) return ""
94
+ return s.length > n ? s.slice(0, n) + "…" : s
95
+ }
96
+
97
+ // End-to-end Cloudflare Artifacts setup: auth, create repo, set remote, push.
98
+ // Every failure logs the exact step, exit codes, stderr, and HTTP bodies so the
99
+ // user can see where in the flow the break happened.
100
+ async function setupCloudflareRemote({ targetDir, repoName, namespace, spin }) {
101
+ // Honor an existing API token as a shortcut — skip wrangler entirely.
102
+ let cfApiToken = process.env.CLOUDFLARE_API_TOKEN
103
+ let tokenSource = "CLOUDFLARE_API_TOKEN env"
104
+
105
+ if (!cfApiToken) {
106
+ // wrangler login requests artifacts:write by default — use it for auth.
107
+ const wrangler = resolveWranglerCli()
108
+ const wranglerLabel = [wrangler.cmd, ...wrangler.prefix].join(" ")
109
+
110
+ p.log.info(`Checking Cloudflare auth via \`${wranglerLabel} whoami\`...`)
111
+ const authCheck = spawnSync(wrangler.cmd, [...wrangler.prefix, "whoami"], { stdio: "pipe" })
112
+ const whoamiOut = stripAnsi(authCheck.stdout?.toString() || "") + "\n" + stripAnsi(authCheck.stderr?.toString() || "")
113
+ let loggedIn = authCheck.status === 0
114
+ // Wrangler's existing session may predate Artifacts — it warns "missing some expected Oauth scopes"
115
+ // and lists "artifacts:write". Detect that and force a re-login.
116
+ const hasArtifactsScope = /^\s*-\s*artifacts\s*\(write\)/im.test(whoamiOut)
117
+ const missingArtifactsScope = /missing some expected Oauth scopes/i.test(whoamiOut)
118
+ && /artifacts:write/i.test(whoamiOut)
119
+ const needsRelogin = loggedIn && (!hasArtifactsScope || missingArtifactsScope)
120
+
121
+ if (!loggedIn) {
122
+ const stderr = authCheck.stderr?.toString().trim()
123
+ p.log.info(`wrangler whoami exited ${authCheck.status}${stderr ? ` (${truncate(stderr, 160)})` : ""}`)
124
+ } else if (needsRelogin) {
125
+ p.log.info("Your wrangler session is missing the artifacts:write scope — re-login required.")
126
+ }
127
+
128
+ if (!loggedIn || needsRelogin) {
129
+ p.log.info("Starting wrangler login (grants artifacts:write scope)...")
130
+ const loginResult = spawnSync(wrangler.cmd, [...wrangler.prefix, "login"], { stdio: "inherit" })
131
+ loggedIn = loginResult.status === 0
132
+ if (!loggedIn) {
133
+ p.log.warn(`wrangler login exited ${loginResult.status} — set CLOUDFLARE_API_TOKEN and re-run.`)
134
+ return null
135
+ }
136
+
137
+ // Verify the new session actually has artifacts:write before proceeding.
138
+ const verify = spawnSync(wrangler.cmd, [...wrangler.prefix, "whoami"], { stdio: "pipe" })
139
+ const verifyOut = stripAnsi(verify.stdout?.toString() || "") + "\n" + stripAnsi(verify.stderr?.toString() || "")
140
+ if (!/^\s*-\s*artifacts\s*\(write\)/im.test(verifyOut)) {
141
+ p.log.warn("wrangler login completed but artifacts:write scope is still missing.")
142
+ p.log.warn("Upgrade wrangler (npm i -g wrangler@latest) or set CLOUDFLARE_API_TOKEN manually.")
143
+ return null
144
+ }
145
+ }
146
+
147
+ // Retrieve the OAuth token wrangler stored; it refreshes automatically if expired.
148
+ p.log.info(`Fetching OAuth token via \`${wranglerLabel} auth token\`...`)
149
+ const tokenResult = spawnSync(wrangler.cmd, [...wrangler.prefix, "auth", "token"], { stdio: "pipe" })
150
+ const rawStdout = tokenResult.stdout?.toString() || ""
151
+ const rawStderr = tokenResult.stderr?.toString() || ""
152
+
153
+ if (tokenResult.status !== 0) {
154
+ p.log.warn(`wrangler auth token exited ${tokenResult.status}`)
155
+ if (rawStderr.trim()) p.log.warn(`stderr: ${truncate(rawStderr.trim(), 400)}`)
156
+ if (rawStdout.trim()) p.log.warn(`stdout: ${truncate(rawStdout.trim(), 400)}`)
157
+ return null
158
+ }
159
+
160
+ cfApiToken = extractBearerToken(rawStdout)
161
+ if (!cfApiToken) {
162
+ p.log.warn("Could not parse a Bearer-safe token from wrangler output.")
163
+ p.log.warn(`raw stdout: ${truncate(rawStdout.trim(), 400)}`)
164
+ if (rawStderr.trim()) p.log.warn(`raw stderr: ${truncate(rawStderr.trim(), 400)}`)
165
+ return null
166
+ }
167
+ tokenSource = "wrangler auth token"
168
+ }
169
+
170
+ // Guard against tokens that contain non-ASCII (which would crash fetch's header encoder).
171
+ if (!/^[\x20-\x7E]+$/.test(cfApiToken)) {
172
+ p.log.warn(`Token from ${tokenSource} contains non-ASCII characters — refusing to use it.`)
173
+ p.log.warn(`token preview: ${truncate(cfApiToken, 60)}`)
174
+ return null
175
+ }
176
+
177
+ p.log.info(`Using token from ${tokenSource} (length ${cfApiToken.length}, prefix ${cfApiToken.slice(0, 8)}…)`)
178
+
179
+ const baseUrl = `https://artifacts.cloudflare.net/v1/api/namespaces/${namespace}`
180
+ spin.start(`Creating Cloudflare Artifact ${pc.dim(repoName)} at ${pc.dim(baseUrl)}`)
181
+
182
+ let res
183
+ let rawBody = ""
184
+ try {
185
+ res = await fetch(`${baseUrl}/repos`, {
186
+ method: "POST",
187
+ headers: {
188
+ "Authorization": `Bearer ${cfApiToken}`,
189
+ "Content-Type": "application/json",
190
+ },
191
+ body: JSON.stringify({ name: repoName, default_branch: "main" }),
192
+ })
193
+ rawBody = await res.text()
194
+ } catch (err) {
195
+ spin.stop(`Network error calling Artifacts API: ${err.message}`, 1)
196
+ return null
197
+ }
198
+
199
+ let createData
200
+ try {
201
+ createData = rawBody ? JSON.parse(rawBody) : {}
202
+ } catch {
203
+ spin.stop(`Artifacts API returned non-JSON (HTTP ${res.status})`, 1)
204
+ p.log.warn(`body: ${truncate(rawBody, 400)}`)
205
+ return null
206
+ }
207
+
208
+ if (!res.ok || !createData?.success) {
209
+ const errMsg = createData?.errors?.[0]?.message || createData?.message || "unknown error"
210
+ spin.stop(`Artifacts API failed (HTTP ${res.status}): ${errMsg}`, 1)
211
+ p.log.warn(`full response: ${truncate(rawBody, 400)}`)
212
+ return null
213
+ }
214
+
215
+ const { remote, token: repoToken } = createData.result
216
+ if (!remote || !repoToken) {
217
+ spin.stop("Artifacts API succeeded but response is missing remote/token", 1)
218
+ p.log.warn(`response: ${truncate(rawBody, 400)}`)
219
+ return null
220
+ }
221
+
222
+ const remoteAddResult = spawnSync("git", ["remote", "add", "origin", remote], { cwd: targetDir, stdio: "pipe" })
223
+ if (remoteAddResult.status !== 0) {
224
+ spin.stop(`git remote add failed (exit ${remoteAddResult.status})`, 1)
225
+ const stderr = remoteAddResult.stderr?.toString().trim()
226
+ if (stderr) p.log.warn(`git stderr: ${truncate(stderr, 400)}`)
227
+ return null
228
+ }
229
+
230
+ const pushResult = spawnSync(
231
+ "git",
232
+ ["-c", `http.extraHeader=Authorization: Bearer ${repoToken}`, "push", "-u", "origin", "main"],
233
+ { cwd: targetDir, stdio: "pipe" }
234
+ )
235
+ if (pushResult.status !== 0) {
236
+ spin.stop(`git push to Cloudflare Artifact failed (exit ${pushResult.status})`, 1)
237
+ const stderr = pushResult.stderr?.toString().trim()
238
+ const stdout = pushResult.stdout?.toString().trim()
239
+ if (stderr) p.log.warn(`git stderr: ${truncate(stderr, 400)}`)
240
+ if (stdout) p.log.warn(`git stdout: ${truncate(stdout, 400)}`)
241
+ return { repoToken, remote }
242
+ }
243
+
244
+ spin.stop(`Cloudflare Artifact created: ${pc.cyan(repoName)}`)
245
+ p.log.info(`Remote: ${pc.dim(remote)}`)
246
+ return { repoToken, remote }
247
+ }
248
+
68
249
  async function installGlobalSkills(qmdPath) {
69
250
  const globalSkillsDir = join(homedir(), ".claude", "skills")
70
251
 
@@ -100,37 +281,56 @@ async function main() {
100
281
  process.env.XDG_CACHE_HOME || join(homedir(), ".cache"),
101
282
  "qmd", "index.sqlite"
102
283
  )
284
+ const toDisplayPath = p => p.startsWith(homedir()) ? "~" + p.slice(homedir().length) : p
285
+ const expandHome = p => p.startsWith("~/") || p === "~" ? join(homedir(), p.slice(1)) : p
286
+ const displayQmdPath = toDisplayPath(defaultQmdPath)
103
287
  let qmdPath
104
288
  if (isInteractive) {
105
289
  const answer = await p.text({
106
290
  message: "Where to store the qmd index?",
107
- placeholder: defaultQmdPath,
108
- defaultValue: defaultQmdPath,
291
+ placeholder: displayQmdPath,
292
+ defaultValue: displayQmdPath,
109
293
  })
110
294
  if (p.isCancel(answer)) { p.cancel("Setup cancelled."); process.exit(0) }
111
- qmdPath = answer
295
+ qmdPath = expandHome(answer)
112
296
  } else {
113
297
  qmdPath = defaultQmdPath
114
298
  }
115
299
 
116
- let createGhRepo = false
117
- let ghRepoName = null
300
+ let remoteProvider = "none"
301
+ let repoName = null
302
+ let cfNamespace = "default"
118
303
  if (isInteractive) {
119
- const confirm = await p.confirm({
120
- message: "Create a private GitHub repo?",
121
- initialValue: false,
304
+ const provider = await p.select({
305
+ message: "Where to host the Git remote?",
306
+ options: [
307
+ { value: "github", label: "GitHub", hint: "default" },
308
+ { value: "cloudflare", label: "Cloudflare Artifacts" },
309
+ { value: "none", label: "Skip — I'll add a remote later" },
310
+ ],
311
+ initialValue: "github",
122
312
  })
123
- if (p.isCancel(confirm)) { p.cancel("Setup cancelled."); process.exit(0) }
124
- createGhRepo = confirm
313
+ if (p.isCancel(provider)) { p.cancel("Setup cancelled."); process.exit(0) }
314
+ remoteProvider = provider
125
315
 
126
- if (createGhRepo) {
316
+ if (remoteProvider !== "none") {
127
317
  const answer = await p.text({
128
- message: "GitHub repo name?",
318
+ message: "Repo name?",
129
319
  placeholder: targetName,
130
320
  defaultValue: targetName,
131
321
  })
132
322
  if (p.isCancel(answer)) { p.cancel("Setup cancelled."); process.exit(0) }
133
- ghRepoName = answer
323
+ repoName = answer
324
+ }
325
+
326
+ if (remoteProvider === "cloudflare") {
327
+ const ns = await p.text({
328
+ message: "Artifacts namespace?",
329
+ placeholder: "default",
330
+ defaultValue: "default",
331
+ })
332
+ if (p.isCancel(ns)) { p.cancel("Setup cancelled."); process.exit(0) }
333
+ cfNamespace = ns
134
334
  }
135
335
  }
136
336
 
@@ -163,7 +363,7 @@ async function main() {
163
363
  // Patch vault files with chosen qmd path
164
364
  spin.start("Configuring qmd index path")
165
365
  await patchVault(targetDir, qmdPath, targetName)
166
- spin.stop(`qmd index → ${pc.dim(qmdPath)}`)
366
+ spin.stop(`qmd index → ${pc.dim(toDisplayPath(qmdPath))}`)
167
367
 
168
368
  // Install mise if not present
169
369
  if (!commandExists("mise")) {
@@ -173,13 +373,24 @@ async function main() {
173
373
  else spin.stop("Failed to install mise — run: npm install -g @jdxcode/mise", 1)
174
374
  }
175
375
 
176
- // Run mise install inside the new vault to install bun
177
- spin.start("Installing bun via mise")
376
+ // Run mise install inside the new vault to install node + pnpm
377
+ spin.start("Installing node + pnpm via mise")
178
378
  run(["mise", "trust"], targetDir)
179
379
  const miseOk = run(["mise", "install"], targetDir)
180
- if (miseOk) spin.stop("bun installed")
380
+ if (miseOk) spin.stop("node + pnpm installed")
181
381
  else spin.stop("mise install failed — run it manually inside your vault", 1)
182
382
 
383
+ // Install vault dependencies via pnpm
384
+ spin.start("Installing vault dependencies")
385
+ const pnpmOk = run(["mise", "exec", "--", "pnpm", "install"], targetDir)
386
+ if (pnpmOk) spin.stop("dependencies installed")
387
+ else spin.stop("pnpm install failed — run it manually inside your vault", 1)
388
+
389
+ // Install global skills
390
+ spin.start("Installing global Claude skills")
391
+ await installGlobalSkills(qmdPath)
392
+ spin.stop(`Global skills installed → ${pc.dim(toDisplayPath(join(homedir(), ".claude", "skills")))}`)
393
+
183
394
  // Git init
184
395
  spin.start("Initializing git repo")
185
396
  const gitOk = run(["git", "init"], targetDir)
@@ -192,9 +403,9 @@ async function main() {
192
403
  }
193
404
 
194
405
  // GitHub repo (optional)
195
- if (createGhRepo) {
406
+ if (remoteProvider === "github") {
196
407
  if (!commandExists("gh")) {
197
- p.log.warn(`gh CLI not found — install from https://cli.github.com, then run:\n gh repo create ${ghRepoName} --private --source=. --remote=origin --push`)
408
+ p.log.warn(`gh CLI not found — install from https://cli.github.com, then run:\n gh repo create ${repoName} --private --source=. --remote=origin --push`)
198
409
  } else {
199
410
  const authCheck = spawnSync("gh", ["auth", "status"], { stdio: "pipe" })
200
411
  let loggedIn = authCheck.status === 0
@@ -205,33 +416,49 @@ async function main() {
205
416
  }
206
417
 
207
418
  if (loggedIn) {
208
- spin.start(`Creating GitHub repo ${pc.dim(ghRepoName)}`)
419
+ spin.start(`Creating GitHub repo ${pc.dim(repoName)}`)
209
420
  const ghOk = run(
210
- ["gh", "repo", "create", ghRepoName, "--private", "--source=.", "--remote=origin", "--push"],
421
+ ["gh", "repo", "create", repoName, "--private", "--source=.", "--remote=origin", "--push"],
211
422
  targetDir
212
423
  )
213
424
  if (ghOk) {
214
- spin.stop(`GitHub repo created (private): ${pc.cyan(ghRepoName)}`)
425
+ spin.stop(`GitHub repo created (private): ${pc.cyan(repoName)}`)
215
426
  } else {
216
- spin.stop(`gh repo create failed — run: gh repo create ${ghRepoName} --private --source=. --remote=origin --push`, 1)
427
+ spin.stop(`gh repo create failed — run: gh repo create ${repoName} --private --source=. --remote=origin --push`, 1)
217
428
  }
218
429
  }
219
430
  }
220
431
  }
221
432
 
222
- // Install global skills
223
- spin.start("Installing global Claude skills")
224
- await installGlobalSkills(qmdPath)
225
- spin.stop("Global skills installed")
433
+ // Cloudflare Artifacts repo (optional)
434
+ if (remoteProvider === "cloudflare") {
435
+ const result = await setupCloudflareRemote({ targetDir, repoName, namespace: cfNamespace, spin })
436
+ if (result?.repoToken) {
437
+ p.log.warn(`Save your Artifacts repo token — it expires and you'll need to mint a new one:\n ${result.repoToken}`)
438
+ }
439
+ }
226
440
 
227
441
  // Next steps
442
+ const remoteSteps = remoteProvider === "cloudflare"
443
+ ? [
444
+ `${pc.dim("# Mint a fresh git push/pull token when the current one expires:")}`,
445
+ `${pc.cyan(`TOKEN=$(wrangler auth token)`)}`,
446
+ `${pc.cyan(`curl -X POST https://artifacts.cloudflare.net/v1/api/namespaces/${cfNamespace}/tokens \\`)}`,
447
+ `${pc.cyan(` -H "Authorization: Bearer $TOKEN" \\`)}`,
448
+ `${pc.cyan(` -H "Content-Type: application/json" \\`)}`,
449
+ `${pc.cyan(` -d '{"repo":"${repoName}","scope":"write","ttl":86400}'`)}`,
450
+ ]
451
+ : remoteProvider === "none"
452
+ ? [
453
+ `${pc.cyan("git remote add origin <url>")} connect to a remote for sync`,
454
+ `${pc.cyan("git push -u origin main")}`,
455
+ ]
456
+ : []
457
+
228
458
  const nextSteps = [
229
459
  `${pc.cyan(`cd ${targetName}`)}`,
230
460
  `${pc.cyan("claude")} open Claude Code, then run ${pc.bold("/setup")}`,
231
- ...(!createGhRepo ? [
232
- `${pc.cyan("git remote add origin <url>")} connect to GitHub for sync`,
233
- `${pc.cyan("git push -u origin main")}`,
234
- ] : []),
461
+ ...remoteSteps,
235
462
  ].join("\n")
236
463
 
237
464
  p.note(nextSteps, "Next steps")
package/package.json CHANGED
@@ -1,19 +1,25 @@
1
1
  {
2
2
  "name": "claude-second-brain",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "The fastest way to start your personal knowledge base powered by Obsidian, Claude Code, qmd, and GitHub.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "claude-second-brain": "./bin/create.js"
8
8
  },
9
+ "scripts": {
10
+ "docs:dev": "vocs dev",
11
+ "docs:build": "vocs build",
12
+ "docs:preview": "vocs preview"
13
+ },
9
14
  "files": [
10
15
  "bin/",
11
16
  "template/",
12
17
  "image.png"
13
18
  ],
14
19
  "engines": {
15
- "node": ">=18.0.0"
20
+ "node": ">=22.0.0"
16
21
  },
22
+ "packageManager": "pnpm@10.8.0",
17
23
  "dependencies": {
18
24
  "@clack/prompts": "^1.2.0",
19
25
  "picocolors": "^1.1.1"
@@ -22,5 +28,12 @@
22
28
  "repository": {
23
29
  "type": "git",
24
30
  "url": "https://github.com/jessepinkman9900/claude-second-brain"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.2.14",
34
+ "react": "^19.2.5",
35
+ "react-dom": "^19.2.5",
36
+ "typescript": "^6.0.2",
37
+ "vocs": "1.4.1"
25
38
  }
26
39
  }
@@ -35,7 +35,7 @@ Runs the full 9-step ingest workflow defined in CLAUDE.md. Do not skip steps.
35
35
  - Add the new source to the Sources Ingested section: one-line description + `[[wiki/sources/slug]]` link
36
36
 
37
37
  **Step 5 — Identify affected wiki pages**
38
- - Run: `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<source topic and key claims>"`
38
+ - Run: `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<source topic and key claims>"`
39
39
  - Also Glob `wiki/*.md` and `wiki/sources/*.md` to catch anything qmd missed
40
40
  - List all pages to create or update before proceeding
41
41
 
@@ -25,9 +25,9 @@ All commands run from the vault root.
25
25
  - Read `CLAUDE.md` to understand the documented schema and any references to collection names
26
26
  - List current state from qmd:
27
27
  ```bash
28
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection list
29
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context list
30
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd status
28
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection list
29
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context list
30
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd status
31
31
  ```
32
32
 
33
33
  ### Step 2 — Analyze the wiki
@@ -74,20 +74,20 @@ If collection names changed:
74
74
  For each existing collection that no longer fits the new schema:
75
75
 
76
76
  ```bash
77
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection remove <old-name>
77
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection remove <old-name>
78
78
  ```
79
79
 
80
80
  For each obsolete context:
81
81
 
82
82
  ```bash
83
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context rm <path>
83
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context rm <path>
84
84
  ```
85
85
 
86
86
  ### Step 8 — Register the new schema
87
87
 
88
88
  Run:
89
89
  ```bash
90
- bun scripts/qmd/setup.ts
90
+ pnpm qmd:setup
91
91
  ```
92
92
 
93
93
  This is idempotent — collections that already exist (e.g. ones you kept) are skipped; new ones are added; contexts are upserted.
@@ -96,7 +96,7 @@ This is idempotent — collections that already exist (e.g. ones you kept) are s
96
96
 
97
97
  Run:
98
98
  ```bash
99
- bun scripts/qmd/reindex.ts
99
+ pnpm qmd:reindex
100
100
  ```
101
101
 
102
102
  This indexes all files under the new collections and generates fresh embeddings.
@@ -104,9 +104,9 @@ This indexes all files under the new collections and generates fresh embeddings.
104
104
  ### Step 10 — Verify and report
105
105
 
106
106
  ```bash
107
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection list
108
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context list
109
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd status
107
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection list
108
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context list
109
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd status
110
110
  ```
111
111
 
112
112
  Confirm the new collections appear, contexts match the plan, and document/embedding counts are non-zero. Report a summary of what changed (collections added/removed/renamed, contexts updated, files re-indexed, references updated in CLAUDE.md and skills).
@@ -6,7 +6,7 @@ argument-hint: "Optional: 'force' to force re-embedding of every chunk (slow, us
6
6
 
7
7
  # Brain Refresh
8
8
 
9
- Refreshes the qmd index so search reflects the current state of the vault. Wraps `bun scripts/qmd/reindex.ts` (incremental) and the qmd CLI's force-embed flag.
9
+ Refreshes the qmd index so search reflects the current state of the vault. Wraps `pnpm qmd:reindex` (incremental) and the qmd CLI's force-embed flag.
10
10
 
11
11
  ## When to Use
12
12
 
@@ -23,7 +23,7 @@ All commands run from the vault root.
23
23
 
24
24
  Run:
25
25
  ```bash
26
- bun scripts/qmd/reindex.ts
26
+ pnpm qmd:reindex
27
27
  ```
28
28
 
29
29
  This script does two things:
@@ -38,10 +38,10 @@ When the user passes `force`, re-embed **every** chunk — not just the changed
38
38
 
39
39
  ```bash
40
40
  # Step 1 — update the file index first
41
- bun scripts/qmd/reindex.ts
41
+ pnpm qmd:reindex
42
42
 
43
43
  # Step 2 — force re-embed everything
44
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd embed -f
44
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd embed -f
45
45
  ```
46
46
 
47
47
  Force mode is slow — confirm with the user before proceeding if the wiki is large.
@@ -51,7 +51,7 @@ Force mode is slow — confirm with the user before proceeding if the wiki is la
51
51
  After refresh, confirm with:
52
52
 
53
53
  ```bash
54
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd status
54
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd status
55
55
  ```
56
56
 
57
57
  Document and embedding counts should be non-zero and reflect recent activity. If embeddings show as `0` or far below document count, re-run the refresh.
@@ -11,7 +11,7 @@ Runs the 4-step query workflow defined in CLAUDE.md. Answers come with inline `[
11
11
  ## Workflow
12
12
 
13
13
  **Step 1 — Search the wiki**
14
- - Run hybrid search: `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<question>"`
14
+ - Run hybrid search: `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<question>"`
15
15
  - Read `wiki/index.md` to confirm coverage and catch any pages qmd didn't surface
16
16
  - Read the 2–5 most relevant pages in full before synthesizing
17
17
 
@@ -13,10 +13,10 @@ Local semantic document index with hybrid search (BM25 + vector), collection man
13
13
  This vault keeps its index at `__QMD_PATH__`. All CLI commands **must** prefix with `INDEX_PATH=__QMD_PATH__`:
14
14
 
15
15
  ```bash
16
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "..."
16
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "..."
17
17
  ```
18
18
 
19
- The scripts `bun scripts/qmd/setup.ts` and `bun scripts/qmd/reindex.ts` write to this same file. The CLI must match.
19
+ The scripts `pnpm qmd:setup` and `pnpm qmd:reindex` write to this same file. The CLI must match.
20
20
 
21
21
  ---
22
22
 
@@ -24,40 +24,40 @@ The scripts `bun scripts/qmd/setup.ts` and `bun scripts/qmd/reindex.ts` write to
24
24
 
25
25
  ```bash
26
26
  # Create/index a collection
27
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection add <path> --name <name> --mask <glob-pattern>
27
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection add <path> --name <name> --mask <glob-pattern>
28
28
  # e.g.:
29
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection add wiki --name wiki --mask "**/*.md"
29
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection add wiki --name wiki --mask "**/*.md"
30
30
 
31
31
  # List all collections with details
32
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection list
32
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection list
33
33
 
34
34
  # Remove a collection
35
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection remove <name>
35
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection remove <name>
36
36
 
37
37
  # Rename a collection
38
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection rename <old-name> <new-name>
38
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection rename <old-name> <new-name>
39
39
  ```
40
40
 
41
41
  ## Listing Files
42
42
 
43
43
  ```bash
44
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd ls
45
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd ls [collection[/path]]
44
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd ls
45
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd ls [collection[/path]]
46
46
  ```
47
47
 
48
48
  ## Context
49
49
 
50
50
  ```bash
51
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context add [path] "description text"
52
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context list
53
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context rm <path>
51
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context add [path] "description text"
52
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context list
53
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context rm <path>
54
54
  ```
55
55
 
56
56
  ## Retrieving Documents
57
57
 
58
58
  ```bash
59
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd get <file>[:line] [-l N] [--from N]
60
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd multi-get <pattern> [-l N] [--max-bytes N]
59
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd get <file>[:line] [-l N] [--from N]
60
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd multi-get <pattern> [-l N] [--max-bytes N]
61
61
  ```
62
62
 
63
63
  **Multi-get format flags:** `--json`, `--csv`, `--md`, `--xml`, `--files`
@@ -66,16 +66,16 @@ INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd multi-get <pattern> [-l N] [--max-bytes
66
66
 
67
67
  ```bash
68
68
  # Show index status and collections
69
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd status
69
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd status
70
70
 
71
71
  # Re-index all collections
72
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd update [--pull]
72
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd update [--pull]
73
73
 
74
74
  # Create vector embeddings (900 tokens/chunk, 15% overlap)
75
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd embed [-f]
75
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd embed [-f]
76
76
 
77
77
  # Remove cache and orphaned data, vacuum DB
78
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd cleanup
78
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd cleanup
79
79
  ```
80
80
 
81
81
  > After bulk ingest sessions, run `update` then `embed` to keep the index fresh.
@@ -108,28 +108,28 @@ INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd cleanup
108
108
 
109
109
  ```bash
110
110
  # Hybrid search across all collections
111
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query "distributed systems consensus"
111
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query "distributed systems consensus"
112
112
 
113
113
  # Search only the wiki collection, JSON output
114
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "transformer architecture" --json
114
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "transformer architecture" --json
115
115
 
116
116
  # Keyword-only search, top 10, markdown output
117
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd search -c wiki "kafka" -n 10 --md
117
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd search -c wiki "kafka" -n 10 --md
118
118
 
119
119
  # Vector search with full documents
120
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd vsearch "attention mechanism" --full
120
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd vsearch "attention mechanism" --full
121
121
 
122
122
  # Filter by score threshold
123
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query "machine learning" --all --min-score 0.7
123
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query "machine learning" --all --min-score 0.7
124
124
  ```
125
125
 
126
126
  ## MCP Server
127
127
 
128
128
  ```bash
129
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd mcp
130
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd mcp --http [--port N]
131
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd mcp --http --daemon
132
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd mcp stop
129
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd mcp
130
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd mcp --http [--port N]
131
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd mcp --http --daemon
132
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd mcp stop
133
133
  ```
134
134
 
135
135
  ## Global Options
@@ -145,24 +145,24 @@ Note: use `INDEX_PATH` env var rather than `--index` — `INDEX_PATH` accepts a
145
145
  ### First-time setup for this vault
146
146
  ```bash
147
147
  # Handled by the /setup skill:
148
- bun scripts/qmd/setup.ts
149
- bun scripts/qmd/reindex.ts
148
+ pnpm qmd:setup
149
+ pnpm qmd:reindex
150
150
  ```
151
151
 
152
152
  ### After a bulk ingest session
153
153
  ```bash
154
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd update
155
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd embed
154
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd update
155
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd embed
156
156
  ```
157
157
 
158
158
  ### Research workflow
159
159
  ```bash
160
160
  # Discover relevant pages
161
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<topic>"
161
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<topic>"
162
162
 
163
163
  # Get a specific file
164
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd get wiki/distributed-systems.md
164
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd get wiki/distributed-systems.md
165
165
 
166
166
  # Get multiple related files
167
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd multi-get "wiki/kafka.md,wiki/distributed-systems.md" --md
167
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd multi-get "wiki/kafka.md,wiki/distributed-systems.md" --md
168
168
  ```
@@ -21,7 +21,7 @@ All commands run from the vault root.
21
21
 
22
22
  Run:
23
23
  ```bash
24
- bun scripts/qmd/setup.ts
24
+ pnpm qmd:setup
25
25
  ```
26
26
 
27
27
  Registers the two core qmd collections (`wiki`, `raw-sources`) and their path-level context descriptions. Idempotent — safe to re-run.
@@ -30,7 +30,7 @@ Registers the two core qmd collections (`wiki`, `raw-sources`) and their path-le
30
30
 
31
31
  Run:
32
32
  ```bash
33
- bun scripts/qmd/reindex.ts
33
+ pnpm qmd:reindex
34
34
  ```
35
35
 
36
36
  Scans all collections for new/changed files and generates vector embeddings. The first run downloads ~2GB of local GGUF models — this will take a while.
@@ -39,7 +39,7 @@ Scans all collections for new/changed files and generates vector embeddings. The
39
39
 
40
40
  Skip step 1 and run only:
41
41
  ```bash
42
- bun scripts/qmd/reindex.ts
42
+ pnpm qmd:reindex
43
43
  ```
44
44
 
45
45
  Do **not** re-run after every single file edit — batch it after a session.
@@ -50,17 +50,17 @@ Run these three commands to confirm everything is working:
50
50
 
51
51
  ```bash
52
52
  # List registered collections (expect: wiki, raw-sources)
53
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd collection list
53
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd collection list
54
54
 
55
55
  # List registered contexts
56
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd context list
56
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd context list
57
57
 
58
58
  # Show index status and embedding counts
59
- INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd status
59
+ INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd status
60
60
  ```
61
61
 
62
62
  Both collections should appear in `collection list`. `status` should show non-zero document and embedding counts — if embeddings are 0, re-run step 2.
63
63
 
64
64
  ## Notes
65
- - `bun` is managed via `mise` — ensure `mise install` has been run before setup
66
- - If `bunx @tobilu/qmd` commands fail after setup, re-run step 1 then step 2
65
+ - `node` and `pnpm` are managed via `mise` — ensure `mise install` and `pnpm install` have been run before setup
66
+ - If `pnpm dlx @tobilu/qmd` commands fail after setup, re-run step 1 then step 2
@@ -111,7 +111,7 @@ Run this workflow whenever the user adds a new source. Do not skip steps.
111
111
  - Add the new source to the Sources section with a one-line description and link.
112
112
 
113
113
  **Step 5 — Identify affected wiki pages**
114
- - Run `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<source topic and key claims>"` to surface related existing wiki pages.
114
+ - Run `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<source topic and key claims>"` to surface related existing wiki pages.
115
115
  - Also Glob `wiki/*.md` and `wiki/sources/*.md` to ensure completeness.
116
116
  - List all pages to create or update.
117
117
 
@@ -142,7 +142,7 @@ Run this workflow whenever the user adds a new source. Do not skip steps.
142
142
  Run this workflow when the user asks a question against the wiki.
143
143
 
144
144
  **Step 1 — Search the wiki**
145
- - Run `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<question>"` to surface semantically relevant pages.
145
+ - Run `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<question>"` to surface semantically relevant pages.
146
146
  - Read `wiki/index.md` to confirm coverage and catch any pages qmd didn't surface.
147
147
  - Read the 2-5 most relevant pages fully.
148
148
 
@@ -211,21 +211,21 @@ N issues found, N fixed. [Brief summary of notable findings.]
211
211
 
212
212
  ## Search Tool
213
213
 
214
- This vault uses **qmd** (`bunx @tobilu/qmd`) for local semantic and full-text search.
215
- Collections and contexts are registered via `bun scripts/qmd/setup.ts` and stored at `__QMD_PATH__` (gitignored).
214
+ This vault uses **qmd** (`pnpm dlx @tobilu/qmd`) for local semantic and full-text search.
215
+ Collections and contexts are registered via `pnpm qmd:setup` and stored at `__QMD_PATH__` (gitignored).
216
216
 
217
217
  Key commands:
218
218
 
219
219
  | Command | Use |
220
220
  |---------|-----|
221
- | `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd query -c wiki "<question>"` | Hybrid search — best for topic discovery |
222
- | `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd search -c wiki "<terms>"` | Fast keyword search (BM25, no LLM) |
223
- | `INDEX_PATH=__QMD_PATH__ bunx @tobilu/qmd vsearch -c wiki "<question>"` | Pure vector/semantic search |
221
+ | `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd query -c wiki "<question>"` | Hybrid search — best for topic discovery |
222
+ | `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd search -c wiki "<terms>"` | Fast keyword search (BM25, no LLM) |
223
+ | `INDEX_PATH=__QMD_PATH__ pnpm dlx @tobilu/qmd vsearch -c wiki "<question>"` | Pure vector/semantic search |
224
224
 
225
225
  Add `--json` for structured output. Omit `-c wiki` to search all collections (wiki, raw-sources, human, daily-notes).
226
226
 
227
227
  **Re-indexing:** After a bulk ingest session run:
228
228
  ```bash
229
- bun scripts/qmd/reindex.ts
229
+ pnpm qmd:reindex
230
230
  ```
231
231
  Do NOT re-index after every single file edit.
@@ -13,10 +13,13 @@ Inspired by [Andrej Karpathy's approach to LLM-powered knowledge management](htt
13
13
  ## Quick start
14
14
 
15
15
  ```bash
16
- # 1. Install tools
16
+ # 1. Install tools (node + pnpm via mise)
17
17
  mise install
18
18
 
19
- # 2. Open Claude Code
19
+ # 2. Install dependencies
20
+ pnpm install
21
+
22
+ # 3. Open Claude Code
20
23
  claude
21
24
  ```
22
25
 
@@ -185,7 +188,7 @@ After a bulk ingest session, re-index to keep search current:
185
188
  /brain-refresh
186
189
  ```
187
190
 
188
- This wraps `bun scripts/qmd/reindex.ts` — you can also run that command directly if you're not inside Claude Code. Pass `force` to `/brain-refresh` to re-embed every chunk (e.g. after changing the embedding model).
191
+ This wraps `pnpm qmd:reindex` — you can also run that command directly if you're not inside Claude Code. Pass `force` to `/brain-refresh` to re-embed every chunk (e.g. after changing the embedding model).
189
192
 
190
193
  ---
191
194
 
@@ -1,2 +1,3 @@
1
1
  [tools]
2
- bun = "1.3.10"
2
+ node = "22"
3
+ pnpm = "10.8.0"
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "claude-second-brain-vault",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "qmd:setup": "tsx scripts/qmd/setup.ts",
7
+ "qmd:reindex": "tsx scripts/qmd/reindex.ts"
8
+ },
9
+ "dependencies": {
10
+ "@tobilu/qmd": "^2.1.0"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^22.10.2",
14
+ "tsx": "^4.19.2",
15
+ "typescript": "^5.7.2"
16
+ },
17
+ "packageManager": "pnpm@10.8.0",
18
+ "engines": {
19
+ "node": ">=22.0.0"
20
+ },
21
+ "pnpm": {
22
+ "onlyBuiltDependencies": [
23
+ "better-sqlite3",
24
+ "esbuild",
25
+ "node-llama-cpp",
26
+ "tree-sitter-go",
27
+ "tree-sitter-javascript",
28
+ "tree-sitter-python",
29
+ "tree-sitter-rust",
30
+ "tree-sitter-typescript"
31
+ ]
32
+ }
33
+ }
@@ -4,10 +4,10 @@
4
4
  * First run downloads ~2GB of GGUF models — expected, one-time.
5
5
  *
6
6
  * Run from vault root:
7
- * bun scripts/qmd/reindex.ts
7
+ * pnpm qmd:reindex
8
8
  *
9
9
  * Suitable as a cronjob:
10
- * 0 * * * * cd /home/user/my-brain && bun scripts/qmd/reindex.ts
10
+ * 0 * * * * cd /home/user/my-brain && pnpm qmd:reindex
11
11
  */
12
12
 
13
13
  import { createStore } from "@tobilu/qmd"
@@ -3,16 +3,17 @@
3
3
  * Safe to re-run: skips collections that already exist, upserts contexts.
4
4
  *
5
5
  * Run once from vault root:
6
- * bun scripts/qmd/setup.ts
6
+ * pnpm qmd:setup
7
7
  *
8
8
  * After setup, index the vault:
9
- * bun scripts/qmd/reindex.ts
9
+ * pnpm qmd:reindex
10
10
  */
11
11
 
12
12
  import { createStore } from "@tobilu/qmd"
13
- import { join } from "path"
13
+ import { dirname, join } from "node:path"
14
+ import { fileURLToPath } from "node:url"
14
15
 
15
- const VAULT = join(import.meta.dir, "../..")
16
+ const VAULT = join(dirname(fileURLToPath(import.meta.url)), "../..")
16
17
  const DB = "__QMD_PATH__"
17
18
 
18
19
  const store = await createStore({ dbPath: DB })
@@ -53,4 +54,4 @@ await store.addContext("raw-sources", "/articles", "Web articles saved as markdo
53
54
  await store.addContext("raw-sources", "/pdfs", "PDF files or their extracted text")
54
55
  await store.addContext("raw-sources", "/personal", "Personal notes flagged for wiki ingestion")
55
56
 
56
- console.log("\nSetup complete. Run: bun scripts/qmd/reindex.ts")
57
+ console.log("\nSetup complete. Run: pnpm qmd:reindex")
@@ -1,29 +1,19 @@
1
1
  {
2
2
  "compilerOptions": {
3
- // Environment setup & latest features
4
3
  "lib": ["ESNext"],
5
4
  "target": "ESNext",
6
- "module": "Preserve",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
7
  "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
8
  "allowJs": true,
10
9
 
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
10
  "verbatimModuleSyntax": true,
15
11
  "noEmit": true,
16
12
 
17
- // Best practices
18
13
  "strict": true,
19
14
  "skipLibCheck": true,
20
15
  "noFallthroughCasesInSwitch": true,
21
16
  "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
-
24
- // Some stricter flags (disabled by default)
25
- "noUnusedLocals": false,
26
- "noUnusedParameters": false,
27
- "noPropertyAccessFromIndexSignature": false
17
+ "noImplicitOverride": true
28
18
  }
29
19
  }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "qmd",
3
- "private": true,
4
- "peerDependencies": {
5
- "typescript": "^5"
6
- },
7
- "dependencies": {
8
- "@tobilu/qmd": "^2.1.0"
9
- },
10
- "devDependencies": {
11
- "@types/node": "^25.6.0",
12
- "@types/bun": "latest"
13
- }
14
- }