@nex-ai/nex 0.1.8 → 0.1.10

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 (59) hide show
  1. package/README.md +5 -1
  2. package/dist/commands/setup.d.ts +2 -1
  3. package/dist/commands/setup.js +55 -10
  4. package/dist/commands/setup.js.map +1 -1
  5. package/dist/lib/installers.d.ts +2 -2
  6. package/dist/lib/installers.js +55 -64
  7. package/dist/lib/installers.js.map +1 -1
  8. package/dist/plugin/auto-capture.d.ts +11 -0
  9. package/dist/plugin/auto-capture.js +135 -0
  10. package/dist/plugin/auto-capture.js.map +1 -0
  11. package/dist/plugin/auto-recall.d.ts +11 -0
  12. package/dist/plugin/auto-recall.js +111 -0
  13. package/dist/plugin/auto-recall.js.map +1 -0
  14. package/dist/plugin/auto-register.d.ts +10 -0
  15. package/dist/plugin/auto-register.js +53 -0
  16. package/dist/plugin/auto-register.js.map +1 -0
  17. package/dist/plugin/auto-scan.d.ts +10 -0
  18. package/dist/plugin/auto-scan.js +49 -0
  19. package/dist/plugin/auto-scan.js.map +1 -0
  20. package/dist/plugin/auto-session-start.d.ts +14 -0
  21. package/dist/plugin/auto-session-start.js +146 -0
  22. package/dist/plugin/auto-session-start.js.map +1 -0
  23. package/dist/plugin/capture-filter.d.ts +21 -0
  24. package/dist/plugin/capture-filter.js +32 -0
  25. package/dist/plugin/capture-filter.js.map +1 -0
  26. package/dist/plugin/config.d.ts +54 -0
  27. package/dist/plugin/config.js +166 -0
  28. package/dist/plugin/config.js.map +1 -0
  29. package/dist/plugin/context-files.d.ts +23 -0
  30. package/dist/plugin/context-files.js +103 -0
  31. package/dist/plugin/context-files.js.map +1 -0
  32. package/dist/plugin/context-format.d.ts +18 -0
  33. package/dist/plugin/context-format.js +34 -0
  34. package/dist/plugin/context-format.js.map +1 -0
  35. package/dist/plugin/file-manifest.d.ts +21 -0
  36. package/dist/plugin/file-manifest.js +48 -0
  37. package/dist/plugin/file-manifest.js.map +1 -0
  38. package/dist/plugin/file-scanner.d.ts +19 -0
  39. package/dist/plugin/file-scanner.js +90 -0
  40. package/dist/plugin/file-scanner.js.map +1 -0
  41. package/dist/plugin/nex-client.d.ts +50 -0
  42. package/dist/plugin/nex-client.js +129 -0
  43. package/dist/plugin/nex-client.js.map +1 -0
  44. package/dist/plugin/rate-limiter.d.ts +26 -0
  45. package/dist/plugin/rate-limiter.js +65 -0
  46. package/dist/plugin/rate-limiter.js.map +1 -0
  47. package/dist/plugin/recall-filter.d.ts +23 -0
  48. package/dist/plugin/recall-filter.js +94 -0
  49. package/dist/plugin/recall-filter.js.map +1 -0
  50. package/dist/plugin/session-store.d.ts +23 -0
  51. package/dist/plugin/session-store.js +74 -0
  52. package/dist/plugin/session-store.js.map +1 -0
  53. package/package.json +2 -1
  54. package/plugin-commands/entities.md +18 -0
  55. package/plugin-commands/integrate.md +34 -0
  56. package/plugin-commands/recall.md +5 -0
  57. package/plugin-commands/register.md +28 -0
  58. package/plugin-commands/remember.md +5 -0
  59. package/plugin-commands/scan.md +8 -0
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Code UserPromptSubmit hook — auto-recall from Nex.
4
+ *
5
+ * Reads the user's prompt from stdin, runs it through the recall filter
6
+ * to decide if recall is needed, queries Nex for relevant context,
7
+ * and outputs { additionalContext: "..." } to inject into the conversation.
8
+ *
9
+ * On ANY error: outputs {} and exits 0 (graceful degradation).
10
+ */
11
+ import { loadConfig, isHookEnabled } from "./config.js";
12
+ import { NexClient } from "./nex-client.js";
13
+ import { formatNexContext } from "./context-format.js";
14
+ import { SessionStore } from "./session-store.js";
15
+ import { shouldRecall, recordRecall } from "./recall-filter.js";
16
+ const sessions = new SessionStore();
17
+ /**
18
+ * Check if this is the first prompt for this session.
19
+ * A session with no stored Nex session ID is considered "first prompt"
20
+ * (SessionStart may have already set one, but that's fine — it means
21
+ * baseline context was loaded, and first user prompt still gets recall).
22
+ */
23
+ function isFirstPrompt(sessionKey) {
24
+ if (!sessionKey)
25
+ return true;
26
+ return !sessions.get(sessionKey);
27
+ }
28
+ async function main() {
29
+ try {
30
+ // Read stdin
31
+ const chunks = [];
32
+ for await (const chunk of process.stdin) {
33
+ chunks.push(chunk);
34
+ }
35
+ const raw = Buffer.concat(chunks).toString("utf-8");
36
+ let input;
37
+ try {
38
+ input = JSON.parse(raw);
39
+ }
40
+ catch {
41
+ process.stderr.write("[nex-recall] Failed to parse stdin JSON\n");
42
+ process.stdout.write("{}");
43
+ return;
44
+ }
45
+ // Check .nex.toml kill switch
46
+ if (!isHookEnabled("recall")) {
47
+ process.stdout.write("{}");
48
+ return;
49
+ }
50
+ const prompt = input.prompt?.trim();
51
+ if (!prompt || prompt.length < 5) {
52
+ process.stdout.write("{}");
53
+ return;
54
+ }
55
+ // Skip slash commands
56
+ if (prompt.startsWith("/")) {
57
+ process.stdout.write("{}");
58
+ return;
59
+ }
60
+ // --- Recall filter: decide if this prompt needs memory recall ---
61
+ const decision = shouldRecall(prompt, isFirstPrompt(input.session_id));
62
+ if (!decision.shouldRecall) {
63
+ process.stdout.write("{}");
64
+ return;
65
+ }
66
+ let cfg;
67
+ try {
68
+ cfg = loadConfig();
69
+ }
70
+ catch (err) {
71
+ process.stderr.write(`[nex-recall] Config error: ${err instanceof Error ? err.message : String(err)}\n`);
72
+ process.stdout.write("{}");
73
+ return;
74
+ }
75
+ const client = new NexClient(cfg.apiKey, cfg.baseUrl);
76
+ // Resolve session ID for multi-turn continuity
77
+ const sessionKey = input.session_id;
78
+ const nexSessionId = sessionKey ? sessions.get(sessionKey) : undefined;
79
+ const result = await client.ask(prompt, nexSessionId, 10_000);
80
+ if (!result.answer) {
81
+ process.stdout.write("{}");
82
+ return;
83
+ }
84
+ // Store session ID for future turns
85
+ if (result.session_id && sessionKey) {
86
+ sessions.set(sessionKey, result.session_id);
87
+ }
88
+ // Record successful recall for debounce
89
+ recordRecall(result.session_id);
90
+ const entityCount = result.entity_references?.length ?? 0;
91
+ const context = formatNexContext({
92
+ answer: result.answer,
93
+ entityCount,
94
+ sessionId: result.session_id,
95
+ });
96
+ // Use hookSpecificOutput for discrete context injection (not shown in transcript)
97
+ const output = JSON.stringify({
98
+ hookSpecificOutput: {
99
+ hookEventName: "UserPromptSubmit",
100
+ additionalContext: context,
101
+ },
102
+ });
103
+ process.stdout.write(output);
104
+ }
105
+ catch (err) {
106
+ process.stderr.write(`[nex-recall] Unexpected error: ${err instanceof Error ? err.message : String(err)}\n`);
107
+ process.stdout.write("{}");
108
+ }
109
+ }
110
+ main().then(() => process.exit(0)).catch(() => process.exit(0));
111
+ //# sourceMappingURL=auto-recall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-recall.js","sourceRoot":"","sources":["../../src/plugin/auto-recall.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;AAOpC;;;;;GAKG;AACH,SAAS,aAAa,CAAC,UAA8B;IACnD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,aAAa;QACb,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;QAC/B,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEpD,IAAI,KAAgB,CAAC;QACrB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAClE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,8BAA8B;QAC9B,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,sBAAsB;QACtB,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,mEAAmE;QACnE,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,IAAI,GAAG,CAAC;QACR,IAAI,CAAC;YACH,GAAG,GAAG,UAAU,EAAE,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA8B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACnF,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAEtD,+CAA+C;QAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEvE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,oCAAoC;QACpC,IAAI,MAAM,CAAC,UAAU,IAAI,UAAU,EAAE,CAAC;YACpC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,CAAC;QAED,wCAAwC;QACxC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEhC,MAAM,WAAW,GAAG,MAAM,CAAC,iBAAiB,EAAE,MAAM,IAAI,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,gBAAgB,CAAC;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,WAAW;YACX,SAAS,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAC,CAAC;QAEH,kFAAkF;QAClF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC;YAC5B,kBAAkB,EAAE;gBAClB,aAAa,EAAE,kBAAkB;gBACjC,iBAAiB,EAAE,OAAO;aAC3B;SACF,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kCAAkC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACvF,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Registration entry point — creates a Nex account and persists the API key.
4
+ *
5
+ * Usage: node dist/auto-register.js <email> [name] [company]
6
+ *
7
+ * On success: prints API key and saves to ~/.nex-mcp.json (shared with MCP server).
8
+ * If already registered (API key exists): prints status and exits.
9
+ */
10
+ export {};
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Registration entry point — creates a Nex account and persists the API key.
4
+ *
5
+ * Usage: node dist/auto-register.js <email> [name] [company]
6
+ *
7
+ * On success: prints API key and saves to ~/.nex-mcp.json (shared with MCP server).
8
+ * If already registered (API key exists): prints status and exits.
9
+ */
10
+ import { loadMcpConfig, persistRegistration, loadBaseUrl, MCP_CONFIG_PATH } from "./config.js";
11
+ import { NexClient } from "./nex-client.js";
12
+ async function main() {
13
+ const email = process.argv[2];
14
+ const name = process.argv[3];
15
+ const company = process.argv[4];
16
+ if (!email) {
17
+ console.error("Usage: auto-register.js <email> [name] [company]");
18
+ process.exit(1);
19
+ }
20
+ // Check if already registered
21
+ const existing = loadMcpConfig();
22
+ if (existing.api_key) {
23
+ console.log("Already registered.");
24
+ console.log(` API key: ${existing.api_key.slice(0, 12)}...`);
25
+ console.log(` Config: ${MCP_CONFIG_PATH}`);
26
+ console.log("\nTo re-register, delete ~/.nex-mcp.json first.");
27
+ return;
28
+ }
29
+ const baseUrl = loadBaseUrl();
30
+ console.log(`Registering ${email} at ${baseUrl} ...`);
31
+ try {
32
+ const result = await NexClient.register(baseUrl, email, name, company);
33
+ if (!result.api_key) {
34
+ console.error("Registration succeeded but no API key returned.");
35
+ console.error("Response:", JSON.stringify(result, null, 2));
36
+ process.exit(1);
37
+ }
38
+ // Persist to shared config
39
+ persistRegistration(result);
40
+ console.log("Registration successful!");
41
+ console.log(` API key: ${result.api_key.slice(0, 12)}...`);
42
+ if (result.workspace_slug)
43
+ console.log(` Workspace: ${result.workspace_slug}`);
44
+ console.log(` Saved to: ${MCP_CONFIG_PATH}`);
45
+ console.log("\nAll Nex memory features are now active. No restart needed.");
46
+ }
47
+ catch (err) {
48
+ console.error(`Registration failed: ${err instanceof Error ? err.message : String(err)}`);
49
+ process.exit(1);
50
+ }
51
+ }
52
+ main();
53
+ //# sourceMappingURL=auto-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-register.js","sourceRoot":"","sources":["../../src/plugin/auto-register.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,aAAa,eAAe,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO,OAAO,MAAM,CAAC,CAAC;IAEtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAEvE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,2BAA2B;QAC3B,mBAAmB,CAAC,MAAiC,CAAC,CAAC;QAEvD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5D,IAAI,MAAM,CAAC,cAAc;YAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAChF,OAAO,CAAC,GAAG,CAAC,eAAe,eAAe,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Standalone entry point for the /scan slash command.
4
+ *
5
+ * Reads optional directory from argv[2] or stdin, scans for project files,
6
+ * and ingests changed ones into Nex. Prints results to stdout.
7
+ *
8
+ * Usage: node dist/auto-scan.js [directory]
9
+ */
10
+ export {};
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Standalone entry point for the /scan slash command.
4
+ *
5
+ * Reads optional directory from argv[2] or stdin, scans for project files,
6
+ * and ingests changed ones into Nex. Prints results to stdout.
7
+ *
8
+ * Usage: node dist/auto-scan.js [directory]
9
+ */
10
+ import { loadConfig, loadScanConfig } from "./config.js";
11
+ import { NexClient } from "./nex-client.js";
12
+ import { RateLimiter } from "./rate-limiter.js";
13
+ import { scanAndIngest } from "./file-scanner.js";
14
+ async function main() {
15
+ try {
16
+ // Determine target directory: argv[2] > cwd
17
+ const targetDir = process.argv[2] || process.cwd();
18
+ let cfg;
19
+ try {
20
+ cfg = loadConfig();
21
+ }
22
+ catch (err) {
23
+ console.error(`Config error: ${err instanceof Error ? err.message : String(err)}`);
24
+ process.exit(1);
25
+ }
26
+ const scanConfig = loadScanConfig();
27
+ if (!scanConfig.enabled) {
28
+ console.log("File scanning is disabled (NEX_SCAN_ENABLED=false).");
29
+ return;
30
+ }
31
+ const client = new NexClient(cfg.apiKey, cfg.baseUrl);
32
+ const rateLimiter = new RateLimiter();
33
+ console.log(`Scanning ${targetDir} ...`);
34
+ const result = await scanAndIngest(client, rateLimiter, targetDir, scanConfig);
35
+ console.log(`Scan complete:`);
36
+ console.log(` Scanned: ${result.scanned} files`);
37
+ console.log(` Ingested: ${result.ingested} files`);
38
+ console.log(` Skipped: ${result.skipped} files (unchanged)`);
39
+ if (result.errors > 0) {
40
+ console.log(` Errors: ${result.errors}`);
41
+ }
42
+ }
43
+ catch (err) {
44
+ console.error(`Scan failed: ${err instanceof Error ? err.message : String(err)}`);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ main();
49
+ //# sourceMappingURL=auto-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-scan.js","sourceRoot":"","sources":["../../src/plugin/auto-scan.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,4CAA4C;QAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAEnD,IAAI,GAAG,CAAC;QACR,IAAI,CAAC;YACH,GAAG,GAAG,UAAU,EAAE,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QAEtC,OAAO,CAAC,GAAG,CAAC,YAAY,SAAS,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAE/E,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,QAAQ,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,QAAQ,QAAQ,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,gBAAgB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Code SessionStart hook — bulk context load from Nex + file scan.
4
+ *
5
+ * Fires once when a new Claude Code session begins. Queries Nex for
6
+ * a baseline context summary and injects it so the agent "already knows"
7
+ * relevant business context from the first message.
8
+ *
9
+ * On startup/clear: also scans project files and ingests changed ones.
10
+ * On compact/resume: skips file scan (files already ingested, just re-query summary).
11
+ *
12
+ * On ANY error: outputs {} and exits 0 (graceful degradation).
13
+ */
14
+ export {};
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Code SessionStart hook — bulk context load from Nex + file scan.
4
+ *
5
+ * Fires once when a new Claude Code session begins. Queries Nex for
6
+ * a baseline context summary and injects it so the agent "already knows"
7
+ * relevant business context from the first message.
8
+ *
9
+ * On startup/clear: also scans project files and ingests changed ones.
10
+ * On compact/resume: skips file scan (files already ingested, just re-query summary).
11
+ *
12
+ * On ANY error: outputs {} and exits 0 (graceful degradation).
13
+ */
14
+ import { loadConfig, loadScanConfig, ConfigError, isHookEnabled } from "./config.js";
15
+ import { NexClient } from "./nex-client.js";
16
+ import { RateLimiter } from "./rate-limiter.js";
17
+ import { formatNexContext } from "./context-format.js";
18
+ import { SessionStore } from "./session-store.js";
19
+ import { recordRecall } from "./recall-filter.js";
20
+ import { scanAndIngest } from "./file-scanner.js";
21
+ import { ingestContextFiles } from "./context-files.js";
22
+ import { fileURLToPath } from "node:url";
23
+ import { dirname, join } from "node:path";
24
+ const sessions = new SessionStore();
25
+ const SESSION_START_QUERY = "Summarize the key active context, recent interactions, and important updates for this user.";
26
+ async function main() {
27
+ try {
28
+ // Read stdin
29
+ const chunks = [];
30
+ for await (const chunk of process.stdin) {
31
+ chunks.push(chunk);
32
+ }
33
+ const raw = Buffer.concat(chunks).toString("utf-8");
34
+ // Check .nex.toml kill switch
35
+ if (!isHookEnabled("session_start")) {
36
+ process.stdout.write("{}");
37
+ return;
38
+ }
39
+ let input = {};
40
+ try {
41
+ input = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ process.stderr.write("[nex-session-start] Failed to parse stdin JSON, continuing with defaults\n");
45
+ }
46
+ let cfg;
47
+ try {
48
+ cfg = loadConfig();
49
+ }
50
+ catch (err) {
51
+ if (err instanceof ConfigError) {
52
+ // No API key — inject registration prompt so Claude handles it
53
+ const pluginDir = dirname(fileURLToPath(import.meta.url));
54
+ const registerScript = join(pluginDir, "auto-register.js");
55
+ const registrationPrompt = [
56
+ "[Nex Memory Plugin] No API key configured. Registration is required to enable persistent memory.",
57
+ "",
58
+ "IMPORTANT: Before proceeding with the user's request, you must set up Nex memory:",
59
+ "1. Ask the user for their email address to register for a Nex account",
60
+ "2. Run the registration command: node " + registerScript + " <email> [name] [company]",
61
+ "3. Once registration succeeds, the API key is saved automatically — all memory features activate immediately",
62
+ "",
63
+ "Keep it brief — just ask for the email and register. No restart needed after registration.",
64
+ ].join("\n");
65
+ const output = JSON.stringify({
66
+ hookSpecificOutput: {
67
+ hookEventName: "SessionStart",
68
+ additionalContext: registrationPrompt,
69
+ },
70
+ });
71
+ process.stdout.write(output);
72
+ return;
73
+ }
74
+ process.stderr.write(`[nex-session-start] Config error: ${err instanceof Error ? err.message : String(err)}\n`);
75
+ process.stdout.write("{}");
76
+ return;
77
+ }
78
+ const client = new NexClient(cfg.apiKey, cfg.baseUrl);
79
+ const contextParts = [];
80
+ // --- File scan on startup or clear ---
81
+ const source = input.source ?? "startup";
82
+ const shouldScan = source === "startup" || source === "clear";
83
+ if (shouldScan) {
84
+ const rateLimiter = new RateLimiter();
85
+ const cwd = process.cwd();
86
+ // --- Ingest CLAUDE.md + memory files (highest priority) ---
87
+ try {
88
+ const ctxResult = await ingestContextFiles(client, rateLimiter, cwd);
89
+ if (ctxResult.ingested > 0) {
90
+ contextParts.push(`[Context files: ${ctxResult.ingested} ingested (${ctxResult.files.join(", ")})]`);
91
+ }
92
+ }
93
+ catch (err) {
94
+ process.stderr.write(`[nex-session-start] Context files error: ${err instanceof Error ? err.message : String(err)}\n`);
95
+ }
96
+ // --- Project file scan ---
97
+ try {
98
+ const scanConfig = loadScanConfig();
99
+ if (scanConfig.enabled) {
100
+ const scanResult = await scanAndIngest(client, rateLimiter, cwd, scanConfig);
101
+ if (scanResult.ingested > 0) {
102
+ contextParts.push(`[File scan: ${scanResult.ingested} file${scanResult.ingested === 1 ? "" : "s"} ingested, ${scanResult.scanned} scanned]`);
103
+ }
104
+ }
105
+ }
106
+ catch (err) {
107
+ process.stderr.write(`[nex-session-start] File scan error: ${err instanceof Error ? err.message : String(err)}\n`);
108
+ }
109
+ }
110
+ // --- Nex context query ---
111
+ const result = await client.ask(SESSION_START_QUERY, undefined, 10_000);
112
+ if (!result.answer && contextParts.length === 0) {
113
+ process.stdout.write("{}");
114
+ return;
115
+ }
116
+ // Store session mapping
117
+ if (result.session_id && input.session_id) {
118
+ sessions.set(input.session_id, result.session_id);
119
+ }
120
+ // Record this as a successful recall for debounce
121
+ recordRecall(result.session_id);
122
+ const entityCount = result.entity_references?.length ?? 0;
123
+ const context = formatNexContext({
124
+ answer: result.answer,
125
+ entityCount,
126
+ sessionId: result.session_id,
127
+ });
128
+ // Append scan summary if any
129
+ const fullContext = contextParts.length > 0
130
+ ? `${context}\n${contextParts.join("\n")}`
131
+ : context;
132
+ const output = JSON.stringify({
133
+ hookSpecificOutput: {
134
+ hookEventName: "SessionStart",
135
+ additionalContext: fullContext,
136
+ },
137
+ });
138
+ process.stdout.write(output);
139
+ }
140
+ catch (err) {
141
+ process.stderr.write(`[nex-session-start] Unexpected error: ${err instanceof Error ? err.message : String(err)}\n`);
142
+ process.stdout.write("{}");
143
+ }
144
+ }
145
+ main().then(() => process.exit(0)).catch(() => process.exit(0));
146
+ //# sourceMappingURL=auto-session-start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-session-start.js","sourceRoot":"","sources":["../../src/plugin/auto-session-start.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Capture filtering for Claude Code — decides what content to send to Nex.
3
+ * Adapted from openclaw-plugin for plain string input.
4
+ */
5
+ export interface CaptureResult {
6
+ text: string;
7
+ skipped: false;
8
+ }
9
+ export interface CaptureSkip {
10
+ reason: string;
11
+ skipped: true;
12
+ }
13
+ /**
14
+ * Filter text for capture eligibility.
15
+ * Works with plain strings (Claude Code messages).
16
+ *
17
+ * Note: Deduplication is handled server-side by the Nex extraction pipeline.
18
+ * Since hooks are short-lived processes (new process per invocation), in-memory
19
+ * dedup caches would be empty on each run and provide no value.
20
+ */
21
+ export declare function captureFilter(text: string): CaptureResult | CaptureSkip;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Capture filtering for Claude Code — decides what content to send to Nex.
3
+ * Adapted from openclaw-plugin for plain string input.
4
+ */
5
+ import { stripNexContext } from "./context-format.js";
6
+ const MIN_LENGTH = 20;
7
+ const MAX_LENGTH = 50_000;
8
+ /**
9
+ * Filter text for capture eligibility.
10
+ * Works with plain strings (Claude Code messages).
11
+ *
12
+ * Note: Deduplication is handled server-side by the Nex extraction pipeline.
13
+ * Since hooks are short-lived processes (new process per invocation), in-memory
14
+ * dedup caches would be empty on each run and provide no value.
15
+ */
16
+ export function captureFilter(text) {
17
+ if (!text || text.trim().length === 0) {
18
+ return { skipped: true, reason: "empty text" };
19
+ }
20
+ // Strip injected context blocks (prevent feedback loop)
21
+ const cleaned = stripNexContext(text);
22
+ // Skip too short
23
+ if (cleaned.length < MIN_LENGTH) {
24
+ return { skipped: true, reason: `too short (${cleaned.length} chars)` };
25
+ }
26
+ // Skip too long
27
+ if (cleaned.length > MAX_LENGTH) {
28
+ return { skipped: true, reason: `too long (${cleaned.length} chars)` };
29
+ }
30
+ return { skipped: false, text: cleaned };
31
+ }
32
+ //# sourceMappingURL=capture-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-filter.js","sourceRoot":"","sources":["../../src/plugin/capture-filter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,UAAU,GAAG,MAAM,CAAC;AAY1B;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACjD,CAAC;IAED,wDAAwD;IACxD,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEtC,iBAAiB;IACjB,IAAI,OAAO,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;QAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,OAAO,CAAC,MAAM,SAAS,EAAE,CAAC;IAC1E,CAAC;IAED,gBAAgB;IAChB,IAAI,OAAO,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;QAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,OAAO,CAAC,MAAM,SAAS,EAAE,CAAC;IACzE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Plugin configuration — reads from environment variables,
3
+ * with fallback to ~/.nex-mcp.json (shared with MCP server).
4
+ */
5
+ export interface NexConfig {
6
+ apiKey: string;
7
+ baseUrl: string;
8
+ }
9
+ export interface ScanConfig {
10
+ extensions: string[];
11
+ maxFileSize: number;
12
+ maxFilesPerScan: number;
13
+ scanDepth: number;
14
+ ignoreDirs: string[];
15
+ enabled: boolean;
16
+ }
17
+ export declare class ConfigError extends Error {
18
+ constructor(message: string);
19
+ }
20
+ /** Shared config file with MCP server — stores registration data. */
21
+ declare const MCP_CONFIG_PATH: string;
22
+ export { MCP_CONFIG_PATH };
23
+ interface McpConfig {
24
+ api_key?: string;
25
+ base_url?: string;
26
+ workspace_id?: string;
27
+ workspace_slug?: string;
28
+ }
29
+ /** Read ~/.nex-mcp.json (shared with MCP server registration). */
30
+ export declare function loadMcpConfig(): McpConfig;
31
+ /** Write registration data to ~/.nex-mcp.json. */
32
+ export declare function persistRegistration(data: Record<string, unknown>): void;
33
+ /**
34
+ * Load config from environment variables, with fallback to ~/.nex-mcp.json.
35
+ *
36
+ * Priority: NEX_API_KEY env > ~/.nex-mcp.json api_key
37
+ * If neither is set, throws ConfigError with registration instructions.
38
+ */
39
+ export declare function loadConfig(): NexConfig;
40
+ /**
41
+ * Load base URL without requiring an API key.
42
+ * Used for registration (which doesn't need auth).
43
+ */
44
+ export declare function loadBaseUrl(): string;
45
+ /**
46
+ * Check if a specific hook is enabled in .nex.toml.
47
+ * Returns true by default (hooks are opt-out).
48
+ */
49
+ export declare function isHookEnabled(hookName: "recall" | "capture" | "session_start"): boolean;
50
+ /**
51
+ * Load scan config from NEX_SCAN_* environment variables.
52
+ * All fields have sensible defaults; NEX_SCAN_ENABLED=false is the kill switch.
53
+ */
54
+ export declare function loadScanConfig(): ScanConfig;