@hir4ta/mneme 0.22.0 → 0.22.3

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/server.js CHANGED
@@ -3394,41 +3394,6 @@ function writeAuditLog(entry) {
3394
3394
  console.error("Failed to write audit log:", error);
3395
3395
  }
3396
3396
  }
3397
- var getUnitsPath = () => path3.join(getMnemeDir(), "units", "units.json");
3398
- function readUnits() {
3399
- const filePath = getUnitsPath();
3400
- if (!fs4.existsSync(filePath)) {
3401
- return {
3402
- schemaVersion: 1,
3403
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3404
- items: []
3405
- };
3406
- }
3407
- const parsed = safeParseJsonFile(filePath);
3408
- if (!parsed || !Array.isArray(parsed.items)) {
3409
- return {
3410
- schemaVersion: 1,
3411
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3412
- items: []
3413
- };
3414
- }
3415
- return parsed;
3416
- }
3417
- function writeUnits(doc) {
3418
- const filePath = getUnitsPath();
3419
- fs4.mkdirSync(path3.dirname(filePath), { recursive: true });
3420
- fs4.writeFileSync(filePath, JSON.stringify(doc, null, 2));
3421
- }
3422
- function makeUnitId(sourceType, sourceId) {
3423
- const safe = sourceId.replace(/[^a-zA-Z0-9_-]/g, "-");
3424
- return `mc-${sourceType}-${safe}`;
3425
- }
3426
- function getPatternKind(type) {
3427
- if (type === "error-solution" || type === "bad") {
3428
- return "pitfall";
3429
- }
3430
- return "playbook";
3431
- }
3432
3397
  var listDatedJsonFiles = (dir) => {
3433
3398
  const files = listJsonFiles(dir);
3434
3399
  return files.filter((filePath) => {
@@ -4654,235 +4619,217 @@ app.delete("/api/patterns/:id", async (c) => {
4654
4619
  return c.json({ error: "Failed to delete pattern" }, 500);
4655
4620
  }
4656
4621
  });
4657
- app.get("/api/units", async (c) => {
4658
- const status = c.req.query("status");
4659
- const doc = readUnits();
4660
- const items = status && ["pending", "approved", "rejected"].includes(status) ? doc.items.filter((item) => item.status === status) : doc.items;
4661
- return c.json({
4662
- ...doc,
4663
- items: items.sort(
4664
- (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
4665
- )
4666
- });
4667
- });
4668
- app.get("/api/units/:id", async (c) => {
4669
- const id = sanitizeId(c.req.param("id"));
4670
- if (!id) {
4671
- return c.json({ error: "Invalid unit id" }, 400);
4672
- }
4673
- const doc = readUnits();
4674
- const item = doc.items.find((unit) => unit.id === id);
4675
- if (!item) {
4676
- return c.json({ error: "Unit not found" }, 404);
4677
- }
4678
- return c.json(item);
4679
- });
4680
- app.get("/api/approval-queue", async (c) => {
4681
- const doc = readUnits();
4682
- const pending = doc.items.filter((item) => item.status === "pending");
4683
- return c.json({
4684
- pending,
4685
- totalPending: pending.length,
4686
- byType: pending.reduce(
4687
- (acc, item) => {
4688
- acc[item.type] = (acc[item.type] || 0) + 1;
4689
- return acc;
4690
- },
4691
- {}
4692
- )
4693
- });
4694
- });
4695
- app.post("/api/units/generate", async (c) => {
4696
- const now = (/* @__PURE__ */ new Date()).toISOString();
4697
- const existing = readUnits();
4698
- const bySourceKey = new Map(
4699
- existing.items.map((item) => [`${item.sourceType}:${item.sourceId}`, item])
4622
+ function collectDevRules() {
4623
+ const items = [];
4624
+ const decisionFiles = listDatedJsonFiles(
4625
+ path3.join(getMnemeDir(), "decisions")
4700
4626
  );
4701
- const generated = [];
4702
- try {
4703
- const decisionFiles = listDatedJsonFiles(
4704
- path3.join(getMnemeDir(), "decisions")
4705
- );
4706
- for (const filePath of decisionFiles) {
4707
- const decision = safeParseJsonFile(filePath);
4708
- if (!decision) continue;
4709
- const sourceId = String(decision.id || "");
4710
- if (!sourceId) continue;
4711
- const sourceType = "decision";
4712
- const key = `${sourceType}:${sourceId}`;
4713
- const previous = bySourceKey.get(key);
4714
- generated.push({
4715
- id: previous?.id || makeUnitId(sourceType, sourceId),
4627
+ for (const filePath of decisionFiles) {
4628
+ const sourceName = path3.basename(filePath, ".json");
4629
+ const raw2 = safeParseJsonFile(filePath);
4630
+ if (!raw2) continue;
4631
+ const entries = [];
4632
+ if (Array.isArray(raw2.items)) {
4633
+ entries.push(...raw2.items);
4634
+ } else if (raw2.id) {
4635
+ entries.push(raw2);
4636
+ }
4637
+ for (const entry of entries) {
4638
+ const id = String(entry.id || "");
4639
+ if (!id) continue;
4640
+ items.push({
4641
+ id,
4716
4642
  type: "decision",
4717
- kind: "policy",
4718
- title: String(decision.title || sourceId),
4643
+ title: String(entry.title || entry.text || id),
4719
4644
  summary: String(
4720
- decision.decision || decision.reasoning || decision.title || ""
4645
+ entry.text || entry.decision || entry.reasoning || entry.title || ""
4721
4646
  ),
4722
- tags: Array.isArray(decision.tags) ? decision.tags.map((tag) => String(tag)) : [],
4723
- sourceId,
4724
- sourceType,
4725
- sourceRefs: [{ type: sourceType, id: sourceId }],
4726
- status: previous?.status || "pending",
4727
- createdAt: previous?.createdAt || now,
4728
- updatedAt: now,
4729
- reviewedAt: previous?.reviewedAt,
4730
- reviewedBy: previous?.reviewedBy
4647
+ tags: Array.isArray(entry.tags) ? entry.tags.map((t) => String(t)) : [],
4648
+ status: entry.status || "approved",
4649
+ priority: entry.priority ? String(entry.priority) : void 0,
4650
+ sourceFile: sourceName,
4651
+ createdAt: String(entry.createdAt || raw2.createdAt || ""),
4652
+ updatedAt: entry.updatedAt ? String(entry.updatedAt) : void 0
4731
4653
  });
4732
4654
  }
4733
- const ruleFiles = ["dev-rules", "review-guidelines"];
4734
- for (const ruleFile of ruleFiles) {
4735
- const filePath = path3.join(rulesDir(), `${ruleFile}.json`);
4736
- const doc = safeParseJsonFile(
4737
- filePath
4738
- );
4739
- if (!doc || !Array.isArray(doc.items)) continue;
4740
- for (const rule of doc.items) {
4741
- const ruleId = String(rule.id || "");
4742
- if (!ruleId) continue;
4743
- const sourceType = "rule";
4744
- const sourceId = `${ruleFile}:${ruleId}`;
4745
- const key = `${sourceType}:${sourceId}`;
4746
- const previous = bySourceKey.get(key);
4747
- const title = String(rule.text || rule.title || rule.rule || ruleId) || ruleId;
4748
- const summary = String(rule.rationale || rule.description || "") || title;
4749
- generated.push({
4750
- id: previous?.id || makeUnitId(sourceType, sourceId),
4751
- type: "rule",
4752
- kind: "policy",
4753
- title,
4754
- summary,
4755
- tags: Array.isArray(rule.tags) ? rule.tags.map((tag) => String(tag)) : [ruleFile],
4756
- sourceId,
4757
- sourceType,
4758
- sourceRefs: [{ type: sourceType, id: sourceId }],
4759
- status: previous?.status || "pending",
4760
- createdAt: previous?.createdAt || now,
4761
- updatedAt: now,
4762
- reviewedAt: previous?.reviewedAt,
4763
- reviewedBy: previous?.reviewedBy
4764
- });
4765
- }
4655
+ }
4656
+ const patternFiles = listJsonFiles(patternsDir());
4657
+ for (const filePath of patternFiles) {
4658
+ const sourceName = path3.basename(filePath, ".json");
4659
+ const doc = safeParseJsonFile(filePath);
4660
+ const entries = doc?.items || doc?.patterns || [];
4661
+ for (const entry of entries) {
4662
+ const id = String(entry.id || "");
4663
+ if (!id) continue;
4664
+ items.push({
4665
+ id,
4666
+ type: "pattern",
4667
+ title: String(
4668
+ entry.title || entry.errorPattern || entry.description || id
4669
+ ),
4670
+ summary: String(
4671
+ entry.solution || entry.description || entry.errorPattern || ""
4672
+ ),
4673
+ tags: Array.isArray(entry.tags) ? entry.tags.map((t) => String(t)) : [sourceName],
4674
+ status: entry.status || "approved",
4675
+ priority: entry.priority ? String(entry.priority) : void 0,
4676
+ sourceFile: sourceName,
4677
+ createdAt: String(entry.createdAt || ""),
4678
+ updatedAt: entry.updatedAt ? String(entry.updatedAt) : void 0
4679
+ });
4766
4680
  }
4767
- const patternFiles = listJsonFiles(patternsDir());
4768
- for (const patternFile of patternFiles) {
4769
- const sourceName = path3.basename(patternFile, ".json");
4770
- const doc = safeParseJsonFile(patternFile);
4771
- const items = doc?.items || doc?.patterns || [];
4772
- for (const pattern of items) {
4773
- const patternId = String(pattern.id || "");
4774
- if (!patternId) continue;
4775
- const sourceType = "pattern";
4776
- const sourceId = `${sourceName}:${patternId}`;
4777
- const key = `${sourceType}:${sourceId}`;
4778
- const previous = bySourceKey.get(key);
4779
- const title = String(
4780
- pattern.title || pattern.errorPattern || pattern.description || patternId
4781
- );
4782
- const summary = String(
4783
- pattern.solution || pattern.description || pattern.errorPattern || ""
4784
- );
4785
- generated.push({
4786
- id: previous?.id || makeUnitId(sourceType, sourceId),
4787
- type: "pattern",
4788
- kind: getPatternKind(String(pattern.type || "")),
4789
- title,
4790
- summary,
4791
- tags: Array.isArray(pattern.tags) ? pattern.tags.map((tag) => String(tag)) : [sourceName],
4792
- sourceId,
4793
- sourceType,
4794
- sourceRefs: [{ type: sourceType, id: sourceId }],
4795
- status: previous?.status || "pending",
4796
- createdAt: previous?.createdAt || now,
4797
- updatedAt: now,
4798
- reviewedAt: previous?.reviewedAt,
4799
- reviewedBy: previous?.reviewedBy
4800
- });
4801
- }
4681
+ }
4682
+ const ruleFileNames = ["dev-rules", "review-guidelines"];
4683
+ for (const ruleFile of ruleFileNames) {
4684
+ const filePath = path3.join(rulesDir(), `${ruleFile}.json`);
4685
+ const doc = safeParseJsonFile(
4686
+ filePath
4687
+ );
4688
+ if (!doc || !Array.isArray(doc.items)) continue;
4689
+ for (const entry of doc.items) {
4690
+ const id = String(entry.id || "");
4691
+ if (!id) continue;
4692
+ items.push({
4693
+ id,
4694
+ type: "rule",
4695
+ title: String(entry.text || entry.title || entry.rule || id),
4696
+ summary: String(entry.rationale || entry.description || ""),
4697
+ tags: Array.isArray(entry.tags) ? entry.tags.map((t) => String(t)) : [ruleFile],
4698
+ status: entry.status || "approved",
4699
+ priority: entry.priority ? String(entry.priority) : void 0,
4700
+ sourceFile: ruleFile,
4701
+ createdAt: String(entry.createdAt || ""),
4702
+ updatedAt: entry.updatedAt ? String(entry.updatedAt) : void 0
4703
+ });
4802
4704
  }
4803
- const next = {
4804
- schemaVersion: 1,
4805
- updatedAt: now,
4806
- items: generated.sort(
4807
- (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
4808
- )
4809
- };
4810
- writeUnits(next);
4811
- writeAuditLog({
4812
- entity: "unit",
4813
- action: "create",
4814
- targetId: "units",
4815
- detail: { count: generated.length }
4816
- });
4705
+ }
4706
+ return items;
4707
+ }
4708
+ app.get("/api/dev-rules", async (c) => {
4709
+ try {
4710
+ const status = c.req.query("status");
4711
+ const items = collectDevRules();
4712
+ const filtered = status && ["approved", "rejected"].includes(status) ? items.filter((item) => item.status === status) : items;
4817
4713
  return c.json({
4818
- generated: generated.length,
4819
- pending: generated.filter((item) => item.status === "pending").length,
4820
- updatedAt: now
4714
+ items: filtered,
4715
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4821
4716
  });
4822
4717
  } catch (error) {
4823
- console.error("Failed to generate units:", error);
4824
- return c.json({ error: "Failed to generate units" }, 500);
4718
+ console.error("Failed to read dev rules:", error);
4719
+ return c.json({ error: "Failed to read dev rules" }, 500);
4825
4720
  }
4826
4721
  });
4827
- app.patch("/api/units/:id/status", async (c) => {
4722
+ app.patch("/api/dev-rules/:type/:sourceFile/:id/status", async (c) => {
4723
+ const type = c.req.param("type");
4724
+ const sourceFile = c.req.param("sourceFile");
4828
4725
  const id = sanitizeId(c.req.param("id"));
4829
4726
  const body = await c.req.json();
4830
- if (!id) {
4831
- return c.json({ error: "Invalid unit id" }, 400);
4727
+ if (!id || !sourceFile) {
4728
+ return c.json({ error: "Invalid parameters" }, 400);
4832
4729
  }
4833
- if (!body.status || !["pending", "approved", "rejected"].includes(body.status)) {
4730
+ if (!body.status || !["approved", "rejected"].includes(body.status)) {
4834
4731
  return c.json({ error: "Invalid status" }, 400);
4835
4732
  }
4836
- const doc = readUnits();
4837
- const index = doc.items.findIndex((item) => item.id === id);
4838
- if (index === -1) {
4839
- return c.json({ error: "Unit not found" }, 404);
4840
- }
4841
- const now = (/* @__PURE__ */ new Date()).toISOString();
4842
- const actor = getCurrentUser();
4843
- const nextItem = {
4844
- ...doc.items[index],
4845
- status: body.status,
4846
- updatedAt: now,
4847
- reviewedAt: now,
4848
- reviewedBy: actor
4849
- };
4850
- doc.items[index] = nextItem;
4851
- doc.updatedAt = now;
4852
- writeUnits(doc);
4853
- writeAuditLog({
4854
- entity: "unit",
4855
- action: "update",
4856
- targetId: id,
4857
- detail: { status: body.status }
4858
- });
4859
- return c.json(nextItem);
4733
+ try {
4734
+ let filePath;
4735
+ if (type === "decision") {
4736
+ const found = findJsonFileById(
4737
+ path3.join(getMnemeDir(), "decisions"),
4738
+ sourceFile
4739
+ );
4740
+ if (!found) return c.json({ error: "Source file not found" }, 404);
4741
+ filePath = found;
4742
+ } else if (type === "pattern") {
4743
+ filePath = path3.join(patternsDir(), `${sourceFile}.json`);
4744
+ } else {
4745
+ filePath = path3.join(rulesDir(), `${sourceFile}.json`);
4746
+ }
4747
+ if (!fs4.existsSync(filePath)) {
4748
+ return c.json({ error: "Source file not found" }, 404);
4749
+ }
4750
+ const raw2 = JSON.parse(fs4.readFileSync(filePath, "utf-8"));
4751
+ const items = raw2.items || raw2.patterns || (raw2.id ? [raw2] : []);
4752
+ const target = items.find((item) => String(item.id) === id);
4753
+ if (!target) {
4754
+ return c.json({ error: "Item not found" }, 404);
4755
+ }
4756
+ target.status = body.status;
4757
+ target.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4758
+ if (!raw2.items && !raw2.patterns && raw2.id) {
4759
+ Object.assign(raw2, target);
4760
+ }
4761
+ fs4.writeFileSync(filePath, `${JSON.stringify(raw2, null, 2)}
4762
+ `);
4763
+ writeAuditLog({
4764
+ entity: "dev-rule",
4765
+ action: "update",
4766
+ targetId: id,
4767
+ detail: { type, sourceFile, status: body.status }
4768
+ });
4769
+ return c.json({ id, type, sourceFile, status: body.status });
4770
+ } catch (error) {
4771
+ console.error("Failed to update dev rule status:", error);
4772
+ return c.json({ error: "Failed to update status" }, 500);
4773
+ }
4860
4774
  });
4861
- app.delete("/api/units/:id", async (c) => {
4775
+ app.delete("/api/dev-rules/:type/:sourceFile/:id", async (c) => {
4776
+ const type = c.req.param("type");
4777
+ const sourceFile = c.req.param("sourceFile");
4862
4778
  const id = sanitizeId(c.req.param("id"));
4863
- if (!id) {
4864
- return c.json({ error: "Invalid unit id" }, 400);
4865
- }
4866
- const doc = readUnits();
4867
- const nextItems = doc.items.filter((item) => item.id !== id);
4868
- if (nextItems.length === doc.items.length) {
4869
- return c.json({ error: "Unit not found" }, 404);
4870
- }
4871
- doc.items = nextItems;
4872
- doc.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4873
- writeUnits(doc);
4874
- writeAuditLog({
4875
- entity: "unit",
4876
- action: "delete",
4877
- targetId: id
4878
- });
4879
- return c.json({ deleted: 1, id });
4779
+ if (!id || !sourceFile) {
4780
+ return c.json({ error: "Invalid parameters" }, 400);
4781
+ }
4782
+ try {
4783
+ let filePath;
4784
+ if (type === "decision") {
4785
+ const found = findJsonFileById(
4786
+ path3.join(getMnemeDir(), "decisions"),
4787
+ sourceFile
4788
+ );
4789
+ if (!found) return c.json({ error: "Source file not found" }, 404);
4790
+ filePath = found;
4791
+ } else if (type === "pattern") {
4792
+ filePath = path3.join(patternsDir(), `${sourceFile}.json`);
4793
+ } else {
4794
+ filePath = path3.join(rulesDir(), `${sourceFile}.json`);
4795
+ }
4796
+ if (!fs4.existsSync(filePath)) {
4797
+ return c.json({ error: "Source file not found" }, 404);
4798
+ }
4799
+ const raw2 = JSON.parse(fs4.readFileSync(filePath, "utf-8"));
4800
+ const arrayKey = raw2.items ? "items" : raw2.patterns ? "patterns" : null;
4801
+ if (arrayKey) {
4802
+ const before = raw2[arrayKey].length;
4803
+ raw2[arrayKey] = raw2[arrayKey].filter(
4804
+ (item) => String(item.id) !== id
4805
+ );
4806
+ if (raw2[arrayKey].length === before) {
4807
+ return c.json({ error: "Item not found" }, 404);
4808
+ }
4809
+ fs4.writeFileSync(filePath, `${JSON.stringify(raw2, null, 2)}
4810
+ `);
4811
+ } else if (raw2.id && String(raw2.id) === id) {
4812
+ fs4.unlinkSync(filePath);
4813
+ } else {
4814
+ return c.json({ error: "Item not found" }, 404);
4815
+ }
4816
+ writeAuditLog({
4817
+ entity: "dev-rule",
4818
+ action: "delete",
4819
+ targetId: id,
4820
+ detail: { type, sourceFile }
4821
+ });
4822
+ return c.json({ deleted: 1, id });
4823
+ } catch (error) {
4824
+ console.error("Failed to delete dev rule:", error);
4825
+ return c.json({ error: "Failed to delete dev rule" }, 500);
4826
+ }
4880
4827
  });
4881
4828
  app.get("/api/knowledge-graph", async (c) => {
4882
4829
  try {
4883
4830
  const mnemeDir2 = getMnemeDir();
4884
4831
  const sessionItems = readAllSessionIndexes(mnemeDir2).items;
4885
- const units = readUnits().items.filter(
4832
+ const devRules = collectDevRules().filter(
4886
4833
  (item) => item.status === "approved"
4887
4834
  );
4888
4835
  const sessionDataMap = /* @__PURE__ */ new Map();
@@ -4914,15 +4861,15 @@ app.get("/api/knowledge-graph", async (c) => {
4914
4861
  appliedCount: null,
4915
4862
  acceptedCount: null
4916
4863
  })),
4917
- ...units.map((item) => ({
4918
- id: `unit:${item.id}`,
4919
- entityType: "unit",
4864
+ ...devRules.map((item) => ({
4865
+ id: `rule:${item.type}:${item.id}`,
4866
+ entityType: "rule",
4920
4867
  entityId: item.id,
4921
4868
  title: item.title,
4922
4869
  tags: item.tags || [],
4923
4870
  createdAt: item.createdAt,
4924
4871
  unitSubtype: item.type || null,
4925
- sourceId: item.sourceId || null,
4872
+ sourceId: item.sourceFile || null,
4926
4873
  appliedCount: null,
4927
4874
  acceptedCount: null,
4928
4875
  branch: null,