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
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Repo walker — finds source files for `ricord ingest`. Honors a hard-coded
3
+ * skip list (node_modules, .git, dist, build, .next, .cache, .ricord) and
4
+ * an extension allow-list. Optional `paths` restricts to those subtrees.
5
+ *
6
+ * Designed to be deterministic so the same repo produces the same file
7
+ * order in every run — the ingest batch is order-stable for sha checks.
8
+ */
9
+ import { readdir } from "node:fs/promises";
10
+ import { join, resolve } from "node:path";
11
+ const SKIP_DIRS = new Set([
12
+ "node_modules", ".git", ".ricord", ".almanac",
13
+ "dist", "build", ".next", ".cache", "coverage",
14
+ ".turbo", ".vercel", "out", "tmp", ".pnp", ".yarn",
15
+ // Build / generated artifact dirs we've seen in real repos
16
+ "graphify-out", ".ricord-cache",
17
+ ]);
18
+ const ALLOWED_EXT = new Set([
19
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs",
20
+ ".rb", ".java", ".kt", ".swift", ".cpp", ".c", ".h", ".hpp", ".cs",
21
+ ".sql", ".graphql", ".proto", ".md", ".mdx", ".toml", ".yaml", ".yml",
22
+ ".json", ".sh", ".bash",
23
+ ]);
24
+ export async function walkRepo(repoRoot, paths) {
25
+ const root = resolve(repoRoot);
26
+ const starts = paths && paths.length > 0 ? paths.map((p) => resolve(root, p)) : [root];
27
+ const out = [];
28
+ const queue = [...starts];
29
+ while (queue.length > 0) {
30
+ const dir = queue.shift();
31
+ let entries;
32
+ try {
33
+ entries = await readdir(dir, { withFileTypes: true });
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ entries.sort((a, b) => a.name.localeCompare(b.name));
39
+ for (const e of entries) {
40
+ if (e.name.startsWith(".") && e.name !== ".env.example") {
41
+ if (e.isDirectory() && !SKIP_DIRS.has(e.name)) {
42
+ queue.push(join(dir, e.name));
43
+ continue;
44
+ }
45
+ continue;
46
+ }
47
+ if (e.isDirectory()) {
48
+ if (SKIP_DIRS.has(e.name))
49
+ continue;
50
+ queue.push(join(dir, e.name));
51
+ continue;
52
+ }
53
+ const dot = e.name.lastIndexOf(".");
54
+ const ext = dot >= 0 ? e.name.slice(dot) : "";
55
+ if (!ALLOWED_EXT.has(ext))
56
+ continue;
57
+ out.push({ absPath: join(dir, e.name), size: 0 });
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ //# sourceMappingURL=walk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../../src/cli/walk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface RepoFile {
2
+ abs: string;
3
+ rel: string;
4
+ ext: string;
5
+ size: number;
6
+ }
7
+ export interface DirGroup {
8
+ rel: string;
9
+ files: RepoFile[];
10
+ }
11
+ export declare function walkRepo(root: string, opts?: {
12
+ respectConfidential?: boolean;
13
+ }): Promise<DirGroup[]>;
14
+ export declare function isCode(file: RepoFile): boolean;
@@ -0,0 +1,120 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import ignoreModule from "ignore";
4
+ // `ignore` ships as `module.exports = factory; factory.default = factory`.
5
+ // Under NodeNext + esModuleInterop the default import resolves to the
6
+ // namespace object on some Node versions, so normalize it to the callable.
7
+ const ignore = typeof ignoreModule === "function"
8
+ ? ignoreModule
9
+ : ignoreModule.default;
10
+ const CODE_EXT = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".sql"]);
11
+ const DOC_EXT = new Set([".md", ".yaml", ".yml", ".toml"]);
12
+ const ALL_EXT = new Set([...CODE_EXT, ...DOC_EXT]);
13
+ const HARD_EXCLUDE_DIRS = new Set([
14
+ "node_modules", ".venv", ".next", "dist", "build", ".git",
15
+ "__pycache__", ".turbo", ".cache", "coverage", ".pytest_cache",
16
+ ]);
17
+ const CONFIDENTIAL_DIRS = new Set(["research", "benchmarks", "marketing"]);
18
+ const CONFIDENTIAL_FILES = (name) => /^(CPO_AUDIT_LOG|MEMORY)\.md$/i.test(name) ||
19
+ /AUDIT.*\.md$/i.test(name);
20
+ const HARD_EXCLUDE_FILES = new Set([
21
+ "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "uv.lock", "poetry.lock",
22
+ ]);
23
+ async function isBinary(absPath) {
24
+ try {
25
+ const fd = await fs.open(absPath, "r");
26
+ try {
27
+ const buf = Buffer.alloc(8192);
28
+ const { bytesRead } = await fd.read(buf, 0, 8192, 0);
29
+ for (let i = 0; i < bytesRead; i++)
30
+ if (buf[i] === 0)
31
+ return true;
32
+ return false;
33
+ }
34
+ finally {
35
+ await fd.close();
36
+ }
37
+ }
38
+ catch {
39
+ return true;
40
+ }
41
+ }
42
+ async function loadGitignore(root) {
43
+ const ig = ignore();
44
+ try {
45
+ const txt = await fs.readFile(path.join(root, ".gitignore"), "utf8");
46
+ ig.add(txt);
47
+ }
48
+ catch { /* no gitignore — fine */ }
49
+ // Always add our hard-exclude patterns as belt-and-suspenders.
50
+ ig.add([...HARD_EXCLUDE_DIRS].map(d => `${d}/`).join("\n"));
51
+ return ig;
52
+ }
53
+ export async function walkRepo(root, opts = {}) {
54
+ const respectConfidential = opts.respectConfidential ?? true;
55
+ const ig = await loadGitignore(root);
56
+ const groups = new Map();
57
+ async function visit(dir) {
58
+ const rel = path.relative(root, dir) || ".";
59
+ if (rel !== ".") {
60
+ const segs = rel.split(path.sep);
61
+ if (segs.some(s => HARD_EXCLUDE_DIRS.has(s)))
62
+ return;
63
+ if (respectConfidential && segs.some(s => CONFIDENTIAL_DIRS.has(s)))
64
+ return;
65
+ // gitignore expects forward slashes + trailing slash for dirs
66
+ if (ig.ignores(rel.split(path.sep).join("/") + "/"))
67
+ return;
68
+ }
69
+ let entries;
70
+ try {
71
+ entries = await fs.readdir(dir, { withFileTypes: true });
72
+ }
73
+ catch {
74
+ return;
75
+ }
76
+ for (const e of entries) {
77
+ const abs = path.join(dir, e.name);
78
+ const childRel = path.relative(root, abs).split(path.sep).join("/");
79
+ if (e.isDirectory()) {
80
+ await visit(abs);
81
+ continue;
82
+ }
83
+ if (!e.isFile())
84
+ continue;
85
+ if (HARD_EXCLUDE_FILES.has(e.name))
86
+ continue;
87
+ if (respectConfidential && CONFIDENTIAL_FILES(e.name))
88
+ continue;
89
+ if (ig.ignores(childRel))
90
+ continue;
91
+ const ext = path.extname(e.name).toLowerCase();
92
+ if (!ALL_EXT.has(ext))
93
+ continue;
94
+ const stat = await fs.stat(abs);
95
+ // Skip absurdly large files — likely generated / data.
96
+ if (stat.size > 512 * 1024)
97
+ continue;
98
+ if (await isBinary(abs))
99
+ continue;
100
+ const dirRel = path.relative(root, dir).split(path.sep).join("/") || ".";
101
+ const file = {
102
+ abs,
103
+ rel: childRel,
104
+ ext,
105
+ size: stat.size,
106
+ };
107
+ if (!groups.has(dirRel))
108
+ groups.set(dirRel, []);
109
+ groups.get(dirRel).push(file);
110
+ }
111
+ }
112
+ await visit(root);
113
+ return [...groups.entries()]
114
+ .map(([rel, files]) => ({ rel, files }))
115
+ .sort((a, b) => a.rel.localeCompare(b.rel));
116
+ }
117
+ export function isCode(file) {
118
+ return CODE_EXT.has(file.ext);
119
+ }
120
+ //# sourceMappingURL=walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walker.js","sourceRoot":"","sources":["../../src/cli/walker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreCompact hook — captures context before Claude Code auto-compacts.
4
+ *
5
+ * When Claude Code's context window fills up, it auto-compacts (summarizes
6
+ * and discards detail). This hook fires BEFORE that, spawning a background
7
+ * flush process to capture the current conversation context.
8
+ *
9
+ * Without this hook, long sessions lose intermediate context to summarization
10
+ * before SessionEnd ever fires.
11
+ *
12
+ * Same architecture as session-end.ts but with a higher MIN_TURNS threshold
13
+ * to avoid saving trivial compactions.
14
+ */
15
+ export {};
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreCompact hook — captures context before Claude Code auto-compacts.
4
+ *
5
+ * When Claude Code's context window fills up, it auto-compacts (summarizes
6
+ * and discards detail). This hook fires BEFORE that, spawning a background
7
+ * flush process to capture the current conversation context.
8
+ *
9
+ * Without this hook, long sessions lose intermediate context to summarization
10
+ * before SessionEnd ever fires.
11
+ *
12
+ * Same architecture as session-end.ts but with a higher MIN_TURNS threshold
13
+ * to avoid saving trivial compactions.
14
+ */
15
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { tmpdir } from "node:os";
18
+ import { spawn } from "node:child_process";
19
+ // Recursion guard
20
+ if (process.env.CLAUDE_INVOKED_BY) {
21
+ process.exit(0);
22
+ }
23
+ const ROOT_DIR = join(import.meta.dirname, "..", "..");
24
+ const DIST_SCRIPTS = join(ROOT_DIR, "dist", "scripts");
25
+ const FLUSH_SCRIPT = join(DIST_SCRIPTS, "flush.js");
26
+ const CONTEXT_DIR = join(tmpdir(), "ricord-contexts");
27
+ const MAX_TURNS = 30;
28
+ const MAX_CONTEXT_CHARS = 15_000;
29
+ const MIN_TURNS = 5; // Higher than session-end (3)
30
+ // ── Credential scrubbing ─────────────────────────────────────────────
31
+ const SCRUB_PATTERNS = [
32
+ /sk-[a-zA-Z0-9]{20,}/g,
33
+ /AKIA[A-Z0-9]{16}/g,
34
+ /ghp_[a-zA-Z0-9]{36,}/g,
35
+ /rc_(?:live|test)_[a-zA-Z0-9_]{10,}/g,
36
+ /Bearer\s+[a-zA-Z0-9._\-]{20,}/gi,
37
+ /(?:api[_-]?key|password|secret|token)\s*[:=]\s*["']?[^\s"']{8,}/gi,
38
+ ];
39
+ function scrub(text) {
40
+ let result = text;
41
+ for (const p of SCRUB_PATTERNS) {
42
+ result = result.replace(p, "[REDACTED]");
43
+ }
44
+ return result;
45
+ }
46
+ // ── Transcript extraction ────────────────────────────────────────────
47
+ function extractConversation(transcriptPath) {
48
+ const turns = [];
49
+ const content = readFileSync(transcriptPath, "utf8");
50
+ for (const line of content.split("\n")) {
51
+ if (!line.trim())
52
+ continue;
53
+ try {
54
+ const entry = JSON.parse(line);
55
+ const msg = entry.message || entry;
56
+ const role = msg.role || "";
57
+ let text = msg.content || "";
58
+ if (!["user", "assistant"].includes(role))
59
+ continue;
60
+ if (Array.isArray(text)) {
61
+ text = text
62
+ .filter((b) => typeof b === "string" || (b.type === "text" && b.text))
63
+ .map((b) => (typeof b === "string" ? b : b.text))
64
+ .join("\n");
65
+ }
66
+ if (typeof text === "string" && text.trim()) {
67
+ const label = role === "user" ? "User" : "Assistant";
68
+ turns.push(`**${label}:** ${text.trim()}`);
69
+ }
70
+ }
71
+ catch {
72
+ continue;
73
+ }
74
+ }
75
+ let recent = turns.slice(-MAX_TURNS);
76
+ let context = recent.join("\n\n");
77
+ if (context.length > MAX_CONTEXT_CHARS) {
78
+ context = context.slice(-MAX_CONTEXT_CHARS);
79
+ const boundary = context.indexOf("\n**");
80
+ if (boundary > 0)
81
+ context = context.slice(boundary + 1);
82
+ }
83
+ return { context: scrub(context), turnCount: recent.length };
84
+ }
85
+ // ── Main ─────────────────────────────────────────────────────────────
86
+ async function main() {
87
+ let hookInput = {};
88
+ try {
89
+ const raw = readFileSync(0, "utf8");
90
+ hookInput = JSON.parse(raw);
91
+ }
92
+ catch {
93
+ return;
94
+ }
95
+ const sessionId = hookInput.session_id || "unknown";
96
+ const transcriptPath = hookInput.transcript_path;
97
+ // Guard against empty transcript_path (known Claude Code bug #13668)
98
+ if (!transcriptPath || !existsSync(transcriptPath))
99
+ return;
100
+ const { context, turnCount } = extractConversation(transcriptPath);
101
+ if (!context.trim() || turnCount < MIN_TURNS)
102
+ return;
103
+ // Write context to temp file
104
+ if (!existsSync(CONTEXT_DIR))
105
+ mkdirSync(CONTEXT_DIR, { recursive: true });
106
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
107
+ const contextFile = join(CONTEXT_DIR, `context-compact-${sessionId}-${timestamp}.md`);
108
+ // SEC-07: Use mode 0o600 so only the owning user can read the context file.
109
+ writeFileSync(contextFile, context, { encoding: "utf8", mode: 0o600 });
110
+ // Spawn flush.js as background process
111
+ if (!existsSync(FLUSH_SCRIPT))
112
+ return;
113
+ try {
114
+ const child = spawn("node", [FLUSH_SCRIPT, contextFile, `${sessionId}-compact`], {
115
+ cwd: ROOT_DIR,
116
+ detached: true,
117
+ stdio: "ignore",
118
+ env: { ...process.env, CLAUDE_INVOKED_BY: "ricord_flush" },
119
+ });
120
+ child.unref();
121
+ }
122
+ catch {
123
+ // Silent failure
124
+ }
125
+ }
126
+ main().catch(() => { });
127
+ //# sourceMappingURL=pre-compact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-compact.js","sourceRoot":"","sources":["../../src/hooks/pre-compact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreToolUse hook — observes Write/Edit/MultiEdit calls to memory paths.
4
+ *
5
+ * Claude Code fires this hook BEFORE executing any tool call. The hook reads
6
+ * the tool name and input from stdin (JSON).
7
+ *
8
+ * Design principle: Ricord is a LAYER ON TOP of Claude Code, not a replacement.
9
+ * We do NOT block Claude's native memory writes. Claude writes to local .md
10
+ * files normally; the turn-end hook syncs changed files to Ricord in background.
11
+ *
12
+ * This hook currently passes all calls through. It remains in place as an
13
+ * observation point — future use: logging, metrics, or tagging writes for sync.
14
+ */
15
+ export {};
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreToolUse hook — observes Write/Edit/MultiEdit calls to memory paths.
4
+ *
5
+ * Claude Code fires this hook BEFORE executing any tool call. The hook reads
6
+ * the tool name and input from stdin (JSON).
7
+ *
8
+ * Design principle: Ricord is a LAYER ON TOP of Claude Code, not a replacement.
9
+ * We do NOT block Claude's native memory writes. Claude writes to local .md
10
+ * files normally; the turn-end hook syncs changed files to Ricord in background.
11
+ *
12
+ * This hook currently passes all calls through. It remains in place as an
13
+ * observation point — future use: logging, metrics, or tagging writes for sync.
14
+ */
15
+ import { readFileSync } from "node:fs";
16
+ function main() {
17
+ try {
18
+ readFileSync(0, "utf8");
19
+ }
20
+ catch { /* ignore */ }
21
+ // Allow everything — do not block Claude's native tooling.
22
+ process.exit(0);
23
+ }
24
+ main();
25
+ //# sourceMappingURL=pre-tool-use.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-tool-use.js","sourceRoot":"","sources":["../../src/hooks/pre-tool-use.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,SAAS,IAAI;IACX,IAAI,CAAC;QAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACvD,2DAA2D;IAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionEnd hook — captures conversation and spawns background flush.
4
+ *
5
+ * When a Claude Code session ends, this hook:
6
+ * 1. Reads the JSONL transcript path from stdin
7
+ * 2. Extracts the last N conversation turns
8
+ * 3. Writes context to a temp file
9
+ * 4. Spawns flush.js as a detached background process
10
+ * 5. flush.js extracts knowledge → saves to Ricord + daily log
11
+ *
12
+ * The hook itself does NO API calls and NO LLM calls — just local file I/O
13
+ * + spawning a background process. Runs in <2 seconds.
14
+ *
15
+ * Architecture borrowed from claude-memory-compiler:
16
+ * - Fast hook (<10s) → spawns slow background process
17
+ * - Recursion guard via CLAUDE_INVOKED_BY env var
18
+ * - Deduplication via last-flush.json
19
+ * - Dual hooks (SessionEnd + PreCompact) for full coverage
20
+ */
21
+ export {};
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionEnd hook — captures conversation and spawns background flush.
4
+ *
5
+ * When a Claude Code session ends, this hook:
6
+ * 1. Reads the JSONL transcript path from stdin
7
+ * 2. Extracts the last N conversation turns
8
+ * 3. Writes context to a temp file
9
+ * 4. Spawns flush.js as a detached background process
10
+ * 5. flush.js extracts knowledge → saves to Ricord + daily log
11
+ *
12
+ * The hook itself does NO API calls and NO LLM calls — just local file I/O
13
+ * + spawning a background process. Runs in <2 seconds.
14
+ *
15
+ * Architecture borrowed from claude-memory-compiler:
16
+ * - Fast hook (<10s) → spawns slow background process
17
+ * - Recursion guard via CLAUDE_INVOKED_BY env var
18
+ * - Deduplication via last-flush.json
19
+ * - Dual hooks (SessionEnd + PreCompact) for full coverage
20
+ */
21
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
22
+ import { join, basename } from "node:path";
23
+ import { tmpdir } from "node:os";
24
+ import { spawn, execSync } from "node:child_process";
25
+ import { createHash } from "node:crypto";
26
+ import { readBufferedTurns, clearBuffer, turnsToMessages } from "../lib/buffer.js";
27
+ import { getActiveProject, clearActiveProject } from "../lib/active-project.js";
28
+ function detectProject(cwd) {
29
+ try {
30
+ const remote = execSync("git remote get-url origin 2>/dev/null", { cwd, encoding: "utf8", timeout: 2000 }).trim();
31
+ const match = remote.match(/\/([^/]+?)(?:\.git)?$/);
32
+ if (match?.[1])
33
+ return match[1];
34
+ }
35
+ catch { }
36
+ return process.env.RICORD_PROJECT || basename(cwd) || "";
37
+ }
38
+ // Recursion guard: if spawned by flush/compile, exit immediately
39
+ if (process.env.CLAUDE_INVOKED_BY) {
40
+ process.exit(0);
41
+ }
42
+ const ROOT_DIR = join(import.meta.dirname, "..", "..");
43
+ const DIST_SCRIPTS = join(ROOT_DIR, "dist", "scripts");
44
+ const FLUSH_SCRIPT = join(DIST_SCRIPTS, "flush.js");
45
+ const CONTEXT_DIR = join(tmpdir(), "ricord-contexts");
46
+ const MAX_TURNS = 30;
47
+ const MAX_CONTEXT_CHARS = 15_000;
48
+ const MIN_TURNS = 3;
49
+ // ── Credential scrubbing (inline to avoid import overhead) ───────────
50
+ const SCRUB_PATTERNS = [
51
+ /sk-[a-zA-Z0-9]{20,}/g,
52
+ /AKIA[A-Z0-9]{16}/g,
53
+ /ghp_[a-zA-Z0-9]{36,}/g,
54
+ /rc_(?:live|test)_[a-zA-Z0-9_]{10,}/g,
55
+ /Bearer\s+[a-zA-Z0-9._\-]{20,}/gi,
56
+ /(?:api[_-]?key|password|secret|token)\s*[:=]\s*["']?[^\s"']{8,}/gi,
57
+ ];
58
+ function scrub(text) {
59
+ let result = text;
60
+ for (const p of SCRUB_PATTERNS) {
61
+ result = result.replace(p, "[REDACTED]");
62
+ }
63
+ return result;
64
+ }
65
+ // ── Transcript extraction ────────────────────────────────────────────
66
+ function extractConversation(transcriptPath) {
67
+ const turns = [];
68
+ const content = readFileSync(transcriptPath, "utf8");
69
+ for (const line of content.split("\n")) {
70
+ if (!line.trim())
71
+ continue;
72
+ try {
73
+ const entry = JSON.parse(line);
74
+ const msg = entry.message || entry;
75
+ const role = msg.role || "";
76
+ let text = msg.content || "";
77
+ if (!["user", "assistant"].includes(role))
78
+ continue;
79
+ if (Array.isArray(text)) {
80
+ text = text
81
+ .filter((b) => typeof b === "string" || (b.type === "text" && b.text))
82
+ .map((b) => (typeof b === "string" ? b : b.text))
83
+ .join("\n");
84
+ }
85
+ if (typeof text === "string" && text.trim()) {
86
+ const label = role === "user" ? "User" : "Assistant";
87
+ turns.push(`**${label}:** ${text.trim()}`);
88
+ }
89
+ }
90
+ catch {
91
+ continue;
92
+ }
93
+ }
94
+ let recent = turns.slice(-MAX_TURNS);
95
+ let context = recent.join("\n\n");
96
+ if (context.length > MAX_CONTEXT_CHARS) {
97
+ context = context.slice(-MAX_CONTEXT_CHARS);
98
+ const boundary = context.indexOf("\n**");
99
+ if (boundary > 0)
100
+ context = context.slice(boundary + 1);
101
+ }
102
+ return { context: scrub(context), turnCount: recent.length };
103
+ }
104
+ // ── Main ─────────────────────────────────────────────────────────────
105
+ async function main() {
106
+ // Read hook input from stdin
107
+ let hookInput = {};
108
+ try {
109
+ const raw = readFileSync(0, "utf8");
110
+ hookInput = JSON.parse(raw);
111
+ }
112
+ catch {
113
+ return; // Can't parse stdin
114
+ }
115
+ const sessionId = hookInput.session_id || "unknown";
116
+ const transcriptPath = hookInput.transcript_path;
117
+ if (!transcriptPath || !existsSync(transcriptPath))
118
+ return;
119
+ // Flush any remaining buffered turns (< BATCH_SIZE) that haven't been sent yet.
120
+ const POSTER = join(ROOT_DIR, "dist", "hooks", "turn-end-post.js");
121
+ if (existsSync(POSTER)) {
122
+ const pending = readBufferedTurns(sessionId);
123
+ if (pending.length > 0) {
124
+ clearBuffer(sessionId);
125
+ const child = spawn("node", [POSTER], {
126
+ cwd: ROOT_DIR,
127
+ detached: true,
128
+ stdio: ["pipe", "ignore", "ignore"],
129
+ env: { ...process.env, CLAUDE_INVOKED_BY: "ricord_turn_end" },
130
+ });
131
+ const pinned = getActiveProject(sessionId);
132
+ const project = pinned !== null ? pinned : detectProject(process.env.CLAUDE_PROJECT_DIR || process.cwd());
133
+ child.stdin?.end(JSON.stringify({
134
+ messages: turnsToMessages(pending),
135
+ session_id: sessionId,
136
+ customId: createHash("sha256").update(`session-end:${sessionId}:${pending.length}`).digest("hex").slice(0, 32),
137
+ async_mode: true,
138
+ queueing: true,
139
+ priority: "background",
140
+ extracted: { anchors: [] },
141
+ extraction_meta: {
142
+ model: "embed-only",
143
+ client: "ricord-mcp-hook/session-end",
144
+ schema_version: 1,
145
+ },
146
+ tags: ["source:claude-code", "session-end-hook", `batch_size:${pending.length}`],
147
+ ...(project ? { project_id: project } : {}),
148
+ metadata: { source: "claude-code", batch: true, session_end: true, turn_count: pending.length, ...(project ? { project_id: project } : {}) },
149
+ }));
150
+ child.unref();
151
+ }
152
+ }
153
+ // Extract conversation
154
+ const { context, turnCount } = extractConversation(transcriptPath);
155
+ if (!context.trim() || turnCount < MIN_TURNS)
156
+ return;
157
+ // Write context to temp file for the background flush process
158
+ if (!existsSync(CONTEXT_DIR))
159
+ mkdirSync(CONTEXT_DIR, { recursive: true });
160
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
161
+ const contextFile = join(CONTEXT_DIR, `context-${sessionId}-${timestamp}.md`);
162
+ // SEC-07: Use mode 0o600 so only the owning user can read the context file.
163
+ writeFileSync(contextFile, context, { encoding: "utf8", mode: 0o600 });
164
+ // Spawn flush.js as a detached background process
165
+ if (!existsSync(FLUSH_SCRIPT)) {
166
+ // Fallback: flush not built yet, skip silently
167
+ return;
168
+ }
169
+ try {
170
+ const child = spawn("node", [FLUSH_SCRIPT, contextFile, sessionId], {
171
+ cwd: ROOT_DIR,
172
+ detached: true,
173
+ stdio: "ignore",
174
+ env: { ...process.env, CLAUDE_INVOKED_BY: "ricord_flush" },
175
+ });
176
+ child.unref();
177
+ }
178
+ catch {
179
+ // Silent failure — don't break Claude Code session teardown
180
+ }
181
+ clearActiveProject(sessionId);
182
+ }
183
+ main().catch(() => {
184
+ // Silent failure
185
+ });
186
+ //# sourceMappingURL=session-end.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-end.js","sourceRoot":"","sources":["../../src/hooks/session-end.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionStart hook — injects Ricord knowledge context into every Claude Code session.
4
+ *
5
+ * Single API call: GET /v1/user/boot-context returns the full session-start
6
+ * digest (instructions, preferences, top procedures, open tasks, active
7
+ * projects, counts). Plus the most recent daily log from disk if any.
8
+ *
9
+ * 2026-05-01 simplification: dropped the previous 3-call sequence (search +
10
+ * boot-context + pages). boot-context already covers what the agent needs;
11
+ * the agent can call ricord_search / ricord_kb on demand.
12
+ *
13
+ * No LLM calls — just one Ricord API read + local file I/O. Runs in <1s.
14
+ */
15
+ export {};