memorix 1.2.0 → 1.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.
@@ -5837,6 +5837,7 @@ __export(claims_exports, {
5837
5837
  buildClaimIdentity: () => buildClaimIdentity,
5838
5838
  deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
5839
5839
  requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
5840
+ reviewClaim: () => reviewClaim,
5840
5841
  selectClaimsForTask: () => selectClaimsForTask,
5841
5842
  supersedeClaim: () => supersedeClaim,
5842
5843
  writeClaim: () => writeClaim
@@ -6023,6 +6024,39 @@ function writeClaim(store, input) {
6023
6024
  };
6024
6025
  });
6025
6026
  }
6027
+ function reviewClaim(store, input) {
6028
+ if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
6029
+ throw new Error("A claim review must approve or reject the claim");
6030
+ }
6031
+ const detail = compactText(input.detail, "review detail");
6032
+ return store.transaction(() => {
6033
+ const claim = store.getClaim(input.claimId);
6034
+ if (!claim) throw new Error("Claim was not found");
6035
+ if (input.reviewState === "approved" && claim.status === "superseded") {
6036
+ throw new Error("A superseded claim cannot be approved");
6037
+ }
6038
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
6039
+ const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
6040
+ const updated = {
6041
+ ...claim,
6042
+ status: nextStatus,
6043
+ reviewState: input.reviewState,
6044
+ updatedAt: now3
6045
+ };
6046
+ store.updateClaim(updated);
6047
+ store.recordEvent({
6048
+ projectId: claim.projectId,
6049
+ claimId: claim.id,
6050
+ kind: "reviewed",
6051
+ fromStatus: claim.status,
6052
+ toStatus: updated.status,
6053
+ detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
6054
+ createdAt: now3
6055
+ });
6056
+ reconcileConflicts(store, claim.projectId, claim.conflictKey, now3);
6057
+ return store.getClaim(claim.id) ?? updated;
6058
+ });
6059
+ }
6026
6060
  function supersedeClaim(store, input) {
6027
6061
  const evidence = validateEvidence(input.evidence);
6028
6062
  return store.transaction(() => {
@@ -6111,7 +6145,7 @@ function deriveLowRiskClaimsFromObservation(store, observation, codeStore) {
6111
6145
  scope: "project",
6112
6146
  confidence,
6113
6147
  observedAt: observation.updatedAt ?? observation.createdAt,
6114
- reviewState: "approved",
6148
+ reviewState: isGit ? "approved" : "needs-review",
6115
6149
  origin: isGit ? "git" : "derived",
6116
6150
  evidence
6117
6151
  });
@@ -7091,6 +7125,48 @@ var init_workflow_store = __esm({
7091
7125
  }
7092
7126
  });
7093
7127
 
7128
+ // src/codegraph/task-lens.ts
7129
+ function containsTaskKeyword(text, keyword) {
7130
+ const matcher = /^[a-z0-9_-]+$/i.test(keyword) ? new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?=[^a-z0-9_-]|$)`, "gi") : new RegExp(keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
7131
+ let match;
7132
+ while ((match = matcher.exec(text)) !== null) {
7133
+ const keywordIndex = match.index + (match[1]?.length ?? 0);
7134
+ if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
7135
+ }
7136
+ return false;
7137
+ }
7138
+ function isNegatedTaskKeyword(text, keywordIndex) {
7139
+ const boundary = Math.max(
7140
+ text.lastIndexOf(".", keywordIndex - 1),
7141
+ text.lastIndexOf("!", keywordIndex - 1),
7142
+ text.lastIndexOf("?", keywordIndex - 1),
7143
+ text.lastIndexOf(";", keywordIndex - 1),
7144
+ text.lastIndexOf("\n", keywordIndex - 1),
7145
+ text.lastIndexOf("\u3002", keywordIndex - 1),
7146
+ text.lastIndexOf("\uFF01", keywordIndex - 1),
7147
+ text.lastIndexOf("\uFF1F", keywordIndex - 1),
7148
+ text.lastIndexOf("\uFF1B", keywordIndex - 1)
7149
+ );
7150
+ let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
7151
+ const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
7152
+ let contrastMatch;
7153
+ let contrastEnd = -1;
7154
+ while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
7155
+ if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
7156
+ if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
7157
+ if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
7158
+ if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
7159
+ const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
7160
+ const chineseClause = prefix.slice(chineseClauseStart + 1);
7161
+ return /(?:\u4e0d\u8981|\u4e0d\u5e94|\u4e0d\u53ef|\u4e0d\u80fd|\u4e0d\u4f1a|\u7981\u6b62|\u52ff|\u4e0d)\s*(?:\u7acb\u5373|\u76f4\u63a5|\u518d|\u73b0\u5728|\u64c5\u81ea|\u5148)?\s*$/.test(chineseClause);
7162
+ }
7163
+ var init_task_lens = __esm({
7164
+ "src/codegraph/task-lens.ts"() {
7165
+ "use strict";
7166
+ init_esm_shims();
7167
+ }
7168
+ });
7169
+
7094
7170
  // src/knowledge/workflows.ts
7095
7171
  var workflows_exports = {};
7096
7172
  __export(workflows_exports, {
@@ -7413,20 +7489,27 @@ function taskScore(workflow, task) {
7413
7489
  const text = task.toLowerCase();
7414
7490
  let score = 0;
7415
7491
  const reasons = [];
7492
+ const matchedLenses = /* @__PURE__ */ new Set();
7493
+ const matchedTriggers = /* @__PURE__ */ new Set();
7416
7494
  for (const lens of workflow.taskLenses) {
7417
7495
  const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
7418
- if (terms.some((term) => text.includes(term))) {
7496
+ if (terms.some((term) => containsTaskKeyword(task, term))) {
7419
7497
  score += 20;
7420
7498
  reasons.push("matches " + lens + " workflow");
7499
+ matchedLenses.add(lens.toLowerCase());
7421
7500
  }
7422
7501
  }
7423
7502
  for (const trigger of workflow.triggers) {
7424
7503
  const term = trigger.toLowerCase().trim();
7425
- if (term.length >= 2 && text.includes(term)) {
7504
+ if (term.length >= 2 && containsTaskKeyword(task, term)) {
7426
7505
  score += 8;
7427
7506
  reasons.push('matches trigger "' + trigger + '"');
7507
+ matchedTriggers.add(term);
7428
7508
  }
7429
7509
  }
7510
+ const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
7511
+ const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
7512
+ if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
7430
7513
  return { score, reasons: [...new Set(reasons)] };
7431
7514
  }
7432
7515
  function selectWorkflows(input) {
@@ -7444,9 +7527,28 @@ function selectWorkflows(input) {
7444
7527
  return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
7445
7528
  }
7446
7529
  function importedWorkflowSpec(input) {
7530
+ let sourceMatter;
7531
+ try {
7532
+ sourceMatter = matter3(input.raw);
7533
+ } catch (error) {
7534
+ throw new Error("Malformed Windsurf workflow Markdown: " + (error instanceof Error ? error.message : String(error)));
7535
+ }
7536
+ if (sourceMatter.data.id !== void 0) {
7537
+ const id = requiredText(sourceMatter.data, "id");
7538
+ const sourcePath2 = "workflows/" + slug(id) + ".md";
7539
+ const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
7540
+ workspaceId: input.workspace.id,
7541
+ sourcePath: sourcePath2,
7542
+ contentHash: hash3(input.raw)
7543
+ });
7544
+ return materializeWorkflow({
7545
+ ...canonical,
7546
+ importedFrom: input.sourcePath.replace(/\\/g, "/")
7547
+ });
7548
+ }
7447
7549
  const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
7448
7550
  const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
7449
- const lenses = inferTaskLenses(entry.name + "\n" + entry.description + "\n" + content);
7551
+ const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
7450
7552
  const createdAt = now2();
7451
7553
  const sourcePath = "workflows/" + slug(entry.name) + ".md";
7452
7554
  const spec = {
@@ -7667,7 +7769,7 @@ async function selectWorkspaceWorkflows(input) {
7667
7769
  errors: synced.errors
7668
7770
  };
7669
7771
  }
7670
- var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
7772
+ var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
7671
7773
  var init_workflows = __esm({
7672
7774
  "src/knowledge/workflows.ts"() {
7673
7775
  "use strict";
@@ -7676,6 +7778,7 @@ var init_workflows = __esm({
7676
7778
  init_workflow_sync();
7677
7779
  init_workspace();
7678
7780
  init_workflow_store();
7781
+ init_task_lens();
7679
7782
  KNOWN_AGENTS = [
7680
7783
  "windsurf",
7681
7784
  "cursor",
@@ -7707,6 +7810,8 @@ var init_workflows = __esm({
7707
7810
  refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
7708
7811
  test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
7709
7812
  };
7813
+ GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
7814
+ GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
7710
7815
  }
7711
7816
  });
7712
7817