ricord 1.0.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.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/commands/ricord-flush.md +29 -0
  4. package/commands/ricord-init.md +129 -0
  5. package/commands/ricord-lint.md +64 -0
  6. package/commands/ricord-query.md +71 -0
  7. package/dist/cli/auth.d.ts +16 -0
  8. package/dist/cli/auth.js +42 -0
  9. package/dist/cli/auth.js.map +1 -0
  10. package/dist/cli/bundle.d.ts +25 -0
  11. package/dist/cli/bundle.js +179 -0
  12. package/dist/cli/bundle.js.map +1 -0
  13. package/dist/cli/cache.d.ts +18 -0
  14. package/dist/cli/cache.js +39 -0
  15. package/dist/cli/cache.js.map +1 -0
  16. package/dist/cli/cli.d.ts +21 -0
  17. package/dist/cli/cli.js +355 -0
  18. package/dist/cli/cli.js.map +1 -0
  19. package/dist/cli/client.d.ts +12 -0
  20. package/dist/cli/client.js +35 -0
  21. package/dist/cli/client.js.map +1 -0
  22. package/dist/cli/commands/build.d.ts +44 -0
  23. package/dist/cli/commands/build.js +437 -0
  24. package/dist/cli/commands/build.js.map +1 -0
  25. package/dist/cli/commands/curate.d.ts +32 -0
  26. package/dist/cli/commands/curate.js +154 -0
  27. package/dist/cli/commands/curate.js.map +1 -0
  28. package/dist/cli/commands/doctor.d.ts +16 -0
  29. package/dist/cli/commands/doctor.js +92 -0
  30. package/dist/cli/commands/doctor.js.map +1 -0
  31. package/dist/cli/commands/ingest.d.ts +25 -0
  32. package/dist/cli/commands/ingest.js +121 -0
  33. package/dist/cli/commands/ingest.js.map +1 -0
  34. package/dist/cli/commands/install.d.ts +16 -0
  35. package/dist/cli/commands/install.js +82 -0
  36. package/dist/cli/commands/install.js.map +1 -0
  37. package/dist/cli/commands/pull.d.ts +24 -0
  38. package/dist/cli/commands/pull.js +104 -0
  39. package/dist/cli/commands/pull.js.map +1 -0
  40. package/dist/cli/commands/push.d.ts +28 -0
  41. package/dist/cli/commands/push.js +164 -0
  42. package/dist/cli/commands/push.js.map +1 -0
  43. package/dist/cli/commands/rollup.d.ts +21 -0
  44. package/dist/cli/commands/rollup.js +118 -0
  45. package/dist/cli/commands/rollup.js.map +1 -0
  46. package/dist/cli/commands/setup.d.ts +7 -0
  47. package/dist/cli/commands/setup.js +43 -0
  48. package/dist/cli/commands/setup.js.map +1 -0
  49. package/dist/cli/commands/sync.d.ts +15 -0
  50. package/dist/cli/commands/sync.js +63 -0
  51. package/dist/cli/commands/sync.js.map +1 -0
  52. package/dist/cli/commands/watch.d.ts +17 -0
  53. package/dist/cli/commands/watch.js +87 -0
  54. package/dist/cli/commands/watch.js.map +1 -0
  55. package/dist/cli/config.d.ts +29 -0
  56. package/dist/cli/config.js +52 -0
  57. package/dist/cli/config.js.map +1 -0
  58. package/dist/cli/extract.d.ts +101 -0
  59. package/dist/cli/extract.js +216 -0
  60. package/dist/cli/extract.js.map +1 -0
  61. package/dist/cli/ingest.d.ts +48 -0
  62. package/dist/cli/ingest.js +74 -0
  63. package/dist/cli/ingest.js.map +1 -0
  64. package/dist/cli/ledger.d.ts +44 -0
  65. package/dist/cli/ledger.js +67 -0
  66. package/dist/cli/ledger.js.map +1 -0
  67. package/dist/cli/llm.d.ts +21 -0
  68. package/dist/cli/llm.js +138 -0
  69. package/dist/cli/llm.js.map +1 -0
  70. package/dist/cli/parse.d.ts +13 -0
  71. package/dist/cli/parse.js +188 -0
  72. package/dist/cli/parse.js.map +1 -0
  73. package/dist/cli/run-explore.d.ts +56 -0
  74. package/dist/cli/run-explore.js +229 -0
  75. package/dist/cli/run-explore.js.map +1 -0
  76. package/dist/cli/summarize.d.ts +15 -0
  77. package/dist/cli/summarize.js +49 -0
  78. package/dist/cli/summarize.js.map +1 -0
  79. package/dist/cli/uninstall.d.ts +6 -0
  80. package/dist/cli/uninstall.js +277 -0
  81. package/dist/cli/uninstall.js.map +1 -0
  82. package/dist/cli/walk.d.ts +13 -0
  83. package/dist/cli/walk.js +62 -0
  84. package/dist/cli/walk.js.map +1 -0
  85. package/dist/cli/walker.d.ts +14 -0
  86. package/dist/cli/walker.js +120 -0
  87. package/dist/cli/walker.js.map +1 -0
  88. package/dist/hooks/pre-compact.d.ts +15 -0
  89. package/dist/hooks/pre-compact.js +127 -0
  90. package/dist/hooks/pre-compact.js.map +1 -0
  91. package/dist/hooks/pre-tool-use.d.ts +15 -0
  92. package/dist/hooks/pre-tool-use.js +25 -0
  93. package/dist/hooks/pre-tool-use.js.map +1 -0
  94. package/dist/hooks/session-end.d.ts +21 -0
  95. package/dist/hooks/session-end.js +186 -0
  96. package/dist/hooks/session-end.js.map +1 -0
  97. package/dist/hooks/session-start.d.ts +15 -0
  98. package/dist/hooks/session-start.js +233 -0
  99. package/dist/hooks/session-start.js.map +1 -0
  100. package/dist/hooks/turn-end-post.d.ts +17 -0
  101. package/dist/hooks/turn-end-post.js +66 -0
  102. package/dist/hooks/turn-end-post.js.map +1 -0
  103. package/dist/hooks/turn-end.d.ts +29 -0
  104. package/dist/hooks/turn-end.js +295 -0
  105. package/dist/hooks/turn-end.js.map +1 -0
  106. package/dist/index.d.ts +24 -0
  107. package/dist/index.js +1547 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/init.d.ts +45 -0
  110. package/dist/init.js +839 -0
  111. package/dist/init.js.map +1 -0
  112. package/dist/lib/active-project.d.ts +14 -0
  113. package/dist/lib/active-project.js +65 -0
  114. package/dist/lib/active-project.js.map +1 -0
  115. package/dist/lib/buffer.d.ts +34 -0
  116. package/dist/lib/buffer.js +79 -0
  117. package/dist/lib/buffer.js.map +1 -0
  118. package/dist/scripts/compile.d.ts +25 -0
  119. package/dist/scripts/compile.js +185 -0
  120. package/dist/scripts/compile.js.map +1 -0
  121. package/dist/scripts/config.d.ts +30 -0
  122. package/dist/scripts/config.js +68 -0
  123. package/dist/scripts/config.js.map +1 -0
  124. package/dist/scripts/flush.d.ts +23 -0
  125. package/dist/scripts/flush.js +230 -0
  126. package/dist/scripts/flush.js.map +1 -0
  127. package/dist/scripts/lint.d.ts +21 -0
  128. package/dist/scripts/lint.js +242 -0
  129. package/dist/scripts/lint.js.map +1 -0
  130. package/dist/scripts/utils.d.ts +43 -0
  131. package/dist/scripts/utils.js +165 -0
  132. package/dist/scripts/utils.js.map +1 -0
  133. package/package.json +74 -0
  134. package/scripts/postinstall.mjs +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ricord AI
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,213 @@
1
+ # ricord-mcp
2
+
3
+ Persistent memory for AI coding assistants. Ricord gives your AI tools long-term knowledge, episodic memory, a knowledge graph, integrations, and more — all via the [Model Context Protocol](https://modelcontextprotocol.io).
4
+
5
+ This package ships **two binaries**:
6
+
7
+ - **`ricord-mcp`** — the MCP server, for Claude Code / Cursor / Windsurf / VS Code
8
+ - **`ricord`** — a CLI for repo ingestion (`ricord init`), auth (`ricord login`), and account info (`ricord whoami`)
9
+
10
+ Both share `~/.ricord/credentials.json`, so logging in once authorizes everything.
11
+
12
+ ## Quick Start (One Command)
13
+
14
+ ```bash
15
+ npx ricord-mcp --setup --client claude --api-key YOUR_API_KEY
16
+ ```
17
+
18
+ Supported clients: `claude-code`, `claude-desktop`, `cursor`, `windsurf`, `vscode`
19
+
20
+ This auto-writes the correct config file for your editor. Get your API key at [ricord.ai/dashboard/api-keys](https://ricord.ai/dashboard/api-keys).
21
+
22
+ ### Manual Setup
23
+
24
+ <details>
25
+ <summary>Claude Code</summary>
26
+
27
+ ```bash
28
+ claude mcp add ricord -- npx ricord-mcp --api-key YOUR_API_KEY
29
+ ```
30
+ </details>
31
+
32
+ <details>
33
+ <summary>Claude Desktop</summary>
34
+
35
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "ricord": {
41
+ "command": "npx",
42
+ "args": ["ricord-mcp", "--api-key", "YOUR_API_KEY"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+ </details>
48
+
49
+ <details>
50
+ <summary>Cursor</summary>
51
+
52
+ Add to `.cursor/mcp.json`:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "ricord": {
58
+ "command": "npx",
59
+ "args": ["ricord-mcp", "--api-key", "YOUR_API_KEY"]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+ </details>
65
+
66
+ <details>
67
+ <summary>Windsurf</summary>
68
+
69
+ Add to `~/.codeium/windsurf/mcp_config.json`:
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "ricord": {
75
+ "command": "npx",
76
+ "args": ["ricord-mcp", "--api-key", "YOUR_API_KEY"]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+ </details>
82
+
83
+ <details>
84
+ <summary>VS Code (Copilot)</summary>
85
+
86
+ Add to `.vscode/mcp.json`:
87
+
88
+ ```json
89
+ {
90
+ "servers": {
91
+ "ricord": {
92
+ "command": "npx",
93
+ "args": ["ricord-mcp", "--api-key", "YOUR_API_KEY"]
94
+ }
95
+ }
96
+ }
97
+ ```
98
+ </details>
99
+
100
+ ## Options
101
+
102
+ | Flag | Env Var | Default | Description |
103
+ |------|---------|---------|-------------|
104
+ | `--api-key` | `RICORD_API_KEY` | required | Your Ricord API key |
105
+ | `--api-base` | `RICORD_API_BASE` | `https://api.ricord.ai` | API endpoint |
106
+ | `--mode` | — | `auto` | `auto`, `manual`, or `hybrid` |
107
+ | `--project` | `RICORD_PROJECT` | — | Project namespace |
108
+
109
+ ### Modes
110
+
111
+ - **auto** (default) — AI automatically saves knowledge when you make decisions, state preferences, or discuss architecture
112
+ - **manual** — Only saves when explicitly asked
113
+ - **hybrid** — Auto-extracts knowledge but marks it as draft for your review
114
+
115
+ ## Tools (9)
116
+
117
+ | Tool | Description | Credits |
118
+ |------|-------------|---------|
119
+ | `ricord_remember` | Save facts, preferences, decisions, procedures, references, playbooks, anti-patterns, episodes | 3 |
120
+ | `ricord_recall` | Retrieve relevant knowledge (hybrid vector + graph search) | 1 |
121
+ | `ricord_correct` | Update existing knowledge by ID | 3 |
122
+ | `ricord_forget` | Delete knowledge items by ID | 0 |
123
+ | `ricord_list` | Browse stored items with optional type filter | 0 |
124
+ | `ricord_profile` | Get or update aggregated user profile | 0 |
125
+ | `ricord_graph` | Explore knowledge graph: entities, neighborhoods, stats, communities | 0 |
126
+ | `ricord_usage` | Check credit balance, tier, and usage stats | 0 |
127
+
128
+ ## Prompts
129
+
130
+ Setup prompts are included for each supported client:
131
+ - `setup-claude-code`
132
+ - `setup-claude-desktop`
133
+ - `setup-cursor`
134
+ - `setup-windsurf`
135
+ - `setup-vscode`
136
+
137
+ ## Repo ingestion (`ricord init` + `/ricord-init` slash command)
138
+
139
+ Two paths, one mental model — both follow Karpathy's [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern: the agent (LLM) does the bookkeeping, Ricord stores the persistent wiki, and the schema (the slash command markdown) is the protocol.
140
+
141
+ ### Path A — inside Claude Code / Codex / OpenCode (preferred)
142
+
143
+ The host agent IS the LLM. Zero external API spend.
144
+
145
+ ```bash
146
+ # In Claude Code
147
+ /ricord-init . # walks repo, agent extracts each bundle
148
+ /ricord-lint # Karpathy lint: contradictions, orphans, stale claims
149
+ /ricord-query "<question>" # ask the wiki, optionally file the answer back
150
+ ```
151
+
152
+ Under the hood, `/ricord-init` runs `ricord init --emit-bundles` to stage `.ricord-cache/bundles/*.md` + `manifest.json`, then iterates the manifest: the agent reads each bundle, applies Ricord's canonical extraction prompt (fetched from `/v1/extraction/prompt`) in-context, and POSTs the structured result to `/v1/ingest/extracted`. The agent also maintains `index.md` (catalog) and `log.md` (chronological) per Karpathy's pattern.
153
+
154
+ ### Path B — headless CLI (for CI, scripts, no host agent)
155
+
156
+ ```bash
157
+ ricord login # one-time OAuth
158
+ ricord init # uses OPENAI_API_KEY (or ANTHROPIC_API_KEY)
159
+ ricord init --dry-run # walk + parse, no POST
160
+ ricord init --no-ingest # also write summaries to disk for review
161
+ ricord init --llm-model claude-sonnet-4-6 # override LLM
162
+ ```
163
+
164
+ In Path B the CLI fetches the same `/v1/extraction/prompt`, runs it through your LLM (~$0.05–0.15 for a small-medium repo with gpt-4o), and POSTs to `/v1/ingest/extracted`. Ricord ships zero generative LLM calls — the only thing the server does is embed bundle text for hybrid search (Vertex, ~$0.025 / 1M chars).
165
+
166
+ Both paths cache by content hash at `<root>/.ricord-cache/`, so re-runs only touch changed directories.
167
+
168
+ After ingest, query your graph:
169
+ - `ricord_search` (MCP tool, or `POST /v1/search`) for hybrid recall
170
+ - `POST /v1/kb/global-query` for GraphRAG map-reduce questions ("What's the architecture?")
171
+ - The Ricord dashboard at [ricord.ai/dashboard](https://ricord.ai/dashboard) for the visual graph
172
+
173
+ ## Hooks (opt-in)
174
+
175
+ Ricord ships two Claude Code hook scripts in `dist/hooks/`. Hooks are opt-in — installing the npm package does not activate them. You wire them into your project's `.claude/settings.json` yourself.
176
+
177
+ ### Available hooks
178
+
179
+ **SessionStart** — injects a compact memory digest at the start of every Claude session. Adds top SOPs, preferences, recent activity, and open tasks to the system prompt.
180
+
181
+ **PreToolUse** — blocks any Write, Edit, or MultiEdit call whose `file_path` targets `MEMORY.md` or the Claude auto-memory paths (`.claude/projects/*/memory/*`, `.ricord/memory/*`). Prevents the AI from silently overwriting your memory files instead of saving knowledge through the Ricord API.
182
+
183
+ ### Wiring hooks into Claude Code
184
+
185
+ Add this to your project's `.claude/settings.json`:
186
+
187
+ ```json
188
+ {
189
+ "hooks": {
190
+ "SessionStart": [
191
+ {
192
+ "type": "command",
193
+ "command": "node node_modules/ricord-mcp/dist/hooks/session-start.js"
194
+ }
195
+ ],
196
+ "PreToolUse": [
197
+ {
198
+ "type": "command",
199
+ "matcher": "Write|Edit|MultiEdit",
200
+ "command": "node node_modules/ricord-mcp/dist/hooks/pre-tool-use.js"
201
+ }
202
+ ]
203
+ }
204
+ }
205
+ ```
206
+
207
+ Or, if you installed globally (`npm install -g ricord-mcp`), replace the `node node_modules/...` path with the global install path (e.g. `$(npm root -g)/ricord-mcp/dist/hooks/session-start.js`).
208
+
209
+ **Note:** both hooks require that `RICORD_API_KEY` is set in your environment (or that `~/.ricord/credentials.json` exists from `ricord login`).
210
+
211
+ ## License
212
+
213
+ MIT
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: Flush this conversation to Ricord — extract anchors + connections, write graph, and roll up affected pages
3
+ ---
4
+
5
+ Run the Ricord client-extraction flush flow on the **current conversation**:
6
+
7
+ 1. Call `ricord_extract` with `action="fetch_prompt"`. Save the returned `prompt`, `output_schema`, and `schema_version`.
8
+
9
+ 2. Build the message list to extract from:
10
+ - All `role=user` messages from this conversation.
11
+ - The **final** `role=assistant` message only (the one followed by Stop, not the intermediate tool-using turns).
12
+ - Skip every other assistant turn, every tool-result block, every system message.
13
+
14
+ 3. Apply the prompt locally (do the extraction yourself). Substitute `{TEXT}` with the message list joined by `\n\n` lines of `<role>: <content>`. Emit a JSON object exactly matching `output_schema`: `{ anchors: [...], connections: [...], tasks: [...] }`. Do not invent facts not stated in the messages.
15
+
16
+ 4. Call `ricord_extract` with `action="flush"`, passing:
17
+ - `messages`: the same filtered list from step 2
18
+ - `extracted`: your JSON from step 3
19
+ - `extraction_meta`: `{ model: "<your model id>", client: "claude-code", schema_version: <from step 1> }`
20
+
21
+ 5. The response includes `pages_written` and any `pages_needing_rollup` ids. For each page id in `pages_needing_rollup`:
22
+ - `GET /v1/kb/pages/<id>/rollup-prompt` (use Bash + curl with the API key from `~/.ricord/credentials.json`)
23
+ - Apply the returned `prompt` locally, emit JSON matching its `output_schema`
24
+ - `POST /v1/kb/pages/<id>/body` with that JSON
25
+
26
+ 6. Report a one-line summary:
27
+ `Flushed: <pages_written> pages, <links_written> edges, <rolled_up> rollups, <tasks_written> tasks.`
28
+
29
+ Do not narrate intermediate steps. Just do the work and produce the summary line.
@@ -0,0 +1,129 @@
1
+ ---
2
+ description: Build / refresh a Ricord knowledge wiki from a repo, URL, file, or conversation — the host LLM (you) is the worker.
3
+ argument-hint: [. | repo | <url> | <file path> | (omit for conversation)]
4
+ ---
5
+
6
+ You are the worker. Ricord is the **wiki store** and the **schema** (extraction prompt + ingest endpoints). This command is the **protocol** — Karpathy's "LLM Wiki" pattern: the agent (you) reads sources, extracts structured knowledge, and the wiki compounds over time. You never call an external LLM API — *you* are the LLM.
7
+
8
+ Target: `$ARGUMENTS` (default: this conversation).
9
+
10
+ ## Source modes
11
+
12
+ - **No argument** → use this conversation. Build a single bundle from `role=user` messages plus the final `role=assistant` message. Skip to "Single-bundle flow" below.
13
+ - **`.` or `repo`** → walk the cwd repo. Use "Multi-bundle flow" below.
14
+ - **URL** (starts with `http://` / `https://`) → `WebFetch` the URL → single bundle.
15
+ - **File path** (anything else) → `Read` it. If it's a directory, treat as repo (multi-bundle).
16
+
17
+ ## Multi-bundle flow (repo mode)
18
+
19
+ This is the Karpathy pattern: the wiki is the agent's persistent artifact, refreshed incrementally.
20
+
21
+ ### Step 1 — Stage bundles
22
+
23
+ ```bash
24
+ ricord init --emit-bundles
25
+ ```
26
+
27
+ This walks the repo with no LLM, no POST. It writes:
28
+ - `.ricord-cache/bundles/<slug>.md` — one structural summary per meaningful directory
29
+ - `.ricord-cache/manifest.json` — `{root, repo, api_base, bundles:[{rel, slug, bundlePath, chars, hash}]}`
30
+
31
+ If `.ricord-cache/manifest.json` already exists and the user hasn't said "re-walk", reuse it.
32
+
33
+ ### Step 2 — Fetch the extraction prompt (once)
34
+
35
+ ```bash
36
+ TOKEN=$(jq -r .api_key ~/.ricord/credentials.json)
37
+ API=$(jq -r .api_base ~/.ricord/credentials.json 2>/dev/null || echo https://api.ricord.ai)
38
+ curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/extraction/prompt" > .ricord-cache/extraction-prompt.json
39
+ SCHEMA_VERSION=$(jq -r .schema_version .ricord-cache/extraction-prompt.json)
40
+ ```
41
+
42
+ Read `.ricord-cache/extraction-prompt.json` once. Hold the `prompt`, `output_schema`, and `caps` in mind for every bundle.
43
+
44
+ ### Step 3 — Per bundle, extract + POST
45
+
46
+ For each bundle in the manifest (iterate, don't parallelize with the Task tool — sequential is fine and keeps the log linear):
47
+
48
+ 1. `Read` the bundle file at `bundlePath`.
49
+ 2. Mentally apply the extraction prompt to the bundle text. Substitute `{TEXT}` with the bundle. Emit JSON matching `output_schema`: `{ anchors: [...], connections: [...], tasks: [...] }`.
50
+ - **Quality bar**: every anchor must be a named concept a human would recognize as a wiki article title (Karpathy: "you are a writer, not a filing clerk").
51
+ - **Type discipline**: `type` must be exactly `"entity"`, `"topic"`, or `"epoch"`. Put finer-grained classification into `subtype` (`"person"`, `"company"`, `"library"`, `"decision"`, `"pattern"`, etc.).
52
+ - **Caps**: max 10 anchors, 30 connections, 5 tasks per bundle. The server rejects more.
53
+ - Skip bundles where there is genuinely nothing to extract (e.g. a 50-char README stub). Note the skip in `log.md` (step 4).
54
+ 3. POST via curl. Build the payload as a JSON file first so quoting in the bundle text doesn't corrupt the call:
55
+
56
+ ```bash
57
+ # Write payload to a temp file, then POST it
58
+ Write /tmp/ricord-payload.json with the JSON body
59
+ curl -sS -X POST "$API/v1/ingest/extracted" \
60
+ -H "Authorization: Bearer $TOKEN" \
61
+ -H "Content-Type: application/json" \
62
+ --data-binary @/tmp/ricord-payload.json
63
+ ```
64
+
65
+ The JSON body:
66
+ ```json
67
+ {
68
+ "messages": [{"role": "user", "content": "<full bundle text>"}],
69
+ "extracted": { "anchors": [...], "connections": [...], "tasks": [...] },
70
+ "extraction_meta": { "model": "<your model id, e.g. claude-opus-4-7>", "client": "claude-code", "schema_version": <from step 2> },
71
+ "tags": ["repo:<repo>", "dir:<rel>", "kind:dir-summary"]
72
+ }
73
+ ```
74
+
75
+ 4. Parse the response. Track `pages_written`, `links_written`, `tasks_written`, and `pages_needing_rollup: [<page_id>, …]`.
76
+
77
+ 5. For each `page_id` in `pages_needing_rollup`, do the rollup pair (Karpathy: "the LLM owns the wiki layer entirely"):
78
+
79
+ ```bash
80
+ curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/kb/pages/$PAGE_ID/rollup-prompt"
81
+ ```
82
+ Read the prompt, mentally produce the rollup body (markdown, citing source bundles), write the body JSON to a temp file, then:
83
+ ```bash
84
+ curl -sS -X POST "$API/v1/kb/pages/$PAGE_ID/body" \
85
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
86
+ --data-binary @/tmp/ricord-rollup.json
87
+ ```
88
+
89
+ ### Step 4 — Maintain `index.md` and `log.md`
90
+
91
+ Karpathy's two special files. They live at `.ricord-cache/wiki/`:
92
+
93
+ - **`index.md`** — catalog. After all bundles are POSTed, write a markdown index: heading per anchor type (Entities, Topics, Epochs), bullet list with a one-line description per anchor. If this is a re-run, *update* the existing file: merge new anchors in, drop ones the new ingest didn't see.
94
+ - **`log.md`** — chronological. Append a single block:
95
+ ```
96
+ ## [<YYYY-MM-DD HH:MM>] ingest | <mode> | <repo or url or file>
97
+ bundles: <N>, anchors: <A>, connections: <C>, tasks: <T>, rollups: <R>, skipped: <S>
98
+ ```
99
+ Karpathy's tip: the consistent `## [date]` prefix makes this greppable (`grep "^## \[" log.md | tail -5`).
100
+
101
+ ### Step 5 — Final summary
102
+
103
+ Emit one line. Nothing more, no intermediate narration:
104
+
105
+ ```
106
+ Init repo: <N> bundles → <A> anchors, <C> connections, <T> tasks, <R> rollups. (skipped: <S>)
107
+ ```
108
+
109
+ ## Single-bundle flow (URL / file / conversation)
110
+
111
+ Skip steps 1, 4-style index. Do:
112
+
113
+ 1. Build one bundle text. Cap at ~30 KB.
114
+ 2. Fetch the extraction prompt (step 2 above).
115
+ 3. Mentally apply prompt → emit JSON → POST `/v1/ingest/extracted` with appropriate tags (`source:url`, `source:file`, or `source:conversation`).
116
+ 4. Handle any `pages_needing_rollup`.
117
+ 5. Append to `log.md` (in `~/.ricord/log.md` for non-repo modes — there's no repo cwd to anchor to).
118
+ 6. Emit a one-line summary.
119
+
120
+ ## Hard rules
121
+
122
+ - Do not call any external LLM API. You are the LLM. The whole point is that the agent does the work and Ricord stores it.
123
+ - Do not invent facts the source doesn't state. Skip bundles where there's nothing real to extract; log the skip.
124
+ - Stay within caps (10 anchors / 30 connections / 5 tasks per call). The server enforces them.
125
+ - Do not narrate intermediate work. The user sees the final summary line only. Errors get reported; progress doesn't.
126
+
127
+ ## Why this design
128
+
129
+ This is Karpathy's "LLM Wiki" applied to Ricord. The repo is the **raw source** (immutable). Ricord stores the **wiki** (KG pages, edges, tasks). This file is the **schema** — the protocol that turns you from a generic chatbot into a disciplined wiki maintainer. The CLI (`ricord init --emit-bundles`) is just the walker; you do the thinking.
@@ -0,0 +1,64 @@
1
+ ---
2
+ description: Karpathy lint pass over the Ricord wiki — surface contradictions, orphans, stale claims, missing cross-refs.
3
+ argument-hint: [(optional) topic or anchor name to focus on]
4
+ ---
5
+
6
+ You are the worker. This is the **Lint** operation from Karpathy's "LLM Wiki": periodically health-check the wiki and surface what's wrong, so it stays useful as it grows.
7
+
8
+ Scope: `$ARGUMENTS` (optional — if given, focus the lint pass on pages mentioning this anchor / topic; if empty, do a full pass).
9
+
10
+ ## Sources to read
11
+
12
+ ```bash
13
+ TOKEN=$(jq -r .api_key ~/.ricord/credentials.json)
14
+ API=$(jq -r .api_base ~/.ricord/credentials.json 2>/dev/null || echo https://api.ricord.ai)
15
+
16
+ # Wiki shape
17
+ curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/kb/pages" > /tmp/ricord-pages.json
18
+ curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/kb/graph" > /tmp/ricord-graph.json
19
+
20
+ # Recent ingest log (last 10 entries if local file exists)
21
+ [ -f .ricord-cache/wiki/log.md ] && grep "^## \[" .ricord-cache/wiki/log.md | tail -10
22
+ ```
23
+
24
+ If a focus argument was given, additionally:
25
+ ```bash
26
+ curl -sS -H "Authorization: Bearer $TOKEN" \
27
+ "$API/v1/search?q=$(jq -nr --arg q "$ARGUMENTS" '$q|@uri')&k=20" > /tmp/ricord-focus.json
28
+ ```
29
+
30
+ ## What to look for
31
+
32
+ Walk the page list + graph and produce a report under five headings. Be specific — cite page titles and ids. Don't pad. If a category is empty, say "(none)" and move on.
33
+
34
+ ### 1. Contradictions
35
+ Pages that state mutually exclusive facts about the same anchor. E.g. one page says project X uses Postgres, another says SQLite. List the conflict + both citations.
36
+
37
+ ### 2. Stale claims
38
+ Pages whose claims are likely outdated based on more recent ingests. Look at `last_updated` and `mentioned_in_log_since` to spot pages that haven't been touched in a long time but cover anchors that have appeared in recent log entries.
39
+
40
+ ### 3. Orphans
41
+ Pages with zero inbound edges in the graph. Either they should be deleted, merged into a broader topic, or new cross-references should be added.
42
+
43
+ ### 4. Missing pages
44
+ Concepts mentioned >= 3 times across the wiki (look at anchor mentions in page bodies) that don't yet have their own page. Karpathy: "important concepts mentioned but lacking their own page."
45
+
46
+ ### 5. Suggested next steps
47
+ A short list of:
48
+ - Questions worth investigating (gaps in the wiki where the user would benefit from a new source)
49
+ - Sources worth ingesting (URLs / docs / files the wiki keeps pointing at but hasn't absorbed)
50
+ - Cross-references worth adding (page pairs that should link but don't)
51
+
52
+ ## Output format
53
+
54
+ A single markdown report. Save it to `.ricord-cache/wiki/lint-<YYYY-MM-DD>.md` and print it. Append a one-liner to `log.md`:
55
+ ```
56
+ ## [<YYYY-MM-DD HH:MM>] lint | scope: <focus or "full">
57
+ contradictions: <X>, stale: <Y>, orphans: <Z>, missing: <W>, suggestions: <N>
58
+ ```
59
+
60
+ ## Hard rules
61
+
62
+ - Do not modify wiki pages in a lint pass. Lint surfaces problems; the user decides which to fix.
63
+ - Do not invent contradictions. Two pages saying different things about *different* aspects of an anchor is not a contradiction.
64
+ - Do not narrate. Output the report.
@@ -0,0 +1,71 @@
1
+ ---
2
+ description: Ask the Ricord wiki a question — search, synthesize an answer with citations, optionally file it back as a new page.
3
+ argument-hint: <your question>
4
+ ---
5
+
6
+ You are the worker. This is the **Query** operation from Karpathy's "LLM Wiki": ask the wiki a question, get an answer synthesized from the relevant pages, and (optionally) file the answer back into the wiki so explorations compound.
7
+
8
+ Question: `$ARGUMENTS`
9
+
10
+ If `$ARGUMENTS` is empty, ask the user what they want to know.
11
+
12
+ ## Search → Read → Synthesize
13
+
14
+ ```bash
15
+ TOKEN=$(jq -r .api_key ~/.ricord/credentials.json)
16
+ API=$(jq -r .api_base ~/.ricord/credentials.json 2>/dev/null || echo https://api.ricord.ai)
17
+
18
+ # Hybrid search: semantic + graph + memory
19
+ Q=$(jq -nr --arg q "$ARGUMENTS" '$q|@uri')
20
+ curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/search?q=$Q&k=12" > /tmp/ricord-search.json
21
+
22
+ # For each top hit, fetch the full page body if it's a KB page
23
+ # (search results return excerpts; full body lives at /v1/kb/pages/<id>)
24
+ ```
25
+
26
+ For each top-ranked result:
27
+ 1. If it's a memory excerpt, use it directly.
28
+ 2. If it's a KB page id, fetch the body: `curl -sS -H "Authorization: Bearer $TOKEN" "$API/v1/kb/pages/<id>"`.
29
+
30
+ Synthesize an answer. **Cite every claim** with the source memory id or KB page title in brackets, e.g. `[mem:abc123]` or `[kb: Vertex migration]`. If the wiki doesn't have enough to answer well, say so — don't fabricate.
31
+
32
+ ## Output
33
+
34
+ Print the answer as markdown:
35
+
36
+ ```markdown
37
+ ## <one-line restatement of the question>
38
+
39
+ <2-5 paragraphs of synthesis with inline citations>
40
+
41
+ ### Sources
42
+ - [page or memory title 1] (<id>)
43
+ - [page or memory title 2] (<id>)
44
+ - …
45
+
46
+ ### Gaps
47
+ - <what the wiki doesn't yet know that would improve this answer, if anything>
48
+ ```
49
+
50
+ ## File the answer back? (optional)
51
+
52
+ Karpathy: "good answers can be filed back into the wiki as new pages."
53
+
54
+ After printing, ask the user one short question: **"File this answer back into the wiki as a page? (y/n)"**
55
+
56
+ If yes:
57
+ 1. POST to `/v1/ingest/extracted` with the answer text as a single message, treating it as a synthesis source. Tag it `kind:synthesis`, `source:query`, and include the original question in metadata.
58
+ 2. Append to `log.md`:
59
+ ```
60
+ ## [<YYYY-MM-DD HH:MM>] query | filed-back
61
+ question: <the question>
62
+ sources_used: <N>, gaps_noted: <Y/N>
63
+ ```
64
+
65
+ If no, just append the query log entry without the filed-back bit.
66
+
67
+ ## Hard rules
68
+
69
+ - Cite everything. Uncited claims are worse than no answer.
70
+ - If the wiki has nothing relevant, say "The wiki doesn't have material on this yet." Suggest sources to ingest.
71
+ - Do not call external search engines or LLM APIs. The only ground truth is the Ricord wiki.
@@ -0,0 +1,16 @@
1
+ export interface Credentials {
2
+ api_key: string;
3
+ api_base?: string;
4
+ created_at: string;
5
+ }
6
+ /**
7
+ * Resolve the Ricord token in this order:
8
+ * 1. explicit `--token` flag
9
+ * 2. RICORD_TOKEN env var
10
+ * 3. RICORD_API_KEY env var (matches what `ricord-mcp` reads)
11
+ * 4. ~/.ricord/credentials.json (written by `ricord login`)
12
+ */
13
+ export declare function resolveToken(explicit?: string): string | undefined;
14
+ export declare function resolveApiBase(explicit?: string): string;
15
+ export declare function loadCredentials(): Credentials | null;
16
+ export declare function maskKey(key: string): string;
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const CREDENTIALS_FILE = join(homedir(), ".ricord", "credentials.json");
5
+ /**
6
+ * Resolve the Ricord token in this order:
7
+ * 1. explicit `--token` flag
8
+ * 2. RICORD_TOKEN env var
9
+ * 3. RICORD_API_KEY env var (matches what `ricord-mcp` reads)
10
+ * 4. ~/.ricord/credentials.json (written by `ricord login`)
11
+ */
12
+ export function resolveToken(explicit) {
13
+ if (explicit && explicit.length > 0)
14
+ return explicit;
15
+ if (process.env.RICORD_TOKEN)
16
+ return process.env.RICORD_TOKEN;
17
+ if (process.env.RICORD_API_KEY)
18
+ return process.env.RICORD_API_KEY;
19
+ return loadCredentials()?.api_key;
20
+ }
21
+ export function resolveApiBase(explicit) {
22
+ if (explicit)
23
+ return explicit;
24
+ if (process.env.RICORD_API_BASE)
25
+ return process.env.RICORD_API_BASE;
26
+ return loadCredentials()?.api_base ?? "https://api.ricord.ai";
27
+ }
28
+ export function loadCredentials() {
29
+ try {
30
+ if (existsSync(CREDENTIALS_FILE)) {
31
+ return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf8"));
32
+ }
33
+ }
34
+ catch { }
35
+ return null;
36
+ }
37
+ export function maskKey(key) {
38
+ if (key.length <= 16)
39
+ return "***";
40
+ return `${key.slice(0, 12)}...${key.slice(-4)}`;
41
+ }
42
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/cli/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAiB;IAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC9D,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAClE,OAAO,eAAe,EAAE,EAAE,OAAO,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC9C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,OAAO,eAAe,EAAE,EAAE,QAAQ,IAAI,uBAAuB,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { DirGroup } from "./walker.js";
2
+ import { ParsedFile } from "./parse.js";
3
+ export interface DirBundle {
4
+ rel: string;
5
+ fileTree: string;
6
+ readme?: string;
7
+ parsedFiles: ParsedFile[];
8
+ externalDeps: string[];
9
+ internalImports: string[];
10
+ usedBy: string[];
11
+ }
12
+ export declare function buildBundle(group: DirGroup, root: string): Promise<DirBundle>;
13
+ /**
14
+ * After all bundles are built, populate `usedBy` on each by inverting the
15
+ * `internalImports` maps. This is the magic that gives the LLM ground-truth
16
+ * "Used by" instead of forcing it to guess.
17
+ */
18
+ export declare function invertUsedBy(bundles: DirBundle[]): void;
19
+ /**
20
+ * Render a bundle to the prompt-ready text the summarizer LLM will see.
21
+ * This is the contract that defines summary quality — keep it tight.
22
+ */
23
+ export declare function renderBundleForPrompt(b: DirBundle, opts?: {
24
+ maxChars?: number;
25
+ }): string;