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.
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/commands/ricord-flush.md +29 -0
- package/commands/ricord-init.md +129 -0
- package/commands/ricord-lint.md +64 -0
- package/commands/ricord-query.md +71 -0
- package/dist/cli/auth.d.ts +16 -0
- package/dist/cli/auth.js +42 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/bundle.d.ts +25 -0
- package/dist/cli/bundle.js +179 -0
- package/dist/cli/bundle.js.map +1 -0
- package/dist/cli/cache.d.ts +18 -0
- package/dist/cli/cache.js +39 -0
- package/dist/cli/cache.js.map +1 -0
- package/dist/cli/cli.d.ts +21 -0
- package/dist/cli/cli.js +355 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/client.d.ts +12 -0
- package/dist/cli/client.js +35 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/commands/build.d.ts +44 -0
- package/dist/cli/commands/build.js +437 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/curate.d.ts +32 -0
- package/dist/cli/commands/curate.js +154 -0
- package/dist/cli/commands/curate.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +16 -0
- package/dist/cli/commands/doctor.js +92 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/ingest.d.ts +25 -0
- package/dist/cli/commands/ingest.js +121 -0
- package/dist/cli/commands/ingest.js.map +1 -0
- package/dist/cli/commands/install.d.ts +16 -0
- package/dist/cli/commands/install.js +82 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +24 -0
- package/dist/cli/commands/pull.js +104 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/push.d.ts +28 -0
- package/dist/cli/commands/push.js +164 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/rollup.d.ts +21 -0
- package/dist/cli/commands/rollup.js +118 -0
- package/dist/cli/commands/rollup.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +7 -0
- package/dist/cli/commands/setup.js +43 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +15 -0
- package/dist/cli/commands/sync.js +63 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +17 -0
- package/dist/cli/commands/watch.js +87 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/config.d.ts +29 -0
- package/dist/cli/config.js +52 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/extract.d.ts +101 -0
- package/dist/cli/extract.js +216 -0
- package/dist/cli/extract.js.map +1 -0
- package/dist/cli/ingest.d.ts +48 -0
- package/dist/cli/ingest.js +74 -0
- package/dist/cli/ingest.js.map +1 -0
- package/dist/cli/ledger.d.ts +44 -0
- package/dist/cli/ledger.js +67 -0
- package/dist/cli/ledger.js.map +1 -0
- package/dist/cli/llm.d.ts +21 -0
- package/dist/cli/llm.js +138 -0
- package/dist/cli/llm.js.map +1 -0
- package/dist/cli/parse.d.ts +13 -0
- package/dist/cli/parse.js +188 -0
- package/dist/cli/parse.js.map +1 -0
- package/dist/cli/run-explore.d.ts +56 -0
- package/dist/cli/run-explore.js +229 -0
- package/dist/cli/run-explore.js.map +1 -0
- package/dist/cli/summarize.d.ts +15 -0
- package/dist/cli/summarize.js +49 -0
- package/dist/cli/summarize.js.map +1 -0
- package/dist/cli/uninstall.d.ts +6 -0
- package/dist/cli/uninstall.js +277 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/cli/walk.d.ts +13 -0
- package/dist/cli/walk.js +62 -0
- package/dist/cli/walk.js.map +1 -0
- package/dist/cli/walker.d.ts +14 -0
- package/dist/cli/walker.js +120 -0
- package/dist/cli/walker.js.map +1 -0
- package/dist/hooks/pre-compact.d.ts +15 -0
- package/dist/hooks/pre-compact.js +127 -0
- package/dist/hooks/pre-compact.js.map +1 -0
- package/dist/hooks/pre-tool-use.d.ts +15 -0
- package/dist/hooks/pre-tool-use.js +25 -0
- package/dist/hooks/pre-tool-use.js.map +1 -0
- package/dist/hooks/session-end.d.ts +21 -0
- package/dist/hooks/session-end.js +186 -0
- package/dist/hooks/session-end.js.map +1 -0
- package/dist/hooks/session-start.d.ts +15 -0
- package/dist/hooks/session-start.js +233 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/turn-end-post.d.ts +17 -0
- package/dist/hooks/turn-end-post.js +66 -0
- package/dist/hooks/turn-end-post.js.map +1 -0
- package/dist/hooks/turn-end.d.ts +29 -0
- package/dist/hooks/turn-end.js +295 -0
- package/dist/hooks/turn-end.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +1547 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +45 -0
- package/dist/init.js +839 -0
- package/dist/init.js.map +1 -0
- package/dist/lib/active-project.d.ts +14 -0
- package/dist/lib/active-project.js +65 -0
- package/dist/lib/active-project.js.map +1 -0
- package/dist/lib/buffer.d.ts +34 -0
- package/dist/lib/buffer.js +79 -0
- package/dist/lib/buffer.js.map +1 -0
- package/dist/scripts/compile.d.ts +25 -0
- package/dist/scripts/compile.js +185 -0
- package/dist/scripts/compile.js.map +1 -0
- package/dist/scripts/config.d.ts +30 -0
- package/dist/scripts/config.js +68 -0
- package/dist/scripts/config.js.map +1 -0
- package/dist/scripts/flush.d.ts +23 -0
- package/dist/scripts/flush.js +230 -0
- package/dist/scripts/flush.js.map +1 -0
- package/dist/scripts/lint.d.ts +21 -0
- package/dist/scripts/lint.js +242 -0
- package/dist/scripts/lint.js.map +1 -0
- package/dist/scripts/utils.d.ts +43 -0
- package/dist/scripts/utils.js +165 -0
- package/dist/scripts/utils.js.map +1 -0
- package/package.json +74 -0
- package/scripts/postinstall.mjs +56 -0
package/dist/cli/walk.js
ADDED
|
@@ -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":";AACA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,uCAAuC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAClH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACpD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;AAC3D,CAAC;AAED,iEAAiE;AACjE,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACvD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AACvD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AACpD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAEtD,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,wEAAwE;AACxE,MAAM,cAAc,GAAG;IACrB,sBAAsB;IACtB,mBAAmB;IACnB,uBAAuB;IACvB,qCAAqC;IACrC,iCAAiC;IACjC,mEAAmE;CACpE,CAAC;AAEF,SAAS,KAAK,CAAC,IAAY;IACzB,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC/B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wEAAwE;AAExE,SAAS,mBAAmB,CAAC,cAAsB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,OAAO,GAAG,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACrD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;YACnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YAC5B,IAAI,IAAI,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;YAE7B,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAEpD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,IAAI,GAAG,IAAI;qBACR,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;qBAC1E,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;qBACrD,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;YAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5C,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;gBACrD,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACvC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,QAAQ,GAAG,CAAC;YAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAC/D,CAAC;AAED,wEAAwE;AAExE,KAAK,UAAU,IAAI;IACjB,6BAA6B;IAC7B,IAAI,SAAS,GAAsD,EAAE,CAAC;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACpC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,oBAAoB;IAC9B,CAAC;IAED,MAAM,SAAS,GAAG,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC;IACpD,MAAM,cAAc,GAAG,SAAS,CAAC,eAAe,CAAC;IACjD,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC;QAAE,OAAO;IAE3D,gFAAgF;IAChF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;IACnE,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,WAAW,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE;gBACpC,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;gBACnC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,iBAAiB,EAAE,iBAAiB,EAAE;aAC9D,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YAC1G,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC9B,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC;gBAClC,UAAU,EAAE,SAAS;gBACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC9G,UAAU,EAAE,IAAI;gBAChB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,YAAY;gBACtB,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC1B,eAAe,EAAE;oBACf,KAAK,EAAE,YAAY;oBACnB,MAAM,EAAE,6BAA6B;oBACrC,cAAc,EAAE,CAAC;iBAClB;gBACD,IAAI,EAAE,CAAC,oBAAoB,EAAE,kBAAkB,EAAE,cAAc,OAAO,CAAC,MAAM,EAAE,CAAC;gBAChF,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;aAC7I,CAAC,CAAC,CAAC;YACJ,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;IACnE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,SAAS,GAAG,SAAS;QAAE,OAAO;IAErD,8DAA8D;IAC9D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1E,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,SAAS,IAAI,SAAS,KAAK,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEvE,kDAAkD;IAClD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,+CAA+C;QAC/C,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE;YAClE,GAAG,EAAE,QAAQ;YACb,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;YACf,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,iBAAiB,EAAE,cAAc,EAAE;SAC3D,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IAED,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;IAChB,iBAAiB;AACnB,CAAC,CAAC,CAAC"}
|
|
@@ -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 {};
|