@plur-ai/core 0.2.8 → 0.3.0

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/dist/index.d.ts CHANGED
@@ -46,6 +46,7 @@ declare const EngramSchema: z.ZodObject<{
46
46
  statement: z.ZodString;
47
47
  rationale: z.ZodOptional<z.ZodString>;
48
48
  contraindications: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
49
+ source: z.ZodOptional<z.ZodString>;
49
50
  source_patterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
50
51
  derivation_count: z.ZodDefault<z.ZodNumber>;
51
52
  pack: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -299,6 +300,7 @@ declare const EngramSchema: z.ZodObject<{
299
300
  };
300
301
  rationale?: string | undefined;
301
302
  contraindications?: string[] | undefined;
303
+ source?: string | undefined;
302
304
  source_patterns?: string[] | undefined;
303
305
  episodic?: {
304
306
  emotional_weight: number;
@@ -362,6 +364,7 @@ declare const EngramSchema: z.ZodObject<{
362
364
  visibility?: "private" | "public" | "template" | undefined;
363
365
  rationale?: string | undefined;
364
366
  contraindications?: string[] | undefined;
367
+ source?: string | undefined;
365
368
  source_patterns?: string[] | undefined;
366
369
  derivation_count?: number | undefined;
367
370
  pack?: string | null | undefined;
package/dist/index.js CHANGED
@@ -158,6 +158,7 @@ var EngramSchema = z2.object({
158
158
  rationale: z2.string().optional(),
159
159
  contraindications: z2.array(z2.string()).optional(),
160
160
  // Lineage
161
+ source: z2.string().optional(),
161
162
  source_patterns: z2.array(z2.string()).optional(),
162
163
  derivation_count: z2.number().int().min(0).default(1),
163
164
  pack: z2.string().nullable().default(null),
@@ -242,6 +243,168 @@ var logger = {
242
243
  }
243
244
  };
244
245
 
246
+ // src/sync.ts
247
+ import { execFileSync } from "child_process";
248
+ import { existsSync as existsSync3, writeFileSync, renameSync, mkdirSync as mkdirSync2 } from "fs";
249
+ import { join as join2, dirname } from "path";
250
+ var GITIGNORE = `# PLUR \u2014 derived/cache files (regenerated automatically)
251
+ embeddings/
252
+ .embeddings-cache.json
253
+ *.db
254
+ *.sqlite
255
+ exchange/
256
+ `;
257
+ function git(args, cwd) {
258
+ return execFileSync("git", args, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
259
+ }
260
+ function gitSafe(args, cwd) {
261
+ try {
262
+ return git(args, cwd);
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+ function isGitRepo(root) {
268
+ return existsSync3(join2(root, ".git"));
269
+ }
270
+ function hasGitCli() {
271
+ try {
272
+ execFileSync("git", ["--version"], { encoding: "utf8", timeout: 5e3 });
273
+ return true;
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+ function getRemote(root) {
279
+ return gitSafe(["remote", "get-url", "origin"], root);
280
+ }
281
+ function isDirty(root) {
282
+ const status = gitSafe(["status", "--porcelain"], root);
283
+ return status !== null && status.length > 0;
284
+ }
285
+ function countDiff(root, direction) {
286
+ const tracking = gitSafe(["rev-parse", "--abbrev-ref", "@{u}"], root);
287
+ if (!tracking) return 0;
288
+ const flag = direction === "ahead" ? "--left-only" : "--right-only";
289
+ const count = gitSafe(["rev-list", flag, "--count", "HEAD...@{u}"], root);
290
+ return count ? parseInt(count, 10) : 0;
291
+ }
292
+ function getSyncStatus(root) {
293
+ if (!isGitRepo(root)) {
294
+ return { initialized: false, remote: null, dirty: false, branch: null, ahead: 0, behind: 0 };
295
+ }
296
+ const branch = gitSafe(["rev-parse", "--abbrev-ref", "HEAD"], root);
297
+ const remote = getRemote(root);
298
+ if (remote) gitSafe(["fetch", "origin", "--quiet"], root);
299
+ return {
300
+ initialized: true,
301
+ remote,
302
+ dirty: isDirty(root),
303
+ branch,
304
+ ahead: countDiff(root, "ahead"),
305
+ behind: countDiff(root, "behind")
306
+ };
307
+ }
308
+ function initRepo(root) {
309
+ git(["init"], root);
310
+ atomicWrite(join2(root, ".gitignore"), GITIGNORE);
311
+ git(["add", "-A"], root);
312
+ git(["commit", "-m", "Initial PLUR engram store"], root);
313
+ }
314
+ function commitChanges(root) {
315
+ if (!isDirty(root)) return 0;
316
+ git(["add", "-A"], root);
317
+ const diff = gitSafe(["diff", "--cached", "--stat", "--shortstat"], root);
318
+ const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
319
+ git(["commit", "-m", `plur sync ${now}`], root);
320
+ const match = diff?.match(/(\d+) file/);
321
+ return match ? parseInt(match[1], 10) : 1;
322
+ }
323
+ function hasConflictMarkers(root) {
324
+ const result = gitSafe(["grep", "-l", "<<<<<<<"], root);
325
+ return result !== null && result.length > 0;
326
+ }
327
+ function pullRebase(root) {
328
+ const result = gitSafe(["pull", "--rebase", "origin", "main"], root);
329
+ if (result !== null) return true;
330
+ gitSafe(["rebase", "--abort"], root);
331
+ const mergeResult = gitSafe(["pull", "origin", "main", "--no-edit"], root);
332
+ if (mergeResult !== null) return true;
333
+ if (hasConflictMarkers(root)) {
334
+ gitSafe(["merge", "--abort"], root);
335
+ throw new Error("Sync conflict: YAML files have merge conflicts that require manual resolution. Your local changes are preserved.");
336
+ }
337
+ git(["add", "-A"], root);
338
+ gitSafe(["commit", "-m", "plur sync: merge conflict resolved (kept both)"], root);
339
+ return true;
340
+ }
341
+ function sync(root, remote) {
342
+ if (!hasGitCli()) {
343
+ throw new Error("git is not installed. Install git to enable sync.");
344
+ }
345
+ if (!isGitRepo(root)) {
346
+ initRepo(root);
347
+ if (remote) {
348
+ git(["remote", "add", "origin", remote], root);
349
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], root);
350
+ git(["push", "-u", "origin", branch], root);
351
+ return { action: "initialized", message: `Initialized and pushed to ${remote}`, remote, files_changed: 0 };
352
+ }
353
+ return {
354
+ action: "initialized",
355
+ message: "Initialized local git repo. Call plur.sync with remote to enable cross-device sync.",
356
+ remote: null,
357
+ files_changed: 0
358
+ };
359
+ }
360
+ const existingRemote = getRemote(root);
361
+ if (remote && !existingRemote) {
362
+ git(["remote", "add", "origin", remote], root);
363
+ const filesChanged2 = commitChanges(root);
364
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], root);
365
+ git(["push", "-u", "origin", branch], root);
366
+ return { action: "synced", message: `Remote added and pushed to ${remote}`, remote, files_changed: filesChanged2 };
367
+ }
368
+ if (!existingRemote) {
369
+ const filesChanged2 = commitChanges(root);
370
+ if (filesChanged2 === 0) {
371
+ 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 };
372
+ }
373
+ return { action: "committed", message: `Committed ${filesChanged2} file(s) locally.`, remote: null, files_changed: filesChanged2 };
374
+ }
375
+ const filesChanged = commitChanges(root);
376
+ gitSafe(["fetch", "origin", "--quiet"], root);
377
+ const behind = countDiff(root, "behind");
378
+ const aheadBefore = countDiff(root, "ahead");
379
+ if (behind > 0) {
380
+ pullRebase(root);
381
+ }
382
+ const aheadAfter = countDiff(root, "ahead");
383
+ if (aheadAfter > 0) {
384
+ gitSafe(["push", "origin"], root);
385
+ }
386
+ if (filesChanged === 0 && behind === 0 && aheadBefore === 0) {
387
+ return { action: "up-to-date", message: "Already in sync.", remote: existingRemote, files_changed: 0 };
388
+ }
389
+ const parts = [];
390
+ if (filesChanged > 0) parts.push(`${filesChanged} file(s) committed`);
391
+ if (behind > 0) parts.push(`pulled ${behind} remote commit(s)`);
392
+ if (aheadAfter === 0 && aheadBefore > 0) parts.push("pushed");
393
+ return {
394
+ action: "synced",
395
+ message: `Synced. ${parts.join(", ")}.`,
396
+ remote: existingRemote,
397
+ files_changed: filesChanged
398
+ };
399
+ }
400
+ function atomicWrite(filePath, content) {
401
+ const dir = dirname(filePath);
402
+ if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
403
+ const tmp = filePath + ".tmp";
404
+ writeFileSync(tmp, content);
405
+ renameSync(tmp, filePath);
406
+ }
407
+
245
408
  // src/engrams.ts
246
409
  function loadEngrams(filePath) {
247
410
  if (!fs.existsSync(filePath)) return [];
@@ -264,7 +427,7 @@ function loadEngrams(filePath) {
264
427
  }
265
428
  function saveEngrams(filePath, engrams) {
266
429
  const content = yaml2.dump({ engrams }, { lineWidth: 120, noRefs: true, quotingType: '"' });
267
- fs.writeFileSync(filePath, content);
430
+ atomicWrite(filePath, content);
268
431
  }
269
432
  function parseSkillMdFrontmatter(filePath) {
270
433
  const content = fs.readFileSync(filePath, "utf8");
@@ -635,7 +798,7 @@ function selectAndSpread(ctx, personalEngrams, packs, config, embeddingBoosts) {
635
798
  }
636
799
 
637
800
  // src/episodes.ts
638
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
801
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
639
802
  import yaml3 from "js-yaml";
640
803
  function generateEpisodeId() {
641
804
  const ts = Date.now();
@@ -654,7 +817,7 @@ function captureEpisode(path2, summary, context) {
654
817
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
655
818
  };
656
819
  episodes.push(episode);
657
- writeFileSync2(path2, yaml3.dump(episodes, { lineWidth: 120, noRefs: true }), "utf8");
820
+ atomicWrite(path2, yaml3.dump(episodes, { lineWidth: 120, noRefs: true }));
658
821
  return episode;
659
822
  }
660
823
  function queryTimeline(path2, query) {
@@ -670,7 +833,7 @@ function queryTimeline(path2, query) {
670
833
  return episodes;
671
834
  }
672
835
  function loadEpisodes(path2) {
673
- if (!existsSync4(path2)) return [];
836
+ if (!existsSync5(path2)) return [];
674
837
  try {
675
838
  const raw = yaml3.load(readFileSync3(path2, "utf8"));
676
839
  return Array.isArray(raw) ? raw : [];
@@ -743,8 +906,9 @@ Rules:
743
906
  }
744
907
 
745
908
  // src/embeddings.ts
746
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
747
- import { join as join2 } from "path";
909
+ import { existsSync as existsSync6, readFileSync as readFileSync4, mkdirSync as mkdirSync3 } from "fs";
910
+ import { join as join3 } from "path";
911
+ import { createHash } from "crypto";
748
912
  var embedPipeline = null;
749
913
  var transformersUnavailable = false;
750
914
  async function getEmbedder() {
@@ -774,7 +938,7 @@ function cosineSimilarity(a, b) {
774
938
  return dot;
775
939
  }
776
940
  function loadCache(cachePath) {
777
- if (!existsSync5(cachePath)) return {};
941
+ if (!existsSync6(cachePath)) return {};
778
942
  try {
779
943
  return JSON.parse(readFileSync4(cachePath, "utf8"));
780
944
  } catch {
@@ -783,19 +947,15 @@ function loadCache(cachePath) {
783
947
  }
784
948
  function saveCache(cachePath, cache2) {
785
949
  const dir = cachePath.substring(0, cachePath.lastIndexOf("/"));
786
- if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
787
- writeFileSync3(cachePath, JSON.stringify(cache2));
950
+ if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
951
+ atomicWrite(cachePath, JSON.stringify(cache2));
788
952
  }
789
953
  function hashStatement(statement) {
790
- let hash = 0;
791
- for (let i = 0; i < statement.length; i++) {
792
- hash = (hash << 5) - hash + statement.charCodeAt(i) | 0;
793
- }
794
- return hash.toString(36);
954
+ return createHash("sha256").update(statement).digest("hex").slice(0, 16);
795
955
  }
796
956
  async function embeddingSearch(engrams, query, limit, storagePath) {
797
957
  if (engrams.length === 0) return [];
798
- const cachePath = storagePath ? join2(storagePath, ".embeddings-cache.json") : ".embeddings-cache.json";
958
+ const cachePath = storagePath ? join3(storagePath, ".embeddings-cache.json") : ".embeddings-cache.json";
799
959
  const cache2 = loadCache(cachePath);
800
960
  const queryEmbedding = await embed(query);
801
961
  if (!queryEmbedding) {
@@ -1023,152 +1183,6 @@ ${manifest.description || ""}
1023
1183
  return { path: outputDir, engram_count: engrams.length };
1024
1184
  }
1025
1185
 
1026
- // src/sync.ts
1027
- import { execSync } from "child_process";
1028
- import { existsSync as existsSync7, writeFileSync as writeFileSync5 } from "fs";
1029
- import { join as join4 } from "path";
1030
- var GITIGNORE = `# PLUR \u2014 derived/cache files (regenerated automatically)
1031
- embeddings/
1032
- *.db
1033
- *.sqlite
1034
- exchange/
1035
- `;
1036
- function git(args, cwd) {
1037
- return execSync(`git ${args}`, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
1038
- }
1039
- function gitSafe(args, cwd) {
1040
- try {
1041
- return git(args, cwd);
1042
- } catch {
1043
- return null;
1044
- }
1045
- }
1046
- function isGitRepo(root) {
1047
- return existsSync7(join4(root, ".git"));
1048
- }
1049
- function hasGitCli() {
1050
- try {
1051
- execSync("git --version", { encoding: "utf8", timeout: 5e3 });
1052
- return true;
1053
- } catch {
1054
- return false;
1055
- }
1056
- }
1057
- function getRemote(root) {
1058
- return gitSafe("remote get-url origin", root);
1059
- }
1060
- function isDirty(root) {
1061
- const status = gitSafe("status --porcelain", root);
1062
- return status !== null && status.length > 0;
1063
- }
1064
- function countDiff(root, direction) {
1065
- const tracking = gitSafe("rev-parse --abbrev-ref @{u}", root);
1066
- if (!tracking) return 0;
1067
- const flag = direction === "ahead" ? "left" : "right";
1068
- const count = gitSafe(`rev-list --${flag}-only --count HEAD...@{u}`, root);
1069
- return count ? parseInt(count, 10) : 0;
1070
- }
1071
- function getSyncStatus(root) {
1072
- if (!isGitRepo(root)) {
1073
- return { initialized: false, remote: null, dirty: false, branch: null, ahead: 0, behind: 0 };
1074
- }
1075
- const branch = gitSafe("rev-parse --abbrev-ref HEAD", root);
1076
- const remote = getRemote(root);
1077
- if (remote) gitSafe("fetch origin --quiet", root);
1078
- return {
1079
- initialized: true,
1080
- remote,
1081
- dirty: isDirty(root),
1082
- branch,
1083
- ahead: countDiff(root, "ahead"),
1084
- behind: countDiff(root, "behind")
1085
- };
1086
- }
1087
- function initRepo(root) {
1088
- git("init", root);
1089
- writeFileSync5(join4(root, ".gitignore"), GITIGNORE);
1090
- git("add -A", root);
1091
- git('commit -m "Initial PLUR engram store"', root);
1092
- }
1093
- function commitChanges(root) {
1094
- if (!isDirty(root)) return 0;
1095
- git("add -A", root);
1096
- const diff = gitSafe("diff --cached --stat --shortstat", root);
1097
- const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1098
- git(`commit -m "plur sync ${now}"`, root);
1099
- const match = diff?.match(/(\d+) file/);
1100
- return match ? parseInt(match[1], 10) : 1;
1101
- }
1102
- function pullRebase(root) {
1103
- const result = gitSafe("pull --rebase origin main", root);
1104
- if (result !== null) return true;
1105
- gitSafe("rebase --abort", root);
1106
- const mergeResult = gitSafe("pull origin main --no-edit", root);
1107
- if (mergeResult !== null) return true;
1108
- git("add -A", root);
1109
- gitSafe('commit -m "plur sync: merge conflict resolved (kept both)"', root);
1110
- return true;
1111
- }
1112
- function sync(root, remote) {
1113
- if (!hasGitCli()) {
1114
- throw new Error("git is not installed. Install git to enable sync.");
1115
- }
1116
- if (!isGitRepo(root)) {
1117
- initRepo(root);
1118
- if (remote) {
1119
- git(`remote add origin ${remote}`, root);
1120
- const branch = git("rev-parse --abbrev-ref HEAD", root);
1121
- git(`push -u origin ${branch}`, root);
1122
- return { action: "initialized", message: `Initialized and pushed to ${remote}`, remote, files_changed: 0 };
1123
- }
1124
- return {
1125
- action: "initialized",
1126
- message: "Initialized local git repo. Call plur.sync with remote to enable cross-device sync.",
1127
- remote: null,
1128
- files_changed: 0
1129
- };
1130
- }
1131
- const existingRemote = getRemote(root);
1132
- if (remote && !existingRemote) {
1133
- git(`remote add origin ${remote}`, root);
1134
- const filesChanged2 = commitChanges(root);
1135
- const branch = git("rev-parse --abbrev-ref HEAD", root);
1136
- git(`push -u origin ${branch}`, root);
1137
- return { action: "synced", message: `Remote added and pushed to ${remote}`, remote, files_changed: filesChanged2 };
1138
- }
1139
- if (!existingRemote) {
1140
- const filesChanged2 = commitChanges(root);
1141
- if (filesChanged2 === 0) {
1142
- 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 };
1143
- }
1144
- return { action: "committed", message: `Committed ${filesChanged2} file(s) locally.`, remote: null, files_changed: filesChanged2 };
1145
- }
1146
- const filesChanged = commitChanges(root);
1147
- gitSafe("fetch origin --quiet", root);
1148
- const behind = countDiff(root, "behind");
1149
- const aheadBefore = countDiff(root, "ahead");
1150
- if (behind > 0) {
1151
- pullRebase(root);
1152
- }
1153
- const aheadAfter = countDiff(root, "ahead");
1154
- if (aheadAfter > 0) {
1155
- gitSafe("push origin", root);
1156
- }
1157
- if (filesChanged === 0 && behind === 0 && aheadBefore === 0) {
1158
- return { action: "up-to-date", message: "Already in sync.", remote: existingRemote, files_changed: 0 };
1159
- }
1160
- const parts = [];
1161
- if (filesChanged > 0) parts.push(`${filesChanged} file(s) committed`);
1162
- if (behind > 0) parts.push(`pulled ${behind} remote commit(s)`);
1163
- if (aheadAfter === 0 && aheadBefore > 0) parts.push("pushed");
1164
- return {
1165
- action: "synced",
1166
- message: `Synced. ${parts.join(", ")}.`,
1167
- remote: existingRemote,
1168
- files_changed: filesChanged
1169
- };
1170
- }
1171
-
1172
1186
  // src/version-check.ts
1173
1187
  var cache = /* @__PURE__ */ new Map();
1174
1188
  async function checkForUpdate(packageName, currentVersion, onResult) {
@@ -1252,6 +1266,7 @@ var Plur = class {
1252
1266
  scope,
1253
1267
  visibility: "private",
1254
1268
  statement,
1269
+ source: context?.source,
1255
1270
  domain: context?.domain,
1256
1271
  activation: {
1257
1272
  retrieval_strength: 0.7,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/core",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,9 +38,8 @@
38
38
  "author": "PLUR <info@plur.ai>",
39
39
  "exports": {
40
40
  ".": {
41
- "import": "./dist/index.js",
42
- "require": "./dist/index.js",
43
- "types": "./dist/index.d.ts"
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/index.js"
44
43
  }
45
44
  },
46
45
  "scripts": {