@plur-ai/core 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @plur-ai/core
2
2
 
3
- The engram engine — store, recall, and inject AI memory.
3
+ The engram engine — store, recall, and inject AI memory. Local-first, zero API calls for search, plain YAML storage.
4
4
 
5
5
  ```bash
6
6
  npm install @plur-ai/core
@@ -10,9 +10,28 @@ npm install @plur-ai/core
10
10
  import { Plur } from '@plur-ai/core'
11
11
 
12
12
  const plur = new Plur()
13
- plur.learn('API uses snake_case', { scope: 'project:myapp', type: 'architectural' })
14
- const injection = plur.inject('fix the endpoint handler', { budget: 2000 })
15
- console.log(injection.directives) // injected context, ready to prepend
13
+
14
+ // Learn from a correction
15
+ plur.learn('toEqual() in Vitest is strict use toMatchObject() for partial matching', {
16
+ type: 'behavioral',
17
+ scope: 'project:my-app',
18
+ domain: 'dev/testing'
19
+ })
20
+
21
+ // Recall (hybrid: BM25 + embeddings via RRF, zero cost)
22
+ const results = await plur.recallHybrid('vitest assertion matching')
23
+
24
+ // Inject relevant engrams into agent context
25
+ const { directives, consider, count, tokens_used } = plur.inject('Write tests for the user service', {
26
+ scope: 'project:my-app',
27
+ budget: 2000
28
+ })
29
+
30
+ // Feedback trains the system
31
+ plur.feedback(results[0].id, 'positive')
32
+
33
+ // Sync across machines
34
+ plur.sync('git@github.com:you/plur-memory.git')
16
35
  ```
17
36
 
18
37
  ## API
@@ -23,11 +42,11 @@ console.log(injection.directives) // injected context, ready to prepend
23
42
  const plur = new Plur({ path: '/custom/storage/path' })
24
43
  ```
25
44
 
26
- Defaults to `~/Plur/`. Override with `PLUR_PATH` env var or `options.path`.
45
+ Defaults to `~/.plur/`. Override with `PLUR_PATH` env var or `options.path`.
27
46
 
28
47
  ### `learn(statement, context?)`
29
48
 
30
- Create an engram. Returns the created `Engram`.
49
+ Create an engram. Detects conflicts with existing engrams in the same scope.
31
50
 
32
51
  ```typescript
33
52
  plur.learn('Always run lint before committing', {
@@ -38,14 +57,24 @@ plur.learn('Always run lint before committing', {
38
57
  })
39
58
  ```
40
59
 
41
- ### `recall(query, options?)`
60
+ ### Search methods
42
61
 
43
- Search engrams by keyword/phrase. Returns `Engram[]`, reactivates accessed engrams.
62
+ Five search modes, from fastest to most accurate:
63
+
64
+ | Method | Speed | API calls | Best for |
65
+ |--------|-------|-----------|----------|
66
+ | `recall(query)` | Instant | None | Quick keyword lookup |
67
+ | `recallSemantic(query)` | ~200ms | None | Meaning-based search (local embeddings) |
68
+ | `recallHybrid(query)` | ~200ms | None | **Best default** — BM25 + embeddings via RRF |
69
+ | `recallAsync(query, { llm })` | ~1s | 1 LLM call | LLM-assisted semantic filtering |
70
+ | `recallExpanded(query, { llm })` | ~3s | 3-5 LLM calls | Query expansion + hybrid + RRF merge |
71
+
72
+ All accept the same options:
44
73
 
45
74
  ```typescript
46
- const results = plur.recall('deployment process', {
75
+ const results = await plur.recallHybrid('deployment process', {
47
76
  scope: 'project:myapp', // includes global + matching scopes
48
- domain: 'software',
77
+ domain: 'software', // prefix match
49
78
  limit: 10,
50
79
  min_strength: 0.5,
51
80
  })
@@ -53,7 +82,7 @@ const results = plur.recall('deployment process', {
53
82
 
54
83
  ### `inject(task, options?)`
55
84
 
56
- Select and score engrams within a token budget. Returns directives and considerations as formatted strings, ready to inject into a system prompt.
85
+ Select and score engrams within a token budget. Returns formatted strings ready to prepend to a system prompt.
57
86
 
58
87
  ```typescript
59
88
  const { directives, consider, count, tokens_used } = plur.inject('refactor the auth module', {
@@ -64,50 +93,56 @@ const { directives, consider, count, tokens_used } = plur.inject('refactor the a
64
93
 
65
94
  ### `feedback(id, signal)`
66
95
 
67
- Rate an engram's usefulness. Adjusts retrieval strength over time.
96
+ Rate an engram's usefulness. Trains injection relevance over time.
68
97
 
69
98
  ```typescript
70
99
  plur.feedback('ENG-001', 'positive') // +0.05 retrieval strength
71
100
  plur.feedback('ENG-002', 'negative') // -0.10 retrieval strength
72
- plur.feedback('ENG-003', 'neutral') // signal recorded, no strength change
73
101
  ```
74
102
 
75
103
  ### `forget(id, reason?)`
76
104
 
77
- Retire an engram. Sets status to `retired` — history is preserved, engram is excluded from recall and injection.
105
+ Retire an engram. History preserved, excluded from recall and injection.
78
106
 
79
107
  ```typescript
80
108
  plur.forget('ENG-001', 'API changed')
81
109
  ```
82
110
 
83
- ### `capture(summary, context?)`
111
+ ### `sync(remote?)`
84
112
 
85
- Append an episode to the episodic timeline.
113
+ Git-based sync across machines. Initializes on first call, commits + push/pull on subsequent calls.
86
114
 
87
115
  ```typescript
88
- plur.capture('Deployed v2.0 to production', {
89
- agent: 'claude-code',
90
- session_id: 'abc123',
91
- channel: 'cli',
92
- tags: ['deploy', 'production'],
93
- })
116
+ // First time init repo and push
117
+ plur.sync('git@github.com:you/plur-memory.git')
118
+
119
+ // Later — commit, pull, push
120
+ plur.sync()
121
+ ```
122
+
123
+ ```typescript
124
+ // Check sync status (no changes made)
125
+ const status = plur.syncStatus()
126
+ // { initialized, remote, dirty, branch, ahead, behind }
94
127
  ```
95
128
 
96
- ### `timeline(query?)`
129
+ ### `capture(summary, context?)` / `timeline(query?)`
97
130
 
98
- Query the episodic timeline. Returns `Episode[]`.
131
+ Episodic memory — record what happened, query the timeline.
99
132
 
100
133
  ```typescript
101
- const episodes = plur.timeline({
102
- since: new Date('2025-01-01'),
134
+ plur.capture('Deployed v2.0 to production', {
103
135
  agent: 'claude-code',
104
- search: 'deploy',
136
+ session_id: 'abc123',
137
+ tags: ['deploy'],
105
138
  })
139
+
140
+ const episodes = plur.timeline({ since: new Date('2025-01-01'), agent: 'claude-code' })
106
141
  ```
107
142
 
108
143
  ### `ingest(content, options?)`
109
144
 
110
- Extract engram candidates from text using pattern matching. Looks for phrases like "we decided", "always", "the convention is", etc.
145
+ Extract engram candidates from text using pattern matching.
111
146
 
112
147
  ```typescript
113
148
  const candidates = plur.ingest(markdownContent, {
@@ -115,7 +150,6 @@ const candidates = plur.ingest(markdownContent, {
115
150
  scope: 'project:myapp',
116
151
  source: 'docs/architecture.md',
117
152
  })
118
- // set extract_only: false (default) to auto-save candidates as engrams
119
153
  ```
120
154
 
121
155
  ### `installPack(source)` / `exportPack(...)` / `listPacks()`
@@ -130,10 +164,21 @@ plur.exportPack(engrams, './output', { name: 'my-pack', version: '1.0.0' })
130
164
 
131
165
  ### `status()`
132
166
 
133
- Return system health.
134
-
135
167
  ```typescript
136
- const { engram_count, episode_count, pack_count, storage_root } = plur.status()
168
+ const { engram_count, episode_count, pack_count, storage_root, config } = plur.status()
169
+ ```
170
+
171
+ ## Storage
172
+
173
+ Everything is plain YAML. Open it, read it, edit it.
174
+
175
+ ```
176
+ ~/.plur/
177
+ ├── engrams.yaml # learned knowledge
178
+ ├── episodes.yaml # session timeline
179
+ ├── candidates.yaml # pending engrams
180
+ ├── config.yaml # settings
181
+ └── packs/ # installed engram packs
137
182
  ```
138
183
 
139
184
  ## License
package/dist/index.d.ts CHANGED
@@ -546,6 +546,21 @@ interface PackInfo {
546
546
  }
547
547
  declare function listPacks(packsDir: string): PackInfo[];
548
548
 
549
+ interface SyncStatus {
550
+ initialized: boolean;
551
+ remote: string | null;
552
+ dirty: boolean;
553
+ branch: string | null;
554
+ ahead: number;
555
+ behind: number;
556
+ }
557
+ interface SyncResult {
558
+ action: 'initialized' | 'committed' | 'synced' | 'up-to-date';
559
+ message: string;
560
+ remote: string | null;
561
+ files_changed: number;
562
+ }
563
+
549
564
  declare const EpisodeSchema: z.ZodObject<{
550
565
  id: z.ZodString;
551
566
  summary: z.ZodString;
@@ -669,6 +684,30 @@ interface TimelineQuery {
669
684
  /** Build searchable text from all engram fields */
670
685
  declare function engramSearchText(engram: Engram): string;
671
686
 
687
+ /**
688
+ * Non-blocking version check against npm registry.
689
+ * Caches result in memory — one fetch per process lifetime.
690
+ * Never throws or blocks startup.
691
+ */
692
+ interface VersionCheckResult {
693
+ current: string;
694
+ latest: string | null;
695
+ updateAvailable: boolean;
696
+ checkedAt: number | null;
697
+ }
698
+ /**
699
+ * Check npm for a newer version. Fetches once, caches forever (process lifetime).
700
+ * Fire-and-forget: call at startup, read later via getCachedUpdateCheck().
701
+ */
702
+ declare function checkForUpdate(packageName: string, currentVersion: string, onResult?: (result: VersionCheckResult) => void): Promise<VersionCheckResult>;
703
+ /**
704
+ * Read the cached version check result. Returns null if checkForUpdate() hasn't
705
+ * completed yet. This is the zero-cost read path for assemblers.
706
+ */
707
+ declare function getCachedUpdateCheck(packageName: string): VersionCheckResult | null;
708
+ /** Clear cache (for testing). */
709
+ declare function clearVersionCache(): void;
710
+
672
711
  interface IngestOptions {
673
712
  source?: string;
674
713
  extract_only?: boolean;
@@ -748,8 +787,12 @@ declare class Plur {
748
787
  };
749
788
  /** List all installed packs. */
750
789
  listPacks(): ReturnType<typeof listPacks>;
790
+ /** Sync engrams to git. Initializes repo on first call, commits + push/pull on subsequent calls. */
791
+ sync(remote?: string): SyncResult;
792
+ /** Get git sync status without making changes. */
793
+ syncStatus(): SyncStatus;
751
794
  /** Return system health info. */
752
795
  status(): StatusResult;
753
796
  }
754
797
 
755
- export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type RecallOptions, type StatusResult, type TimelineQuery, engramSearchText };
798
+ export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type RecallOptions, type StatusResult, type SyncResult, type SyncStatus, type TimelineQuery, type VersionCheckResult, checkForUpdate, clearVersionCache, engramSearchText, getCachedUpdateCheck };
package/dist/index.js CHANGED
@@ -759,10 +759,10 @@ function loadCache(cachePath) {
759
759
  return {};
760
760
  }
761
761
  }
762
- function saveCache(cachePath, cache) {
762
+ function saveCache(cachePath, cache2) {
763
763
  const dir = cachePath.substring(0, cachePath.lastIndexOf("/"));
764
764
  if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
765
- writeFileSync3(cachePath, JSON.stringify(cache));
765
+ writeFileSync3(cachePath, JSON.stringify(cache2));
766
766
  }
767
767
  function hashStatement(statement) {
768
768
  let hash = 0;
@@ -774,18 +774,18 @@ function hashStatement(statement) {
774
774
  async function embeddingSearch(engrams, query, limit, storagePath) {
775
775
  if (engrams.length === 0) return [];
776
776
  const cachePath = storagePath ? join2(storagePath, ".embeddings-cache.json") : ".embeddings-cache.json";
777
- const cache = loadCache(cachePath);
777
+ const cache2 = loadCache(cachePath);
778
778
  const queryEmbedding = await embed(query);
779
779
  const similarities = [];
780
780
  for (const engram of engrams) {
781
781
  const searchText = engramSearchText(engram);
782
782
  const hash = hashStatement(searchText);
783
783
  let engramEmbedding;
784
- if (cache[engram.id]?.hash === hash) {
785
- engramEmbedding = new Float32Array(cache[engram.id].embedding);
784
+ if (cache2[engram.id]?.hash === hash) {
785
+ engramEmbedding = new Float32Array(cache2[engram.id].embedding);
786
786
  } else {
787
787
  engramEmbedding = await embed(searchText);
788
- cache[engram.id] = {
788
+ cache2[engram.id] = {
789
789
  hash,
790
790
  embedding: Array.from(engramEmbedding)
791
791
  };
@@ -793,7 +793,7 @@ async function embeddingSearch(engrams, query, limit, storagePath) {
793
793
  const score = cosineSimilarity(queryEmbedding, engramEmbedding);
794
794
  similarities.push({ engram, score });
795
795
  }
796
- saveCache(cachePath, cache);
796
+ saveCache(cachePath, cache2);
797
797
  similarities.sort((a, b) => b.score - a.score);
798
798
  return similarities.slice(0, limit).map((s) => s.engram);
799
799
  }
@@ -996,6 +996,203 @@ ${manifest.description || ""}
996
996
  return { path: outputDir, engram_count: engrams.length };
997
997
  }
998
998
 
999
+ // src/sync.ts
1000
+ import { execSync } from "child_process";
1001
+ import { existsSync as existsSync7, writeFileSync as writeFileSync5 } from "fs";
1002
+ import { join as join4 } from "path";
1003
+ var GITIGNORE = `# PLUR \u2014 derived/cache files (regenerated automatically)
1004
+ embeddings/
1005
+ *.db
1006
+ *.sqlite
1007
+ exchange/
1008
+ `;
1009
+ function git(args, cwd) {
1010
+ return execSync(`git ${args}`, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
1011
+ }
1012
+ function gitSafe(args, cwd) {
1013
+ try {
1014
+ return git(args, cwd);
1015
+ } catch {
1016
+ return null;
1017
+ }
1018
+ }
1019
+ function isGitRepo(root) {
1020
+ return existsSync7(join4(root, ".git"));
1021
+ }
1022
+ function hasGitCli() {
1023
+ try {
1024
+ execSync("git --version", { encoding: "utf8", timeout: 5e3 });
1025
+ return true;
1026
+ } catch {
1027
+ return false;
1028
+ }
1029
+ }
1030
+ function getRemote(root) {
1031
+ return gitSafe("remote get-url origin", root);
1032
+ }
1033
+ function isDirty(root) {
1034
+ const status = gitSafe("status --porcelain", root);
1035
+ return status !== null && status.length > 0;
1036
+ }
1037
+ function countDiff(root, direction) {
1038
+ const tracking = gitSafe("rev-parse --abbrev-ref @{u}", root);
1039
+ if (!tracking) return 0;
1040
+ const flag = direction === "ahead" ? "left" : "right";
1041
+ const count = gitSafe(`rev-list --${flag}-only --count HEAD...@{u}`, root);
1042
+ return count ? parseInt(count, 10) : 0;
1043
+ }
1044
+ function getSyncStatus(root) {
1045
+ if (!isGitRepo(root)) {
1046
+ return { initialized: false, remote: null, dirty: false, branch: null, ahead: 0, behind: 0 };
1047
+ }
1048
+ const branch = gitSafe("rev-parse --abbrev-ref HEAD", root);
1049
+ const remote = getRemote(root);
1050
+ if (remote) gitSafe("fetch origin --quiet", root);
1051
+ return {
1052
+ initialized: true,
1053
+ remote,
1054
+ dirty: isDirty(root),
1055
+ branch,
1056
+ ahead: countDiff(root, "ahead"),
1057
+ behind: countDiff(root, "behind")
1058
+ };
1059
+ }
1060
+ function initRepo(root) {
1061
+ git("init", root);
1062
+ writeFileSync5(join4(root, ".gitignore"), GITIGNORE);
1063
+ git("add -A", root);
1064
+ git('commit -m "Initial PLUR engram store"', root);
1065
+ }
1066
+ function commitChanges(root) {
1067
+ if (!isDirty(root)) return 0;
1068
+ git("add -A", root);
1069
+ const diff = gitSafe("diff --cached --stat --shortstat", root);
1070
+ const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1071
+ git(`commit -m "plur sync ${now}"`, root);
1072
+ const match = diff?.match(/(\d+) file/);
1073
+ return match ? parseInt(match[1], 10) : 1;
1074
+ }
1075
+ function pullRebase(root) {
1076
+ const result = gitSafe("pull --rebase origin main", root);
1077
+ if (result !== null) return true;
1078
+ gitSafe("rebase --abort", root);
1079
+ const mergeResult = gitSafe("pull origin main --no-edit", root);
1080
+ if (mergeResult !== null) return true;
1081
+ git("add -A", root);
1082
+ gitSafe('commit -m "plur sync: merge conflict resolved (kept both)"', root);
1083
+ return true;
1084
+ }
1085
+ function sync(root, remote) {
1086
+ if (!hasGitCli()) {
1087
+ throw new Error("git is not installed. Install git to enable sync.");
1088
+ }
1089
+ if (!isGitRepo(root)) {
1090
+ initRepo(root);
1091
+ if (remote) {
1092
+ git(`remote add origin ${remote}`, root);
1093
+ const branch = git("rev-parse --abbrev-ref HEAD", root);
1094
+ git(`push -u origin ${branch}`, root);
1095
+ return { action: "initialized", message: `Initialized and pushed to ${remote}`, remote, files_changed: 0 };
1096
+ }
1097
+ return {
1098
+ action: "initialized",
1099
+ message: "Initialized local git repo. Call plur.sync with remote to enable cross-device sync.",
1100
+ remote: null,
1101
+ files_changed: 0
1102
+ };
1103
+ }
1104
+ const existingRemote = getRemote(root);
1105
+ if (remote && !existingRemote) {
1106
+ git(`remote add origin ${remote}`, root);
1107
+ const filesChanged2 = commitChanges(root);
1108
+ const branch = git("rev-parse --abbrev-ref HEAD", root);
1109
+ git(`push -u origin ${branch}`, root);
1110
+ return { action: "synced", message: `Remote added and pushed to ${remote}`, remote, files_changed: filesChanged2 };
1111
+ }
1112
+ if (!existingRemote) {
1113
+ const filesChanged2 = commitChanges(root);
1114
+ if (filesChanged2 === 0) {
1115
+ return { action: "up-to-date", message: 'No changes to commit. Add a remote with plur.sync({ remote: "..." }) to enable cross-device sync.', remote: null, files_changed: 0 };
1116
+ }
1117
+ return { action: "committed", message: `Committed ${filesChanged2} file(s) locally.`, remote: null, files_changed: filesChanged2 };
1118
+ }
1119
+ const filesChanged = commitChanges(root);
1120
+ gitSafe("fetch origin --quiet", root);
1121
+ const behind = countDiff(root, "behind");
1122
+ const aheadBefore = countDiff(root, "ahead");
1123
+ if (behind > 0) {
1124
+ pullRebase(root);
1125
+ }
1126
+ const aheadAfter = countDiff(root, "ahead");
1127
+ if (aheadAfter > 0) {
1128
+ gitSafe("push origin", root);
1129
+ }
1130
+ if (filesChanged === 0 && behind === 0 && aheadBefore === 0) {
1131
+ return { action: "up-to-date", message: "Already in sync.", remote: existingRemote, files_changed: 0 };
1132
+ }
1133
+ const parts = [];
1134
+ if (filesChanged > 0) parts.push(`${filesChanged} file(s) committed`);
1135
+ if (behind > 0) parts.push(`pulled ${behind} remote commit(s)`);
1136
+ if (aheadAfter === 0 && aheadBefore > 0) parts.push("pushed");
1137
+ return {
1138
+ action: "synced",
1139
+ message: `Synced. ${parts.join(", ")}.`,
1140
+ remote: existingRemote,
1141
+ files_changed: filesChanged
1142
+ };
1143
+ }
1144
+
1145
+ // src/version-check.ts
1146
+ var cache = /* @__PURE__ */ new Map();
1147
+ async function checkForUpdate(packageName, currentVersion, onResult) {
1148
+ const cached = cache.get(packageName);
1149
+ if (cached) {
1150
+ if (onResult) onResult(cached);
1151
+ return cached;
1152
+ }
1153
+ const result = { current: currentVersion, latest: null, updateAvailable: false, checkedAt: null };
1154
+ try {
1155
+ const controller = new AbortController();
1156
+ const timeout = setTimeout(() => controller.abort(), 3e3);
1157
+ const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
1158
+ signal: controller.signal,
1159
+ headers: { Accept: "application/json" }
1160
+ });
1161
+ clearTimeout(timeout);
1162
+ if (!res.ok) {
1163
+ cache.set(packageName, result);
1164
+ return result;
1165
+ }
1166
+ const data = await res.json();
1167
+ if (!data.version) {
1168
+ cache.set(packageName, result);
1169
+ return result;
1170
+ }
1171
+ result.latest = data.version;
1172
+ result.updateAvailable = isNewer(data.version, currentVersion);
1173
+ result.checkedAt = Date.now();
1174
+ } catch {
1175
+ }
1176
+ cache.set(packageName, result);
1177
+ if (onResult) onResult(result);
1178
+ return result;
1179
+ }
1180
+ function getCachedUpdateCheck(packageName) {
1181
+ return cache.get(packageName) ?? null;
1182
+ }
1183
+ function clearVersionCache() {
1184
+ cache.clear();
1185
+ }
1186
+ function isNewer(a, b) {
1187
+ const pa = a.split(".").map(Number);
1188
+ const pb = b.split(".").map(Number);
1189
+ for (let i = 0; i < 3; i++) {
1190
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
1191
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
1192
+ }
1193
+ return false;
1194
+ }
1195
+
999
1196
  // src/index.ts
1000
1197
  var INGEST_PATTERNS = [
1001
1198
  { re: /(?:we decided|the decision is|agreed to)\s+(.+?)\.?$/gim, type: "architectural" },
@@ -1245,6 +1442,14 @@ var Plur = class {
1245
1442
  listPacks() {
1246
1443
  return listPacks(this.paths.packs);
1247
1444
  }
1445
+ /** Sync engrams to git. Initializes repo on first call, commits + push/pull on subsequent calls. */
1446
+ sync(remote) {
1447
+ return sync(this.paths.root, remote);
1448
+ }
1449
+ /** Get git sync status without making changes. */
1450
+ syncStatus() {
1451
+ return getSyncStatus(this.paths.root);
1452
+ }
1248
1453
  /** Return system health info. */
1249
1454
  status() {
1250
1455
  const engrams = loadEngrams(this.paths.engrams);
@@ -1261,5 +1466,8 @@ var Plur = class {
1261
1466
  };
1262
1467
  export {
1263
1468
  Plur,
1264
- engramSearchText
1469
+ checkForUpdate,
1470
+ clearVersionCache,
1471
+ engramSearchText,
1472
+ getCachedUpdateCheck
1265
1473
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",