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.
package/dist/cli/index.js CHANGED
@@ -3212,7 +3212,7 @@ ${import_picocolors.default.gray(m3)} ${s2}
3212
3212
 
3213
3213
  // src/cli/version.ts
3214
3214
  function getCliVersion() {
3215
- return true ? "1.2.0" : pkg.version;
3215
+ return true ? "1.2.1" : pkg.version;
3216
3216
  }
3217
3217
  var init_version = __esm({
3218
3218
  "src/cli/version.ts"() {
@@ -23372,9 +23372,9 @@ async function detectInstalledAgents() {
23372
23372
  } catch {
23373
23373
  }
23374
23374
  try {
23375
- const { execSync: execSync11 } = await import("child_process");
23375
+ const { execSync: execSync10 } = await import("child_process");
23376
23376
  const whereCmd = process.platform === "win32" ? "where gemini" : "which gemini";
23377
- execSync11(whereCmd, { stdio: "ignore" });
23377
+ execSync10(whereCmd, { stdio: "ignore" });
23378
23378
  agents.push("gemini-cli");
23379
23379
  } catch {
23380
23380
  }
@@ -23907,6 +23907,7 @@ __export(setup_exports, {
23907
23907
  getMcpAdapter: () => getMcpAdapter,
23908
23908
  getSetupAgentTargets: () => getSetupAgentTargets,
23909
23909
  getSetupIntegrationRows: () => getSetupIntegrationRows2,
23910
+ installAgentSetup: () => installAgentSetup,
23910
23911
  installAntigravityPluginPackage: () => installAntigravityPluginPackage,
23911
23912
  installGeminiExtensionPackage: () => installGeminiExtensionPackage,
23912
23913
  installHermesPluginPackage: () => installHermesPluginPackage,
@@ -24738,13 +24739,19 @@ async function installAgentSetup(agent, plan, global2) {
24738
24739
  } else if (hasOmpPackage) {
24739
24740
  v3.info(`${agent}: extension command, hook events, and skills are bundled in the Oh-my-Pi package`);
24740
24741
  } else if (hasExtensionPackage) {
24741
- if (plan.actions.includes("hooks") || plan.actions.includes("project-guidance")) {
24742
+ if (plan.actions.includes("hooks")) {
24742
24743
  const result = await installHooks2(agent, targetRoot, global2);
24743
24744
  v3.success(`${agent}: integration files -> ${result.configPath}`);
24745
+ } else if (plan.actions.includes("project-guidance")) {
24746
+ const rulesPath = await installAgentGuidance2(agent, targetRoot, global2);
24747
+ v3.success(`${agent}: guidance -> ${rulesPath}`);
24744
24748
  }
24745
- } else if (agent === "opencode" || plan.actions.includes("hooks") || plan.actions.includes("project-guidance")) {
24749
+ } else if (plan.actions.includes("hooks")) {
24746
24750
  const result = await installHooks2(agent, targetRoot, global2);
24747
24751
  v3.success(`${agent}: integration files -> ${result.configPath}`);
24752
+ } else if (plan.actions.includes("project-guidance")) {
24753
+ const rulesPath = await installAgentGuidance2(agent, targetRoot, global2);
24754
+ v3.success(`${agent}: guidance -> ${rulesPath}`);
24748
24755
  }
24749
24756
  if (agent === "pi") {
24750
24757
  v3.info("pi: Pi has no MCP config lane in the current CLI; use the installed package extension and skill.");
@@ -28205,6 +28212,7 @@ __export(claims_exports, {
28205
28212
  buildClaimIdentity: () => buildClaimIdentity,
28206
28213
  deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
28207
28214
  requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
28215
+ reviewClaim: () => reviewClaim,
28208
28216
  selectClaimsForTask: () => selectClaimsForTask,
28209
28217
  supersedeClaim: () => supersedeClaim,
28210
28218
  writeClaim: () => writeClaim
@@ -28391,6 +28399,39 @@ function writeClaim(store2, input) {
28391
28399
  };
28392
28400
  });
28393
28401
  }
28402
+ function reviewClaim(store2, input) {
28403
+ if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
28404
+ throw new Error("A claim review must approve or reject the claim");
28405
+ }
28406
+ const detail = compactText(input.detail, "review detail");
28407
+ return store2.transaction(() => {
28408
+ const claim = store2.getClaim(input.claimId);
28409
+ if (!claim) throw new Error("Claim was not found");
28410
+ if (input.reviewState === "approved" && claim.status === "superseded") {
28411
+ throw new Error("A superseded claim cannot be approved");
28412
+ }
28413
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
28414
+ const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
28415
+ const updated = {
28416
+ ...claim,
28417
+ status: nextStatus,
28418
+ reviewState: input.reviewState,
28419
+ updatedAt: now3
28420
+ };
28421
+ store2.updateClaim(updated);
28422
+ store2.recordEvent({
28423
+ projectId: claim.projectId,
28424
+ claimId: claim.id,
28425
+ kind: "reviewed",
28426
+ fromStatus: claim.status,
28427
+ toStatus: updated.status,
28428
+ detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
28429
+ createdAt: now3
28430
+ });
28431
+ reconcileConflicts(store2, claim.projectId, claim.conflictKey, now3);
28432
+ return store2.getClaim(claim.id) ?? updated;
28433
+ });
28434
+ }
28394
28435
  function supersedeClaim(store2, input) {
28395
28436
  const evidence = validateEvidence(input.evidence);
28396
28437
  return store2.transaction(() => {
@@ -28479,7 +28520,7 @@ function deriveLowRiskClaimsFromObservation(store2, observation, codeStore) {
28479
28520
  scope: "project",
28480
28521
  confidence,
28481
28522
  observedAt: observation.updatedAt ?? observation.createdAt,
28482
- reviewState: "approved",
28523
+ reviewState: isGit ? "approved" : "needs-review",
28483
28524
  origin: isGit ? "git" : "derived",
28484
28525
  evidence
28485
28526
  });
@@ -32811,6 +32852,350 @@ var init_workflow_sync = __esm({
32811
32852
  }
32812
32853
  });
32813
32854
 
32855
+ // src/codegraph/task-lens.ts
32856
+ var task_lens_exports = {};
32857
+ __export(task_lens_exports, {
32858
+ containsTaskKeyword: () => containsTaskKeyword,
32859
+ lensPathCandidates: () => lensPathCandidates,
32860
+ lensVerificationHints: () => lensVerificationHints,
32861
+ rankLensPaths: () => rankLensPaths,
32862
+ rankLensSources: () => rankLensSources,
32863
+ resolveTaskLens: () => resolveTaskLens,
32864
+ scoreLensSource: () => scoreLensSource,
32865
+ shouldShowLensSource: () => shouldShowLensSource
32866
+ });
32867
+ function normalizePath2(path34) {
32868
+ return path34.replace(/\\/g, "/");
32869
+ }
32870
+ function tokenize3(text) {
32871
+ return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
32872
+ }
32873
+ function containsTaskKeyword(text, keyword) {
32874
+ 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");
32875
+ let match;
32876
+ while ((match = matcher.exec(text)) !== null) {
32877
+ const keywordIndex = match.index + (match[1]?.length ?? 0);
32878
+ if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
32879
+ }
32880
+ return false;
32881
+ }
32882
+ function isNegatedTaskKeyword(text, keywordIndex) {
32883
+ const boundary = Math.max(
32884
+ text.lastIndexOf(".", keywordIndex - 1),
32885
+ text.lastIndexOf("!", keywordIndex - 1),
32886
+ text.lastIndexOf("?", keywordIndex - 1),
32887
+ text.lastIndexOf(";", keywordIndex - 1),
32888
+ text.lastIndexOf("\n", keywordIndex - 1),
32889
+ text.lastIndexOf("\u3002", keywordIndex - 1),
32890
+ text.lastIndexOf("\uFF01", keywordIndex - 1),
32891
+ text.lastIndexOf("\uFF1F", keywordIndex - 1),
32892
+ text.lastIndexOf("\uFF1B", keywordIndex - 1)
32893
+ );
32894
+ let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
32895
+ const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
32896
+ let contrastMatch;
32897
+ let contrastEnd = -1;
32898
+ while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
32899
+ if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
32900
+ if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
32901
+ if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
32902
+ if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
32903
+ const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
32904
+ const chineseClause = prefix.slice(chineseClauseStart + 1);
32905
+ 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);
32906
+ }
32907
+ function resolveTaskLens(task) {
32908
+ const normalized = (task ?? "").toLowerCase();
32909
+ if (!normalized.trim()) return LENSES.general;
32910
+ let best = {
32911
+ id: "general",
32912
+ score: 0,
32913
+ priority: Number.MAX_SAFE_INTEGER
32914
+ };
32915
+ for (const id of LENS_PRIORITY) {
32916
+ const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsTaskKeyword(normalized, keyword) ? 1 : 0), 0);
32917
+ const priority = LENS_PRIORITY.indexOf(id);
32918
+ if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
32919
+ best = { id, score, priority };
32920
+ }
32921
+ }
32922
+ return best.score > 0 ? LENSES[best.id] : LENSES.general;
32923
+ }
32924
+ function pathKindScore(path34, lens) {
32925
+ const normalized = normalizePath2(path34).toLowerCase();
32926
+ const name = normalized.split("/").pop() ?? normalized;
32927
+ const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
32928
+ const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
32929
+ const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
32930
+ const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
32931
+ const isChangelog = name === "changelog.md" || name === "changes.md";
32932
+ const isWorkflow = normalized.startsWith(".github/workflows/");
32933
+ switch (lens.id) {
32934
+ case "bugfix":
32935
+ return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
32936
+ case "test":
32937
+ return (isTest ? 90 : 0) + (isSource ? 45 : 0);
32938
+ case "release":
32939
+ return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
32940
+ case "onboarding":
32941
+ return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
32942
+ case "docs":
32943
+ return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
32944
+ case "refactor":
32945
+ return (isSource ? 70 : 0) + (isTest ? 65 : 0);
32946
+ case "feature":
32947
+ return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
32948
+ default:
32949
+ if (isSource || isTest) return 60;
32950
+ if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
32951
+ if (inDocs) return 20;
32952
+ return 0;
32953
+ }
32954
+ }
32955
+ function tokenScore(text, tokens) {
32956
+ if (tokens.length === 0) return 0;
32957
+ const normalized = text.toLowerCase();
32958
+ return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
32959
+ }
32960
+ function sourceTaskMatchScore(source, task) {
32961
+ const tokens = tokenize3(task ?? "");
32962
+ if (tokens.length === 0) return 0;
32963
+ const text = [
32964
+ source.title,
32965
+ source.path ?? "",
32966
+ source.symbol ?? ""
32967
+ ].join("\n").toLowerCase();
32968
+ const matches = tokens.filter((token) => text.includes(token)).length;
32969
+ if (matches >= 2) return 40;
32970
+ if (matches === 1) return 24;
32971
+ return 0;
32972
+ }
32973
+ function fallbackPathRank(path34) {
32974
+ const normalized = normalizePath2(path34);
32975
+ if (normalized.startsWith("src/")) return 0;
32976
+ if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
32977
+ if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
32978
+ if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
32979
+ return 4;
32980
+ }
32981
+ function rankLensPaths(paths, lens, task) {
32982
+ const tokens = tokenize3(task ?? "");
32983
+ return [...new Set(paths.map(normalizePath2))].map((path34, index) => ({
32984
+ path: path34,
32985
+ index,
32986
+ score: pathKindScore(path34, lens) + tokenScore(path34, tokens),
32987
+ fallback: fallbackPathRank(path34)
32988
+ })).sort((a3, b3) => b3.score - a3.score || a3.fallback - b3.fallback || a3.index - b3.index || a3.path.localeCompare(b3.path)).map((item) => item.path);
32989
+ }
32990
+ function lensPathCandidates(lens) {
32991
+ switch (lens.id) {
32992
+ case "release":
32993
+ return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
32994
+ case "onboarding":
32995
+ return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
32996
+ case "docs":
32997
+ return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
32998
+ case "test":
32999
+ case "bugfix":
33000
+ return ["tests", "test"];
33001
+ default:
33002
+ return [];
33003
+ }
33004
+ }
33005
+ function scoreLensSource(source, lens, task) {
33006
+ const tokens = tokenize3(task ?? "");
33007
+ const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
33008
+ return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
33009
+ }
33010
+ function rankLensSources(sources, lens, task) {
33011
+ return [...sources].map((source, index) => ({
33012
+ source,
33013
+ index,
33014
+ score: scoreLensSource(source, lens, task)
33015
+ })).sort((a3, b3) => b3.score - a3.score || a3.source.observationId - b3.source.observationId || a3.index - b3.index).map((item) => item.source);
33016
+ }
33017
+ function lensVerificationHints(lens) {
33018
+ switch (lens.id) {
33019
+ case "bugfix":
33020
+ return [
33021
+ "run the smallest failing test or repro first",
33022
+ "inspect the changed code path before trusting old memory"
33023
+ ];
33024
+ case "test":
33025
+ return [
33026
+ "run the exact focused test file or test name first",
33027
+ "inspect fixtures and harness setup before changing assertions"
33028
+ ];
33029
+ case "release":
33030
+ return [
33031
+ "run build, tests, package smoke, and publish dry-run where available",
33032
+ "verify package metadata, changelog, and Git state before publishing"
33033
+ ];
33034
+ case "onboarding":
33035
+ return [
33036
+ "read the docs/start files first, then inspect only the code paths needed for the task",
33037
+ "treat old implementation memories as leads until current code confirms them"
33038
+ ];
33039
+ case "refactor":
33040
+ return [
33041
+ "inspect call sites and tests before editing shared code",
33042
+ "run the narrow affected test plus one regression smoke"
33043
+ ];
33044
+ case "docs":
33045
+ return [
33046
+ "check headings, links, commands, and examples against current code",
33047
+ "run the smallest docs or package smoke available"
33048
+ ];
33049
+ case "feature":
33050
+ return [
33051
+ "inspect the closest existing implementation pattern before adding new code",
33052
+ "run focused tests plus one user-flow smoke after changes"
33053
+ ];
33054
+ default:
33055
+ return [
33056
+ "inspect the Start here files before editing",
33057
+ "run the smallest relevant test or smoke command after changes"
33058
+ ];
33059
+ }
33060
+ }
33061
+ function shouldShowLensSource(source, lens, task) {
33062
+ if (lens.id === "general") return true;
33063
+ const score = scoreLensSource(source, lens, task);
33064
+ if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
33065
+ return score > 0;
33066
+ }
33067
+ var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
33068
+ var init_task_lens = __esm({
33069
+ "src/codegraph/task-lens.ts"() {
33070
+ "use strict";
33071
+ init_esm_shims();
33072
+ STOP_WORDS = /* @__PURE__ */ new Set([
33073
+ "a",
33074
+ "an",
33075
+ "and",
33076
+ "as",
33077
+ "for",
33078
+ "in",
33079
+ "into",
33080
+ "of",
33081
+ "on",
33082
+ "onto",
33083
+ "the",
33084
+ "this",
33085
+ "that",
33086
+ "to",
33087
+ "with",
33088
+ "work",
33089
+ "project",
33090
+ "continue",
33091
+ "\u7EE7\u7EED",
33092
+ "\u9879\u76EE"
33093
+ ]);
33094
+ LENSES = {
33095
+ bugfix: {
33096
+ id: "bugfix",
33097
+ description: "debug the failure with current code and the smallest repro first",
33098
+ sourceLimit: 6,
33099
+ cautionLimit: 4,
33100
+ hideUnrelatedCautionDetails: true,
33101
+ hideUnrelatedReliableDetails: false
33102
+ },
33103
+ feature: {
33104
+ id: "feature",
33105
+ description: "build a scoped feature from nearby source, types, and user flow",
33106
+ sourceLimit: 6,
33107
+ cautionLimit: 3,
33108
+ hideUnrelatedCautionDetails: true,
33109
+ hideUnrelatedReliableDetails: false
33110
+ },
33111
+ release: {
33112
+ id: "release",
33113
+ description: "prepare a release using current metadata, changelog, build, and package checks",
33114
+ sourceLimit: 4,
33115
+ cautionLimit: 2,
33116
+ hideUnrelatedCautionDetails: true,
33117
+ hideUnrelatedReliableDetails: true
33118
+ },
33119
+ onboarding: {
33120
+ id: "onboarding",
33121
+ description: "understand the project shape before trusting old implementation details",
33122
+ sourceLimit: 4,
33123
+ cautionLimit: 2,
33124
+ hideUnrelatedCautionDetails: true,
33125
+ hideUnrelatedReliableDetails: true
33126
+ },
33127
+ refactor: {
33128
+ id: "refactor",
33129
+ description: "change structure carefully by reading shared code, call sites, and tests",
33130
+ sourceLimit: 6,
33131
+ cautionLimit: 4,
33132
+ hideUnrelatedCautionDetails: true,
33133
+ hideUnrelatedReliableDetails: false
33134
+ },
33135
+ docs: {
33136
+ id: "docs",
33137
+ description: "update documentation against current code and public entry points",
33138
+ sourceLimit: 5,
33139
+ cautionLimit: 2,
33140
+ hideUnrelatedCautionDetails: true,
33141
+ hideUnrelatedReliableDetails: true
33142
+ },
33143
+ test: {
33144
+ id: "test",
33145
+ description: "work from tests, fixtures, harnesses, and the related source files",
33146
+ sourceLimit: 6,
33147
+ cautionLimit: 3,
33148
+ hideUnrelatedCautionDetails: true,
33149
+ hideUnrelatedReliableDetails: false
33150
+ },
33151
+ general: {
33152
+ id: "general",
33153
+ description: "balanced project handoff with current facts, code memory, and verification hints",
33154
+ sourceLimit: 8,
33155
+ cautionLimit: 5,
33156
+ hideUnrelatedCautionDetails: false,
33157
+ hideUnrelatedReliableDetails: false
33158
+ }
33159
+ };
33160
+ KEYWORDS = {
33161
+ bugfix: [
33162
+ "bug",
33163
+ "crash",
33164
+ "incident",
33165
+ "debug",
33166
+ "error",
33167
+ "fail",
33168
+ "failing",
33169
+ "fix",
33170
+ "issue",
33171
+ "regression",
33172
+ "repro",
33173
+ "\u62A5\u9519",
33174
+ "\u5D29\u6E83",
33175
+ "\u6545\u969C",
33176
+ "\u5931\u8D25",
33177
+ "\u4FEE\u590D",
33178
+ "\u95EE\u9898"
33179
+ ],
33180
+ feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
33181
+ release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
33182
+ onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
33183
+ refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
33184
+ docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
33185
+ test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
33186
+ };
33187
+ LENS_PRIORITY = [
33188
+ "bugfix",
33189
+ "release",
33190
+ "test",
33191
+ "refactor",
33192
+ "feature",
33193
+ "docs",
33194
+ "onboarding"
33195
+ ];
33196
+ }
33197
+ });
33198
+
32814
33199
  // src/knowledge/workflows.ts
32815
33200
  var workflows_exports = {};
32816
33201
  __export(workflows_exports, {
@@ -33132,20 +33517,27 @@ function taskScore(workflow, task) {
33132
33517
  const text = task.toLowerCase();
33133
33518
  let score = 0;
33134
33519
  const reasons = [];
33520
+ const matchedLenses = /* @__PURE__ */ new Set();
33521
+ const matchedTriggers = /* @__PURE__ */ new Set();
33135
33522
  for (const lens of workflow.taskLenses) {
33136
33523
  const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
33137
- if (terms.some((term) => text.includes(term))) {
33524
+ if (terms.some((term) => containsTaskKeyword(task, term))) {
33138
33525
  score += 20;
33139
33526
  reasons.push("matches " + lens + " workflow");
33527
+ matchedLenses.add(lens.toLowerCase());
33140
33528
  }
33141
33529
  }
33142
33530
  for (const trigger of workflow.triggers) {
33143
33531
  const term = trigger.toLowerCase().trim();
33144
- if (term.length >= 2 && text.includes(term)) {
33532
+ if (term.length >= 2 && containsTaskKeyword(task, term)) {
33145
33533
  score += 8;
33146
33534
  reasons.push('matches trigger "' + trigger + '"');
33535
+ matchedTriggers.add(term);
33147
33536
  }
33148
33537
  }
33538
+ const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
33539
+ const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
33540
+ if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
33149
33541
  return { score, reasons: [...new Set(reasons)] };
33150
33542
  }
33151
33543
  function selectWorkflows(input) {
@@ -33163,9 +33555,28 @@ function selectWorkflows(input) {
33163
33555
  return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
33164
33556
  }
33165
33557
  function importedWorkflowSpec(input) {
33558
+ let sourceMatter;
33559
+ try {
33560
+ sourceMatter = (0, import_gray_matter2.default)(input.raw);
33561
+ } catch (error2) {
33562
+ throw new Error("Malformed Windsurf workflow Markdown: " + (error2 instanceof Error ? error2.message : String(error2)));
33563
+ }
33564
+ if (sourceMatter.data.id !== void 0) {
33565
+ const id = requiredText(sourceMatter.data, "id");
33566
+ const sourcePath2 = "workflows/" + slug(id) + ".md";
33567
+ const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
33568
+ workspaceId: input.workspace.id,
33569
+ sourcePath: sourcePath2,
33570
+ contentHash: hash2(input.raw)
33571
+ });
33572
+ return materializeWorkflow({
33573
+ ...canonical,
33574
+ importedFrom: input.sourcePath.replace(/\\/g, "/")
33575
+ });
33576
+ }
33166
33577
  const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
33167
33578
  const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
33168
- const lenses = inferTaskLenses(entry.name + "\n" + entry.description + "\n" + content);
33579
+ const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
33169
33580
  const createdAt = now();
33170
33581
  const sourcePath = "workflows/" + slug(entry.name) + ".md";
33171
33582
  const spec = {
@@ -33386,7 +33797,7 @@ async function selectWorkspaceWorkflows(input) {
33386
33797
  errors: synced.errors
33387
33798
  };
33388
33799
  }
33389
- var import_gray_matter2, KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
33800
+ var import_gray_matter2, KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
33390
33801
  var init_workflows = __esm({
33391
33802
  "src/knowledge/workflows.ts"() {
33392
33803
  "use strict";
@@ -33396,6 +33807,7 @@ var init_workflows = __esm({
33396
33807
  init_workflow_sync();
33397
33808
  init_workspace();
33398
33809
  init_workflow_store();
33810
+ init_task_lens();
33399
33811
  KNOWN_AGENTS = [
33400
33812
  "windsurf",
33401
33813
  "cursor",
@@ -33427,6 +33839,8 @@ var init_workflows = __esm({
33427
33839
  refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
33428
33840
  test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
33429
33841
  };
33842
+ GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
33843
+ GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
33430
33844
  }
33431
33845
  });
33432
33846
 
@@ -33774,7 +34188,8 @@ var init_workset = __esm({
33774
34188
  // src/codegraph/current-facts.ts
33775
34189
  var current_facts_exports = {};
33776
34190
  __export(current_facts_exports, {
33777
- collectCurrentProjectFacts: () => collectCurrentProjectFacts
34191
+ collectCurrentProjectFacts: () => collectCurrentProjectFacts,
34192
+ formatGitFact: () => formatGitFact
33778
34193
  });
33779
34194
  import { execFileSync } from "child_process";
33780
34195
  import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
@@ -33819,11 +34234,16 @@ function runGit(rootPath, args) {
33819
34234
  }
33820
34235
  }
33821
34236
  function readGitFacts(rootPath) {
33822
- const branch = runGit(rootPath, ["branch", "--show-current"]);
34237
+ const available = runGit(rootPath, ["rev-parse", "--is-inside-work-tree"]) === "true";
34238
+ if (!available) {
34239
+ return { available: false, dirty: false, detached: false };
34240
+ }
34241
+ const branch = runGit(rootPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]) ?? runGit(rootPath, ["branch", "--show-current"]);
33823
34242
  const commit = runGit(rootPath, ["rev-parse", "--short", "HEAD"]);
33824
34243
  const latestCommit = runGit(rootPath, ["log", "-1", "--pretty=%s"]);
33825
34244
  const dirty = Boolean(runGit(rootPath, ["status", "--porcelain"]));
33826
34245
  return {
34246
+ available: true,
33827
34247
  ...branch ? { branch } : {},
33828
34248
  ...commit ? { commit } : {},
33829
34249
  ...latestCommit ? { latestCommit } : {},
@@ -33831,6 +34251,15 @@ function readGitFacts(rootPath) {
33831
34251
  detached: !branch
33832
34252
  };
33833
34253
  }
34254
+ function formatGitFact(git) {
34255
+ if (!git.available) return "Git: unavailable";
34256
+ const parts = [];
34257
+ if (git.detached) parts.push("detached HEAD");
34258
+ else if (git.branch) parts.push("branch " + git.branch);
34259
+ if (git.commit) parts.push("commit " + git.commit);
34260
+ parts.push(git.dirty ? "dirty worktree" : "clean worktree");
34261
+ return "Git: " + parts.join(", ");
34262
+ }
33834
34263
  function parseProgressNote(content) {
33835
34264
  const lastUpdated = content.match(/Last updated\*\*:\s*(\d{4}-\d{2}-\d{2})/i) ?? content.match(/Last updated:\s*(\d{4}-\d{2}-\d{2})/i);
33836
34265
  const branchHint = content.match(/Branch\*\*:\s*([^\r\n]+)/i) ?? content.match(/Branch:\s*([^\r\n]+)/i);
@@ -35101,319 +35530,6 @@ var init_project_context = __esm({
35101
35530
  }
35102
35531
  });
35103
35532
 
35104
- // src/codegraph/task-lens.ts
35105
- var task_lens_exports = {};
35106
- __export(task_lens_exports, {
35107
- lensPathCandidates: () => lensPathCandidates,
35108
- lensVerificationHints: () => lensVerificationHints,
35109
- rankLensPaths: () => rankLensPaths,
35110
- rankLensSources: () => rankLensSources,
35111
- resolveTaskLens: () => resolveTaskLens,
35112
- scoreLensSource: () => scoreLensSource,
35113
- shouldShowLensSource: () => shouldShowLensSource
35114
- });
35115
- function normalizePath2(path34) {
35116
- return path34.replace(/\\/g, "/");
35117
- }
35118
- function tokenize3(text) {
35119
- return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
35120
- }
35121
- function containsKeyword(text, keyword) {
35122
- if (/^[a-z0-9_-]+$/i.test(keyword)) {
35123
- return new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9_-]|$)`, "i").test(text);
35124
- }
35125
- return text.includes(keyword);
35126
- }
35127
- function resolveTaskLens(task) {
35128
- const normalized = (task ?? "").toLowerCase();
35129
- if (!normalized.trim()) return LENSES.general;
35130
- let best = {
35131
- id: "general",
35132
- score: 0,
35133
- priority: Number.MAX_SAFE_INTEGER
35134
- };
35135
- for (const id of LENS_PRIORITY) {
35136
- const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsKeyword(normalized, keyword) ? 1 : 0), 0);
35137
- const priority = LENS_PRIORITY.indexOf(id);
35138
- if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
35139
- best = { id, score, priority };
35140
- }
35141
- }
35142
- return best.score > 0 ? LENSES[best.id] : LENSES.general;
35143
- }
35144
- function pathKindScore(path34, lens) {
35145
- const normalized = normalizePath2(path34).toLowerCase();
35146
- const name = normalized.split("/").pop() ?? normalized;
35147
- const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
35148
- const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
35149
- const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
35150
- const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
35151
- const isChangelog = name === "changelog.md" || name === "changes.md";
35152
- const isWorkflow = normalized.startsWith(".github/workflows/");
35153
- switch (lens.id) {
35154
- case "bugfix":
35155
- return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
35156
- case "test":
35157
- return (isTest ? 90 : 0) + (isSource ? 45 : 0);
35158
- case "release":
35159
- return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
35160
- case "onboarding":
35161
- return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
35162
- case "docs":
35163
- return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
35164
- case "refactor":
35165
- return (isSource ? 70 : 0) + (isTest ? 65 : 0);
35166
- case "feature":
35167
- return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
35168
- default:
35169
- if (isSource || isTest) return 60;
35170
- if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
35171
- if (inDocs) return 20;
35172
- return 0;
35173
- }
35174
- }
35175
- function tokenScore(text, tokens) {
35176
- if (tokens.length === 0) return 0;
35177
- const normalized = text.toLowerCase();
35178
- return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
35179
- }
35180
- function sourceTaskMatchScore(source, task) {
35181
- const tokens = tokenize3(task ?? "");
35182
- if (tokens.length === 0) return 0;
35183
- const text = [
35184
- source.title,
35185
- source.path ?? "",
35186
- source.symbol ?? ""
35187
- ].join("\n").toLowerCase();
35188
- const matches = tokens.filter((token) => text.includes(token)).length;
35189
- if (matches >= 2) return 40;
35190
- if (matches === 1) return 24;
35191
- return 0;
35192
- }
35193
- function fallbackPathRank(path34) {
35194
- const normalized = normalizePath2(path34);
35195
- if (normalized.startsWith("src/")) return 0;
35196
- if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
35197
- if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
35198
- if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
35199
- return 4;
35200
- }
35201
- function rankLensPaths(paths, lens, task) {
35202
- const tokens = tokenize3(task ?? "");
35203
- return [...new Set(paths.map(normalizePath2))].map((path34, index) => ({
35204
- path: path34,
35205
- index,
35206
- score: pathKindScore(path34, lens) + tokenScore(path34, tokens),
35207
- fallback: fallbackPathRank(path34)
35208
- })).sort((a3, b3) => b3.score - a3.score || a3.fallback - b3.fallback || a3.index - b3.index || a3.path.localeCompare(b3.path)).map((item) => item.path);
35209
- }
35210
- function lensPathCandidates(lens) {
35211
- switch (lens.id) {
35212
- case "release":
35213
- return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
35214
- case "onboarding":
35215
- return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
35216
- case "docs":
35217
- return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
35218
- case "test":
35219
- case "bugfix":
35220
- return ["tests", "test"];
35221
- default:
35222
- return [];
35223
- }
35224
- }
35225
- function scoreLensSource(source, lens, task) {
35226
- const tokens = tokenize3(task ?? "");
35227
- const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
35228
- return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
35229
- }
35230
- function rankLensSources(sources, lens, task) {
35231
- return [...sources].map((source, index) => ({
35232
- source,
35233
- index,
35234
- score: scoreLensSource(source, lens, task)
35235
- })).sort((a3, b3) => b3.score - a3.score || a3.source.observationId - b3.source.observationId || a3.index - b3.index).map((item) => item.source);
35236
- }
35237
- function lensVerificationHints(lens) {
35238
- switch (lens.id) {
35239
- case "bugfix":
35240
- return [
35241
- "run the smallest failing test or repro first",
35242
- "inspect the changed code path before trusting old memory"
35243
- ];
35244
- case "test":
35245
- return [
35246
- "run the exact focused test file or test name first",
35247
- "inspect fixtures and harness setup before changing assertions"
35248
- ];
35249
- case "release":
35250
- return [
35251
- "run build, tests, package smoke, and publish dry-run where available",
35252
- "verify package metadata, changelog, and Git state before publishing"
35253
- ];
35254
- case "onboarding":
35255
- return [
35256
- "read the docs/start files first, then inspect only the code paths needed for the task",
35257
- "treat old implementation memories as leads until current code confirms them"
35258
- ];
35259
- case "refactor":
35260
- return [
35261
- "inspect call sites and tests before editing shared code",
35262
- "run the narrow affected test plus one regression smoke"
35263
- ];
35264
- case "docs":
35265
- return [
35266
- "check headings, links, commands, and examples against current code",
35267
- "run the smallest docs or package smoke available"
35268
- ];
35269
- case "feature":
35270
- return [
35271
- "inspect the closest existing implementation pattern before adding new code",
35272
- "run focused tests plus one user-flow smoke after changes"
35273
- ];
35274
- default:
35275
- return [
35276
- "inspect the Start here files before editing",
35277
- "run the smallest relevant test or smoke command after changes"
35278
- ];
35279
- }
35280
- }
35281
- function shouldShowLensSource(source, lens, task) {
35282
- if (lens.id === "general") return true;
35283
- const score = scoreLensSource(source, lens, task);
35284
- if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
35285
- return score > 0;
35286
- }
35287
- var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
35288
- var init_task_lens = __esm({
35289
- "src/codegraph/task-lens.ts"() {
35290
- "use strict";
35291
- init_esm_shims();
35292
- STOP_WORDS = /* @__PURE__ */ new Set([
35293
- "a",
35294
- "an",
35295
- "and",
35296
- "as",
35297
- "for",
35298
- "in",
35299
- "into",
35300
- "of",
35301
- "on",
35302
- "onto",
35303
- "the",
35304
- "this",
35305
- "that",
35306
- "to",
35307
- "with",
35308
- "work",
35309
- "project",
35310
- "continue",
35311
- "\u7EE7\u7EED",
35312
- "\u9879\u76EE"
35313
- ]);
35314
- LENSES = {
35315
- bugfix: {
35316
- id: "bugfix",
35317
- description: "debug the failure with current code and the smallest repro first",
35318
- sourceLimit: 6,
35319
- cautionLimit: 4,
35320
- hideUnrelatedCautionDetails: true,
35321
- hideUnrelatedReliableDetails: false
35322
- },
35323
- feature: {
35324
- id: "feature",
35325
- description: "build a scoped feature from nearby source, types, and user flow",
35326
- sourceLimit: 6,
35327
- cautionLimit: 3,
35328
- hideUnrelatedCautionDetails: true,
35329
- hideUnrelatedReliableDetails: false
35330
- },
35331
- release: {
35332
- id: "release",
35333
- description: "prepare a release using current metadata, changelog, build, and package checks",
35334
- sourceLimit: 4,
35335
- cautionLimit: 2,
35336
- hideUnrelatedCautionDetails: true,
35337
- hideUnrelatedReliableDetails: true
35338
- },
35339
- onboarding: {
35340
- id: "onboarding",
35341
- description: "understand the project shape before trusting old implementation details",
35342
- sourceLimit: 4,
35343
- cautionLimit: 2,
35344
- hideUnrelatedCautionDetails: true,
35345
- hideUnrelatedReliableDetails: true
35346
- },
35347
- refactor: {
35348
- id: "refactor",
35349
- description: "change structure carefully by reading shared code, call sites, and tests",
35350
- sourceLimit: 6,
35351
- cautionLimit: 4,
35352
- hideUnrelatedCautionDetails: true,
35353
- hideUnrelatedReliableDetails: false
35354
- },
35355
- docs: {
35356
- id: "docs",
35357
- description: "update documentation against current code and public entry points",
35358
- sourceLimit: 5,
35359
- cautionLimit: 2,
35360
- hideUnrelatedCautionDetails: true,
35361
- hideUnrelatedReliableDetails: true
35362
- },
35363
- test: {
35364
- id: "test",
35365
- description: "work from tests, fixtures, harnesses, and the related source files",
35366
- sourceLimit: 6,
35367
- cautionLimit: 3,
35368
- hideUnrelatedCautionDetails: true,
35369
- hideUnrelatedReliableDetails: false
35370
- },
35371
- general: {
35372
- id: "general",
35373
- description: "balanced project handoff with current facts, code memory, and verification hints",
35374
- sourceLimit: 8,
35375
- cautionLimit: 5,
35376
- hideUnrelatedCautionDetails: false,
35377
- hideUnrelatedReliableDetails: false
35378
- }
35379
- };
35380
- KEYWORDS = {
35381
- bugfix: [
35382
- "bug",
35383
- "crash",
35384
- "debug",
35385
- "error",
35386
- "fail",
35387
- "failing",
35388
- "fix",
35389
- "issue",
35390
- "regression",
35391
- "repro",
35392
- "\u62A5\u9519",
35393
- "\u5D29\u6E83",
35394
- "\u5931\u8D25",
35395
- "\u4FEE\u590D",
35396
- "\u95EE\u9898"
35397
- ],
35398
- feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
35399
- release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
35400
- onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
35401
- refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
35402
- docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
35403
- test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
35404
- };
35405
- LENS_PRIORITY = [
35406
- "bugfix",
35407
- "release",
35408
- "test",
35409
- "refactor",
35410
- "feature",
35411
- "docs",
35412
- "onboarding"
35413
- ];
35414
- }
35415
- });
35416
-
35417
35533
  // src/codegraph/auto-context.ts
35418
35534
  var auto_context_exports = {};
35419
35535
  __export(auto_context_exports, {
@@ -35709,15 +35825,7 @@ function formatCurrentFactsLines(facts) {
35709
35825
  if (facts.latestChangelog) {
35710
35826
  lines.push(`- Latest changelog: ${facts.latestChangelog.version}${facts.latestChangelog.date ? ` (${facts.latestChangelog.date})` : ""}`);
35711
35827
  }
35712
- const gitParts = [];
35713
- if (facts.git.detached) {
35714
- gitParts.push("detached HEAD");
35715
- } else if (facts.git.branch) {
35716
- gitParts.push(`branch ${facts.git.branch}`);
35717
- }
35718
- if (facts.git.commit) gitParts.push(`commit ${facts.git.commit}`);
35719
- gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
35720
- lines.push(`- Git: ${gitParts.join(", ")}`);
35828
+ lines.push("- " + formatGitFact(facts.git));
35721
35829
  if (facts.git.latestCommit) lines.push(`- Latest commit: ${facts.git.latestCommit}`);
35722
35830
  lines.push("- Current facts above outrank progress/dev-log files when they conflict.");
35723
35831
  if (facts.staleNotes.length > 0) {
@@ -35739,11 +35847,7 @@ function worksetFactLines(facts) {
35739
35847
  if (facts.latestChangelog) {
35740
35848
  lines.push("Latest changelog: " + facts.latestChangelog.version + (facts.latestChangelog.date ? " (" + facts.latestChangelog.date + ")" : ""));
35741
35849
  }
35742
- const gitParts = [];
35743
- if (facts.git.branch) gitParts.push("branch " + facts.git.branch);
35744
- if (facts.git.commit) gitParts.push("commit " + facts.git.commit);
35745
- gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
35746
- lines.push("Git: " + gitParts.join(", "));
35850
+ lines.push(formatGitFact(facts.git));
35747
35851
  for (const note of facts.staleNotes.slice(0, 1)) {
35748
35852
  lines.push(
35749
35853
  "Historical note: " + note.path + (note.branchHint ? " (branch hint " + note.branchHint + "; " + note.reason + ")" : " (" + note.reason + ")")
@@ -36263,7 +36367,7 @@ function compactFacts(project) {
36263
36367
  if (current.latestChangelog) {
36264
36368
  facts.push("Latest changelog: " + current.latestChangelog.version + (current.latestChangelog.date ? " (" + current.latestChangelog.date + ")" : ""));
36265
36369
  }
36266
- facts.push("Git: " + (current.git.branch ? "branch " + current.git.branch + ", " : "") + (current.git.dirty ? "dirty worktree" : "clean worktree"));
36370
+ facts.push(formatGitFact(current.git));
36267
36371
  return { facts, dirty: current.git.dirty };
36268
36372
  }
36269
36373
  var codegraph_default;
@@ -36999,6 +37103,8 @@ function usage() {
36999
37103
  " memorix knowledge init [--mode local]",
37000
37104
  " memorix knowledge init --mode versioned --path C:\\project\\docs\\knowledge",
37001
37105
  " memorix knowledge status",
37106
+ " memorix knowledge claims",
37107
+ ' memorix knowledge review --id <claim-id> --review approved --detail "checked current source evidence"',
37002
37108
  " memorix knowledge compile",
37003
37109
  " memorix knowledge lint",
37004
37110
  " memorix knowledge apply --proposal <id> [--force]",
@@ -37024,6 +37130,10 @@ function workflowVerdict(value) {
37024
37130
  if (value === "passed" || value === "failed" || value === "not-run") return value;
37025
37131
  throw new Error("workflow verdict must be passed, failed, or not-run");
37026
37132
  }
37133
+ function claimReviewState(value) {
37134
+ if (value === "approved" || value === "rejected") return value;
37135
+ throw new Error("claim review must be approved or rejected");
37136
+ }
37027
37137
  function evidenceList(value) {
37028
37138
  if (typeof value !== "string" || !value.trim()) return [];
37029
37139
  return [...new Set(value.split(",").map((item) => item.trim()).filter(Boolean))];
@@ -37036,6 +37146,7 @@ var init_knowledge = __esm({
37036
37146
  init_dist2();
37037
37147
  init_store();
37038
37148
  init_claim_store();
37149
+ init_claims();
37039
37150
  init_wiki();
37040
37151
  init_workspace();
37041
37152
  init_workspace_store();
@@ -37061,6 +37172,8 @@ var init_knowledge = __esm({
37061
37172
  failure: { type: "string", description: "Sanitized failure reason for a workflow run" },
37062
37173
  snapshot: { type: "string", description: "Starting code snapshot id for a workflow run" },
37063
37174
  evidence: { type: "string", description: "Comma-separated evidence ids selected for a workflow run" },
37175
+ review: { type: "string", description: "Claim review verdict: approved or rejected" },
37176
+ detail: { type: "string", description: "Evidence check performed for a claim review" },
37064
37177
  json: { type: "boolean", description: "Emit machine-readable JSON output" }
37065
37178
  },
37066
37179
  run: async ({ args }) => {
@@ -37096,6 +37209,34 @@ var init_knowledge = __esm({
37096
37209
  await claimStore.init(dataDir);
37097
37210
  const workspaceStore = new KnowledgeWorkspaceStore();
37098
37211
  await workspaceStore.init(dataDir);
37212
+ if (action === "claims") {
37213
+ const claims = claimStore.listClaims(project.id, { limit: 100 });
37214
+ emitResult(
37215
+ { project, workspace, claims },
37216
+ "Claims: " + claims.length + " total, " + claims.filter((claim) => claim.reviewState === "needs-review").length + " awaiting review.",
37217
+ asJson
37218
+ );
37219
+ return;
37220
+ }
37221
+ if (action === "review") {
37222
+ const claimId = requiredText2(args.id, "claim id");
37223
+ const existing = claimStore.getClaim(claimId);
37224
+ if (!existing || existing.projectId !== project.id) {
37225
+ emitError("Claim was not found for this knowledge workspace.", asJson);
37226
+ return;
37227
+ }
37228
+ const claim = reviewClaim(claimStore, {
37229
+ claimId,
37230
+ reviewState: claimReviewState(args.review),
37231
+ detail: requiredText2(args.detail, "review detail")
37232
+ });
37233
+ emitResult(
37234
+ { project, workspace, claim },
37235
+ "Claim " + claim.id + " marked " + claim.reviewState + ".",
37236
+ asJson
37237
+ );
37238
+ return;
37239
+ }
37099
37240
  if (action === "workflow") {
37100
37241
  const workflowAction = (positional[1] || "list").toLowerCase();
37101
37242
  const workflowStore = new WorkflowStore();
@@ -64407,6 +64548,24 @@ async function createAutoRelations(obs, extracted, graphManager) {
64407
64548
  const relationType = inferRelationType(obs);
64408
64549
  const relations = [];
64409
64550
  const selfName = obs.entityName.toLowerCase();
64551
+ const explicitRelated = [...new Set((obs.relatedEntities ?? []).map((name) => name.trim()).filter((name) => name && name.toLowerCase() !== selfName))];
64552
+ if (explicitRelated.length > 0) {
64553
+ await graphManager.createEntities(explicitRelated.map((name) => ({
64554
+ name,
64555
+ entityType: "related",
64556
+ observations: []
64557
+ })));
64558
+ for (const name of explicitRelated) {
64559
+ const matchedEntity = graphManager.findEntityByName(name);
64560
+ if (matchedEntity) {
64561
+ relations.push({
64562
+ from: obs.entityName,
64563
+ to: matchedEntity.name,
64564
+ relationType: "related_entity"
64565
+ });
64566
+ }
64567
+ }
64568
+ }
64410
64569
  const candidates = [
64411
64570
  ...extracted.identifiers,
64412
64571
  ...extracted.files.map((f4) => f4.split("/").pop()?.replace(/\.\w+$/, "") ?? ""),
@@ -64452,6 +64611,36 @@ var init_auto_relations = __esm({
64452
64611
  }
64453
64612
  });
64454
64613
 
64614
+ // src/memory/graph-scope.ts
64615
+ function projectGraphEntityNames(observations2) {
64616
+ const entityNames = /* @__PURE__ */ new Set();
64617
+ for (const observation of observations2) {
64618
+ if ((observation.status ?? "active") !== "active") continue;
64619
+ const entityName = observation.entityName?.trim();
64620
+ if (entityName) entityNames.add(entityName);
64621
+ for (const relatedEntity of observation.relatedEntities ?? []) {
64622
+ const name = relatedEntity.trim();
64623
+ if (name) entityNames.add(name);
64624
+ }
64625
+ }
64626
+ return entityNames;
64627
+ }
64628
+ function scopeKnowledgeGraphToProject(graph, observations2) {
64629
+ const entityNames = projectGraphEntityNames(observations2);
64630
+ const entities = graph.entities.filter((entity) => entityNames.has(entity.name));
64631
+ const visibleNames = new Set(entities.map((entity) => entity.name));
64632
+ const relations = graph.relations.filter(
64633
+ (relation) => visibleNames.has(relation.from) && visibleNames.has(relation.to)
64634
+ );
64635
+ return { entities, relations, entityNames };
64636
+ }
64637
+ var init_graph_scope = __esm({
64638
+ "src/memory/graph-scope.ts"() {
64639
+ "use strict";
64640
+ init_esm_shims();
64641
+ }
64642
+ });
64643
+
64455
64644
  // src/rules/utils.ts
64456
64645
  import { createHash as createHash13 } from "crypto";
64457
64646
  function hashContent2(content) {
@@ -67157,13 +67346,8 @@ function prepareDashboardConfig(projectRoot) {
67157
67346
  }
67158
67347
  }
67159
67348
  function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
67160
- const entityNames = new Set(
67161
- projectObs.filter((o3) => (o3.status ?? "active") === "active" && o3.entityName).map((o3) => o3.entityName)
67162
- );
67163
- const entities = allEntities.filter((e3) => entityNames.has(e3.name));
67164
- const entityNameSet = new Set(entities.map((e3) => e3.name));
67165
- const relations = allRelations.filter((r4) => entityNameSet.has(r4.from) && entityNameSet.has(r4.to));
67166
- return { entities: entities.length, relations: relations.length, entityNames };
67349
+ const scoped = scopeKnowledgeGraphToProject({ entities: allEntities, relations: allRelations }, projectObs);
67350
+ return { entities: scoped.entities.length, relations: scoped.relations.length, entityNames: scoped.entityNames };
67167
67351
  }
67168
67352
  async function handleApi(req, res, dataDir, projectId, projectName, baseDir, projectRoot, projectResolved, mode = "standalone", port = 3210) {
67169
67353
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
@@ -67236,13 +67420,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
67236
67420
  const gStore = getGraphStore();
67237
67421
  const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
67238
67422
  const graphObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
67239
- const projectEntityNames = new Set(
67240
- graphObs.filter((o3) => o3.entityName).map((o3) => o3.entityName)
67241
- );
67242
- const entities = graph.entities.filter((e3) => projectEntityNames.has(e3.name));
67243
- const entityNameSet = new Set(entities.map((e3) => e3.name));
67244
- const relations = graph.relations.filter((r4) => entityNameSet.has(r4.from) && entityNameSet.has(r4.to));
67245
- sendJson(res, { entities, relations });
67423
+ const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
67424
+ sendJson(res, { entities: scoped.entities, relations: scoped.relations });
67246
67425
  break;
67247
67426
  }
67248
67427
  case "/observations": {
@@ -67434,19 +67613,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
67434
67613
  const allObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
67435
67614
  const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
67436
67615
  const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
67437
- const graphObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
67438
- const projectEntityNames = new Set(
67439
- graphObs.filter((o3) => o3.entityName).map((o3) => o3.entityName)
67440
- );
67441
- const scopedEntities = fullGraph.entities.filter((e3) => projectEntityNames.has(e3.name));
67442
- const scopedEntityNameSet = new Set(scopedEntities.map((e3) => e3.name));
67443
- const scopedRelations = fullGraph.relations.filter((r4) => scopedEntityNameSet.has(r4.from) && scopedEntityNameSet.has(r4.to));
67616
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
67444
67617
  const graph = generateKnowledgeGraph2({
67445
67618
  projectId: effectiveProjectId,
67446
67619
  observations: allObs,
67447
67620
  miniSkills: skills,
67448
- graphEntities: scopedEntities,
67449
- graphRelations: scopedRelations
67621
+ graphEntities: scoped.entities,
67622
+ graphRelations: scoped.relations
67450
67623
  });
67451
67624
  sendJson(res, graph);
67452
67625
  break;
@@ -67668,16 +67841,11 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
67668
67841
  const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
67669
67842
  const observations2 = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
67670
67843
  const nextId2 = await getObservationStore().loadIdCounter();
67671
- const exportEntityNames = new Set(
67672
- observations2.filter((o3) => (o3.status ?? "active") === "active" && o3.entityName).map((o3) => o3.entityName)
67673
- );
67674
- const exportEntities = fullGraph.entities.filter((e3) => exportEntityNames.has(e3.name));
67675
- const exportEntitySet = new Set(exportEntities.map((e3) => e3.name));
67676
- const exportRelations = fullGraph.relations.filter((r4) => exportEntitySet.has(r4.from) && exportEntitySet.has(r4.to));
67844
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
67677
67845
  const exportData = {
67678
67846
  project: { id: effectiveProjectId, name: effectiveProjectName },
67679
67847
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
67680
- graph: { entities: exportEntities, relations: exportRelations },
67848
+ graph: { entities: scoped.entities, relations: scoped.relations },
67681
67849
  observations: observations2,
67682
67850
  nextId: nextId2
67683
67851
  };
@@ -67994,6 +68162,7 @@ var init_server3 = __esm({
67994
68162
  init_dotenv_loader();
67995
68163
  init_yaml_loader();
67996
68164
  init_yaml_loader();
68165
+ init_graph_scope();
67997
68166
  MIME_TYPES = {
67998
68167
  ".html": "text/html; charset=utf-8",
67999
68168
  ".css": "text/css; charset=utf-8",
@@ -68571,7 +68740,7 @@ The path should point to a directory containing a .git folder.`
68571
68740
  };
68572
68741
  const server = existingServer ?? new McpServer({
68573
68742
  name: "memorix",
68574
- version: true ? "1.2.0" : "1.0.1"
68743
+ version: true ? "1.2.1" : "1.0.1"
68575
68744
  });
68576
68745
  const originalRegisterTool = server.registerTool.bind(server);
68577
68746
  server.registerTool = ((name, ...args) => {
@@ -69303,7 +69472,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69303
69472
  { getResolvedConfig: getResolvedConfig2 },
69304
69473
  { getExternalCodeGraphContext: getExternalCodeGraphContext2 },
69305
69474
  { getObservationStore: getObservationStore2 },
69306
- { collectCurrentProjectFacts: collectCurrentProjectFacts2 },
69475
+ { collectCurrentProjectFacts: collectCurrentProjectFacts2, formatGitFact: formatGitFact2 },
69307
69476
  { resolveTaskLens: resolveTaskLens2 }
69308
69477
  ] = await Promise.all([
69309
69478
  Promise.resolve().then(() => (init_store(), store_exports)),
@@ -69344,7 +69513,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69344
69513
  if (currentFacts.latestChangelog) {
69345
69514
  worksetFacts.push("Latest changelog: " + currentFacts.latestChangelog.version + (currentFacts.latestChangelog.date ? " (" + currentFacts.latestChangelog.date + ")" : ""));
69346
69515
  }
69347
- worksetFacts.push("Git: " + (currentFacts.git.branch ? "branch " + currentFacts.git.branch + ", " : "") + (currentFacts.git.dirty ? "dirty worktree" : "clean worktree"));
69516
+ worksetFacts.push(formatGitFact2(currentFacts.git));
69348
69517
  const codeState = snapshot ? "- Code state: " + (snapshot.baseRevision ? snapshot.baseRevision.slice(0, 12) : "Git unavailable") + ", " + snapshot.worktreeState + " worktree, epoch " + snapshot.sourceEpoch : "- Code state: no completed snapshot yet";
69349
69518
  const pack = await attachTaskWorkset2({
69350
69519
  pack: basePack,
@@ -69376,11 +69545,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69376
69545
  "memorix_knowledge",
69377
69546
  {
69378
69547
  title: "Knowledge Workspace",
69379
- description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace, inspect status, compile source-backed proposals, lint, apply a reviewed proposal, or manage canonical workflows. It is intentionally absent from the default micro/lite profiles.",
69548
+ description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace, review source-backed claims, compile proposals, lint, apply a reviewed proposal, or manage canonical workflows. It is intentionally absent from the default micro/lite profiles.",
69380
69549
  inputSchema: {
69381
69550
  action: external_exports.enum([
69382
69551
  "workspace_init",
69383
69552
  "status",
69553
+ "claim_list",
69554
+ "claim_review",
69384
69555
  "compile",
69385
69556
  "lint",
69386
69557
  "proposal_apply",
@@ -69395,6 +69566,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69395
69566
  path: external_exports.string().optional().describe("Explicit versioned workspace path, used only by workspace_init"),
69396
69567
  proposalId: external_exports.string().optional().describe("Pending proposal id for proposal_apply"),
69397
69568
  allowManualOverwrite: external_exports.boolean().optional().default(false).describe("Explicitly allow proposal_apply to replace a manually edited page"),
69569
+ claimId: external_exports.string().optional().describe("Source-backed claim id for claim_review"),
69570
+ claimReviewState: external_exports.enum(["approved", "rejected"]).optional().describe("Deliberate review verdict for claim_review"),
69571
+ reviewDetail: external_exports.string().max(2e3).optional().describe("Evidence check performed before approving or rejecting a claim"),
69398
69572
  workflowId: external_exports.string().optional().describe("Canonical workflow id for workflow preview, apply, or run"),
69399
69573
  agent: external_exports.string().optional().describe("Target agent for a workflow adapter"),
69400
69574
  task: external_exports.string().optional().describe("Task text for workflow selection or a workflow run"),
@@ -69411,6 +69585,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69411
69585
  path: workspacePath,
69412
69586
  proposalId,
69413
69587
  allowManualOverwrite,
69588
+ claimId,
69589
+ claimReviewState: claimReviewState2,
69590
+ reviewDetail,
69414
69591
  workflowId,
69415
69592
  agent,
69416
69593
  task,
@@ -69433,6 +69610,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69433
69610
  };
69434
69611
  const [
69435
69612
  { ClaimStore: ClaimStore2 },
69613
+ { reviewClaim: reviewClaim2 },
69436
69614
  { CodeGraphStore: CodeGraphStore2 },
69437
69615
  { initializeKnowledgeWorkspace: initializeKnowledgeWorkspace2, loadKnowledgeWorkspace: loadKnowledgeWorkspace2 },
69438
69616
  { KnowledgeWorkspaceStore: KnowledgeWorkspaceStore2 },
@@ -69448,6 +69626,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69448
69626
  }
69449
69627
  ] = await Promise.all([
69450
69628
  Promise.resolve().then(() => (init_claim_store(), claim_store_exports)),
69629
+ Promise.resolve().then(() => (init_claims(), claims_exports)),
69451
69630
  Promise.resolve().then(() => (init_store(), store_exports)),
69452
69631
  Promise.resolve().then(() => (init_workspace(), workspace_exports)),
69453
69632
  Promise.resolve().then(() => (init_workspace_store(), workspace_store_exports)),
@@ -69497,10 +69676,52 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69497
69676
  targetPath: proposal.targetPath,
69498
69677
  reason: proposal.reason,
69499
69678
  createdAt: proposal.createdAt
69500
- }))
69679
+ })),
69680
+ reviewableClaims: claims.listClaims(project.id, { limit: 100 }).filter((claim) => claim.reviewState === "needs-review").map((claim) => ({ id: claim.id, subject: claim.subject, predicate: claim.predicate, objectValue: claim.objectValue }))
69501
69681
  }
69502
69682
  });
69503
69683
  }
69684
+ if (action === "claim_list") {
69685
+ return text({
69686
+ claims: claims.listClaims(project.id, { limit: 100 }).map((claim) => ({
69687
+ id: claim.id,
69688
+ subject: claim.subject,
69689
+ predicate: claim.predicate,
69690
+ objectValue: claim.objectValue,
69691
+ status: claim.status,
69692
+ reviewState: claim.reviewState,
69693
+ origin: claim.origin,
69694
+ confidence: claim.confidence,
69695
+ evidenceCount: claims.listEvidence(claim.id).length
69696
+ })),
69697
+ next: "Approve only after checking the linked evidence. Rejected claims stay out of retrieval and publication."
69698
+ });
69699
+ }
69700
+ if (action === "claim_review") {
69701
+ const requestedClaimId = requireText(claimId, "claimId");
69702
+ const detail = requireText(reviewDetail, "reviewDetail");
69703
+ if (!requestedClaimId || !claimReviewState2 || !detail) {
69704
+ return text({ error: "claimId, claimReviewState, and reviewDetail are required for claim_review." }, true);
69705
+ }
69706
+ const existing = claims.getClaim(requestedClaimId);
69707
+ if (!existing || existing.projectId !== project.id) {
69708
+ return text({ error: "Claim was not found for the current project." }, true);
69709
+ }
69710
+ const claim = reviewClaim2(claims, {
69711
+ claimId: requestedClaimId,
69712
+ reviewState: claimReviewState2,
69713
+ detail
69714
+ });
69715
+ return text({
69716
+ claim: {
69717
+ id: claim.id,
69718
+ status: claim.status,
69719
+ reviewState: claim.reviewState,
69720
+ updatedAt: claim.updatedAt
69721
+ },
69722
+ next: claim.reviewState === "approved" ? "This approved source-backed claim can now be considered by knowledge compilation." : "This rejected claim is excluded from retrieval and knowledge compilation."
69723
+ });
69724
+ }
69504
69725
  if (action === "compile") {
69505
69726
  const result = await compileKnowledgeWorkspace2({ workspace, claims });
69506
69727
  return text({
@@ -69539,7 +69760,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69539
69760
  if (action === "workflow_import") {
69540
69761
  const result = await importWindsurfWorkflows2({ workspace, projectRoot });
69541
69762
  return text({
69542
- imported: result.imported.map((workflow2) => ({ id: workflow2.id, title: workflow2.title, sourcePath: workflow2.sourcePath })),
69763
+ imported: result.imported.map((workflow2) => ({
69764
+ id: workflow2.id,
69765
+ title: workflow2.title,
69766
+ sourcePath: workflow2.sourcePath,
69767
+ importedFrom: workflow2.importedFrom,
69768
+ verificationGates: workflow2.verificationGates
69769
+ })),
69543
69770
  skipped: result.skipped
69544
69771
  });
69545
69772
  }
@@ -69551,7 +69778,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
69551
69778
  title: workflow2.title,
69552
69779
  status: workflow2.status,
69553
69780
  taskLenses: workflow2.taskLenses,
69554
- sourcePath: workflow2.sourcePath
69781
+ sourcePath: workflow2.sourcePath,
69782
+ importedFrom: workflow2.importedFrom,
69783
+ verificationGates: workflow2.verificationGates
69555
69784
  })),
69556
69785
  parseErrors: synced.errors
69557
69786
  });
@@ -70380,13 +70609,11 @@ Archived memories are hidden from default search but can be found with status: "
70380
70609
  async function scopeGraphToProject(graph) {
70381
70610
  const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
70382
70611
  const allObs = await withFreshIndex(() => getAllObservations2());
70383
- const projectEntityNames = new Set(
70384
- allObs.filter((o3) => o3.projectId === project.id && (o3.status ?? "active") === "active" && o3.entityName).map((o3) => o3.entityName)
70612
+ const scoped = scopeKnowledgeGraphToProject(
70613
+ graph,
70614
+ allObs.filter((observation) => observation.projectId === project.id)
70385
70615
  );
70386
- const entities = graph.entities.filter((e3) => projectEntityNames.has(e3.name));
70387
- const entityNameSet = new Set(entities.map((e3) => e3.name));
70388
- const relations = graph.relations.filter((r4) => entityNameSet.has(r4.from) && entityNameSet.has(r4.to));
70389
- return { entities, relations };
70616
+ return { entities: scoped.entities, relations: scoped.relations };
70390
70617
  }
70391
70618
  server.registerTool(
70392
70619
  "read_graph",
@@ -71986,6 +72213,7 @@ var init_server4 = __esm({
71986
72213
  init_attribution_guard();
71987
72214
  init_auto_relations();
71988
72215
  init_entity_extractor();
72216
+ init_graph_scope();
71989
72217
  init_engine();
71990
72218
  init_graph_context();
71991
72219
  init_detector();
@@ -73916,6 +74144,7 @@ var init_serve_http = __esm({
73916
74144
  init_esm_shims();
73917
74145
  init_dist2();
73918
74146
  init_tool_profile();
74147
+ init_graph_scope();
73919
74148
  DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
73920
74149
  serve_http_default = defineCommand({
73921
74150
  meta: {
@@ -74546,12 +74775,7 @@ var init_serve_http = __esm({
74546
74775
  }
74547
74776
  } catch {
74548
74777
  }
74549
- const projectEntityNames = new Set(
74550
- observations2.filter((o3) => o3.entityName).map((o3) => o3.entityName)
74551
- );
74552
- const projectEntities = graph.entities.filter((e3) => projectEntityNames.has(e3.name));
74553
- const projectEntitySet = new Set(projectEntities.map((e3) => e3.name));
74554
- const projectRelations = graph.relations.filter((r4) => projectEntitySet.has(r4.from) && projectEntitySet.has(r4.to));
74778
+ const projectGraph = scopeKnowledgeGraphToProject(graph, observations2);
74555
74779
  let maintenance = { total: 0, pending: 0, running: 0, retrying: 0, completed: 0, failed: 0 };
74556
74780
  let lifecycle;
74557
74781
  try {
@@ -74562,8 +74786,8 @@ var init_serve_http = __esm({
74562
74786
  } catch {
74563
74787
  }
74564
74788
  sendJson2({
74565
- entities: projectEntities.length,
74566
- relations: projectRelations.length,
74789
+ entities: projectGraph.entities.length,
74790
+ relations: projectGraph.relations.length,
74567
74791
  observations: observations2.length,
74568
74792
  nextId: nextId2,
74569
74793
  typeCounts,
@@ -74612,13 +74836,8 @@ var init_serve_http = __esm({
74612
74836
  await initGraphStore2(graphDataDir);
74613
74837
  const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
74614
74838
  const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, "active");
74615
- const projectEntityNames = new Set(
74616
- allObs.filter((o3) => o3.entityName).map((o3) => o3.entityName)
74617
- );
74618
- const entities = fullGraph.entities.filter((e3) => projectEntityNames.has(e3.name));
74619
- const entityNameSet = new Set(entities.map((e3) => e3.name));
74620
- const relations = fullGraph.relations.filter((r4) => entityNameSet.has(r4.from) && entityNameSet.has(r4.to));
74621
- sendJson2({ entities, relations });
74839
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
74840
+ sendJson2({ entities: scoped.entities, relations: scoped.relations });
74622
74841
  return;
74623
74842
  }
74624
74843
  if (apiPath === "/sessions") {
@@ -74678,19 +74897,13 @@ var init_serve_http = __esm({
74678
74897
  const allObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, "active");
74679
74898
  const skills = await getMiniSkillStore2().loadByProject(kgProjectId);
74680
74899
  const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
74681
- const graphObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, "active");
74682
- const projectEntityNames = new Set(
74683
- graphObs.filter((o3) => o3.entityName).map((o3) => o3.entityName)
74684
- );
74685
- const scopedEntities = fullGraph.entities.filter((e3) => projectEntityNames.has(e3.name));
74686
- const scopedEntityNameSet = new Set(scopedEntities.map((e3) => e3.name));
74687
- const scopedRelations = fullGraph.relations.filter((r4) => scopedEntityNameSet.has(r4.from) && scopedEntityNameSet.has(r4.to));
74900
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
74688
74901
  const graph = generateKnowledgeGraph2({
74689
74902
  projectId: kgProjectId,
74690
74903
  observations: allObs,
74691
74904
  miniSkills: skills,
74692
- graphEntities: scopedEntities,
74693
- graphRelations: scopedRelations
74905
+ graphEntities: scoped.entities,
74906
+ graphRelations: scoped.relations
74694
74907
  });
74695
74908
  sendJson2(graph);
74696
74909
  return;
@@ -74966,16 +75179,11 @@ var init_serve_http = __esm({
74966
75179
  const observations2 = await loadDashboardProjectObservations(expDataDir, expProjectId, "active");
74967
75180
  const expStore = await getDashboardObservationStore(expDataDir);
74968
75181
  const nextId2 = await expStore.loadIdCounter();
74969
- const exportEntityNames = new Set(
74970
- observations2.filter((o3) => o3.entityName).map((o3) => o3.entityName)
74971
- );
74972
- const exportEntities = fullGraph.entities.filter((e3) => exportEntityNames.has(e3.name));
74973
- const exportEntitySet = new Set(exportEntities.map((e3) => e3.name));
74974
- const exportRelations = fullGraph.relations.filter((r4) => exportEntitySet.has(r4.from) && exportEntitySet.has(r4.to));
75182
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
74975
75183
  const exportData = {
74976
75184
  project: { id: expProjectId, name: expProjectName },
74977
75185
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
74978
- graph: { entities: exportEntities, relations: exportRelations },
75186
+ graph: { entities: scoped.entities, relations: scoped.relations },
74979
75187
  observations: observations2,
74980
75188
  nextId: nextId2
74981
75189
  };
@@ -77101,8 +77309,8 @@ async function installSingleAgent2(agent, cwd, global2) {
77101
77309
  console.log(` Events: ${config2.events.join(", ")}`);
77102
77310
  if (agent === "copilot" && process.platform === "win32") {
77103
77311
  try {
77104
- const { execSync: execSync11 } = await import("child_process");
77105
- execSync11("pwsh --version", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "ignore"] });
77312
+ const { execSync: execSync10 } = await import("child_process");
77313
+ execSync10("pwsh --version", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "ignore"] });
77106
77314
  } catch {
77107
77315
  console.warn(`[WARN] ${agent}: pwsh (PowerShell v7+) not found. Copilot hooks will use the bash field via Git Bash.`);
77108
77316
  console.warn(` For best Windows support, install PowerShell v7+: winget install Microsoft.PowerShell`);
@@ -79826,7 +80034,7 @@ var init_doctor = __esm({
79826
80034
  const { existsSync: existsSync29, readFileSync: readFileSync23 } = await import("fs");
79827
80035
  const { join: join32, basename: basename5 } = await import("path");
79828
80036
  const { homedir: homedir34 } = await import("os");
79829
- const { execSync: execSync11 } = await import("child_process");
80037
+ const { execSync: execSync10 } = await import("child_process");
79830
80038
  const report = {};
79831
80039
  const issues = [];
79832
80040
  const tips = [];
@@ -82449,12 +82657,20 @@ var init_worktree = __esm({
82449
82657
  });
82450
82658
 
82451
82659
  // src/orchestrate/verify-gate.ts
82452
- import { spawn as spawn4, execSync as execSync8 } from "child_process";
82660
+ import { spawn as spawn4 } from "child_process";
82453
82661
  function runGate(gate, command, cwd, timeoutMs, outputBudget = DEFAULT_OUTPUT_BUDGET) {
82454
82662
  const start = Date.now();
82455
82663
  return new Promise((resolve5) => {
82456
82664
  const chunks = [];
82457
82665
  let totalBytes = 0;
82666
+ let settled = false;
82667
+ let timer;
82668
+ const finish = (result) => {
82669
+ if (settled) return;
82670
+ settled = true;
82671
+ if (timer) clearTimeout(timer);
82672
+ resolve5(result);
82673
+ };
82458
82674
  const proc = spawn4(command, {
82459
82675
  cwd,
82460
82676
  shell: true,
@@ -82474,8 +82690,12 @@ function runGate(gate, command, cwd, timeoutMs, outputBudget = DEFAULT_OUTPUT_BU
82474
82690
  let killed = false;
82475
82691
  const killTree = () => {
82476
82692
  try {
82477
- if (process.platform === "win32") {
82478
- execSync8(`taskkill /F /T /PID ${proc.pid}`, { stdio: "ignore" });
82693
+ if (process.platform === "win32" && proc.pid) {
82694
+ const killer = spawn4("taskkill.exe", ["/F", "/T", "/PID", String(proc.pid)], {
82695
+ stdio: "ignore",
82696
+ windowsHide: true
82697
+ });
82698
+ killer.unref();
82479
82699
  } else {
82480
82700
  process.kill(-proc.pid, "SIGKILL");
82481
82701
  }
@@ -82485,17 +82705,34 @@ function runGate(gate, command, cwd, timeoutMs, outputBudget = DEFAULT_OUTPUT_BU
82485
82705
  } catch {
82486
82706
  }
82487
82707
  }
82708
+ try {
82709
+ proc.stdout?.destroy();
82710
+ } catch {
82711
+ }
82712
+ try {
82713
+ proc.stderr?.destroy();
82714
+ } catch {
82715
+ }
82716
+ proc.unref();
82488
82717
  };
82489
- const timer = setTimeout(() => {
82718
+ timer = setTimeout(() => {
82490
82719
  killed = true;
82491
82720
  killTree();
82721
+ const output = Buffer.concat(chunks).toString("utf-8");
82722
+ finish({
82723
+ gate,
82724
+ passed: false,
82725
+ output: output + `
82726
+ [TIMEOUT] Gate "${gate}" killed after ${timeoutMs}ms`,
82727
+ durationMs: Date.now() - start,
82728
+ command
82729
+ });
82492
82730
  }, timeoutMs);
82493
82731
  proc.on("close", (code) => {
82494
- clearTimeout(timer);
82495
82732
  const output = Buffer.concat(chunks).toString("utf-8");
82496
82733
  const durationMs = Date.now() - start;
82497
82734
  if (killed) {
82498
- resolve5({
82735
+ finish({
82499
82736
  gate,
82500
82737
  passed: false,
82501
82738
  output: output + `
@@ -82505,7 +82742,7 @@ function runGate(gate, command, cwd, timeoutMs, outputBudget = DEFAULT_OUTPUT_BU
82505
82742
  });
82506
82743
  return;
82507
82744
  }
82508
- resolve5({
82745
+ finish({
82509
82746
  gate,
82510
82747
  passed: code === 0,
82511
82748
  output,
@@ -82514,8 +82751,7 @@ function runGate(gate, command, cwd, timeoutMs, outputBudget = DEFAULT_OUTPUT_BU
82514
82751
  });
82515
82752
  });
82516
82753
  proc.on("error", (err) => {
82517
- clearTimeout(timer);
82518
- resolve5({
82754
+ finish({
82519
82755
  gate,
82520
82756
  passed: false,
82521
82757
  output: `[ERROR] Failed to spawn gate command: ${err.message}`,
@@ -83169,7 +83405,7 @@ var coordinator_exports = {};
83169
83405
  __export(coordinator_exports, {
83170
83406
  runCoordinationLoop: () => runCoordinationLoop
83171
83407
  });
83172
- import { execSync as execSync9 } from "child_process";
83408
+ import { execSync as execSync8 } from "child_process";
83173
83409
  async function runCoordinationLoop(config2) {
83174
83410
  const {
83175
83411
  projectDir: projectDir2,
@@ -84110,7 +84346,7 @@ function sleep3(ms) {
84110
84346
  }
84111
84347
  function isGitRepository(projectDir2) {
84112
84348
  try {
84113
- execSync9("git rev-parse --is-inside-work-tree", {
84349
+ execSync8("git rev-parse --is-inside-work-tree", {
84114
84350
  cwd: projectDir2,
84115
84351
  encoding: "utf-8",
84116
84352
  timeout: 5e3,
@@ -84123,7 +84359,7 @@ function isGitRepository(projectDir2) {
84123
84359
  }
84124
84360
  function getGitStatus(projectDir2) {
84125
84361
  try {
84126
- return execSync9("git status --porcelain", {
84362
+ return execSync8("git status --porcelain", {
84127
84363
  cwd: projectDir2,
84128
84364
  encoding: "utf-8",
84129
84365
  timeout: 5e3,
@@ -88532,19 +88768,19 @@ ${hookScript}`, "utf-8");
88532
88768
  const handleBackgroundAction = useCallback5(async (action) => {
88533
88769
  setStatusMsg(null);
88534
88770
  try {
88535
- const { execSync: execSync11 } = await import("child_process");
88771
+ const { execSync: execSync10 } = await import("child_process");
88536
88772
  if (background.running) {
88537
88773
  switch (action) {
88538
88774
  case "w":
88539
88775
  if (background.dashboard) {
88540
88776
  try {
88541
- execSync11(`start "" "${background.dashboard}"`, { stdio: "pipe" });
88777
+ execSync10(`start "" "${background.dashboard}"`, { stdio: "pipe" });
88542
88778
  } catch {
88543
88779
  try {
88544
- execSync11(`open "${background.dashboard}"`, { stdio: "pipe" });
88780
+ execSync10(`open "${background.dashboard}"`, { stdio: "pipe" });
88545
88781
  } catch {
88546
88782
  try {
88547
- execSync11(`xdg-open "${background.dashboard}"`, { stdio: "pipe" });
88783
+ execSync10(`xdg-open "${background.dashboard}"`, { stdio: "pipe" });
88548
88784
  } catch {
88549
88785
  }
88550
88786
  }
@@ -88554,7 +88790,7 @@ ${hookScript}`, "utf-8");
88554
88790
  break;
88555
88791
  case "1":
88556
88792
  try {
88557
- execSync11("memorix background restart", { stdio: "pipe", timeout: 15e3 });
88793
+ execSync10("memorix background restart", { stdio: "pipe", timeout: 15e3 });
88558
88794
  setStatusMsg({ text: "Restarted.", type: "success" });
88559
88795
  } catch (e3) {
88560
88796
  setStatusMsg({ text: `Restart failed: ${e3}`, type: "error" });
@@ -88562,7 +88798,7 @@ ${hookScript}`, "utf-8");
88562
88798
  break;
88563
88799
  case "2":
88564
88800
  try {
88565
- execSync11("memorix background stop", { stdio: "pipe", timeout: 1e4 });
88801
+ execSync10("memorix background stop", { stdio: "pipe", timeout: 1e4 });
88566
88802
  setStatusMsg({ text: "Stopped.", type: "success" });
88567
88803
  } catch (e3) {
88568
88804
  setStatusMsg({ text: `Stop failed: ${e3}`, type: "error" });
@@ -88576,7 +88812,7 @@ ${hookScript}`, "utf-8");
88576
88812
  switch (action) {
88577
88813
  case "1":
88578
88814
  try {
88579
- execSync11("memorix background start", { stdio: "pipe", timeout: 15e3 });
88815
+ execSync10("memorix background start", { stdio: "pipe", timeout: 15e3 });
88580
88816
  setStatusMsg({ text: "Started.", type: "success" });
88581
88817
  } catch (e3) {
88582
88818
  setStatusMsg({ text: `Start failed: ${e3}`, type: "error" });
@@ -88595,17 +88831,17 @@ ${hookScript}`, "utf-8");
88595
88831
  const handleDashboardAction = useCallback5(async (action) => {
88596
88832
  setStatusMsg(null);
88597
88833
  try {
88598
- const { execSync: execSync11 } = await import("child_process");
88834
+ const { execSync: execSync10 } = await import("child_process");
88599
88835
  if (background.healthy && background.dashboard) {
88600
88836
  if (action === "1") {
88601
88837
  try {
88602
- execSync11(`start "" "${background.dashboard}"`, { stdio: "pipe" });
88838
+ execSync10(`start "" "${background.dashboard}"`, { stdio: "pipe" });
88603
88839
  } catch {
88604
88840
  try {
88605
- execSync11(`open "${background.dashboard}"`, { stdio: "pipe" });
88841
+ execSync10(`open "${background.dashboard}"`, { stdio: "pipe" });
88606
88842
  } catch {
88607
88843
  try {
88608
- execSync11(`xdg-open "${background.dashboard}"`, { stdio: "pipe" });
88844
+ execSync10(`xdg-open "${background.dashboard}"`, { stdio: "pipe" });
88609
88845
  } catch {
88610
88846
  }
88611
88847
  }
@@ -88617,7 +88853,7 @@ ${hookScript}`, "utf-8");
88617
88853
  } else {
88618
88854
  if (action === "1") {
88619
88855
  try {
88620
- execSync11("memorix background start", { stdio: "pipe", timeout: 15e3 });
88856
+ execSync10("memorix background start", { stdio: "pipe", timeout: 15e3 });
88621
88857
  setStatusMsg({ text: "Started.", type: "success" });
88622
88858
  setBackground(await getBackgroundStatus());
88623
88859
  } catch (e3) {