@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.
- package/README.md +5 -1
- package/dist/commands/setup.d.ts +2 -1
- package/dist/commands/setup.js +55 -10
- package/dist/commands/setup.js.map +1 -1
- package/dist/lib/installers.d.ts +2 -2
- package/dist/lib/installers.js +55 -64
- package/dist/lib/installers.js.map +1 -1
- package/dist/plugin/auto-capture.d.ts +11 -0
- package/dist/plugin/auto-capture.js +135 -0
- package/dist/plugin/auto-capture.js.map +1 -0
- package/dist/plugin/auto-recall.d.ts +11 -0
- package/dist/plugin/auto-recall.js +111 -0
- package/dist/plugin/auto-recall.js.map +1 -0
- package/dist/plugin/auto-register.d.ts +10 -0
- package/dist/plugin/auto-register.js +53 -0
- package/dist/plugin/auto-register.js.map +1 -0
- package/dist/plugin/auto-scan.d.ts +10 -0
- package/dist/plugin/auto-scan.js +49 -0
- package/dist/plugin/auto-scan.js.map +1 -0
- package/dist/plugin/auto-session-start.d.ts +14 -0
- package/dist/plugin/auto-session-start.js +146 -0
- package/dist/plugin/auto-session-start.js.map +1 -0
- package/dist/plugin/capture-filter.d.ts +21 -0
- package/dist/plugin/capture-filter.js +32 -0
- package/dist/plugin/capture-filter.js.map +1 -0
- package/dist/plugin/config.d.ts +54 -0
- package/dist/plugin/config.js +166 -0
- package/dist/plugin/config.js.map +1 -0
- package/dist/plugin/context-files.d.ts +23 -0
- package/dist/plugin/context-files.js +103 -0
- package/dist/plugin/context-files.js.map +1 -0
- package/dist/plugin/context-format.d.ts +18 -0
- package/dist/plugin/context-format.js +34 -0
- package/dist/plugin/context-format.js.map +1 -0
- package/dist/plugin/file-manifest.d.ts +21 -0
- package/dist/plugin/file-manifest.js +48 -0
- package/dist/plugin/file-manifest.js.map +1 -0
- package/dist/plugin/file-scanner.d.ts +19 -0
- package/dist/plugin/file-scanner.js +90 -0
- package/dist/plugin/file-scanner.js.map +1 -0
- package/dist/plugin/nex-client.d.ts +50 -0
- package/dist/plugin/nex-client.js +129 -0
- package/dist/plugin/nex-client.js.map +1 -0
- package/dist/plugin/rate-limiter.d.ts +26 -0
- package/dist/plugin/rate-limiter.js +65 -0
- package/dist/plugin/rate-limiter.js.map +1 -0
- package/dist/plugin/recall-filter.d.ts +23 -0
- package/dist/plugin/recall-filter.js +94 -0
- package/dist/plugin/recall-filter.js.map +1 -0
- package/dist/plugin/session-store.d.ts +23 -0
- package/dist/plugin/session-store.js +74 -0
- package/dist/plugin/session-store.js.map +1 -0
- package/package.json +2 -1
- package/plugin-commands/entities.md +18 -0
- package/plugin-commands/integrate.md +34 -0
- package/plugin-commands/recall.md +5 -0
- package/plugin-commands/register.md +28 -0
- package/plugin-commands/remember.md +5 -0
- package/plugin-commands/scan.md +8 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the Nex Developer API.
|
|
3
|
+
* Adapted from openclaw-plugin — uses native fetch with timeout.
|
|
4
|
+
*/
|
|
5
|
+
// --- Error types ---
|
|
6
|
+
export class NexAuthError extends Error {
|
|
7
|
+
constructor(message = "Invalid or missing API key") {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "NexAuthError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class NexRateLimitError extends Error {
|
|
13
|
+
retryAfterMs;
|
|
14
|
+
constructor(retryAfterMs = 60_000) {
|
|
15
|
+
super(`Rate limited — retry after ${retryAfterMs}ms`);
|
|
16
|
+
this.name = "NexRateLimitError";
|
|
17
|
+
this.retryAfterMs = retryAfterMs;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class NexServerError extends Error {
|
|
21
|
+
status;
|
|
22
|
+
constructor(status, body) {
|
|
23
|
+
super(`Nex API error ${status}${body ? `: ${body}` : ""}`);
|
|
24
|
+
this.name = "NexServerError";
|
|
25
|
+
this.status = status;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// --- Client ---
|
|
29
|
+
export class NexClient {
|
|
30
|
+
apiKey;
|
|
31
|
+
baseUrl;
|
|
32
|
+
constructor(apiKey, baseUrl) {
|
|
33
|
+
this.apiKey = apiKey;
|
|
34
|
+
this.baseUrl = baseUrl;
|
|
35
|
+
}
|
|
36
|
+
async request(method, path, body, timeoutMs = 10_000) {
|
|
37
|
+
const url = `${this.baseUrl}/api/developers${path}`;
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
40
|
+
try {
|
|
41
|
+
const headers = {
|
|
42
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
43
|
+
};
|
|
44
|
+
if (body !== undefined) {
|
|
45
|
+
headers["Content-Type"] = "application/json";
|
|
46
|
+
}
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
method,
|
|
49
|
+
headers,
|
|
50
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
});
|
|
53
|
+
if (res.status === 401 || res.status === 403) {
|
|
54
|
+
throw new NexAuthError();
|
|
55
|
+
}
|
|
56
|
+
if (res.status === 429) {
|
|
57
|
+
const retryAfter = res.headers.get("retry-after");
|
|
58
|
+
const ms = retryAfter ? parseInt(retryAfter, 10) * 1000 : 60_000;
|
|
59
|
+
throw new NexRateLimitError(ms);
|
|
60
|
+
}
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
let errorBody;
|
|
63
|
+
try {
|
|
64
|
+
errorBody = await res.text();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// ignore
|
|
68
|
+
}
|
|
69
|
+
throw new NexServerError(res.status, errorBody);
|
|
70
|
+
}
|
|
71
|
+
const text = await res.text();
|
|
72
|
+
if (!text || !text.trim())
|
|
73
|
+
return {};
|
|
74
|
+
return JSON.parse(text);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Register a new account and get an API key.
|
|
82
|
+
* Does NOT require an existing API key — uses the public registration endpoint.
|
|
83
|
+
*/
|
|
84
|
+
static async register(baseUrl, email, name, companyName) {
|
|
85
|
+
const url = `${baseUrl}/api/v1/agents/register`;
|
|
86
|
+
const body = { email, source: "claude-code" };
|
|
87
|
+
if (name)
|
|
88
|
+
body.name = name;
|
|
89
|
+
if (companyName)
|
|
90
|
+
body.company_name = companyName;
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const timer = setTimeout(() => controller.abort(), 30_000);
|
|
93
|
+
try {
|
|
94
|
+
const res = await fetch(url, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { "Content-Type": "application/json" },
|
|
97
|
+
body: JSON.stringify(body),
|
|
98
|
+
signal: controller.signal,
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
let errorBody;
|
|
102
|
+
try {
|
|
103
|
+
errorBody = await res.text();
|
|
104
|
+
}
|
|
105
|
+
catch { /* ignore */ }
|
|
106
|
+
throw new NexServerError(res.status, errorBody);
|
|
107
|
+
}
|
|
108
|
+
return await res.json();
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Ingest text content into the Nex knowledge graph. */
|
|
115
|
+
async ingest(content, context, timeoutMs) {
|
|
116
|
+
const body = { content };
|
|
117
|
+
if (context)
|
|
118
|
+
body.context = context;
|
|
119
|
+
return this.request("POST", "/v1/context/text", body, timeoutMs ?? 60_000);
|
|
120
|
+
}
|
|
121
|
+
/** Ask a question against the Nex knowledge graph. */
|
|
122
|
+
async ask(query, sessionId, timeoutMs) {
|
|
123
|
+
const body = { query };
|
|
124
|
+
if (sessionId)
|
|
125
|
+
body.session_id = sessionId;
|
|
126
|
+
return this.request("POST", "/v1/context/ask", body, timeoutMs);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=nex-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nex-client.js","sourceRoot":"","sources":["../../src/plugin/nex-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based sliding window rate limiter.
|
|
3
|
+
* Designed for Nex /text endpoint (10 req/min).
|
|
4
|
+
*
|
|
5
|
+
* Since Claude Code hooks are short-lived processes (start, run, exit),
|
|
6
|
+
* in-memory state is lost between invocations. This implementation persists
|
|
7
|
+
* timestamps to a JSON file so rate limits are respected across invocations.
|
|
8
|
+
*/
|
|
9
|
+
export interface RateLimiterConfig {
|
|
10
|
+
maxRequests: number;
|
|
11
|
+
windowMs: number;
|
|
12
|
+
dataDir: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class RateLimiter {
|
|
15
|
+
private config;
|
|
16
|
+
private filePath;
|
|
17
|
+
constructor(config?: Partial<RateLimiterConfig>);
|
|
18
|
+
private readTimestamps;
|
|
19
|
+
private writeTimestamps;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a request can proceed, and if so, record the timestamp.
|
|
22
|
+
* Returns true if the request is allowed, false if rate limited.
|
|
23
|
+
*/
|
|
24
|
+
canProceed(): boolean;
|
|
25
|
+
get pending(): number;
|
|
26
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based sliding window rate limiter.
|
|
3
|
+
* Designed for Nex /text endpoint (10 req/min).
|
|
4
|
+
*
|
|
5
|
+
* Since Claude Code hooks are short-lived processes (start, run, exit),
|
|
6
|
+
* in-memory state is lost between invocations. This implementation persists
|
|
7
|
+
* timestamps to a JSON file so rate limits are respected across invocations.
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
const DEFAULTS = {
|
|
13
|
+
maxRequests: 10,
|
|
14
|
+
windowMs: 60_000,
|
|
15
|
+
dataDir: join(homedir(), ".nex"),
|
|
16
|
+
};
|
|
17
|
+
export class RateLimiter {
|
|
18
|
+
config;
|
|
19
|
+
filePath;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = { ...DEFAULTS, ...config };
|
|
22
|
+
this.filePath = join(this.config.dataDir, "rate-limiter.json");
|
|
23
|
+
mkdirSync(this.config.dataDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
readTimestamps() {
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
28
|
+
const data = JSON.parse(raw);
|
|
29
|
+
if (Array.isArray(data))
|
|
30
|
+
return data;
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
writeTimestamps(timestamps) {
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(this.filePath, JSON.stringify(timestamps), "utf-8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Best-effort — if we can't write, proceed without rate limiting
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if a request can proceed, and if so, record the timestamp.
|
|
47
|
+
* Returns true if the request is allowed, false if rate limited.
|
|
48
|
+
*/
|
|
49
|
+
canProceed() {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const timestamps = this.readTimestamps().filter((t) => now - t < this.config.windowMs);
|
|
52
|
+
if (timestamps.length >= this.config.maxRequests) {
|
|
53
|
+
// Write back pruned timestamps
|
|
54
|
+
this.writeTimestamps(timestamps);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
timestamps.push(now);
|
|
58
|
+
this.writeTimestamps(timestamps);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
get pending() {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/plugin/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAQlC,MAAM,QAAQ,GAAsB;IAClC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,OAAO,WAAW;IACd,MAAM,CAAoB;IAC1B,QAAQ,CAAS;IAEzB,YAAY,MAAmC;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAC/D,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,UAAoB;QAC1C,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,iEAAiE;QACnE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAC7C,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CACtC,CAAC;QAEF,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACjD,+BAA+B;YAC/B,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO;QACT,OAAO,CAAC,CAAC;IACX,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart prompt classifier for selective recall.
|
|
3
|
+
*
|
|
4
|
+
* Determines whether a prompt is likely knowledge-seeking (needs recall)
|
|
5
|
+
* or a pure coding directive (skip recall). Also handles debounce —
|
|
6
|
+
* skips recall if a successful recall happened within the debounce window,
|
|
7
|
+
* unless the current prompt contains question words.
|
|
8
|
+
*
|
|
9
|
+
* Persistence: last-recall timestamp stored in ~/.nex/recall-state.json
|
|
10
|
+
*/
|
|
11
|
+
export interface RecallDecision {
|
|
12
|
+
shouldRecall: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Record that a successful recall just happened.
|
|
17
|
+
* Call this after a non-empty /ask response.
|
|
18
|
+
*/
|
|
19
|
+
export declare function recordRecall(sessionId?: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Determine whether this prompt should trigger a Nex /ask recall.
|
|
22
|
+
*/
|
|
23
|
+
export declare function shouldRecall(prompt: string, isFirstPrompt: boolean): RecallDecision;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart prompt classifier for selective recall.
|
|
3
|
+
*
|
|
4
|
+
* Determines whether a prompt is likely knowledge-seeking (needs recall)
|
|
5
|
+
* or a pure coding directive (skip recall). Also handles debounce —
|
|
6
|
+
* skips recall if a successful recall happened within the debounce window,
|
|
7
|
+
* unless the current prompt contains question words.
|
|
8
|
+
*
|
|
9
|
+
* Persistence: last-recall timestamp stored in ~/.nex/recall-state.json
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
const DATA_DIR = join(homedir(), ".nex");
|
|
15
|
+
const STATE_FILE = join(DATA_DIR, "recall-state.json");
|
|
16
|
+
const DEBOUNCE_MS = 30_000; // 30 seconds
|
|
17
|
+
// --- Question words that signal knowledge-seeking intent ---
|
|
18
|
+
const QUESTION_WORDS = /\b(who|what|when|where|why|how|which|tell|explain|describe|summarize|summarise|list|find|show|get|any|does|did|is|are|was|were|have|has|do|can|could|should|would|will)\b/i;
|
|
19
|
+
// --- Tool/action commands that are pure coding directives ---
|
|
20
|
+
const TOOL_COMMANDS = /^\s*(run|build|test|lint|format|deploy|install|uninstall|start|stop|restart|commit|push|pull|merge|rebase|checkout|fetch|init|fix|refactor|rename|move|delete|remove|add|create|update|upgrade|downgrade|migrate|generate|scaffold|compile|bundle|watch|serve|debug|profile|bench|clean|reset|undo|redo|revert|squash|cherry-pick|tag|release|publish|npm|npx|yarn|pnpm|bun|pip|cargo|go|make|docker|kubectl|terraform|git|gh|cd|ls|cat|grep|find|mkdir|rm|cp|mv|touch|echo|export|source|chmod|chown|curl|wget|ssh|scp)\b/i;
|
|
21
|
+
// --- File reference pattern (paths, extensions) ---
|
|
22
|
+
const FILE_REF = /(?:[\w./\\-]+\.\w{1,10}|src\/|lib\/|dist\/|node_modules\/|\.\/|\.\.\/)/;
|
|
23
|
+
// --- Code-heavy: >50% non-alpha characters (brackets, operators, etc.) ---
|
|
24
|
+
function isCodeHeavy(text) {
|
|
25
|
+
const alpha = text.replace(/[^a-zA-Z]/g, "").length;
|
|
26
|
+
return alpha < text.length * 0.5;
|
|
27
|
+
}
|
|
28
|
+
function readState() {
|
|
29
|
+
try {
|
|
30
|
+
const raw = readFileSync(STATE_FILE, "utf-8");
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { lastRecallAt: 0 };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function writeState(state) {
|
|
38
|
+
try {
|
|
39
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
40
|
+
writeFileSync(STATE_FILE, JSON.stringify(state), "utf-8");
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// best-effort
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Record that a successful recall just happened.
|
|
48
|
+
* Call this after a non-empty /ask response.
|
|
49
|
+
*/
|
|
50
|
+
export function recordRecall(sessionId) {
|
|
51
|
+
writeState({ lastRecallAt: Date.now(), lastRecallSessionId: sessionId });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Determine whether this prompt should trigger a Nex /ask recall.
|
|
55
|
+
*/
|
|
56
|
+
export function shouldRecall(prompt, isFirstPrompt) {
|
|
57
|
+
const trimmed = prompt.trim();
|
|
58
|
+
// Always recall on first prompt of session
|
|
59
|
+
if (isFirstPrompt) {
|
|
60
|
+
return { shouldRecall: true, reason: "first-prompt" };
|
|
61
|
+
}
|
|
62
|
+
// Explicit opt-out: prompt starts with !
|
|
63
|
+
if (trimmed.startsWith("!")) {
|
|
64
|
+
return { shouldRecall: false, reason: "opt-out" };
|
|
65
|
+
}
|
|
66
|
+
// Too short — likely a directive like "yes", "ok", "done"
|
|
67
|
+
if (trimmed.length < 15) {
|
|
68
|
+
return { shouldRecall: false, reason: "too-short" };
|
|
69
|
+
}
|
|
70
|
+
// Has question words — likely knowledge-seeking
|
|
71
|
+
const hasQuestion = QUESTION_WORDS.test(trimmed);
|
|
72
|
+
// Check debounce: skip if recent successful recall, unless question
|
|
73
|
+
if (!hasQuestion) {
|
|
74
|
+
const state = readState();
|
|
75
|
+
if (state.lastRecallAt > 0 && Date.now() - state.lastRecallAt < DEBOUNCE_MS) {
|
|
76
|
+
return { shouldRecall: false, reason: "debounce" };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Tool/action commands — pure coding directives
|
|
80
|
+
if (TOOL_COMMANDS.test(trimmed) && !hasQuestion) {
|
|
81
|
+
return { shouldRecall: false, reason: "tool-command" };
|
|
82
|
+
}
|
|
83
|
+
// Code-heavy with file references and no question words
|
|
84
|
+
if (isCodeHeavy(trimmed) && FILE_REF.test(trimmed) && !hasQuestion) {
|
|
85
|
+
return { shouldRecall: false, reason: "code-prompt" };
|
|
86
|
+
}
|
|
87
|
+
// Has question words — always recall
|
|
88
|
+
if (hasQuestion) {
|
|
89
|
+
return { shouldRecall: true, reason: "question" };
|
|
90
|
+
}
|
|
91
|
+
// Default: recall (err on the side of providing context)
|
|
92
|
+
return { shouldRecall: true, reason: "default" };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=recall-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-filter.js","sourceRoot":"","sources":["../../src/plugin/recall-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based session store mapping Claude Code session IDs to Nex session IDs.
|
|
3
|
+
*
|
|
4
|
+
* Since Claude Code hooks are short-lived processes (start, run, exit),
|
|
5
|
+
* in-memory state is lost between invocations. This implementation persists
|
|
6
|
+
* session mappings to a JSON file so multi-turn continuity works.
|
|
7
|
+
*/
|
|
8
|
+
export interface SessionStoreConfig {
|
|
9
|
+
maxSize: number;
|
|
10
|
+
dataDir: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class SessionStore {
|
|
13
|
+
private filePath;
|
|
14
|
+
private maxSize;
|
|
15
|
+
constructor(config?: Partial<SessionStoreConfig>);
|
|
16
|
+
private readStore;
|
|
17
|
+
private writeStore;
|
|
18
|
+
get(sessionKey: string): string | undefined;
|
|
19
|
+
set(sessionKey: string, nexSessionId: string): void;
|
|
20
|
+
delete(sessionKey: string): boolean;
|
|
21
|
+
get size(): number;
|
|
22
|
+
clear(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based session store mapping Claude Code session IDs to Nex session IDs.
|
|
3
|
+
*
|
|
4
|
+
* Since Claude Code hooks are short-lived processes (start, run, exit),
|
|
5
|
+
* in-memory state is lost between invocations. This implementation persists
|
|
6
|
+
* session mappings to a JSON file so multi-turn continuity works.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
const DEFAULT_MAX = 100;
|
|
12
|
+
const DEFAULT_DATA_DIR = join(homedir(), ".nex");
|
|
13
|
+
export class SessionStore {
|
|
14
|
+
filePath;
|
|
15
|
+
maxSize;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
const dataDir = config?.dataDir ?? DEFAULT_DATA_DIR;
|
|
18
|
+
this.maxSize = config?.maxSize ?? DEFAULT_MAX;
|
|
19
|
+
this.filePath = join(dataDir, "claude-sessions.json");
|
|
20
|
+
mkdirSync(dataDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
readStore() {
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
25
|
+
const data = JSON.parse(raw);
|
|
26
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
writeStore(store) {
|
|
36
|
+
try {
|
|
37
|
+
writeFileSync(this.filePath, JSON.stringify(store), "utf-8");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Best-effort — if we can't write, session continuity degrades gracefully
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
get(sessionKey) {
|
|
44
|
+
const store = this.readStore();
|
|
45
|
+
return store[sessionKey];
|
|
46
|
+
}
|
|
47
|
+
set(sessionKey, nexSessionId) {
|
|
48
|
+
const store = this.readStore();
|
|
49
|
+
store[sessionKey] = nexSessionId;
|
|
50
|
+
// Evict oldest entries if over max size
|
|
51
|
+
const keys = Object.keys(store);
|
|
52
|
+
while (keys.length > this.maxSize) {
|
|
53
|
+
const oldest = keys.shift();
|
|
54
|
+
delete store[oldest];
|
|
55
|
+
}
|
|
56
|
+
this.writeStore(store);
|
|
57
|
+
}
|
|
58
|
+
delete(sessionKey) {
|
|
59
|
+
const store = this.readStore();
|
|
60
|
+
if (sessionKey in store) {
|
|
61
|
+
delete store[sessionKey];
|
|
62
|
+
this.writeStore(store);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
get size() {
|
|
68
|
+
return Object.keys(this.readStore()).length;
|
|
69
|
+
}
|
|
70
|
+
clear() {
|
|
71
|
+
this.writeStore({});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/plugin/session-store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nex-ai/nex",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Nex CLI provides organizational context & memory to AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"plugin-commands",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Search for entities (people, companies, topics) in Nex knowledge base
|
|
3
|
+
---
|
|
4
|
+
Search for entities matching: $ARGUMENTS
|
|
5
|
+
If no query provided, respond: "Usage: /entities <search query>"
|
|
6
|
+
|
|
7
|
+
Use the `mcp__nex__query_context` tool with the query.
|
|
8
|
+
From the response, extract the `entity_references` array.
|
|
9
|
+
If no entities found, respond: "No matching entities found."
|
|
10
|
+
|
|
11
|
+
Format each entity as a bullet list:
|
|
12
|
+
```
|
|
13
|
+
Found {count} entities:
|
|
14
|
+
- {name} ({type_label}) — {mention_count} mentions
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Type labels: type "14" → Person, type "15" → Company, all others → Entity.
|
|
18
|
+
Only show mention count if available (count > 0).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Help the user connect third-party integrations (Gmail, Google Calendar, Outlook, Outlook Calendar, Slack, Attio, HubSpot, Salesforce) to their Nex workspace.
|
|
2
|
+
|
|
3
|
+
## Available Integrations
|
|
4
|
+
|
|
5
|
+
| Type | Provider | Display Name |
|
|
6
|
+
|------|----------|-------------|
|
|
7
|
+
| email | google | Gmail |
|
|
8
|
+
| calendar | google | Google Calendar |
|
|
9
|
+
| email | microsoft | Outlook |
|
|
10
|
+
| calendar | microsoft | Outlook Calendar |
|
|
11
|
+
| messaging | slack | Slack |
|
|
12
|
+
| crm | attio | Attio |
|
|
13
|
+
| crm | hubspot | HubSpot |
|
|
14
|
+
| crm | salesforce | Salesforce |
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
1. First, list current integrations to see what's already connected:
|
|
19
|
+
Use the `list_integrations` MCP tool, or suggest the user runs `nex integrate list` in the terminal.
|
|
20
|
+
|
|
21
|
+
2. To connect a new integration:
|
|
22
|
+
Use the `connect_integration` MCP tool with the `type` and `provider` from the table above.
|
|
23
|
+
This returns an `auth_url` — open it in the user's browser.
|
|
24
|
+
Alternatively, the user can run: `nex integrate connect <type> <provider>`
|
|
25
|
+
|
|
26
|
+
3. Poll for completion:
|
|
27
|
+
Use the `get_connect_status` MCP tool with the `connect_id` every few seconds until `status` is `"connected"`.
|
|
28
|
+
|
|
29
|
+
4. To disconnect:
|
|
30
|
+
Use the `disconnect_integration` MCP tool with the `connection_id`.
|
|
31
|
+
Alternatively: `nex integrate disconnect <id>`
|
|
32
|
+
|
|
33
|
+
5. To check overall setup status:
|
|
34
|
+
Suggest the user runs `nex setup status` to see all platforms, integrations, and config status.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Register for a Nex API key (required for first-time setup)
|
|
3
|
+
---
|
|
4
|
+
Register for a Nex account to get an API key for the memory plugin.
|
|
5
|
+
|
|
6
|
+
If $ARGUMENTS contains an email address, use it directly. Otherwise, ask the user for their email.
|
|
7
|
+
|
|
8
|
+
Run the registration script. Find the script path by running:
|
|
9
|
+
```
|
|
10
|
+
node -e "console.log(require('path').join(require('path').dirname(require.resolve('@nex-ai/nex/package.json')), 'dist', 'plugin', 'auto-register.js'))"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
14
|
+
```
|
|
15
|
+
node <resolved-path> <email> [name] [company]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If the resolve fails, try the global install path:
|
|
19
|
+
```
|
|
20
|
+
node $(npm prefix -g)/lib/node_modules/@nex-ai/nex/dist/plugin/auto-register.js <email> [name] [company]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
After successful registration:
|
|
24
|
+
1. The API key is saved to ~/.nex-mcp.json automatically
|
|
25
|
+
2. All Nex memory features (auto-recall, auto-capture, file scanning) will work immediately
|
|
26
|
+
3. No need to set NEX_API_KEY manually
|
|
27
|
+
|
|
28
|
+
If already registered, inform the user their existing API key is active.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scan and ingest project files into Nex knowledge base
|
|
3
|
+
---
|
|
4
|
+
Scan the current project directory for business documents (md, txt, csv, json, yaml)
|
|
5
|
+
and ingest new or changed files into the Nex knowledge base.
|
|
6
|
+
Use the mcp__nex__context_add_text tool for each file found.
|
|
7
|
+
If $ARGUMENTS contains a path, scan that directory instead.
|
|
8
|
+
Report which files were ingested and which were skipped.
|