@a-company/paradigm 3.17.1 → 3.18.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.
@@ -5,12 +5,12 @@ import * as fs9 from "fs";
5
5
  import * as path10 from "path";
6
6
  import * as yaml8 from "js-yaml";
7
7
 
8
- // ../paradigm-mcp/node_modules/.pnpm/@a-company+premise-core@0.2.0_typescript@5.9.3/node_modules/@a-company/premise-core/dist/index.js
8
+ // ../premise/core/dist/index.js
9
9
  import * as yaml3 from "js-yaml";
10
10
  import { z as z2 } from "zod";
11
11
  import * as path3 from "path";
12
12
 
13
- // ../paradigm-mcp/node_modules/.pnpm/@a-company+purpose-core@3.5.0_typescript@5.9.3/node_modules/@a-company/purpose-core/dist/index.js
13
+ // ../purpose/core/dist/index.js
14
14
  import * as fs from "fs";
15
15
  import * as yaml from "js-yaml";
16
16
  import { z } from "zod";
@@ -617,7 +617,7 @@ function validatePurposeItem(id, item, itemType, prefix, issues) {
617
617
  }
618
618
  }
619
619
 
620
- // ../paradigm-mcp/node_modules/.pnpm/@a-company+portal-core@0.1.0/node_modules/@a-company/portal-core/dist/index.js
620
+ // ../portal/core/dist/index.js
621
621
  import * as fs2 from "fs";
622
622
  import * as path2 from "path";
623
623
  import * as yaml2 from "js-yaml";
@@ -761,7 +761,7 @@ async function findGateFiles(rootDir) {
761
761
  return files;
762
762
  }
763
763
 
764
- // ../paradigm-mcp/node_modules/.pnpm/@a-company+premise-core@0.2.0_typescript@5.9.3/node_modules/@a-company/premise-core/dist/index.js
764
+ // ../premise/core/dist/index.js
765
765
  var PositionSchema = z2.object({
766
766
  x: z2.number(),
767
767
  y: z2.number()
@@ -926,7 +926,9 @@ async function aggregateFromPremise(premiseFile, rootDir) {
926
926
  data: item,
927
927
  description: item.description,
928
928
  anchors: item.anchors?.map((a) => parseAnchorString(a)),
929
- appliesTo: item["applies-to"]
929
+ appliesTo: item["applies-to"],
930
+ tags: item.tags,
931
+ enforcement: item.enforcement
930
932
  }));
931
933
  }
932
934
  const symbolRefs = extractSymbolReferences(parsed);
@@ -1218,7 +1220,7 @@ function getAllSymbols(index) {
1218
1220
  return Array.from(index.entries.values());
1219
1221
  }
1220
1222
 
1221
- // ../paradigm-mcp/node_modules/.pnpm/@a-company+probe-core@3.5.0/node_modules/@a-company/probe-core/dist/generator.js
1223
+ // ../probe/core/dist/generator.js
1222
1224
  var PARADIGM_VERSION = "0.1.0";
1223
1225
  var SCHEMA_VERSION = "1.0.0";
1224
1226
  function generateScanIndex(input, options) {
@@ -3403,9 +3405,72 @@ function inferSeverity(data, entry) {
3403
3405
  import * as fs6 from "fs";
3404
3406
  import * as path7 from "path";
3405
3407
  import * as yaml5 from "js-yaml";
3408
+ import { execSync as execSync2 } from "child_process";
3409
+ import * as os2 from "os";
3406
3410
  var LORE_DIR = ".paradigm/lore";
3407
3411
  var ENTRIES_DIR = "entries";
3408
3412
  var TIMELINE_FILE = "timeline.yaml";
3413
+ function inferProvider(model) {
3414
+ const lower = model.toLowerCase();
3415
+ if (lower.includes("claude") || lower.includes("anthropic")) return "anthropic";
3416
+ if (lower.includes("gpt") || lower.includes("openai") || lower.includes("o1") || lower.includes("o3")) return "openai";
3417
+ if (lower.includes("gemini") || lower.includes("google") || lower.includes("palm")) return "google";
3418
+ if (lower.includes("llama") || lower.includes("meta")) return "meta";
3419
+ if (lower.includes("mistral") || lower.includes("mixtral")) return "mistral";
3420
+ if (lower.includes("deepseek")) return "deepseek";
3421
+ if (lower.includes("cohere") || lower.includes("command")) return "cohere";
3422
+ return "unknown";
3423
+ }
3424
+ function normalizeLoreEntry(raw) {
3425
+ const entry = raw;
3426
+ const author = entry.author;
3427
+ if (typeof author === "string") {
3428
+ return raw;
3429
+ }
3430
+ if (author && typeof author === "object" && !Array.isArray(author)) {
3431
+ const old = author;
3432
+ if (old.type === "agent") {
3433
+ entry.author = "unknown";
3434
+ entry.agent = {
3435
+ provider: old.model ? inferProvider(old.model) : inferProvider(old.id),
3436
+ model: old.model || old.id
3437
+ };
3438
+ } else {
3439
+ entry.author = old.id || "unknown";
3440
+ }
3441
+ delete entry.assistedBy;
3442
+ }
3443
+ return entry;
3444
+ }
3445
+ function sanitizeAuthor(name) {
3446
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 20) || "unknown";
3447
+ }
3448
+ function resolveAuthor() {
3449
+ const envAuthor = process.env.PARADIGM_AUTHOR;
3450
+ if (envAuthor) return sanitizeAuthor(envAuthor);
3451
+ try {
3452
+ const gitName = execSync2("git config user.name", { encoding: "utf-8", timeout: 3e3 }).trim();
3453
+ if (gitName) return sanitizeAuthor(gitName);
3454
+ } catch {
3455
+ }
3456
+ try {
3457
+ const username = os2.userInfo().username;
3458
+ if (username) return sanitizeAuthor(username);
3459
+ } catch {
3460
+ }
3461
+ return "unknown";
3462
+ }
3463
+ function isLoreFile(filename) {
3464
+ return filename.endsWith(".yaml") || filename.endsWith(".lore");
3465
+ }
3466
+ function resolveEntryPath(rootDir, dateStr, entryId) {
3467
+ const dirPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
3468
+ const lorePath = path7.join(dirPath, `${entryId}.lore`);
3469
+ if (fs6.existsSync(lorePath)) return lorePath;
3470
+ const yamlPath = path7.join(dirPath, `${entryId}.yaml`);
3471
+ if (fs6.existsSync(yamlPath)) return yamlPath;
3472
+ return null;
3473
+ }
3409
3474
  async function loadLoreEntries(rootDir, filter) {
3410
3475
  const entriesPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR);
3411
3476
  if (!fs6.existsSync(entriesPath)) {
@@ -3418,12 +3483,12 @@ async function loadLoreEntries(rootDir, filter) {
3418
3483
  if (filter?.dateFrom && dateDir < filter.dateFrom.slice(0, 10)) continue;
3419
3484
  if (filter?.dateTo && dateDir > filter.dateTo.slice(0, 10)) continue;
3420
3485
  const dirPath = path7.join(entriesPath, dateDir);
3421
- const files = fs6.readdirSync(dirPath).filter((f) => f.endsWith(".yaml")).sort();
3486
+ const files = fs6.readdirSync(dirPath).filter(isLoreFile).sort();
3422
3487
  for (const file of files) {
3423
3488
  try {
3424
3489
  const content = fs6.readFileSync(path7.join(dirPath, file), "utf8");
3425
- const entry = yaml5.load(content);
3426
- entries.push(entry);
3490
+ const raw = yaml5.load(content);
3491
+ entries.push(normalizeLoreEntry(raw));
3427
3492
  } catch {
3428
3493
  }
3429
3494
  }
@@ -3434,11 +3499,12 @@ async function loadLoreEntry(rootDir, entryId) {
3434
3499
  const dateMatch = entryId.match(/^L-(\d{4}-\d{2}-\d{2})-/);
3435
3500
  if (dateMatch) {
3436
3501
  const dateStr = dateMatch[1];
3437
- const entryPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
3438
- if (fs6.existsSync(entryPath)) {
3502
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
3503
+ if (entryPath) {
3439
3504
  try {
3440
3505
  const content = fs6.readFileSync(entryPath, "utf8");
3441
- return yaml5.load(content);
3506
+ const raw = yaml5.load(content);
3507
+ return normalizeLoreEntry(raw);
3442
3508
  } catch {
3443
3509
  return null;
3444
3510
  }
@@ -3459,6 +3525,16 @@ async function loadLoreTimeline(rootDir) {
3459
3525
  return null;
3460
3526
  }
3461
3527
  }
3528
+ function captureGitContext(cwd) {
3529
+ try {
3530
+ const ref = execSync2("git rev-parse HEAD", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3531
+ const branch = execSync2("git rev-parse --abbrev-ref HEAD", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3532
+ const status = execSync2("git status --porcelain", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3533
+ return { ref, branch, dirty: status.length > 0 };
3534
+ } catch {
3535
+ return void 0;
3536
+ }
3537
+ }
3462
3538
  async function recordLoreEntry(rootDir, entry) {
3463
3539
  const lorePath = path7.join(rootDir, LORE_DIR);
3464
3540
  const dateStr = entry.timestamp.slice(0, 10);
@@ -3466,10 +3542,16 @@ async function recordLoreEntry(rootDir, entry) {
3466
3542
  if (!fs6.existsSync(datePath)) {
3467
3543
  fs6.mkdirSync(datePath, { recursive: true });
3468
3544
  }
3545
+ if (!entry.author) {
3546
+ entry.author = resolveAuthor();
3547
+ }
3548
+ if (!entry.git_context) {
3549
+ entry.git_context = captureGitContext(rootDir);
3550
+ }
3469
3551
  if (!entry.id) {
3470
- entry.id = generateLoreId(rootDir, dateStr);
3552
+ entry.id = generateLoreId(rootDir, dateStr, entry.author, entry.timestamp);
3471
3553
  }
3472
- const entryPath = path7.join(datePath, `${entry.id}.yaml`);
3554
+ const entryPath = path7.join(datePath, `${entry.id}.lore`);
3473
3555
  fs6.writeFileSync(entryPath, yaml5.dump(entry, { lineWidth: -1, noRefs: true }));
3474
3556
  await rebuildTimeline(rootDir);
3475
3557
  return entry.id;
@@ -3485,12 +3567,13 @@ async function rebuildTimeline(rootDir) {
3485
3567
  const dateDirs = fs6.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d));
3486
3568
  for (const dateDir of dateDirs) {
3487
3569
  const dirPath = path7.join(entriesPath, dateDir);
3488
- const files = fs6.readdirSync(dirPath).filter((f) => f.endsWith(".yaml"));
3570
+ const files = fs6.readdirSync(dirPath).filter(isLoreFile);
3489
3571
  for (const file of files) {
3490
3572
  try {
3491
3573
  const content = fs6.readFileSync(path7.join(dirPath, file), "utf8");
3492
- const entry = yaml5.load(content);
3493
- authors.add(entry.author.id);
3574
+ const raw = yaml5.load(content);
3575
+ const entry = normalizeLoreEntry(raw);
3576
+ authors.add(entry.author);
3494
3577
  entryCount++;
3495
3578
  if (!lastUpdated || entry.timestamp > lastUpdated) {
3496
3579
  lastUpdated = entry.timestamp;
@@ -3526,7 +3609,7 @@ async function rebuildTimeline(rootDir) {
3526
3609
  function migrateLegacyEntries(rootDir) {
3527
3610
  const entriesPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR);
3528
3611
  if (!fs6.existsSync(entriesPath)) return 0;
3529
- const rootFiles = fs6.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml") && !f.startsWith("."));
3612
+ const rootFiles = fs6.readdirSync(entriesPath).filter((f) => isLoreFile(f) && !f.startsWith("."));
3530
3613
  let migrated = 0;
3531
3614
  for (const file of rootFiles) {
3532
3615
  const filePath = path7.join(entriesPath, file);
@@ -3536,12 +3619,15 @@ function migrateLegacyEntries(rootDir) {
3536
3619
  const content = fs6.readFileSync(filePath, "utf8");
3537
3620
  const raw = yaml5.load(content);
3538
3621
  if (raw.author && typeof raw.author === "object") continue;
3622
+ if (typeof raw.author === "string" && raw.timestamp) continue;
3539
3623
  const dateStr = typeof raw.date === "string" ? raw.date.slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3540
3624
  const datePath = path7.join(entriesPath, dateStr);
3541
3625
  if (!fs6.existsSync(datePath)) {
3542
3626
  fs6.mkdirSync(datePath, { recursive: true });
3543
3627
  }
3544
- const id = generateLoreId(rootDir, dateStr);
3628
+ const author = resolveAuthor();
3629
+ const timestamp = `${dateStr}T00:00:00.000Z`;
3630
+ const id = generateLoreId(rootDir, dateStr, author, timestamp);
3545
3631
  const oldType = String(raw.type || "agent-session");
3546
3632
  const v2Type = ["agent-session", "human-note", "decision", "review", "incident", "milestone"].includes(oldType) ? oldType : "agent-session";
3547
3633
  let verification;
@@ -3555,9 +3641,10 @@ function migrateLegacyEntries(rootDir) {
3555
3641
  const v2Entry = {
3556
3642
  id,
3557
3643
  type: v2Type,
3558
- timestamp: `${dateStr}T00:00:00.000Z`,
3559
- author: { type: "agent", id: "unknown" },
3560
- title: String(raw.title || file.replace(".yaml", "")),
3644
+ timestamp,
3645
+ author: "unknown",
3646
+ agent: { provider: "unknown", model: "unknown" },
3647
+ title: String(raw.title || file.replace(/\.(yaml|lore)$/, "")),
3561
3648
  summary: String(raw.summary || ""),
3562
3649
  symbols_touched: Array.isArray(raw.symbols_touched) ? raw.symbols_touched : [],
3563
3650
  files_modified: Array.isArray(raw.files_modified) ? raw.files_modified : void 0,
@@ -3565,7 +3652,7 @@ function migrateLegacyEntries(rootDir) {
3565
3652
  tags: ["migrated", oldType]
3566
3653
  };
3567
3654
  fs6.writeFileSync(
3568
- path7.join(datePath, `${id}.yaml`),
3655
+ path7.join(datePath, `${id}.lore`),
3569
3656
  yaml5.dump(v2Entry, { lineWidth: -1, noRefs: true })
3570
3657
  );
3571
3658
  fs6.unlinkSync(filePath);
@@ -3579,8 +3666,8 @@ async function updateLoreEntry(rootDir, entryId, partial) {
3579
3666
  const entry = await loadLoreEntry(rootDir, entryId);
3580
3667
  if (!entry) return false;
3581
3668
  const dateStr = entry.timestamp.slice(0, 10);
3582
- const entryPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
3583
- if (!fs6.existsSync(entryPath)) return false;
3669
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
3670
+ if (!entryPath) return false;
3584
3671
  if (partial.title !== void 0) entry.title = partial.title;
3585
3672
  if (partial.summary !== void 0) entry.summary = partial.summary;
3586
3673
  if (partial.type !== void 0) entry.type = partial.type;
@@ -3605,11 +3692,11 @@ async function deleteLoreEntry(rootDir, entryId) {
3605
3692
  const entry = await loadLoreEntry(rootDir, entryId);
3606
3693
  if (!entry) return false;
3607
3694
  const dateStr = entry.timestamp.slice(0, 10);
3608
- const entryPath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
3609
- if (!fs6.existsSync(entryPath)) return false;
3695
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
3696
+ if (!entryPath) return false;
3610
3697
  fs6.unlinkSync(entryPath);
3611
3698
  const dateDir = path7.dirname(entryPath);
3612
- const remaining = fs6.readdirSync(dateDir).filter((f) => f.endsWith(".yaml"));
3699
+ const remaining = fs6.readdirSync(dateDir).filter(isLoreFile);
3613
3700
  if (remaining.length === 0) {
3614
3701
  fs6.rmdirSync(dateDir);
3615
3702
  }
@@ -3619,10 +3706,16 @@ async function deleteLoreEntry(rootDir, entryId) {
3619
3706
  function applyFilter(entries, filter) {
3620
3707
  let result = entries;
3621
3708
  if (filter.author) {
3622
- result = result.filter((e) => e.author.id === filter.author);
3709
+ result = result.filter((e) => e.author === filter.author);
3623
3710
  }
3624
- if (filter.authorType) {
3625
- result = result.filter((e) => e.author.type === filter.authorType);
3711
+ if (filter.hasAgent !== void 0) {
3712
+ result = result.filter(
3713
+ (e) => filter.hasAgent ? e.agent != null : e.agent == null
3714
+ );
3715
+ } else if (filter.authorType) {
3716
+ result = result.filter(
3717
+ (e) => filter.authorType === "agent" ? e.agent != null : e.agent == null
3718
+ );
3626
3719
  }
3627
3720
  if (filter.symbol) {
3628
3721
  result = result.filter(
@@ -3651,17 +3744,24 @@ function applyFilter(entries, filter) {
3651
3744
  if (filter.limit) result = result.slice(0, filter.limit);
3652
3745
  return result;
3653
3746
  }
3654
- function generateLoreId(rootDir, dateStr) {
3747
+ function generateLoreId(rootDir, dateStr, author, timestamp) {
3748
+ const sanitized = sanitizeAuthor(author);
3749
+ const ts = new Date(timestamp);
3750
+ const hh = String(ts.getUTCHours()).padStart(2, "0");
3751
+ const mm = String(ts.getUTCMinutes()).padStart(2, "0");
3752
+ const ss = String(ts.getUTCSeconds()).padStart(2, "0");
3753
+ const timeStr = `${hh}${mm}${ss}`;
3754
+ const prefix = `L-${dateStr}-${sanitized}-${timeStr}`;
3655
3755
  const datePath = path7.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
3656
3756
  if (!fs6.existsSync(datePath)) {
3657
- return `L-${dateStr}-001`;
3757
+ return `${prefix}-001`;
3658
3758
  }
3659
- const existing = fs6.readdirSync(datePath).filter((f) => f.startsWith("L-") && f.endsWith(".yaml")).map((f) => {
3660
- const match = f.match(/L-\d{4}-\d{2}-\d{2}-(\d+)\.yaml/);
3759
+ const existing = fs6.readdirSync(datePath).filter((f) => f.startsWith(prefix) && isLoreFile(f)).map((f) => {
3760
+ const match = f.match(/-(\d{3})\.(yaml|lore)$/);
3661
3761
  return match ? parseInt(match[1], 10) : 0;
3662
3762
  });
3663
3763
  const next = existing.length > 0 ? Math.max(...existing) + 1 : 1;
3664
- return `L-${dateStr}-${String(next).padStart(3, "0")}`;
3764
+ return `${prefix}-${String(next).padStart(3, "0")}`;
3665
3765
  }
3666
3766
 
3667
3767
  // ../paradigm-mcp/src/utils/aspect-lore-bridge.ts
@@ -4470,7 +4570,7 @@ function assertStep(step, event) {
4470
4570
  async function validateAgainstSentinel(persona, options = {}) {
4471
4571
  const steps = [];
4472
4572
  try {
4473
- const { SentinelStorage } = await import("./dist-YHDSIZQD.js");
4573
+ const { SentinelStorage } = await import("./dist-IKBGY7FQ.js");
4474
4574
  const storage = new SentinelStorage();
4475
4575
  const events = storage.queryEvents?.({
4476
4576
  schemaId: "paradigm-personas",
@@ -3,23 +3,52 @@ import {
3
3
  __require
4
4
  } from "./chunk-ZXMDA7VB.js";
5
5
 
6
+ // src/core/lore/resolve-author.ts
7
+ import { execSync } from "child_process";
8
+ import * as os from "os";
9
+ function sanitizeAuthor(name) {
10
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 20) || "unknown";
11
+ }
12
+ function resolveAuthor() {
13
+ const envAuthor = process.env.PARADIGM_AUTHOR;
14
+ if (envAuthor) return sanitizeAuthor(envAuthor);
15
+ try {
16
+ const gitName = execSync("git config user.name", { encoding: "utf-8", timeout: 3e3 }).trim();
17
+ if (gitName) return sanitizeAuthor(gitName);
18
+ } catch {
19
+ }
20
+ try {
21
+ const username = os.userInfo().username;
22
+ if (username) return sanitizeAuthor(username);
23
+ } catch {
24
+ }
25
+ return "unknown";
26
+ }
27
+
6
28
  // src/core/lore/storage.ts
7
29
  import * as fs from "fs";
8
30
  import * as path from "path";
9
31
  import * as yaml from "js-yaml";
32
+ import { execSync as execSync2 } from "child_process";
10
33
 
11
34
  // src/core/lore/filter.ts
12
35
  function applyLoreFilter(entries, filter) {
13
36
  let result = entries;
14
37
  if (filter.author) {
15
- result = result.filter((e) => e.author.id === filter.author);
38
+ result = result.filter((e) => e.author === filter.author);
16
39
  }
17
- if (filter.authorType) {
18
- result = result.filter((e) => e.author.type === filter.authorType);
40
+ if (filter.hasAgent !== void 0) {
41
+ result = result.filter(
42
+ (e) => filter.hasAgent ? e.agent != null : e.agent == null
43
+ );
44
+ } else if (filter.authorType) {
45
+ result = result.filter(
46
+ (e) => filter.authorType === "agent" ? e.agent != null : e.agent == null
47
+ );
19
48
  }
20
49
  if (filter.symbol) {
21
50
  result = result.filter(
22
- (e) => e.symbols_touched.includes(filter.symbol) || e.symbols_created?.includes(filter.symbol)
51
+ (e) => e.symbols_touched?.includes(filter.symbol) || e.symbols_created?.includes(filter.symbol)
23
52
  );
24
53
  }
25
54
  if (filter.dateFrom) {
@@ -58,10 +87,65 @@ function applyLoreFilter(entries, filter) {
58
87
  return result;
59
88
  }
60
89
 
90
+ // src/core/lore/normalize.ts
91
+ function inferProvider(model) {
92
+ const lower = model.toLowerCase();
93
+ if (lower.includes("claude") || lower.includes("anthropic")) return "anthropic";
94
+ if (lower.includes("gpt") || lower.includes("openai") || lower.includes("o1") || lower.includes("o3")) return "openai";
95
+ if (lower.includes("gemini") || lower.includes("google") || lower.includes("palm")) return "google";
96
+ if (lower.includes("llama") || lower.includes("meta")) return "meta";
97
+ if (lower.includes("mistral") || lower.includes("mixtral")) return "mistral";
98
+ if (lower.includes("deepseek")) return "deepseek";
99
+ if (lower.includes("cohere") || lower.includes("command")) return "cohere";
100
+ return "unknown";
101
+ }
102
+ function normalizeLoreEntry(raw) {
103
+ const entry = raw;
104
+ const author = entry.author;
105
+ if (typeof author === "string") {
106
+ return raw;
107
+ }
108
+ if (author && typeof author === "object" && !Array.isArray(author)) {
109
+ const old = author;
110
+ if (old.type === "agent") {
111
+ entry.author = "unknown";
112
+ entry.agent = {
113
+ provider: old.model ? inferProvider(old.model) : inferProvider(old.id),
114
+ model: old.model || old.id
115
+ };
116
+ } else {
117
+ entry.author = old.id || "unknown";
118
+ }
119
+ delete entry.assistedBy;
120
+ }
121
+ return entry;
122
+ }
123
+
61
124
  // src/core/lore/storage.ts
62
125
  var LORE_DIR = ".paradigm/lore";
63
126
  var ENTRIES_DIR = "entries";
64
127
  var TIMELINE_FILE = "timeline.yaml";
128
+ function isLoreFile(filename) {
129
+ return filename.endsWith(".yaml") || filename.endsWith(".lore");
130
+ }
131
+ function resolveEntryPath(rootDir, dateStr, entryId) {
132
+ const dirPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
133
+ const lorePath = path.join(dirPath, `${entryId}.lore`);
134
+ if (fs.existsSync(lorePath)) return lorePath;
135
+ const yamlPath = path.join(dirPath, `${entryId}.yaml`);
136
+ if (fs.existsSync(yamlPath)) return yamlPath;
137
+ return null;
138
+ }
139
+ function captureGitContext(cwd) {
140
+ try {
141
+ const ref = execSync2("git rev-parse HEAD", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
142
+ const branch = execSync2("git rev-parse --abbrev-ref HEAD", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
143
+ const status = execSync2("git status --porcelain", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
144
+ return { ref, branch, dirty: status.length > 0 };
145
+ } catch {
146
+ return void 0;
147
+ }
148
+ }
65
149
  function validateLoreSymbols(rootDir, symbols) {
66
150
  const result = { unregistered: [], warnings: [] };
67
151
  if (symbols.length === 0) return result;
@@ -129,10 +213,16 @@ async function recordLore(rootDir, entry, options) {
129
213
  if (!fs.existsSync(datePath)) {
130
214
  fs.mkdirSync(datePath, { recursive: true });
131
215
  }
216
+ if (!entry.author) {
217
+ entry.author = resolveAuthor();
218
+ }
219
+ if (!entry.git_context) {
220
+ entry.git_context = captureGitContext(rootDir);
221
+ }
132
222
  if (!entry.id) {
133
- entry.id = generateLoreId(rootDir, dateStr);
223
+ entry.id = generateLoreId(rootDir, dateStr, entry.author, entry.timestamp);
134
224
  }
135
- const entryPath = path.join(datePath, `${entry.id}.yaml`);
225
+ const entryPath = path.join(datePath, `${entry.id}.lore`);
136
226
  fs.writeFileSync(entryPath, yaml.dump(entry, { lineWidth: -1, noRefs: true }));
137
227
  await rebuildTimeline(rootDir);
138
228
  return { validation };
@@ -149,12 +239,12 @@ async function loadLoreEntries(rootDir, filter) {
149
239
  if (filter?.dateFrom && dateDir < filter.dateFrom.slice(0, 10)) continue;
150
240
  if (filter?.dateTo && dateDir > filter.dateTo.slice(0, 10)) continue;
151
241
  const dirPath = path.join(entriesPath, dateDir);
152
- const files = fs.readdirSync(dirPath).filter((f) => f.endsWith(".yaml")).sort();
242
+ const files = fs.readdirSync(dirPath).filter(isLoreFile).sort();
153
243
  for (const file of files) {
154
244
  try {
155
245
  const content = fs.readFileSync(path.join(dirPath, file), "utf8");
156
- const entry = yaml.load(content);
157
- entries.push(entry);
246
+ const raw = yaml.load(content);
247
+ entries.push(normalizeLoreEntry(raw));
158
248
  } catch {
159
249
  }
160
250
  }
@@ -174,12 +264,13 @@ async function rebuildTimeline(rootDir) {
174
264
  const dateDirs = fs.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d));
175
265
  for (const dateDir of dateDirs) {
176
266
  const dirPath = path.join(entriesPath, dateDir);
177
- const files = fs.readdirSync(dirPath).filter((f) => f.endsWith(".yaml"));
267
+ const files = fs.readdirSync(dirPath).filter(isLoreFile);
178
268
  for (const file of files) {
179
269
  try {
180
270
  const content = fs.readFileSync(path.join(dirPath, file), "utf8");
181
- const entry = yaml.load(content);
182
- authors.add(entry.author.id);
271
+ const raw = yaml.load(content);
272
+ const entry = normalizeLoreEntry(raw);
273
+ authors.add(entry.author);
183
274
  entryCount++;
184
275
  if (!lastUpdated || entry.timestamp > lastUpdated) {
185
276
  lastUpdated = entry.timestamp;
@@ -219,8 +310,8 @@ async function addReview(rootDir, entryId, review) {
219
310
  return false;
220
311
  }
221
312
  const dateStr = entry.timestamp.slice(0, 10);
222
- const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
223
- if (!fs.existsSync(entryPath)) {
313
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
314
+ if (!entryPath) {
224
315
  return false;
225
316
  }
226
317
  entry.review = review;
@@ -231,11 +322,12 @@ async function loadLoreEntry(rootDir, entryId) {
231
322
  const dateMatch = entryId.match(/^L-(\d{4}-\d{2}-\d{2})-/);
232
323
  if (dateMatch) {
233
324
  const dateStr = dateMatch[1];
234
- const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
235
- if (fs.existsSync(entryPath)) {
325
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
326
+ if (entryPath) {
236
327
  try {
237
328
  const content = fs.readFileSync(entryPath, "utf8");
238
- return yaml.load(content);
329
+ const raw = yaml.load(content);
330
+ return normalizeLoreEntry(raw);
239
331
  } catch {
240
332
  return null;
241
333
  }
@@ -248,8 +340,8 @@ async function updateLoreEntry(rootDir, entryId, partial) {
248
340
  const entry = await loadLoreEntry(rootDir, entryId);
249
341
  if (!entry) return false;
250
342
  const dateStr = entry.timestamp.slice(0, 10);
251
- const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
252
- if (!fs.existsSync(entryPath)) return false;
343
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
344
+ if (!entryPath) return false;
253
345
  if (partial.title !== void 0) entry.title = partial.title;
254
346
  if (partial.summary !== void 0) entry.summary = partial.summary;
255
347
  if (partial.type !== void 0) entry.type = partial.type;
@@ -274,11 +366,11 @@ async function deleteLoreEntry(rootDir, entryId) {
274
366
  const entry = await loadLoreEntry(rootDir, entryId);
275
367
  if (!entry) return false;
276
368
  const dateStr = entry.timestamp.slice(0, 10);
277
- const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
278
- if (!fs.existsSync(entryPath)) return false;
369
+ const entryPath = resolveEntryPath(rootDir, dateStr, entryId);
370
+ if (!entryPath) return false;
279
371
  fs.unlinkSync(entryPath);
280
372
  const dateDir = path.dirname(entryPath);
281
- const remaining = fs.readdirSync(dateDir).filter((f) => f.endsWith(".yaml"));
373
+ const remaining = fs.readdirSync(dateDir).filter(isLoreFile);
282
374
  if (remaining.length === 0) {
283
375
  fs.rmdirSync(dateDir);
284
376
  }
@@ -288,7 +380,7 @@ async function deleteLoreEntry(rootDir, entryId) {
288
380
  function migrateLegacyEntries(rootDir) {
289
381
  const entriesPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR);
290
382
  if (!fs.existsSync(entriesPath)) return 0;
291
- const rootFiles = fs.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml") && !f.startsWith("."));
383
+ const rootFiles = fs.readdirSync(entriesPath).filter((f) => isLoreFile(f) && !f.startsWith("."));
292
384
  let migrated = 0;
293
385
  for (const file of rootFiles) {
294
386
  const filePath = path.join(entriesPath, file);
@@ -298,12 +390,15 @@ function migrateLegacyEntries(rootDir) {
298
390
  const content = fs.readFileSync(filePath, "utf8");
299
391
  const raw = yaml.load(content);
300
392
  if (raw.author && typeof raw.author === "object") continue;
393
+ if (typeof raw.author === "string" && raw.timestamp) continue;
301
394
  const dateStr = typeof raw.date === "string" ? raw.date.slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
302
395
  const datePath = path.join(entriesPath, dateStr);
303
396
  if (!fs.existsSync(datePath)) {
304
397
  fs.mkdirSync(datePath, { recursive: true });
305
398
  }
306
- const id = generateLoreId(rootDir, dateStr);
399
+ const author = resolveAuthor();
400
+ const timestamp = `${dateStr}T00:00:00.000Z`;
401
+ const id = generateLoreId(rootDir, dateStr, author, timestamp);
307
402
  const oldType = String(raw.type || "agent-session");
308
403
  const v2Type = ["agent-session", "human-note", "decision", "review", "incident", "milestone"].includes(oldType) ? oldType : "agent-session";
309
404
  let verification;
@@ -317,9 +412,10 @@ function migrateLegacyEntries(rootDir) {
317
412
  const v2Entry = {
318
413
  id,
319
414
  type: v2Type,
320
- timestamp: `${dateStr}T00:00:00.000Z`,
321
- author: { type: "agent", id: "unknown" },
322
- title: String(raw.title || file.replace(".yaml", "")),
415
+ timestamp,
416
+ author: "unknown",
417
+ agent: { provider: "unknown", model: "unknown" },
418
+ title: String(raw.title || file.replace(/\.(yaml|lore)$/, "")),
323
419
  summary: String(raw.summary || ""),
324
420
  symbols_touched: Array.isArray(raw.symbols_touched) ? raw.symbols_touched : [],
325
421
  files_modified: Array.isArray(raw.files_modified) ? raw.files_modified : void 0,
@@ -327,7 +423,7 @@ function migrateLegacyEntries(rootDir) {
327
423
  tags: ["migrated", oldType]
328
424
  };
329
425
  fs.writeFileSync(
330
- path.join(datePath, `${id}.yaml`),
426
+ path.join(datePath, `${id}.lore`),
331
427
  yaml.dump(v2Entry, { lineWidth: -1, noRefs: true })
332
428
  );
333
429
  fs.unlinkSync(filePath);
@@ -337,20 +433,28 @@ function migrateLegacyEntries(rootDir) {
337
433
  }
338
434
  return migrated;
339
435
  }
340
- function generateLoreId(rootDir, dateStr) {
436
+ function generateLoreId(rootDir, dateStr, author, timestamp) {
437
+ const sanitized = sanitizeAuthor(author);
438
+ const ts = new Date(timestamp);
439
+ const hh = String(ts.getUTCHours()).padStart(2, "0");
440
+ const mm = String(ts.getUTCMinutes()).padStart(2, "0");
441
+ const ss = String(ts.getUTCSeconds()).padStart(2, "0");
442
+ const timeStr = `${hh}${mm}${ss}`;
443
+ const prefix = `L-${dateStr}-${sanitized}-${timeStr}`;
341
444
  const datePath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
342
445
  if (!fs.existsSync(datePath)) {
343
- return `L-${dateStr}-001`;
446
+ return `${prefix}-001`;
344
447
  }
345
- const existing = fs.readdirSync(datePath).filter((f) => f.startsWith("L-") && f.endsWith(".yaml")).map((f) => {
346
- const match = f.match(/L-\d{4}-\d{2}-\d{2}-(\d+)\.yaml/);
448
+ const existing = fs.readdirSync(datePath).filter((f) => f.startsWith(prefix) && isLoreFile(f)).map((f) => {
449
+ const match = f.match(/-(\d{3})\.(yaml|lore)$/);
347
450
  return match ? parseInt(match[1], 10) : 0;
348
451
  });
349
452
  const next = existing.length > 0 ? Math.max(...existing) + 1 : 1;
350
- return `L-${dateStr}-${String(next).padStart(3, "0")}`;
453
+ return `${prefix}-${String(next).padStart(3, "0")}`;
351
454
  }
352
455
 
353
456
  export {
457
+ resolveAuthor,
354
458
  recordLore,
355
459
  loadLoreEntries,
356
460
  addReview,