@nex-ai/nex 0.1.34 → 0.1.35

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 (69) hide show
  1. package/dist/index.js +5 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp/channel.d.ts +28 -0
  4. package/dist/mcp/channel.js +135 -0
  5. package/dist/mcp/channel.js.map +1 -0
  6. package/dist/mcp/client.d.ts +20 -0
  7. package/dist/mcp/client.js +130 -0
  8. package/dist/mcp/client.js.map +1 -0
  9. package/dist/mcp/config.d.ts +14 -0
  10. package/dist/mcp/config.js +33 -0
  11. package/dist/mcp/config.js.map +1 -0
  12. package/dist/mcp/context-files.d.ts +9 -0
  13. package/dist/mcp/context-files.js +90 -0
  14. package/dist/mcp/context-files.js.map +1 -0
  15. package/dist/mcp/file-manifest.d.ts +27 -0
  16. package/dist/mcp/file-manifest.js +64 -0
  17. package/dist/mcp/file-manifest.js.map +1 -0
  18. package/dist/mcp/file-scanner.d.ts +17 -0
  19. package/dist/mcp/file-scanner.js +77 -0
  20. package/dist/mcp/file-scanner.js.map +1 -0
  21. package/dist/mcp/index.d.ts +2 -0
  22. package/dist/mcp/index.js +51 -0
  23. package/dist/mcp/index.js.map +1 -0
  24. package/dist/mcp/rate-limiter.d.ts +14 -0
  25. package/dist/mcp/rate-limiter.js +60 -0
  26. package/dist/mcp/rate-limiter.js.map +1 -0
  27. package/dist/mcp/server.d.ts +6 -0
  28. package/dist/mcp/server.js +42 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/mcp/session-store.d.ts +16 -0
  31. package/dist/mcp/session-store.js +70 -0
  32. package/dist/mcp/session-store.js.map +1 -0
  33. package/dist/mcp/tools/context.d.ts +3 -0
  34. package/dist/mcp/tools/context.js +65 -0
  35. package/dist/mcp/tools/context.js.map +1 -0
  36. package/dist/mcp/tools/insights.d.ts +3 -0
  37. package/dist/mcp/tools/insights.js +24 -0
  38. package/dist/mcp/tools/insights.js.map +1 -0
  39. package/dist/mcp/tools/integrations.d.ts +3 -0
  40. package/dist/mcp/tools/integrations.js +27 -0
  41. package/dist/mcp/tools/integrations.js.map +1 -0
  42. package/dist/mcp/tools/lists.d.ts +3 -0
  43. package/dist/mcp/tools/lists.js +101 -0
  44. package/dist/mcp/tools/lists.js.map +1 -0
  45. package/dist/mcp/tools/notes.d.ts +3 -0
  46. package/dist/mcp/tools/notes.js +52 -0
  47. package/dist/mcp/tools/notes.js.map +1 -0
  48. package/dist/mcp/tools/records.d.ts +3 -0
  49. package/dist/mcp/tools/records.js +74 -0
  50. package/dist/mcp/tools/records.js.map +1 -0
  51. package/dist/mcp/tools/register.d.ts +3 -0
  52. package/dist/mcp/tools/register.js +30 -0
  53. package/dist/mcp/tools/register.js.map +1 -0
  54. package/dist/mcp/tools/relationships.d.ts +3 -0
  55. package/dist/mcp/tools/relationships.js +47 -0
  56. package/dist/mcp/tools/relationships.js.map +1 -0
  57. package/dist/mcp/tools/scan.d.ts +3 -0
  58. package/dist/mcp/tools/scan.js +37 -0
  59. package/dist/mcp/tools/scan.js.map +1 -0
  60. package/dist/mcp/tools/schema.d.ts +3 -0
  61. package/dist/mcp/tools/schema.js +108 -0
  62. package/dist/mcp/tools/schema.js.map +1 -0
  63. package/dist/mcp/tools/search.d.ts +3 -0
  64. package/dist/mcp/tools/search.js +8 -0
  65. package/dist/mcp/tools/search.js.map +1 -0
  66. package/dist/mcp/tools/tasks.d.ts +3 -0
  67. package/dist/mcp/tools/tasks.js +88 -0
  68. package/dist/mcp/tools/tasks.js.map +1 -0
  69. package/package.json +6 -3
package/dist/index.js CHANGED
@@ -98,6 +98,11 @@ When no command is given, launches the interactive TUI.`);
98
98
  // Interactive commands → fall through to Commander (rich TUI with pickers, spinners, workflows)
99
99
  const INTERACTIVE_COMMANDS = new Set(["setup", "integrate", "scan", "register", "status"]);
100
100
  const firstArg = cleanArgs[0]?.toLowerCase();
101
+ // nex mcp → start embedded MCP server
102
+ if (firstArg === "mcp") {
103
+ await import("./mcp/index.js");
104
+ return;
105
+ }
101
106
  if (firstArg && !INTERACTIVE_COMMANDS.has(firstArg)) {
102
107
  // Non-interactive subcommand → dispatch and exit
103
108
  const result = await dispatchTokens(cleanArgs, ctx);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Nex Channel — pushes daily digests and proactive notifications
3
+ * into active Claude Code sessions via the Channels API.
4
+ *
5
+ * Uses the existing Nex API client to query context and insights,
6
+ * then forwards results as channel notifications.
7
+ */
8
+ import type { NexApiClient } from "./client.js";
9
+ /**
10
+ * Minimal interface for the underlying MCP Server's notification method.
11
+ * We use this instead of importing the full Server type to avoid coupling
12
+ * to the SDK's internal type hierarchy.
13
+ */
14
+ interface NotificationSender {
15
+ notification(notification: {
16
+ method: string;
17
+ params?: Record<string, unknown>;
18
+ }): Promise<void>;
19
+ }
20
+ /**
21
+ * Start the Nex notification channel. Call this after the MCP server
22
+ * has connected to its transport.
23
+ *
24
+ * @param server The underlying MCP `Server` instance (mcpServer.server)
25
+ * @param client An authenticated NexApiClient
26
+ */
27
+ export declare function startChannel(server: NotificationSender, client: NexApiClient): void;
28
+ export {};
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Nex Channel — pushes daily digests and proactive notifications
3
+ * into active Claude Code sessions via the Channels API.
4
+ *
5
+ * Uses the existing Nex API client to query context and insights,
6
+ * then forwards results as channel notifications.
7
+ */
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ // ---------------------------------------------------------------------------
12
+ // Constants
13
+ // ---------------------------------------------------------------------------
14
+ const STATE_DIR = join(homedir(), ".nex");
15
+ const STATE_PATH = join(STATE_DIR, "channel-state.json");
16
+ /** How often to check whether a daily digest is due (1 hour). */
17
+ const DIGEST_CHECK_INTERVAL_MS = 60 * 60 * 1000;
18
+ /** Minimum gap between daily digests (24 hours). */
19
+ const DIGEST_COOLDOWN_MS = 24 * 60 * 60 * 1000;
20
+ /** How often to poll for proactive notifications (5 minutes). */
21
+ const NOTIFY_INTERVAL_MS = 5 * 60 * 1000;
22
+ /** Delay before the first digest check after startup (10 seconds). */
23
+ const INITIAL_DIGEST_DELAY_MS = 10_000;
24
+ /** Delay before the first notification check after startup (15 seconds). */
25
+ const INITIAL_NOTIFY_DELAY_MS = 15_000;
26
+ // ---------------------------------------------------------------------------
27
+ // State persistence
28
+ // ---------------------------------------------------------------------------
29
+ function loadState() {
30
+ try {
31
+ if (existsSync(STATE_PATH)) {
32
+ return JSON.parse(readFileSync(STATE_PATH, "utf-8"));
33
+ }
34
+ }
35
+ catch {
36
+ // Corrupted or unreadable — start fresh
37
+ }
38
+ return { lastDigestAt: 0, lastNotifyCheckAt: 0 };
39
+ }
40
+ function saveState(state) {
41
+ try {
42
+ if (!existsSync(STATE_DIR))
43
+ mkdirSync(STATE_DIR, { recursive: true });
44
+ writeFileSync(STATE_PATH, JSON.stringify(state));
45
+ }
46
+ catch {
47
+ // Best-effort — don't crash if write fails
48
+ }
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Push helper
52
+ // ---------------------------------------------------------------------------
53
+ async function pushChannelEvent(server, content, meta = {}) {
54
+ await server.notification({
55
+ method: "notifications/claude/channel",
56
+ params: { content, meta },
57
+ });
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Digest logic
61
+ // ---------------------------------------------------------------------------
62
+ async function checkDigest(server, client, state) {
63
+ const now = Date.now();
64
+ if (now - state.lastDigestAt < DIGEST_COOLDOWN_MS)
65
+ return;
66
+ try {
67
+ const result = (await client.post("/v1/context/ask", {
68
+ query: "Provide a comprehensive daily digest: summarize all key context " +
69
+ "collected in the last 24 hours, including important updates, new " +
70
+ "relationships, deal changes, upcoming events, and any actionable " +
71
+ "items. Be specific with names, dates, and numbers.",
72
+ }));
73
+ if (result.answer && result.answer.trim().length > 0) {
74
+ await pushChannelEvent(server, result.answer, {
75
+ type: "daily_digest",
76
+ period: "24h",
77
+ });
78
+ state.lastDigestAt = now;
79
+ saveState(state);
80
+ }
81
+ }
82
+ catch (err) {
83
+ console.error("[nex-channel] digest check failed:", err);
84
+ }
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // Proactive notification logic
88
+ // ---------------------------------------------------------------------------
89
+ async function checkNotifications(server, client, state) {
90
+ const now = Date.now();
91
+ try {
92
+ const lastCheck = state.lastNotifyCheckAt || now - NOTIFY_INTERVAL_MS;
93
+ const minutesSinceLastCheck = Math.max(5, Math.ceil((now - lastCheck) / 60_000));
94
+ const result = (await client.get(`/v1/insights?last=${minutesSinceLastCheck}m&limit=10`));
95
+ state.lastNotifyCheckAt = now;
96
+ saveState(state);
97
+ if (result.insights && result.insights.length > 0) {
98
+ const summary = result.insights
99
+ .map((i) => `- [${i.type || "update"}${i.importance ? ` | ${i.importance}` : ""}] ${i.content || JSON.stringify(i)}`)
100
+ .join("\n");
101
+ await pushChannelEvent(server, summary, {
102
+ type: "proactive_notification",
103
+ count: String(result.insights.length),
104
+ });
105
+ }
106
+ }
107
+ catch (err) {
108
+ console.error("[nex-channel] notification check failed:", err);
109
+ }
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Entry point
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Start the Nex notification channel. Call this after the MCP server
116
+ * has connected to its transport.
117
+ *
118
+ * @param server The underlying MCP `Server` instance (mcpServer.server)
119
+ * @param client An authenticated NexApiClient
120
+ */
121
+ export function startChannel(server, client) {
122
+ if (!client.isAuthenticated)
123
+ return;
124
+ const state = loadState();
125
+ // --- Daily digest ---
126
+ // Initial check after a short delay to let the session settle
127
+ setTimeout(() => checkDigest(server, client, state), INITIAL_DIGEST_DELAY_MS);
128
+ // Then check hourly (only fires digest if 24h have passed)
129
+ setInterval(() => checkDigest(server, client, state), DIGEST_CHECK_INTERVAL_MS);
130
+ // --- Proactive notifications ---
131
+ setTimeout(() => checkNotifications(server, client, state), INITIAL_NOTIFY_DELAY_MS);
132
+ setInterval(() => checkNotifications(server, client, state), NOTIFY_INTERVAL_MS);
133
+ console.error("[nex-channel] started (digest: 24h, notifications: 5m)");
134
+ }
135
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/mcp/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare class NexApiError extends Error {
2
+ status: number;
3
+ statusText: string;
4
+ body: unknown;
5
+ constructor(status: number, statusText: string, body: unknown);
6
+ }
7
+ export declare class NexApiClient {
8
+ private apiKey;
9
+ constructor(apiKey?: string);
10
+ get isAuthenticated(): boolean;
11
+ setApiKey(key: string): void;
12
+ private requireAuth;
13
+ private request;
14
+ register(email: string, name?: string, companyName?: string, source?: string): Promise<unknown>;
15
+ get(path: string): Promise<unknown>;
16
+ post(path: string, body?: unknown): Promise<unknown>;
17
+ put(path: string, body?: unknown): Promise<unknown>;
18
+ patch(path: string, body?: unknown): Promise<unknown>;
19
+ delete(path: string): Promise<unknown>;
20
+ }
@@ -0,0 +1,130 @@
1
+ import { loadConfig } from "./config.js";
2
+ function getBaseUrl() {
3
+ const config = loadConfig();
4
+ const base = process.env.NEX_API_BASE_URL || config.base_url || config.dev_url || "https://app.nex.ai";
5
+ return `${base.replace(/\/+$/, "")}/api/developers`;
6
+ }
7
+ function getRegisterUrl() {
8
+ const config = loadConfig();
9
+ const base = process.env.NEX_API_BASE_URL || config.base_url || config.dev_url || "https://app.nex.ai";
10
+ return `${base.replace(/\/+$/, "")}/api/v1/agents/register`;
11
+ }
12
+ export class NexApiError extends Error {
13
+ status;
14
+ statusText;
15
+ body;
16
+ constructor(status, statusText, body) {
17
+ super(`Nex API error ${status}: ${statusText}`);
18
+ this.status = status;
19
+ this.statusText = statusText;
20
+ this.body = body;
21
+ this.name = "NexApiError";
22
+ }
23
+ }
24
+ export class NexApiClient {
25
+ apiKey;
26
+ constructor(apiKey) {
27
+ this.apiKey = apiKey;
28
+ }
29
+ get isAuthenticated() {
30
+ return this.apiKey !== undefined && this.apiKey.length > 0;
31
+ }
32
+ setApiKey(key) {
33
+ this.apiKey = key;
34
+ }
35
+ requireAuth() {
36
+ if (!this.isAuthenticated) {
37
+ throw new NexApiError(401, "Not registered", {
38
+ message: "No API key configured. Call the 'register' tool first with your email to get an API key.",
39
+ });
40
+ }
41
+ }
42
+ async request(method, path, body) {
43
+ this.requireAuth();
44
+ const url = `${getBaseUrl()}${path}`;
45
+ const headers = {
46
+ Authorization: `Bearer ${this.apiKey}`,
47
+ };
48
+ if (body !== undefined) {
49
+ headers["Content-Type"] = "application/json";
50
+ }
51
+ const res = await fetch(url, {
52
+ method,
53
+ headers,
54
+ body: body !== undefined ? JSON.stringify(body) : undefined,
55
+ signal: AbortSignal.timeout(120_000),
56
+ });
57
+ if (res.status === 401 || res.status === 403) {
58
+ throw new NexApiError(res.status, res.statusText, {
59
+ message: "API key expired or invalid. Run 'nex register --email <email>' to get a new key.",
60
+ });
61
+ }
62
+ if (!res.ok) {
63
+ let errorBody;
64
+ try {
65
+ errorBody = await res.json();
66
+ }
67
+ catch {
68
+ errorBody = await res.text();
69
+ }
70
+ throw new NexApiError(res.status, res.statusText, errorBody);
71
+ }
72
+ const text = await res.text();
73
+ if (!text)
74
+ return {};
75
+ try {
76
+ return JSON.parse(text);
77
+ }
78
+ catch {
79
+ return { message: text };
80
+ }
81
+ }
82
+ async register(email, name, companyName, source) {
83
+ const body = {
84
+ email,
85
+ source: source ?? "mcp",
86
+ };
87
+ if (name !== undefined)
88
+ body.name = name;
89
+ if (companyName !== undefined)
90
+ body.company_name = companyName;
91
+ const res = await fetch(getRegisterUrl(), {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify(body),
95
+ signal: AbortSignal.timeout(120_000),
96
+ });
97
+ if (!res.ok) {
98
+ let errorBody;
99
+ try {
100
+ errorBody = await res.json();
101
+ }
102
+ catch {
103
+ errorBody = await res.text();
104
+ }
105
+ throw new NexApiError(res.status, res.statusText, errorBody);
106
+ }
107
+ const data = await res.json();
108
+ const apiKey = data.api_key;
109
+ if (typeof apiKey === "string" && apiKey.length > 0) {
110
+ this.apiKey = apiKey;
111
+ }
112
+ return data;
113
+ }
114
+ async get(path) {
115
+ return this.request("GET", path);
116
+ }
117
+ async post(path, body) {
118
+ return this.request("POST", path, body);
119
+ }
120
+ async put(path, body) {
121
+ return this.request("PUT", path, body);
122
+ }
123
+ async patch(path, body) {
124
+ return this.request("PATCH", path, body);
125
+ }
126
+ async delete(path) {
127
+ return this.request("DELETE", path);
128
+ }
129
+ }
130
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ declare const CONFIG_PATH: string;
2
+ interface NexMcpConfig {
3
+ api_key?: string;
4
+ base_url?: string;
5
+ dev_url?: string;
6
+ workspace_id?: string;
7
+ workspace_slug?: string;
8
+ [key: string]: unknown;
9
+ }
10
+ export declare function loadConfig(): NexMcpConfig;
11
+ export declare function saveConfig(config: NexMcpConfig): void;
12
+ export declare function loadApiKey(): string | undefined;
13
+ export declare function persistRegistration(data: Record<string, unknown>): void;
14
+ export { CONFIG_PATH };
@@ -0,0 +1,33 @@
1
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join, dirname } from "node:path";
4
+ const CONFIG_PATH = join(homedir(), ".nex", "config.json");
5
+ export function loadConfig() {
6
+ try {
7
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
8
+ return JSON.parse(raw);
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ export function saveConfig(config) {
15
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
16
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
17
+ }
18
+ export function loadApiKey() {
19
+ return process.env.NEX_API_KEY || loadConfig().api_key || undefined;
20
+ }
21
+ export function persistRegistration(data) {
22
+ const existing = loadConfig();
23
+ if (typeof data.api_key === "string")
24
+ existing.api_key = data.api_key;
25
+ if (typeof data.workspace_id === "string" || typeof data.workspace_id === "number") {
26
+ existing.workspace_id = String(data.workspace_id);
27
+ }
28
+ if (typeof data.workspace_slug === "string")
29
+ existing.workspace_slug = data.workspace_slug;
30
+ saveConfig(existing);
31
+ }
32
+ export { CONFIG_PATH };
33
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/mcp/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAW3D,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU,EAAE,CAAC,OAAO,IAAI,SAAS,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACtE,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnF,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;QAAE,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;IAC3F,UAAU,CAAC,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { NexApiClient } from "./client.js";
2
+ import type { RateLimiter } from "./rate-limiter.js";
3
+ export interface ContextFilesResult {
4
+ ingested: number;
5
+ skipped: number;
6
+ errors: number;
7
+ files: string[];
8
+ }
9
+ export declare function ingestContextFiles(client: NexApiClient, rateLimiter: RateLimiter, cwd: string): Promise<ContextFilesResult>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Ingests Claude Code context files (CLAUDE.md + memory files) into Nex.
3
+ *
4
+ * Reads from both global and project-level locations:
5
+ * - ~/.claude/CLAUDE.md (global instructions)
6
+ * - {cwd}/CLAUDE.md (project instructions)
7
+ * - ~/.claude/projects/{project-key}/memory/*.md (memory files)
8
+ *
9
+ * Uses the file manifest for change detection — unchanged files are skipped.
10
+ */
11
+ import { existsSync, readdirSync, statSync, readFileSync } from "node:fs";
12
+ import { join, extname, basename } from "node:path";
13
+ import { homedir } from "node:os";
14
+ import { readManifest, writeManifest, isChanged, markIngested } from "./file-manifest.js";
15
+ const CLAUDE_DIR = join(homedir(), ".claude");
16
+ const INGEST_TIMEOUT_MS = 10_000;
17
+ const MAX_FILE_SIZE = 100_000;
18
+ function projectKey(cwd) {
19
+ return cwd.replace(/\//g, "-");
20
+ }
21
+ function collectContextFiles(cwd) {
22
+ const files = [];
23
+ const key = projectKey(cwd);
24
+ const globalClaude = join(CLAUDE_DIR, "CLAUDE.md");
25
+ if (existsSync(globalClaude)) {
26
+ files.push({ path: globalClaude, contextTag: "claude-md:global" });
27
+ }
28
+ const projectClaude = join(cwd, "CLAUDE.md");
29
+ if (existsSync(projectClaude)) {
30
+ files.push({ path: projectClaude, contextTag: "claude-md:project" });
31
+ }
32
+ const memoryDir = join(CLAUDE_DIR, "projects", key, "memory");
33
+ if (existsSync(memoryDir)) {
34
+ try {
35
+ const entries = readdirSync(memoryDir, { withFileTypes: true });
36
+ for (const entry of entries) {
37
+ if (!entry.isFile())
38
+ continue;
39
+ if (extname(entry.name).toLowerCase() !== ".md")
40
+ continue;
41
+ const fullPath = join(memoryDir, entry.name);
42
+ const name = basename(entry.name, ".md");
43
+ files.push({ path: fullPath, contextTag: `claude-memory:${name}` });
44
+ }
45
+ }
46
+ catch {
47
+ // memoryDir unreadable — skip
48
+ }
49
+ }
50
+ return files;
51
+ }
52
+ export async function ingestContextFiles(client, rateLimiter, cwd) {
53
+ const result = { ingested: 0, skipped: 0, errors: 0, files: [] };
54
+ const manifest = readManifest();
55
+ const candidates = collectContextFiles(cwd);
56
+ let dirty = false;
57
+ for (const { path, contextTag } of candidates) {
58
+ try {
59
+ const stat = statSync(path);
60
+ if (!isChanged(path, stat, manifest)) {
61
+ result.skipped++;
62
+ continue;
63
+ }
64
+ if (!rateLimiter.canProceed()) {
65
+ process.stderr.write("[nex-context-files] Rate limited — stopping context file ingest\n");
66
+ result.skipped += candidates.length - result.ingested - result.skipped - result.errors;
67
+ break;
68
+ }
69
+ let content = readFileSync(path, "utf-8");
70
+ if (content.length > MAX_FILE_SIZE) {
71
+ content = content.slice(0, MAX_FILE_SIZE) + "\n[...truncated]";
72
+ }
73
+ await client.post("/v1/context/text", { content, context: contextTag });
74
+ rateLimiter.recordRequest();
75
+ markIngested(path, stat, contextTag, manifest);
76
+ result.ingested++;
77
+ result.files.push(contextTag);
78
+ dirty = true;
79
+ }
80
+ catch (err) {
81
+ process.stderr.write(`[nex-context-files] Failed to ingest ${contextTag}: ${err instanceof Error ? err.message : String(err)}\n`);
82
+ result.errors++;
83
+ }
84
+ }
85
+ if (dirty) {
86
+ writeManifest(manifest);
87
+ }
88
+ return result;
89
+ }
90
+ //# sourceMappingURL=context-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-files.js","sourceRoot":"","sources":["../../src/mcp/context-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Persistent file manifest — tracks which files have been ingested
3
+ * using mtime + size as change detection.
4
+ *
5
+ * Stored at ~/.nex/file-scan-manifest.json.
6
+ */
7
+ import { type Stats } from "node:fs";
8
+ export interface FileManifestEntry {
9
+ mtime: number;
10
+ size: number;
11
+ ingestedAt: number;
12
+ context: string;
13
+ }
14
+ export interface FileManifest {
15
+ version: 1;
16
+ lastScanAt?: number;
17
+ files: Record<string, FileManifestEntry>;
18
+ }
19
+ export declare function readManifest(): FileManifest;
20
+ export declare function writeManifest(manifest: FileManifest): void;
21
+ export declare function isChanged(path: string, stat: Stats, manifest: FileManifest): boolean;
22
+ export declare function markIngested(path: string, stat: Stats, context: string, manifest: FileManifest): void;
23
+ /**
24
+ * Record that a full scan completed at this moment.
25
+ */
26
+ export declare function markScanned(manifest: FileManifest): void;
27
+ export declare function isScanFresh(manifest: FileManifest, windowMs?: number): boolean;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Persistent file manifest — tracks which files have been ingested
3
+ * using mtime + size as change detection.
4
+ *
5
+ * Stored at ~/.nex/file-scan-manifest.json.
6
+ */
7
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { homedir } from "node:os";
10
+ const DATA_DIR = join(homedir(), ".nex");
11
+ const MANIFEST_PATH = join(DATA_DIR, "file-scan-manifest.json");
12
+ export function readManifest() {
13
+ try {
14
+ const raw = readFileSync(MANIFEST_PATH, "utf-8");
15
+ const data = JSON.parse(raw);
16
+ if (data && data.version === 1 && data.files) {
17
+ return data;
18
+ }
19
+ return { version: 1, files: {} };
20
+ }
21
+ catch {
22
+ return { version: 1, files: {} };
23
+ }
24
+ }
25
+ export function writeManifest(manifest) {
26
+ try {
27
+ mkdirSync(DATA_DIR, { recursive: true });
28
+ writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2), "utf-8");
29
+ }
30
+ catch {
31
+ // Best-effort — if we can't write, next scan re-ingests
32
+ }
33
+ }
34
+ export function isChanged(path, stat, manifest) {
35
+ const entry = manifest.files[path];
36
+ if (!entry)
37
+ return true;
38
+ return entry.mtime !== stat.mtimeMs || entry.size !== stat.size;
39
+ }
40
+ export function markIngested(path, stat, context, manifest) {
41
+ manifest.files[path] = {
42
+ mtime: stat.mtimeMs,
43
+ size: stat.size,
44
+ ingestedAt: Date.now(),
45
+ context,
46
+ };
47
+ }
48
+ /**
49
+ * Record that a full scan completed at this moment.
50
+ */
51
+ export function markScanned(manifest) {
52
+ manifest.lastScanAt = Date.now();
53
+ }
54
+ /**
55
+ * Check if a scan completed recently (within the given window).
56
+ * Default window: 1 hour.
57
+ */
58
+ const DEFAULT_SCAN_FRESHNESS_MS = 60 * 60 * 1000; // 1 hour
59
+ export function isScanFresh(manifest, windowMs = DEFAULT_SCAN_FRESHNESS_MS) {
60
+ if (!manifest.lastScanAt)
61
+ return false;
62
+ return Date.now() - manifest.lastScanAt < windowMs;
63
+ }
64
+ //# sourceMappingURL=file-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-manifest.js","sourceRoot":"","sources":["../../src/mcp/file-manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AACzC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;AAehE,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAsB;IAClD,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;IAC1D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAW,EAAE,QAAsB;IACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAW,EAAE,OAAe,EAAE,QAAsB;IAC7F,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;QACrB,KAAK,EAAE,IAAI,CAAC,OAAO;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;QACtB,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAsB;IAChD,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AAE3D,MAAM,UAAU,WAAW,CAAC,QAAsB,EAAE,QAAQ,GAAG,yBAAyB;IACtF,IAAI,CAAC,QAAQ,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC;AACrD,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { NexApiClient } from "./client.js";
2
+ import type { RateLimiter } from "./rate-limiter.js";
3
+ export interface ScanConfig {
4
+ enabled: boolean;
5
+ extensions: string[];
6
+ maxFileSize: number;
7
+ maxFilesPerScan: number;
8
+ scanDepth: number;
9
+ ignoreDirs: string[];
10
+ }
11
+ export interface ScanResult {
12
+ scanned: number;
13
+ ingested: number;
14
+ skipped: number;
15
+ errors: number;
16
+ }
17
+ export declare function scanAndIngest(client: NexApiClient, rateLimiter: RateLimiter, cwd: string, config: ScanConfig): Promise<ScanResult>;