@juspay/neurolink 9.84.2 → 9.85.1

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 (70) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +410 -375
  3. package/dist/cli/factories/commandFactory.d.ts +17 -0
  4. package/dist/cli/factories/commandFactory.js +254 -0
  5. package/dist/cli/parser.js +2 -0
  6. package/dist/cli/utils/skillsFlags.d.ts +10 -0
  7. package/dist/cli/utils/skillsFlags.js +22 -0
  8. package/dist/index.d.ts +5 -0
  9. package/dist/index.js +6 -0
  10. package/dist/lib/index.d.ts +5 -0
  11. package/dist/lib/index.js +6 -0
  12. package/dist/lib/neurolink.d.ts +28 -0
  13. package/dist/lib/neurolink.js +117 -0
  14. package/dist/lib/server/routes/agentRoutes.js +156 -1
  15. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +39 -1
  16. package/dist/lib/server/routes/claudeProxyRoutes.js +300 -41
  17. package/dist/lib/server/utils/validation.d.ts +32 -0
  18. package/dist/lib/server/utils/validation.js +18 -0
  19. package/dist/lib/session/globalSessionState.d.ts +10 -1
  20. package/dist/lib/session/globalSessionState.js +18 -0
  21. package/dist/lib/skills/skillMatcher.d.ts +20 -0
  22. package/dist/lib/skills/skillMatcher.js +80 -0
  23. package/dist/lib/skills/skillStoreRedis.d.ts +22 -0
  24. package/dist/lib/skills/skillStoreRedis.js +98 -0
  25. package/dist/lib/skills/skillStoreS3.d.ts +41 -0
  26. package/dist/lib/skills/skillStoreS3.js +233 -0
  27. package/dist/lib/skills/skillStores.d.ts +44 -0
  28. package/dist/lib/skills/skillStores.js +252 -0
  29. package/dist/lib/skills/skillTools.d.ts +19 -0
  30. package/dist/lib/skills/skillTools.js +340 -0
  31. package/dist/lib/skills/skillsManager.d.ts +54 -0
  32. package/dist/lib/skills/skillsManager.js +220 -0
  33. package/dist/lib/types/config.d.ts +10 -0
  34. package/dist/lib/types/generate.d.ts +8 -0
  35. package/dist/lib/types/index.d.ts +1 -0
  36. package/dist/lib/types/index.js +1 -0
  37. package/dist/lib/types/proxy.d.ts +30 -2
  38. package/dist/lib/types/skills.d.ts +296 -0
  39. package/dist/lib/types/skills.js +17 -0
  40. package/dist/lib/types/stream.d.ts +8 -0
  41. package/dist/neurolink.d.ts +28 -0
  42. package/dist/neurolink.js +117 -0
  43. package/dist/server/routes/agentRoutes.js +156 -1
  44. package/dist/server/routes/claudeProxyRoutes.d.ts +39 -1
  45. package/dist/server/routes/claudeProxyRoutes.js +300 -41
  46. package/dist/server/utils/validation.d.ts +32 -0
  47. package/dist/server/utils/validation.js +18 -0
  48. package/dist/session/globalSessionState.d.ts +10 -1
  49. package/dist/session/globalSessionState.js +18 -0
  50. package/dist/skills/skillMatcher.d.ts +20 -0
  51. package/dist/skills/skillMatcher.js +79 -0
  52. package/dist/skills/skillStoreRedis.d.ts +22 -0
  53. package/dist/skills/skillStoreRedis.js +97 -0
  54. package/dist/skills/skillStoreS3.d.ts +41 -0
  55. package/dist/skills/skillStoreS3.js +232 -0
  56. package/dist/skills/skillStores.d.ts +44 -0
  57. package/dist/skills/skillStores.js +251 -0
  58. package/dist/skills/skillTools.d.ts +19 -0
  59. package/dist/skills/skillTools.js +339 -0
  60. package/dist/skills/skillsManager.d.ts +54 -0
  61. package/dist/skills/skillsManager.js +219 -0
  62. package/dist/types/config.d.ts +10 -0
  63. package/dist/types/generate.d.ts +8 -0
  64. package/dist/types/index.d.ts +1 -0
  65. package/dist/types/index.js +1 -0
  66. package/dist/types/proxy.d.ts +30 -2
  67. package/dist/types/skills.d.ts +296 -0
  68. package/dist/types/skills.js +16 -0
  69. package/dist/types/stream.d.ts +8 -0
  70. package/package.json +2 -1
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Pure index-filtering + prompt-index formatting helpers.
3
+ *
4
+ * Matching semantics are ported from curator's battle-tested
5
+ * `searchSkillsFromIndex`: case-insensitive substring match on
6
+ * name/displayName/description, optional tag filter on top, scope filter
7
+ * that always admits global skills, and active-only visibility.
8
+ */
9
+ /** Strip instructions from a full definition to form an index entry. */
10
+ export function toSkillIndexItem(skill) {
11
+ const { instructions: _instructions, ...indexItem } = skill;
12
+ return indexItem;
13
+ }
14
+ /** Filter index entries by query/tag/scope. Active skills only. */
15
+ export function filterSkillIndex(items, query) {
16
+ const lowerQuery = query.query?.toLowerCase();
17
+ const lowerTag = query.tag?.toLowerCase();
18
+ const matched = items.filter((item) => {
19
+ if ((item.status ?? "active") !== "active") {
20
+ return false;
21
+ }
22
+ // Scope filter: global skills always pass; scoped skills require a
23
+ // matching scopeId. When the caller provides no scopeId, scoped skills
24
+ // still pass (curator semantics — unscoped callers see everything).
25
+ if (query.scopeId && item.scope === "scoped") {
26
+ if (!(item.scopeIds ?? []).includes(query.scopeId)) {
27
+ return false;
28
+ }
29
+ }
30
+ if (lowerQuery) {
31
+ const nameMatch = item.name.toLowerCase().includes(lowerQuery) ||
32
+ (item.displayName ?? "").toLowerCase().includes(lowerQuery) ||
33
+ item.description.toLowerCase().includes(lowerQuery);
34
+ if (!nameMatch) {
35
+ return false;
36
+ }
37
+ }
38
+ if (lowerTag) {
39
+ const tagMatch = (item.tags ?? []).some((t) => t.toLowerCase().includes(lowerTag));
40
+ if (!tagMatch) {
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ });
46
+ return query.limit !== undefined ? matched.slice(0, query.limit) : matched;
47
+ }
48
+ /**
49
+ * Render the compact skills index injected into the system prompt.
50
+ * Names + descriptions only — instructions are never included; the model
51
+ * loads them via search_skills. Returns null when nothing is visible so
52
+ * callers can skip injection entirely.
53
+ */
54
+ export function formatSkillsPromptIndex(items, maxItems) {
55
+ if (items.length === 0) {
56
+ return null;
57
+ }
58
+ const visible = items.slice(0, maxItems);
59
+ const lines = visible.map((item) => {
60
+ const label = item.displayName
61
+ ? `${item.name} (${item.displayName})`
62
+ : item.name;
63
+ const tags = item.tags && item.tags.length > 0
64
+ ? ` [tags: ${item.tags.join(", ")}]`
65
+ : "";
66
+ return `- ${label}: ${item.description}${tags}`;
67
+ });
68
+ const truncationNote = items.length > visible.length
69
+ ? `\n(${items.length - visible.length} more skills exist — use search_skills or list_skills to discover them.)`
70
+ : "";
71
+ return ([
72
+ "## Available Skills",
73
+ "The following team-defined skills (SOPs, playbooks, workflows) are available.",
74
+ "Before answering from general knowledge, check whether one applies to the user's request.",
75
+ "To use a skill, call the search_skills tool to load its full instructions, then follow them exactly.",
76
+ "",
77
+ ...lines,
78
+ ].join("\n") + truncationNote);
79
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Redis-backed skill store.
3
+ *
4
+ * Reuses NeuroLink's pooled Redis client (`redis` v5 is already a core
5
+ * dependency, shared with Redis conversation memory). One JSON value per
6
+ * skill under `<keyPrefix><id>`; the index is derived with SCAN + MGET —
7
+ * no separate index document to drift. Skills are persistent (no TTL).
8
+ */
9
+ import type { SkillDefinition, SkillIndexItem, SkillRedisStorageConfig, SkillStore } from "../types/index.js";
10
+ export declare class RedisSkillStore implements SkillStore {
11
+ private readonly keyPrefix;
12
+ private readonly normalizedConfig;
13
+ private clientPromise;
14
+ constructor(config: SkillRedisStorageConfig);
15
+ private getClient;
16
+ private skillKey;
17
+ get(id: string): Promise<SkillDefinition | null>;
18
+ put(skill: SkillDefinition): Promise<void>;
19
+ delete(id: string): Promise<void>;
20
+ index(): Promise<SkillIndexItem[]>;
21
+ private parseSkill;
22
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Redis-backed skill store.
3
+ *
4
+ * Reuses NeuroLink's pooled Redis client (`redis` v5 is already a core
5
+ * dependency, shared with Redis conversation memory). One JSON value per
6
+ * skill under `<keyPrefix><id>`; the index is derived with SCAN + MGET —
7
+ * no separate index document to drift. Skills are persistent (no TTL).
8
+ */
9
+ import { getNormalizedConfig, getPooledRedisClient, scanKeys, } from "../utils/redis.js";
10
+ import { logger } from "../utils/logger.js";
11
+ import { toSkillIndexItem } from "./skillMatcher.js";
12
+ const DEFAULT_KEY_PREFIX = "neurolink:skills:";
13
+ export class RedisSkillStore {
14
+ keyPrefix;
15
+ normalizedConfig;
16
+ clientPromise = null;
17
+ constructor(config) {
18
+ this.keyPrefix = config.keyPrefix ?? DEFAULT_KEY_PREFIX;
19
+ this.normalizedConfig = getNormalizedConfig({
20
+ ...(config.url ? { url: config.url } : {}),
21
+ ...(config.host ? { host: config.host } : {}),
22
+ ...(config.port !== undefined ? { port: config.port } : {}),
23
+ ...(config.username ? { username: config.username } : {}),
24
+ ...(config.password ? { password: config.password } : {}),
25
+ ...(config.db !== undefined ? { db: config.db } : {}),
26
+ keyPrefix: this.keyPrefix,
27
+ });
28
+ }
29
+ getClient() {
30
+ if (!this.clientPromise) {
31
+ this.clientPromise = getPooledRedisClient(this.normalizedConfig).catch((error) => {
32
+ // Reset so a later call can retry after a transient outage.
33
+ this.clientPromise = null;
34
+ throw error;
35
+ });
36
+ }
37
+ return this.clientPromise;
38
+ }
39
+ skillKey(id) {
40
+ return `${this.keyPrefix}${id}`;
41
+ }
42
+ async get(id) {
43
+ const client = await this.getClient();
44
+ const raw = await client.get(this.skillKey(id));
45
+ return raw ? this.parseSkill(String(raw), this.skillKey(id)) : null;
46
+ }
47
+ async put(skill) {
48
+ const client = await this.getClient();
49
+ // Persistent — deliberately no TTL: skills must not expire silently.
50
+ await client.set(this.skillKey(skill.id), JSON.stringify(skill));
51
+ }
52
+ async delete(id) {
53
+ const client = await this.getClient();
54
+ await client.del(this.skillKey(id));
55
+ }
56
+ async index() {
57
+ const client = await this.getClient();
58
+ const keys = await scanKeys(client, `${this.keyPrefix}*`);
59
+ if (keys.length === 0) {
60
+ return [];
61
+ }
62
+ const values = await client.mGet(keys);
63
+ const items = [];
64
+ values.forEach((value, i) => {
65
+ if (!value) {
66
+ return;
67
+ }
68
+ const skill = this.parseSkill(String(value), keys[i]);
69
+ if (skill) {
70
+ items.push(toSkillIndexItem(skill));
71
+ }
72
+ });
73
+ return items;
74
+ }
75
+ parseSkill(raw, key) {
76
+ try {
77
+ const parsed = JSON.parse(raw);
78
+ if (typeof parsed?.id === "string" &&
79
+ typeof parsed?.name === "string" &&
80
+ typeof parsed?.description === "string" &&
81
+ typeof parsed?.instructions === "string") {
82
+ return parsed;
83
+ }
84
+ logger.warn("[SkillStoreRedis] Value is not a valid skill — skipping", {
85
+ key,
86
+ });
87
+ return null;
88
+ }
89
+ catch (error) {
90
+ logger.warn("[SkillStoreRedis] Failed to parse skill value", {
91
+ key,
92
+ error: error instanceof Error ? error.message : String(error),
93
+ });
94
+ return null;
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * S3-backed skill store.
3
+ *
4
+ * Layout (curator-compatible):
5
+ * <prefix>skills/<id>.json — one JSON-serialized SkillDefinition per object
6
+ * <prefix>index.json — { lastUpdated, skills: SkillIndexItem[] }
7
+ *
8
+ * The index document is upserted on every write and rebuilt from a bucket
9
+ * listing when missing or unparsable (self-healing). Concurrent writers can
10
+ * race the index read-modify-write; the rebuild path recovers from any
11
+ * resulting drift, matching curator's production behavior.
12
+ *
13
+ * @aws-sdk/client-s3 is an optional peer — loaded lazily with the same
14
+ * createRequire pattern as memory's @juspay/hippocampus so importing
15
+ * NeuroLink core never requires the AWS SDK. Tests (and hosts with
16
+ * pre-configured clients) inject a SkillS3ObjectOps implementation instead.
17
+ */
18
+ import type { SkillDefinition, SkillIndexItem, SkillS3ObjectOps, SkillS3StorageConfig, SkillStore } from "../types/index.js";
19
+ export declare class S3SkillStore implements SkillStore {
20
+ private readonly config;
21
+ /** Test/host seam — omit to build ops from @aws-sdk/client-s3 lazily. */
22
+ private readonly injectedOps?;
23
+ private readonly prefix;
24
+ private ops;
25
+ constructor(config: SkillS3StorageConfig,
26
+ /** Test/host seam — omit to build ops from @aws-sdk/client-s3 lazily. */
27
+ injectedOps?: SkillS3ObjectOps | undefined);
28
+ private getOps;
29
+ private skillKey;
30
+ private indexKey;
31
+ get(id: string): Promise<SkillDefinition | null>;
32
+ put(skill: SkillDefinition): Promise<void>;
33
+ delete(id: string): Promise<void>;
34
+ index(): Promise<SkillIndexItem[]>;
35
+ private writeIndex;
36
+ /**
37
+ * Read index.json; when missing or unparsable, rebuild it from a listing
38
+ * of the skills/ prefix and persist the rebuilt document (self-heal).
39
+ */
40
+ private readOrRebuildIndex;
41
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * S3-backed skill store.
3
+ *
4
+ * Layout (curator-compatible):
5
+ * <prefix>skills/<id>.json — one JSON-serialized SkillDefinition per object
6
+ * <prefix>index.json — { lastUpdated, skills: SkillIndexItem[] }
7
+ *
8
+ * The index document is upserted on every write and rebuilt from a bucket
9
+ * listing when missing or unparsable (self-healing). Concurrent writers can
10
+ * race the index read-modify-write; the rebuild path recovers from any
11
+ * resulting drift, matching curator's production behavior.
12
+ *
13
+ * @aws-sdk/client-s3 is an optional peer — loaded lazily with the same
14
+ * createRequire pattern as memory's @juspay/hippocampus so importing
15
+ * NeuroLink core never requires the AWS SDK. Tests (and hosts with
16
+ * pre-configured clients) inject a SkillS3ObjectOps implementation instead.
17
+ */
18
+ import { createRequire } from "node:module";
19
+ import { logger } from "../utils/logger.js";
20
+ import { toSkillIndexItem } from "./skillMatcher.js";
21
+ const lazyRequire = createRequire(import.meta.url);
22
+ let cachedS3Module;
23
+ function loadS3Module() {
24
+ if (cachedS3Module !== undefined) {
25
+ return cachedS3Module;
26
+ }
27
+ try {
28
+ cachedS3Module = lazyRequire("@aws-sdk/client-s3");
29
+ return cachedS3Module;
30
+ }
31
+ catch (error) {
32
+ cachedS3Module = null;
33
+ logger.debug("[SkillStoreS3] @aws-sdk/client-s3 is not installed; S3 skill storage unavailable.", { error: error instanceof Error ? error.message : String(error) });
34
+ return null;
35
+ }
36
+ }
37
+ /** Build default object ops from @aws-sdk/client-s3. Throws when absent. */
38
+ function createDefaultOps(config) {
39
+ const mod = loadS3Module();
40
+ if (!mod) {
41
+ throw new Error("Skills S3 storage requires @aws-sdk/client-s3. Run `pnpm add @aws-sdk/client-s3` " +
42
+ "(or your package manager equivalent), or supply a custom store instead.");
43
+ }
44
+ const client = new mod.S3Client({
45
+ ...(config.region ? { region: config.region } : {}),
46
+ ...(config.endpoint ? { endpoint: config.endpoint } : {}),
47
+ ...(config.forcePathStyle !== undefined
48
+ ? { forcePathStyle: config.forcePathStyle }
49
+ : {}),
50
+ ...(config.credentials ? { credentials: config.credentials } : {}),
51
+ });
52
+ const bucket = config.bucket;
53
+ return {
54
+ async getObject(key) {
55
+ try {
56
+ const response = (await client.send(new mod.GetObjectCommand({ Bucket: bucket, Key: key })));
57
+ return (await response.Body?.transformToString()) ?? null;
58
+ }
59
+ catch (error) {
60
+ const name = error.name;
61
+ if (name === "NoSuchKey" || name === "NotFound") {
62
+ return null;
63
+ }
64
+ throw error;
65
+ }
66
+ },
67
+ async putObject(key, body) {
68
+ await client.send(new mod.PutObjectCommand({
69
+ Bucket: bucket,
70
+ Key: key,
71
+ Body: body,
72
+ ContentType: "application/json",
73
+ }));
74
+ },
75
+ async deleteObject(key) {
76
+ await client.send(new mod.DeleteObjectCommand({ Bucket: bucket, Key: key }));
77
+ },
78
+ async listKeys(prefix) {
79
+ const keys = [];
80
+ let continuationToken;
81
+ do {
82
+ const response = (await client.send(new mod.ListObjectsV2Command({
83
+ Bucket: bucket,
84
+ Prefix: prefix,
85
+ ...(continuationToken
86
+ ? { ContinuationToken: continuationToken }
87
+ : {}),
88
+ })));
89
+ for (const item of response.Contents ?? []) {
90
+ if (item.Key) {
91
+ keys.push(item.Key);
92
+ }
93
+ }
94
+ continuationToken = response.IsTruncated
95
+ ? response.NextContinuationToken
96
+ : undefined;
97
+ } while (continuationToken);
98
+ return keys;
99
+ },
100
+ };
101
+ }
102
+ export class S3SkillStore {
103
+ config;
104
+ injectedOps;
105
+ prefix;
106
+ ops = null;
107
+ constructor(config,
108
+ /** Test/host seam — omit to build ops from @aws-sdk/client-s3 lazily. */
109
+ injectedOps) {
110
+ this.config = config;
111
+ this.injectedOps = injectedOps;
112
+ const rawPrefix = config.prefix ?? "neurolink-skills/";
113
+ this.prefix =
114
+ rawPrefix === "" || rawPrefix.endsWith("/") ? rawPrefix : `${rawPrefix}/`;
115
+ }
116
+ getOps() {
117
+ if (!this.ops) {
118
+ this.ops = this.injectedOps ?? createDefaultOps(this.config);
119
+ }
120
+ return this.ops;
121
+ }
122
+ skillKey(id) {
123
+ return `${this.prefix}skills/${id}.json`;
124
+ }
125
+ indexKey() {
126
+ return `${this.prefix}index.json`;
127
+ }
128
+ async get(id) {
129
+ const body = await this.getOps().getObject(this.skillKey(id));
130
+ if (!body) {
131
+ return null;
132
+ }
133
+ try {
134
+ return JSON.parse(body);
135
+ }
136
+ catch (error) {
137
+ logger.warn("[SkillStoreS3] Failed to parse skill object", {
138
+ id,
139
+ error: error instanceof Error ? error.message : String(error),
140
+ });
141
+ return null;
142
+ }
143
+ }
144
+ async put(skill) {
145
+ const ops = this.getOps();
146
+ await ops.putObject(this.skillKey(skill.id), JSON.stringify(skill, null, 2));
147
+ const index = await this.readOrRebuildIndex();
148
+ const entry = toSkillIndexItem(skill);
149
+ const position = index.skills.findIndex((s) => s.id === skill.id);
150
+ if (position >= 0) {
151
+ index.skills[position] = entry;
152
+ }
153
+ else {
154
+ index.skills.push(entry);
155
+ }
156
+ await this.writeIndex(index);
157
+ }
158
+ async delete(id) {
159
+ const ops = this.getOps();
160
+ await ops.deleteObject(this.skillKey(id));
161
+ const index = await this.readOrRebuildIndex();
162
+ index.skills = index.skills.filter((s) => s.id !== id);
163
+ await this.writeIndex(index);
164
+ }
165
+ async index() {
166
+ const index = await this.readOrRebuildIndex();
167
+ return index.skills;
168
+ }
169
+ async writeIndex(index) {
170
+ index.lastUpdated = new Date().toISOString();
171
+ await this.getOps().putObject(this.indexKey(), JSON.stringify(index, null, 2));
172
+ }
173
+ /**
174
+ * Read index.json; when missing or unparsable, rebuild it from a listing
175
+ * of the skills/ prefix and persist the rebuilt document (self-heal).
176
+ */
177
+ async readOrRebuildIndex() {
178
+ const ops = this.getOps();
179
+ const raw = await ops.getObject(this.indexKey());
180
+ if (raw) {
181
+ try {
182
+ const parsed = JSON.parse(raw);
183
+ if (Array.isArray(parsed.skills)) {
184
+ return parsed;
185
+ }
186
+ }
187
+ catch (error) {
188
+ logger.warn("[SkillStoreS3] index.json unparsable — rebuilding", {
189
+ error: error instanceof Error ? error.message : String(error),
190
+ });
191
+ }
192
+ }
193
+ const skillsPrefix = `${this.prefix}skills/`;
194
+ const keys = await ops.listKeys(skillsPrefix);
195
+ const skills = [];
196
+ for (const key of keys) {
197
+ if (!key.endsWith(".json")) {
198
+ continue;
199
+ }
200
+ const body = await ops.getObject(key);
201
+ if (!body) {
202
+ continue;
203
+ }
204
+ try {
205
+ skills.push(toSkillIndexItem(JSON.parse(body)));
206
+ }
207
+ catch {
208
+ logger.warn("[SkillStoreS3] Skipping unparsable skill during rebuild", {
209
+ key,
210
+ });
211
+ }
212
+ }
213
+ const rebuilt = {
214
+ lastUpdated: new Date().toISOString(),
215
+ skills,
216
+ };
217
+ try {
218
+ await this.writeIndex(rebuilt);
219
+ logger.info("[SkillStoreS3] Rebuilt skills index", {
220
+ count: skills.length,
221
+ });
222
+ }
223
+ catch (error) {
224
+ // Index persistence is best-effort; the rebuilt in-memory copy is
225
+ // still returned so reads keep working.
226
+ logger.warn("[SkillStoreS3] Failed to persist rebuilt index", {
227
+ error: error instanceof Error ? error.message : String(error),
228
+ });
229
+ }
230
+ return rebuilt;
231
+ }
232
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Built-in skill stores + the storage-config factory.
3
+ *
4
+ * First-party backends: in-process memory (tests, embedded), filesystem
5
+ * (curator-style JSON alongside Claude-skills-style markdown — `<name>.md`
6
+ * or `<name>/SKILL.md` with YAML frontmatter), S3 (skillStoreS3.ts, optional
7
+ * @aws-sdk/client-s3 peer), and Redis (skillStoreRedis.ts, pooled core
8
+ * client). Anything else plugs in via `{ type: "custom", store }`.
9
+ */
10
+ import type { SkillDefinition, SkillIndexItem, SkillStore, SkillsStorageConfig } from "../types/index.js";
11
+ /** In-process store backed by a Map. */
12
+ export declare class InMemorySkillStore implements SkillStore {
13
+ private skills;
14
+ constructor(seed?: SkillDefinition[]);
15
+ get(id: string): Promise<SkillDefinition | null>;
16
+ put(skill: SkillDefinition): Promise<void>;
17
+ delete(id: string): Promise<void>;
18
+ index(): Promise<SkillIndexItem[]>;
19
+ }
20
+ /**
21
+ * Directory-backed store. Read layouts:
22
+ * - `<dir>/<id>.json` — JSON SkillDefinition (mutable)
23
+ * - `<dir>/<name>.md` — frontmatter markdown (read-only source)
24
+ * - `<dir>/<name>/SKILL.md` — Claude-skills directory layout (read-only source)
25
+ * Mutations always write `<id>.json`; a JSON file shadows a markdown skill
26
+ * with the same id, so updating a markdown-sourced skill "copies up" to JSON.
27
+ */
28
+ export declare class FileSystemSkillStore implements SkillStore {
29
+ private readonly baseDir;
30
+ private cache;
31
+ constructor(baseDir: string);
32
+ invalidate(): void;
33
+ get(id: string): Promise<SkillDefinition | null>;
34
+ put(skill: SkillDefinition): Promise<void>;
35
+ delete(id: string): Promise<void>;
36
+ index(): Promise<SkillIndexItem[]>;
37
+ /**
38
+ * Scan the directory into an id→skill map. Markdown sources load first
39
+ * so JSON files (the mutable layer) shadow them on id collision.
40
+ */
41
+ private load;
42
+ }
43
+ /** Resolve a storage config to a concrete store. Defaults to memory. */
44
+ export declare function createSkillStore(config?: SkillsStorageConfig): SkillStore;