@hebbianvault/mcp 0.2.0 → 0.3.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 (71) hide show
  1. package/README.md +35 -1
  2. package/dist/absorb/cli.d.ts +25 -0
  3. package/dist/absorb/cli.d.ts.map +1 -0
  4. package/dist/absorb/cli.js +140 -0
  5. package/dist/absorb/cli.js.map +1 -0
  6. package/dist/absorb/importers.d.ts +53 -0
  7. package/dist/absorb/importers.d.ts.map +1 -0
  8. package/dist/absorb/importers.js +132 -0
  9. package/dist/absorb/importers.js.map +1 -0
  10. package/dist/absorb/secrets.d.ts +34 -0
  11. package/dist/absorb/secrets.d.ts.map +1 -0
  12. package/dist/absorb/secrets.js +93 -0
  13. package/dist/absorb/secrets.js.map +1 -0
  14. package/dist/client.d.ts +4 -3
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +44 -22
  17. package/dist/client.js.map +1 -1
  18. package/dist/config.d.ts +2 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +8 -1
  21. package/dist/config.js.map +1 -1
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +29 -9
  25. package/dist/index.js.map +1 -1
  26. package/dist/package_info.d.ts +2 -0
  27. package/dist/package_info.d.ts.map +1 -0
  28. package/dist/package_info.js +6 -0
  29. package/dist/package_info.js.map +1 -0
  30. package/dist/server_info.d.ts +3 -0
  31. package/dist/server_info.d.ts.map +1 -0
  32. package/dist/server_info.js +4 -0
  33. package/dist/server_info.js.map +1 -0
  34. package/dist/tools/ask.d.ts.map +1 -1
  35. package/dist/tools/ask.js +4 -2
  36. package/dist/tools/ask.js.map +1 -1
  37. package/dist/tools/context.d.ts +25 -0
  38. package/dist/tools/context.d.ts.map +1 -0
  39. package/dist/tools/context.js +84 -0
  40. package/dist/tools/context.js.map +1 -0
  41. package/dist/tools/graph_helpers.d.ts +1 -0
  42. package/dist/tools/graph_helpers.d.ts.map +1 -1
  43. package/dist/tools/graph_helpers.js +26 -2
  44. package/dist/tools/graph_helpers.js.map +1 -1
  45. package/dist/tools/index.d.ts +2 -1
  46. package/dist/tools/index.d.ts.map +1 -1
  47. package/dist/tools/index.js +2 -1
  48. package/dist/tools/index.js.map +1 -1
  49. package/dist/tools/provenance.d.ts.map +1 -1
  50. package/dist/tools/provenance.js +5 -3
  51. package/dist/tools/provenance.js.map +1 -1
  52. package/dist/tools/read_node.d.ts.map +1 -1
  53. package/dist/tools/read_node.js +4 -2
  54. package/dist/tools/read_node.js.map +1 -1
  55. package/dist/tools/recent_activity.d.ts.map +1 -1
  56. package/dist/tools/recent_activity.js +4 -2
  57. package/dist/tools/recent_activity.js.map +1 -1
  58. package/dist/tools/salience.d.ts.map +1 -1
  59. package/dist/tools/salience.js +4 -2
  60. package/dist/tools/salience.js.map +1 -1
  61. package/dist/tools/search.d.ts.map +1 -1
  62. package/dist/tools/search.js +4 -2
  63. package/dist/tools/search.js.map +1 -1
  64. package/dist/tools/traverse.d.ts.map +1 -1
  65. package/dist/tools/traverse.js +5 -3
  66. package/dist/tools/traverse.js.map +1 -1
  67. package/dist/tools/untrusted_content.d.ts +20 -0
  68. package/dist/tools/untrusted_content.d.ts.map +1 -0
  69. package/dist/tools/untrusted_content.js +59 -0
  70. package/dist/tools/untrusted_content.js.map +1 -0
  71. package/package.json +9 -1
package/README.md CHANGED
@@ -78,6 +78,7 @@ Write `~/.config/hebbian/mcp-tenant.json`:
78
78
  | `HEBBIAN_API_TOKEN` | Your token (required). `HEBBIAN_TOKEN` is also accepted. |
79
79
  | `HEBBIAN_API_URL` | Override the API base URL (Enterprise self-host). Defaults to the Hebbian SaaS API. |
80
80
  | `HEBBIAN_TENANT` | Optional workspace slug — only needed if your account belongs to more than one workspace. |
81
+ | `HEBBIAN_GRAPH_PAGINATION` | Opt in to paginated workspace graph retrieval (`1`, `true`, `yes`, or `on`). |
81
82
 
82
83
  ## Token scope
83
84
 
@@ -90,6 +91,7 @@ Your token decides what the adapter can see and do — a personal-workspace toke
90
91
  | `hebbian_read_node` | Read a single node by UUID (content, metadata, connected edges) |
91
92
  | `hebbian_search` | Find nodes in your workspace matching a query |
92
93
  | `hebbian_ask` | Ask a question and get an answer backed by source quotes |
94
+ | `hebbian_context` | Describe a task and a token budget, get back a salience-ranked context pack that fits |
93
95
  | `hebbian_capture` | Write a note into your workspace |
94
96
  | `hebbian_traverse` | Explore nodes connected to a starting node |
95
97
  | `hebbian_provenance` | See where a node's knowledge came from |
@@ -98,6 +100,38 @@ Your token decides what the adapter can see and do — a personal-workspace toke
98
100
 
99
101
  Results only ever include what your token is allowed to see.
100
102
 
103
+ ## Onboard an agent you already use
104
+
105
+ Every agent you already run carries its own context: a Claude Code memory directory, CLAUDE.md files, a folder of markdown notes. The `absorb` command brings that accumulated context into Hebbian, so an agent you have used for months arrives with what it already knows instead of a blank slate.
106
+
107
+ Absorbed files land as seeds in your review lane (they do not write straight into the workspace), attributed to the agent principal you name. You review and promote them like any other proposed knowledge.
108
+
109
+ First, register the agent and issue it a token from your Hebbian integrations page (AI Tools tab), and note its agent id. Then point `absorb` at the store:
110
+
111
+ ```bash
112
+ # A Claude Code memory directory (MEMORY.md index + CLAUDE.md files + notes)
113
+ HEBBIAN_API_TOKEN=your_agent_token \
114
+ npx -y @hebbianvault/mcp absorb claude-code ~/.claude/projects/my-project --agent <agent_id>
115
+
116
+ # Any directory of markdown files
117
+ HEBBIAN_API_TOKEN=your_agent_token \
118
+ npx -y @hebbianvault/mcp absorb markdown ./docs --agent <agent_id>
119
+
120
+ # Walk and report what would be uploaded, without uploading
121
+ npx -y @hebbianvault/mcp absorb claude-code ~/.claude/projects/my-project --dry-run
122
+ ```
123
+
124
+ Each `*.md` file becomes one seed. The title is the file's first heading (or its name), and the source identifier is its path relative to the directory you pointed at. Re-running `absorb` on the same directory is safe: a file already absorbed for that agent is reported as a duplicate, not uploaded twice.
125
+
126
+ ### What never leaves your machine
127
+
128
+ `absorb` excludes secrets before anything is sent:
129
+
130
+ - Files whose names look like credentials are skipped entirely: `.env*`, anything with `credentials`, `secret`, or `token` in the name, plus `.pem`, `.key`, and SSH key files.
131
+ - Token-shaped strings inside the files it does read are redacted before upload: `sk-...`, `ghp_...`, `hbn_...`, Slack and AWS keys, JWTs, long hex and base64 blobs, and Bearer header values.
132
+
133
+ Every run prints a summary of how many files were skipped and how many strings were redacted, so you can see exactly what was filtered.
134
+
101
135
  ## Security
102
136
 
103
137
  - Tokens are bearer credentials — treat like passwords.
@@ -107,7 +141,7 @@ Results only ever include what your token is allowed to see.
107
141
 
108
142
  ## Python
109
143
 
110
- A Python sibling package is available as `hebbianvault-mcp` on PyPI. Same 8 tools, same configuration.
144
+ A Python sibling package is available as `hebbianvault-mcp` on PyPI. Same 9 tools, same configuration.
111
145
 
112
146
  ## Development
113
147
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * src/absorb/cli.ts
3
+ *
4
+ * The `absorb` command (ADR-055 §4). Onboard an agent you already use by
5
+ * uploading its existing context store as review-lane seeds.
6
+ *
7
+ * hebbian-mcp absorb claude-code <dir> --agent <agent_id>
8
+ * hebbian-mcp absorb markdown <dir> --agent <agent_id>
9
+ *
10
+ * Reads *.md files locally (Claude Code memory dir: MEMORY.md index + CLAUDE.md
11
+ * files; generic markdown: any *.md), skips credential-named files, redacts
12
+ * token-shaped strings, then batches the items to
13
+ * POST /v1/agents/{agent_id}/absorb with the agent token from the existing
14
+ * config/env convention (config.ts).
15
+ *
16
+ * --dry-run walks and reports without uploading. The auth token + API URL come
17
+ * from HEBBIAN_API_TOKEN / config file exactly like the MCP server.
18
+ */
19
+ /**
20
+ * Run the absorb command. Returns the process exit code (0 ok, non-zero error).
21
+ * Reads the store, prints the skipped/redacted summary, then (unless --dry-run)
22
+ * uploads in batches and prints the per-batch tally.
23
+ */
24
+ export declare function runAbsorb(argv: string[]): Promise<number>;
25
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/absorb/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA6FH;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsE/D"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * src/absorb/cli.ts
3
+ *
4
+ * The `absorb` command (ADR-055 §4). Onboard an agent you already use by
5
+ * uploading its existing context store as review-lane seeds.
6
+ *
7
+ * hebbian-mcp absorb claude-code <dir> --agent <agent_id>
8
+ * hebbian-mcp absorb markdown <dir> --agent <agent_id>
9
+ *
10
+ * Reads *.md files locally (Claude Code memory dir: MEMORY.md index + CLAUDE.md
11
+ * files; generic markdown: any *.md), skips credential-named files, redacts
12
+ * token-shaped strings, then batches the items to
13
+ * POST /v1/agents/{agent_id}/absorb with the agent token from the existing
14
+ * config/env convention (config.ts).
15
+ *
16
+ * --dry-run walks and reports without uploading. The auth token + API URL come
17
+ * from HEBBIAN_API_TOKEN / config file exactly like the MCP server.
18
+ */
19
+ import { HebbianClient } from "../client.js";
20
+ import { loadConfig } from "../config.js";
21
+ import { isSupportedStore, scanDirectory, supportedStores, } from "./importers.js";
22
+ /** Server-side batch cap (ADR-055 §2). Keep in sync with MAX_BATCH_ITEMS. */
23
+ const BATCH_SIZE = 200;
24
+ function parseFlags(argv) {
25
+ const positionals = [];
26
+ let agentId;
27
+ let dryRun = false;
28
+ for (let i = 0; i < argv.length; i += 1) {
29
+ const a = argv[i];
30
+ if (a === "--agent" || a === "--agent-id") {
31
+ agentId = argv[i + 1];
32
+ i += 1;
33
+ }
34
+ else if (a.startsWith("--agent=")) {
35
+ agentId = a.slice("--agent=".length);
36
+ }
37
+ else if (a === "--dry-run") {
38
+ dryRun = true;
39
+ }
40
+ else {
41
+ positionals.push(a);
42
+ }
43
+ }
44
+ return { agentId, dryRun, positionals };
45
+ }
46
+ const USAGE = [
47
+ "Usage: hebbian-mcp absorb <store> <dir> --agent <agent_id> [--dry-run]",
48
+ "",
49
+ ` store one of: ${supportedStores().join(", ")}`,
50
+ " dir path to the context store directory",
51
+ " --agent the agent principal UUID to absorb into (the agent you already use)",
52
+ " --dry-run walk and report; do not upload",
53
+ "",
54
+ "Auth: set HEBBIAN_API_TOKEN to the agent's token (or use the config file).",
55
+ "Reads *.md files, skips credential-named files, redacts token-shaped strings,",
56
+ "then uploads each file as a review-lane seed attributed to the agent.",
57
+ ].join("\n");
58
+ /** Resolve + validate args. Returns null (after printing usage) on a usage error. */
59
+ function resolveArgs(argv) {
60
+ const { agentId, dryRun, positionals } = parseFlags(argv);
61
+ const [store, dir] = positionals;
62
+ if (!store || !dir) {
63
+ process.stderr.write(`${USAGE}\n`);
64
+ return null;
65
+ }
66
+ if (!isSupportedStore(store)) {
67
+ process.stderr.write(`absorb: unsupported store "${store}". Supported: ${supportedStores().join(", ")}\n`);
68
+ return null;
69
+ }
70
+ if (!dryRun && !agentId) {
71
+ process.stderr.write("absorb: --agent <agent_id> is required (or use --dry-run)\n");
72
+ return null;
73
+ }
74
+ return { store, dir, agentId: agentId ?? "", dryRun };
75
+ }
76
+ function chunk(arr, size) {
77
+ const out = [];
78
+ for (let i = 0; i < arr.length; i += size)
79
+ out.push(arr.slice(i, i + size));
80
+ return out;
81
+ }
82
+ /**
83
+ * Run the absorb command. Returns the process exit code (0 ok, non-zero error).
84
+ * Reads the store, prints the skipped/redacted summary, then (unless --dry-run)
85
+ * uploads in batches and prints the per-batch tally.
86
+ */
87
+ export async function runAbsorb(argv) {
88
+ const args = resolveArgs(argv);
89
+ if (args === null)
90
+ return 2;
91
+ const scan = scanDirectory(args.dir, args.store);
92
+ // Honest summary line (ADR-055 §4): how many files, skipped secrets, redactions.
93
+ process.stderr.write(`[absorb] store=${args.store} dir=${args.dir}: ` +
94
+ `${scan.items.length} file(s) to absorb, ` +
95
+ `skipped ${scan.skippedSecretFiles.length} credential-named file(s), ` +
96
+ `redacted ${scan.redactedSecrets} token-shaped string(s) across ${scan.redactedItems} file(s)\n`);
97
+ if (scan.skippedSecretFiles.length > 0) {
98
+ for (const f of scan.skippedSecretFiles) {
99
+ process.stderr.write(`[absorb] skipped (looks like secrets): ${f}\n`);
100
+ }
101
+ }
102
+ if (scan.items.length === 0) {
103
+ process.stderr.write("[absorb] nothing to absorb.\n");
104
+ return 0;
105
+ }
106
+ if (args.dryRun) {
107
+ process.stderr.write(`[absorb] dry-run: would upload ${scan.items.length} item(s) to ` +
108
+ `/v1/agents/${args.agentId || "<agent_id>"}/absorb in ` +
109
+ `${chunk(scan.items, BATCH_SIZE).length} batch(es). No upload performed.\n`);
110
+ return 0;
111
+ }
112
+ const config = loadConfig();
113
+ const client = new HebbianClient(config.apiUrl, config.token, config.tenant, config.graphPagination);
114
+ let accepted = 0;
115
+ let duplicates = 0;
116
+ let errors = 0;
117
+ const batches = chunk(scan.items, BATCH_SIZE);
118
+ for (let i = 0; i < batches.length; i += 1) {
119
+ const batch = batches[i];
120
+ try {
121
+ const resp = (await client.post(`/v1/agents/${args.agentId}/absorb`, {
122
+ items: batch,
123
+ }));
124
+ accepted += resp.accepted ?? 0;
125
+ duplicates += resp.duplicates ?? 0;
126
+ errors += resp.errors ?? 0;
127
+ process.stderr.write(`[absorb] batch ${i + 1}/${batches.length}: ` +
128
+ `accepted ${resp.accepted ?? 0}, duplicate ${resp.duplicates ?? 0}, error ${resp.errors ?? 0}\n`);
129
+ }
130
+ catch (err) {
131
+ const message = err instanceof Error ? err.message : String(err);
132
+ process.stderr.write(`[absorb] batch ${i + 1}/${batches.length} failed: ${message}\n`);
133
+ return 1;
134
+ }
135
+ }
136
+ process.stderr.write(`[absorb] done: ${accepted} new seed(s), ${duplicates} already absorbed, ${errors} error(s). ` +
137
+ "New seeds await review in the Hebbian review lane.\n");
138
+ return errors > 0 ? 1 : 0;
139
+ }
140
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/absorb/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * src/absorb/importers.ts
3
+ *
4
+ * Local importers for the absorb command (ADR-055 §4). An importer walks a
5
+ * directory, reads the files relevant to one store kind, and turns each into an
6
+ * AbsorbItem. It NEVER posts anything — the CLI batches the items to the API.
7
+ *
8
+ * Two importers ship in this slice:
9
+ * claude-code — a Claude Code memory directory: every *.md file, including
10
+ * the MEMORY.md index and any CLAUDE.md files.
11
+ * markdown — generic catch-all: walk a directory tree for every *.md file.
12
+ *
13
+ * Both reuse the same secret guards (secrets.ts): a credential-named file is
14
+ * skipped, and token-shaped strings are redacted from the content before it is
15
+ * handed back. Each importer reports skipped/redacted counts so the CLI can
16
+ * print an honest summary.
17
+ */
18
+ /** One memory item ready to absorb. Matches the API's AbsorbItem shape. */
19
+ export interface AbsorbItem {
20
+ store_kind: string;
21
+ /** Stable identifier within the store — the file's path relative to the root. */
22
+ source_id: string;
23
+ /** First markdown heading, else the filename. */
24
+ title: string;
25
+ /** Redacted file content. */
26
+ content: string;
27
+ /** Filesystem birthtime (ISO-8601), best-effort. */
28
+ created_at?: string;
29
+ /** Filesystem mtime (ISO-8601). */
30
+ updated_at?: string;
31
+ }
32
+ /** Outcome of walking a directory: items to upload + what was skipped/redacted. */
33
+ export interface ScanResult {
34
+ items: AbsorbItem[];
35
+ skippedSecretFiles: string[];
36
+ redactedItems: number;
37
+ redactedSecrets: number;
38
+ }
39
+ declare const SUPPORTED_STORES: readonly ["claude-code", "markdown"];
40
+ export type StoreKind = (typeof SUPPORTED_STORES)[number];
41
+ export declare function isSupportedStore(kind: string): kind is StoreKind;
42
+ export declare function supportedStores(): readonly string[];
43
+ /**
44
+ * Walk a directory and build absorb items for one store kind.
45
+ *
46
+ * Shared by both importers — they differ only in the store_kind tag and (for
47
+ * documentation) the expectation of what lives in the directory; the file
48
+ * selection (every *.md, including MEMORY.md and CLAUDE.md) is identical, which
49
+ * is exactly what a Claude Code memory directory contains.
50
+ */
51
+ export declare function scanDirectory(root: string, storeKind: string): ScanResult;
52
+ export {};
53
+ //# sourceMappingURL=importers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importers.d.ts","sourceRoot":"","sources":["../../src/absorb/importers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAeD,QAAA,MAAM,gBAAgB,sCAAuC,CAAC;AAC9D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,SAAS,CAEhE;AAED,wBAAgB,eAAe,IAAI,SAAS,MAAM,EAAE,CAEnD;AA4CD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAwCzE"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * src/absorb/importers.ts
3
+ *
4
+ * Local importers for the absorb command (ADR-055 §4). An importer walks a
5
+ * directory, reads the files relevant to one store kind, and turns each into an
6
+ * AbsorbItem. It NEVER posts anything — the CLI batches the items to the API.
7
+ *
8
+ * Two importers ship in this slice:
9
+ * claude-code — a Claude Code memory directory: every *.md file, including
10
+ * the MEMORY.md index and any CLAUDE.md files.
11
+ * markdown — generic catch-all: walk a directory tree for every *.md file.
12
+ *
13
+ * Both reuse the same secret guards (secrets.ts): a credential-named file is
14
+ * skipped, and token-shaped strings are redacted from the content before it is
15
+ * handed back. Each importer reports skipped/redacted counts so the CLI can
16
+ * print an honest summary.
17
+ */
18
+ import { readdirSync, readFileSync, statSync } from "node:fs";
19
+ import { join, relative, sep } from "node:path";
20
+ import { redactSecrets, shouldSkipFile } from "./secrets.js";
21
+ /** Directories never descended into (noise + secret stores). */
22
+ const SKIP_DIRS = new Set([
23
+ "node_modules",
24
+ ".git",
25
+ ".venv",
26
+ "venv",
27
+ "__pycache__",
28
+ "dist",
29
+ "build",
30
+ ".next",
31
+ ".cache",
32
+ ]);
33
+ const SUPPORTED_STORES = ["claude-code", "markdown"];
34
+ export function isSupportedStore(kind) {
35
+ return SUPPORTED_STORES.includes(kind);
36
+ }
37
+ export function supportedStores() {
38
+ return SUPPORTED_STORES;
39
+ }
40
+ /** Recursively collect every *.md file under root (relative paths). */
41
+ function collectMarkdownFiles(root) {
42
+ const out = [];
43
+ const walk = (dir) => {
44
+ let entries;
45
+ try {
46
+ entries = readdirSync(dir);
47
+ }
48
+ catch {
49
+ return; // unreadable dir — skip quietly
50
+ }
51
+ for (const entry of entries) {
52
+ const abs = join(dir, entry);
53
+ let st;
54
+ try {
55
+ st = statSync(abs);
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ if (st.isDirectory()) {
61
+ if (!SKIP_DIRS.has(entry))
62
+ walk(abs);
63
+ continue;
64
+ }
65
+ if (st.isFile() && entry.toLowerCase().endsWith(".md")) {
66
+ out.push(relative(root, abs));
67
+ }
68
+ }
69
+ };
70
+ walk(root);
71
+ return out.sort();
72
+ }
73
+ /** First markdown heading in the content, else the basename without extension. */
74
+ function deriveTitle(content, relPath) {
75
+ for (const raw of content.split("\n")) {
76
+ const line = raw.trim();
77
+ const m = /^#{1,6}\s+(.+?)\s*#*$/.exec(line);
78
+ if (m && m[1])
79
+ return m[1].trim();
80
+ }
81
+ const base = relPath.split(/[\\/]/).pop() ?? relPath;
82
+ return base.replace(/\.md$/i, "");
83
+ }
84
+ /**
85
+ * Walk a directory and build absorb items for one store kind.
86
+ *
87
+ * Shared by both importers — they differ only in the store_kind tag and (for
88
+ * documentation) the expectation of what lives in the directory; the file
89
+ * selection (every *.md, including MEMORY.md and CLAUDE.md) is identical, which
90
+ * is exactly what a Claude Code memory directory contains.
91
+ */
92
+ export function scanDirectory(root, storeKind) {
93
+ const rel = collectMarkdownFiles(root);
94
+ const items = [];
95
+ const skippedSecretFiles = [];
96
+ let redactedItems = 0;
97
+ let redactedSecrets = 0;
98
+ for (const relPath of rel) {
99
+ const base = relPath.split(/[\\/]/).pop() ?? relPath;
100
+ if (shouldSkipFile(base)) {
101
+ skippedSecretFiles.push(relPath);
102
+ continue;
103
+ }
104
+ const abs = join(root, relPath);
105
+ let raw;
106
+ let st;
107
+ try {
108
+ raw = readFileSync(abs, "utf-8");
109
+ st = statSync(abs);
110
+ }
111
+ catch {
112
+ continue;
113
+ }
114
+ const { content, redactedCount } = redactSecrets(raw);
115
+ if (redactedCount > 0) {
116
+ redactedItems += 1;
117
+ redactedSecrets += redactedCount;
118
+ }
119
+ // Normalise source_id to forward slashes so it is stable across OSes.
120
+ const sourceId = relPath.split(sep).join("/");
121
+ items.push({
122
+ store_kind: storeKind,
123
+ source_id: sourceId,
124
+ title: deriveTitle(content, relPath),
125
+ content,
126
+ created_at: st.birthtime?.toISOString?.(),
127
+ updated_at: st.mtime?.toISOString?.(),
128
+ });
129
+ }
130
+ return { items, skippedSecretFiles, redactedItems, redactedSecrets };
131
+ }
132
+ //# sourceMappingURL=importers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importers.js","sourceRoot":"","sources":["../../src/absorb/importers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * src/absorb/secrets.ts
3
+ *
4
+ * Secret exclusion + redaction for the absorb importers (ADR-055 §4).
5
+ *
6
+ * Two layers, both non-negotiable:
7
+ * 1. File exclusion — never read a file whose name looks like it holds
8
+ * credentials (.env*, *credentials*, *secret*, *token*-named files).
9
+ * 2. Content redaction — even in a file we DO read, replace token-shaped
10
+ * strings (long base64/hex, sk-..., ghp_..., hbn_..., AWS keys, JWTs)
11
+ * with a [REDACTED] marker before the content ever leaves the machine.
12
+ *
13
+ * Nothing here talks to the network. The importer calls shouldSkipFile() per
14
+ * file and redactSecrets() on every body it is about to upload.
15
+ */
16
+ /**
17
+ * True when a file should be skipped entirely because its NAME indicates it
18
+ * holds credentials. Matches on the basename only (path is ignored), case-
19
+ * insensitively.
20
+ */
21
+ export declare function shouldSkipFile(filename: string): boolean;
22
+ /** Result of redacting a string: the cleaned text + how many secrets were hit. */
23
+ export interface RedactionResult {
24
+ content: string;
25
+ redactedCount: number;
26
+ }
27
+ /**
28
+ * Redact token-shaped strings from content. Returns the cleaned content and the
29
+ * number of distinct matches replaced. Idempotent on already-clean text
30
+ * (redactedCount === 0). The Bearer rule keeps the leading "Bearer " word so the
31
+ * surrounding prose still reads sensibly.
32
+ */
33
+ export declare function redactSecrets(content: string): RedactionResult;
34
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/absorb/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAqDH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAa9D"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * src/absorb/secrets.ts
3
+ *
4
+ * Secret exclusion + redaction for the absorb importers (ADR-055 §4).
5
+ *
6
+ * Two layers, both non-negotiable:
7
+ * 1. File exclusion — never read a file whose name looks like it holds
8
+ * credentials (.env*, *credentials*, *secret*, *token*-named files).
9
+ * 2. Content redaction — even in a file we DO read, replace token-shaped
10
+ * strings (long base64/hex, sk-..., ghp_..., hbn_..., AWS keys, JWTs)
11
+ * with a [REDACTED] marker before the content ever leaves the machine.
12
+ *
13
+ * Nothing here talks to the network. The importer calls shouldSkipFile() per
14
+ * file and redactSecrets() on every body it is about to upload.
15
+ */
16
+ /** A file basename matches one of these → skip it entirely. */
17
+ const SKIP_NAME_PATTERNS = [
18
+ /^\.env(\..*)?$/i, // .env, .env.local, .env.production, …
19
+ /credentials/i, // aws credentials, gcloud credentials, *credentials*.json
20
+ /secret/i, // secrets.yaml, my-secret.md, *secret*
21
+ /(^|[._-])tokens?([._-]|$)/i, // token.txt, api-tokens.json, auth_token.md
22
+ /\.pem$/i, // private keys
23
+ /\.key$/i,
24
+ /id_rsa/i, // ssh keys
25
+ /\.p12$/i,
26
+ /\.pfx$/i,
27
+ ];
28
+ /**
29
+ * Token-shaped substrings to redact from content. Ordered specific → generic so
30
+ * a prefixed key (sk-…, ghp_…) is matched by its own rule before the generic
31
+ * long-base64 catch-all. Each match is replaced with [REDACTED].
32
+ */
33
+ const REDACT_PATTERNS = [
34
+ // OpenAI / Anthropic style: sk-… , sk-ant-…
35
+ /\bsk-[A-Za-z0-9_-]{16,}\b/g,
36
+ // GitHub tokens: ghp_, gho_, ghu_, ghs_, ghr_, github_pat_
37
+ /\bgh[opusr]_[A-Za-z0-9]{16,}\b/g,
38
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
39
+ // Hebbian tokens.
40
+ /\bhbn_[A-Za-z0-9_-]{16,}\b/g,
41
+ // Slack tokens: xoxb-, xoxp-, xapp-…
42
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
43
+ // AWS access key id.
44
+ /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,
45
+ // Google API key.
46
+ /\bAIza[A-Za-z0-9_-]{30,}\b/g,
47
+ // JWT (three base64url segments).
48
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
49
+ // Bearer headers — redact the value, keep the word.
50
+ /\b([Bb]earer\s+)[A-Za-z0-9._-]{20,}\b/g,
51
+ // URL-embedded credentials: scheme://user:password@host (postgres://, redis://,
52
+ // amqp://, mongodb://, …). Redact the password, keep scheme/user/host so the
53
+ // surrounding prose still reads sensibly.
54
+ /\b([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\/\s]*:)[^@\s]{3,}(?=@)/g,
55
+ // Supabase secret / personal-access keys.
56
+ /\bsb_secret_[A-Za-z0-9_-]{10,}\b/g,
57
+ /\bsbp_[A-Za-z0-9]{20,}\b/g,
58
+ // Generic long hex (>= 40 chars) — sha-like / hex secrets.
59
+ /\b[0-9a-fA-F]{40,}\b/g,
60
+ // Generic long base64-ish blob (>= 40 chars). Last so prefixed keys win first.
61
+ /\b[A-Za-z0-9+/]{40,}={0,2}\b/g,
62
+ ];
63
+ const REDACTION_MARKER = "[REDACTED]";
64
+ /**
65
+ * True when a file should be skipped entirely because its NAME indicates it
66
+ * holds credentials. Matches on the basename only (path is ignored), case-
67
+ * insensitively.
68
+ */
69
+ export function shouldSkipFile(filename) {
70
+ const base = filename.split(/[\\/]/).pop() ?? filename;
71
+ return SKIP_NAME_PATTERNS.some((re) => re.test(base));
72
+ }
73
+ /**
74
+ * Redact token-shaped strings from content. Returns the cleaned content and the
75
+ * number of distinct matches replaced. Idempotent on already-clean text
76
+ * (redactedCount === 0). The Bearer rule keeps the leading "Bearer " word so the
77
+ * surrounding prose still reads sensibly.
78
+ */
79
+ export function redactSecrets(content) {
80
+ let redactedCount = 0;
81
+ let out = content;
82
+ for (const re of REDACT_PATTERNS) {
83
+ out = out.replace(re, (match, ...groups) => {
84
+ redactedCount += 1;
85
+ // Rules with a capture group ("Bearer ", "scheme://user:") keep the prefix
86
+ // so the surrounding prose still reads sensibly; only the secret is replaced.
87
+ const prefix = typeof groups[0] === "string" ? groups[0] : "";
88
+ return `${prefix}${REDACTION_MARKER}`;
89
+ });
90
+ }
91
+ return { content: out, redactedCount };
92
+ }
93
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/absorb/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,+DAA+D;AAC/D,MAAM,kBAAkB,GAAa;IACnC,iBAAiB,EAAE,uCAAuC;IAC1D,cAAc,EAAE,0DAA0D;IAC1E,SAAS,EAAE,uCAAuC;IAClD,4BAA4B,EAAE,4CAA4C;IAC1E,SAAS,EAAE,eAAe;IAC1B,SAAS;IACT,SAAS,EAAE,WAAW;IACtB,SAAS;IACT,SAAS;CACV,CAAC;AAEF;;;;GAIG;AACH,MAAM,eAAe,GAAa;IAChC,4CAA4C;IAC5C,4BAA4B;IAC5B,2DAA2D;IAC3D,iCAAiC;IACjC,mCAAmC;IACnC,kBAAkB;IAClB,6BAA6B;IAC7B,qCAAqC;IACrC,mCAAmC;IACnC,qBAAqB;IACrB,gCAAgC;IAChC,kBAAkB;IAClB,6BAA6B;IAC7B,kCAAkC;IAClC,iEAAiE;IACjE,oDAAoD;IACpD,wCAAwC;IACxC,gFAAgF;IAChF,6EAA6E;IAC7E,0CAA0C;IAC1C,6DAA6D;IAC7D,0CAA0C;IAC1C,mCAAmC;IACnC,2BAA2B;IAC3B,2DAA2D;IAC3D,uBAAuB;IACvB,+EAA+E;IAC/E,+BAA+B;CAChC,CAAC;AAEF,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAEtC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IACvD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,GAAG,GAAG,OAAO,CAAC;IAClB,KAAK,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC;QACjC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,MAAM,EAAE,EAAE;YACzC,aAAa,IAAI,CAAC,CAAC;YACnB,2EAA2E;YAC3E,8EAA8E;YAC9E,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,GAAG,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;AACzC,CAAC"}
package/dist/client.d.ts CHANGED
@@ -21,7 +21,9 @@ export declare class HebbianClient {
21
21
  private readonly apiUrl;
22
22
  private readonly token;
23
23
  private readonly tenant?;
24
- constructor(apiUrl: string, token: string, tenant?: string);
24
+ /** Whether graph-derived tools should request UUID-keyset paginated graphs. */
25
+ readonly graphPagination: boolean;
26
+ constructor(apiUrl: string, token: string, tenant?: string, graphPagination?: boolean);
25
27
  /**
26
28
  * Execute a GET request against the Hebbian API.
27
29
  * @param path Path relative to apiUrl (must start with "/")
@@ -35,9 +37,8 @@ export declare class HebbianClient {
35
37
  */
36
38
  post(path: string, body: Record<string, unknown>): Promise<unknown>;
37
39
  /** Build an absolute URL from path + optional query params. */
38
- private buildUrl;
39
- /** Standard request headers including bearer auth. */
40
40
  private headers;
41
+ private buildUrl;
41
42
  /**
42
43
  * Parse and validate a fetch response.
43
44
  * Throws HebbianApiError on non-2xx status codes.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2EAA2E;AAC3E,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,UAAU,EAAE,MAAM;aAClB,SAAS,EAAE,MAAM;aAEjB,MAAM,CAAC,EAAE,OAAO;gBAHhB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,OAAO,YAAA;IAMlC,0DAA0D;IAC1D,WAAW,IAAI,MAAM;CAsBtB;AAUD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;gBAErB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAO1D;;;;OAIG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAC5D,OAAO,CAAC,OAAO,CAAC;IASnB;;;;OAIG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC;IAanB,+DAA+D;IAC/D,OAAO,CAAC,QAAQ;IAehB,sDAAsD;IACtD,OAAO,CAAC,OAAO;IAcf;;;OAGG;YACW,cAAc;CAgC7B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,2EAA2E;AAC3E,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,UAAU,EAAE,MAAM;aAClB,SAAS,EAAE,MAAM;aAEjB,MAAM,CAAC,EAAE,OAAO;gBAHhB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,OAAO,YAAA;IAMlC,0DAA0D;IAC1D,WAAW,IAAI,MAAM;CAsBtB;AA6BD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,+EAA+E;IAC/E,SAAgB,eAAe,EAAE,OAAO,CAAC;gBAE7B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,eAAe,UAAQ;IAQnF;;;;OAIG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAC5D,OAAO,CAAC,OAAO,CAAC;IASnB;;;;OAIG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC;IAanB,+DAA+D;IAC/D,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,QAAQ;IAehB;;;OAGG;YACW,cAAc;CAiC7B"}