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/CHANGELOG.md +14 -1
- package/README.md +4 -2
- package/README.zh-CN.md +4 -2
- package/dist/cli/index.js +677 -441
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +830 -646
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js +110 -5
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +14 -1
- package/dist/sdk.js +830 -646
- package/dist/sdk.js.map +1 -1
- package/docs/API_REFERENCE.md +3 -1
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/SETUP.md +7 -1
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/commands/codegraph.ts +2 -3
- package/src/cli/commands/knowledge.ts +40 -0
- package/src/cli/commands/serve-http.ts +14 -41
- package/src/cli/commands/setup.ts +9 -3
- package/src/codegraph/auto-context.ts +3 -15
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/task-lens.ts +49 -5
- package/src/dashboard/server.ts +15 -46
- package/src/knowledge/claims.ts +51 -1
- package/src/knowledge/types.ts +1 -0
- package/src/knowledge/workflows.ts +34 -3
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/graph-scope.ts +46 -0
- package/src/orchestrate/verify-gate.ts +33 -10
- package/src/server.ts +75 -13
package/dist/index.js
CHANGED
|
@@ -9062,6 +9062,36 @@ var init_session_store = __esm({
|
|
|
9062
9062
|
}
|
|
9063
9063
|
});
|
|
9064
9064
|
|
|
9065
|
+
// src/memory/graph-scope.ts
|
|
9066
|
+
function projectGraphEntityNames(observations2) {
|
|
9067
|
+
const entityNames = /* @__PURE__ */ new Set();
|
|
9068
|
+
for (const observation of observations2) {
|
|
9069
|
+
if ((observation.status ?? "active") !== "active") continue;
|
|
9070
|
+
const entityName = observation.entityName?.trim();
|
|
9071
|
+
if (entityName) entityNames.add(entityName);
|
|
9072
|
+
for (const relatedEntity of observation.relatedEntities ?? []) {
|
|
9073
|
+
const name = relatedEntity.trim();
|
|
9074
|
+
if (name) entityNames.add(name);
|
|
9075
|
+
}
|
|
9076
|
+
}
|
|
9077
|
+
return entityNames;
|
|
9078
|
+
}
|
|
9079
|
+
function scopeKnowledgeGraphToProject(graph, observations2) {
|
|
9080
|
+
const entityNames = projectGraphEntityNames(observations2);
|
|
9081
|
+
const entities = graph.entities.filter((entity) => entityNames.has(entity.name));
|
|
9082
|
+
const visibleNames = new Set(entities.map((entity) => entity.name));
|
|
9083
|
+
const relations = graph.relations.filter(
|
|
9084
|
+
(relation) => visibleNames.has(relation.from) && visibleNames.has(relation.to)
|
|
9085
|
+
);
|
|
9086
|
+
return { entities, relations, entityNames };
|
|
9087
|
+
}
|
|
9088
|
+
var init_graph_scope = __esm({
|
|
9089
|
+
"src/memory/graph-scope.ts"() {
|
|
9090
|
+
"use strict";
|
|
9091
|
+
init_esm_shims();
|
|
9092
|
+
}
|
|
9093
|
+
});
|
|
9094
|
+
|
|
9065
9095
|
// src/memory/disclosure-policy.ts
|
|
9066
9096
|
function resolveEvidenceBasis(fields) {
|
|
9067
9097
|
if (fields.commitHash || fields.source === "git" || fields.sourceDetail === "git-ingest") {
|
|
@@ -12174,6 +12204,7 @@ __export(claims_exports, {
|
|
|
12174
12204
|
buildClaimIdentity: () => buildClaimIdentity,
|
|
12175
12205
|
deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
|
|
12176
12206
|
requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
|
|
12207
|
+
reviewClaim: () => reviewClaim,
|
|
12177
12208
|
selectClaimsForTask: () => selectClaimsForTask,
|
|
12178
12209
|
supersedeClaim: () => supersedeClaim,
|
|
12179
12210
|
writeClaim: () => writeClaim
|
|
@@ -12360,6 +12391,39 @@ function writeClaim(store, input) {
|
|
|
12360
12391
|
};
|
|
12361
12392
|
});
|
|
12362
12393
|
}
|
|
12394
|
+
function reviewClaim(store, input) {
|
|
12395
|
+
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
12396
|
+
throw new Error("A claim review must approve or reject the claim");
|
|
12397
|
+
}
|
|
12398
|
+
const detail = compactText(input.detail, "review detail");
|
|
12399
|
+
return store.transaction(() => {
|
|
12400
|
+
const claim = store.getClaim(input.claimId);
|
|
12401
|
+
if (!claim) throw new Error("Claim was not found");
|
|
12402
|
+
if (input.reviewState === "approved" && claim.status === "superseded") {
|
|
12403
|
+
throw new Error("A superseded claim cannot be approved");
|
|
12404
|
+
}
|
|
12405
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
12406
|
+
const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
|
|
12407
|
+
const updated = {
|
|
12408
|
+
...claim,
|
|
12409
|
+
status: nextStatus,
|
|
12410
|
+
reviewState: input.reviewState,
|
|
12411
|
+
updatedAt: now3
|
|
12412
|
+
};
|
|
12413
|
+
store.updateClaim(updated);
|
|
12414
|
+
store.recordEvent({
|
|
12415
|
+
projectId: claim.projectId,
|
|
12416
|
+
claimId: claim.id,
|
|
12417
|
+
kind: "reviewed",
|
|
12418
|
+
fromStatus: claim.status,
|
|
12419
|
+
toStatus: updated.status,
|
|
12420
|
+
detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
|
|
12421
|
+
createdAt: now3
|
|
12422
|
+
});
|
|
12423
|
+
reconcileConflicts(store, claim.projectId, claim.conflictKey, now3);
|
|
12424
|
+
return store.getClaim(claim.id) ?? updated;
|
|
12425
|
+
});
|
|
12426
|
+
}
|
|
12363
12427
|
function supersedeClaim(store, input) {
|
|
12364
12428
|
const evidence = validateEvidence(input.evidence);
|
|
12365
12429
|
return store.transaction(() => {
|
|
@@ -12448,7 +12512,7 @@ function deriveLowRiskClaimsFromObservation(store, observation, codeStore) {
|
|
|
12448
12512
|
scope: "project",
|
|
12449
12513
|
confidence,
|
|
12450
12514
|
observedAt: observation.updatedAt ?? observation.createdAt,
|
|
12451
|
-
reviewState: "approved",
|
|
12515
|
+
reviewState: isGit ? "approved" : "needs-review",
|
|
12452
12516
|
origin: isGit ? "git" : "derived",
|
|
12453
12517
|
evidence
|
|
12454
12518
|
});
|
|
@@ -13324,148 +13388,492 @@ var init_workflow_store = __esm({
|
|
|
13324
13388
|
}
|
|
13325
13389
|
});
|
|
13326
13390
|
|
|
13327
|
-
// src/
|
|
13328
|
-
var
|
|
13329
|
-
__export(
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13339
|
-
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13340
|
-
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13391
|
+
// src/codegraph/task-lens.ts
|
|
13392
|
+
var task_lens_exports = {};
|
|
13393
|
+
__export(task_lens_exports, {
|
|
13394
|
+
containsTaskKeyword: () => containsTaskKeyword,
|
|
13395
|
+
lensPathCandidates: () => lensPathCandidates,
|
|
13396
|
+
lensVerificationHints: () => lensVerificationHints,
|
|
13397
|
+
rankLensPaths: () => rankLensPaths,
|
|
13398
|
+
rankLensSources: () => rankLensSources,
|
|
13399
|
+
resolveTaskLens: () => resolveTaskLens,
|
|
13400
|
+
scoreLensSource: () => scoreLensSource,
|
|
13401
|
+
shouldShowLensSource: () => shouldShowLensSource
|
|
13341
13402
|
});
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
import path17 from "path";
|
|
13345
|
-
import matter11 from "gray-matter";
|
|
13346
|
-
function hash3(value) {
|
|
13347
|
-
return createHash12("sha256").update(value).digest("hex");
|
|
13348
|
-
}
|
|
13349
|
-
function now2() {
|
|
13350
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13351
|
-
}
|
|
13352
|
-
function slug(value) {
|
|
13353
|
-
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13354
|
-
return normalized || "workflow";
|
|
13403
|
+
function normalizePath2(path25) {
|
|
13404
|
+
return path25.replace(/\\/g, "/");
|
|
13355
13405
|
}
|
|
13356
|
-
function
|
|
13357
|
-
return
|
|
13406
|
+
function tokenize2(text) {
|
|
13407
|
+
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
13358
13408
|
}
|
|
13359
|
-
function
|
|
13360
|
-
const
|
|
13361
|
-
|
|
13362
|
-
|
|
13409
|
+
function containsTaskKeyword(text, keyword) {
|
|
13410
|
+
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");
|
|
13411
|
+
let match;
|
|
13412
|
+
while ((match = matcher.exec(text)) !== null) {
|
|
13413
|
+
const keywordIndex = match.index + (match[1]?.length ?? 0);
|
|
13414
|
+
if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
|
|
13363
13415
|
}
|
|
13364
|
-
return
|
|
13416
|
+
return false;
|
|
13365
13417
|
}
|
|
13366
|
-
function
|
|
13367
|
-
const
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13418
|
+
function isNegatedTaskKeyword(text, keywordIndex) {
|
|
13419
|
+
const boundary = Math.max(
|
|
13420
|
+
text.lastIndexOf(".", keywordIndex - 1),
|
|
13421
|
+
text.lastIndexOf("!", keywordIndex - 1),
|
|
13422
|
+
text.lastIndexOf("?", keywordIndex - 1),
|
|
13423
|
+
text.lastIndexOf(";", keywordIndex - 1),
|
|
13424
|
+
text.lastIndexOf("\n", keywordIndex - 1),
|
|
13425
|
+
text.lastIndexOf("\u3002", keywordIndex - 1),
|
|
13426
|
+
text.lastIndexOf("\uFF01", keywordIndex - 1),
|
|
13427
|
+
text.lastIndexOf("\uFF1F", keywordIndex - 1),
|
|
13428
|
+
text.lastIndexOf("\uFF1B", keywordIndex - 1)
|
|
13429
|
+
);
|
|
13430
|
+
let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
|
|
13431
|
+
const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
|
|
13432
|
+
let contrastMatch;
|
|
13433
|
+
let contrastEnd = -1;
|
|
13434
|
+
while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
|
|
13435
|
+
if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
|
|
13436
|
+
if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
|
|
13437
|
+
if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
|
|
13438
|
+
if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
|
|
13439
|
+
const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
|
|
13440
|
+
const chineseClause = prefix.slice(chineseClauseStart + 1);
|
|
13441
|
+
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);
|
|
13371
13442
|
}
|
|
13372
|
-
function
|
|
13373
|
-
const
|
|
13374
|
-
if (
|
|
13375
|
-
|
|
13376
|
-
|
|
13443
|
+
function resolveTaskLens(task) {
|
|
13444
|
+
const normalized = (task ?? "").toLowerCase();
|
|
13445
|
+
if (!normalized.trim()) return LENSES.general;
|
|
13446
|
+
let best = {
|
|
13447
|
+
id: "general",
|
|
13448
|
+
score: 0,
|
|
13449
|
+
priority: Number.MAX_SAFE_INTEGER
|
|
13450
|
+
};
|
|
13451
|
+
for (const id of LENS_PRIORITY) {
|
|
13452
|
+
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsTaskKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
13453
|
+
const priority = LENS_PRIORITY.indexOf(id);
|
|
13454
|
+
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
13455
|
+
best = { id, score, priority };
|
|
13456
|
+
}
|
|
13377
13457
|
}
|
|
13378
|
-
return [
|
|
13458
|
+
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
13379
13459
|
}
|
|
13380
|
-
function
|
|
13381
|
-
const
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13460
|
+
function pathKindScore(path25, lens) {
|
|
13461
|
+
const normalized = normalizePath2(path25).toLowerCase();
|
|
13462
|
+
const name = normalized.split("/").pop() ?? normalized;
|
|
13463
|
+
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
13464
|
+
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
13465
|
+
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
13466
|
+
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
13467
|
+
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
13468
|
+
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
13469
|
+
switch (lens.id) {
|
|
13470
|
+
case "bugfix":
|
|
13471
|
+
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
13472
|
+
case "test":
|
|
13473
|
+
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
13474
|
+
case "release":
|
|
13475
|
+
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
13476
|
+
case "onboarding":
|
|
13477
|
+
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
13478
|
+
case "docs":
|
|
13479
|
+
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
13480
|
+
case "refactor":
|
|
13481
|
+
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
13482
|
+
case "feature":
|
|
13483
|
+
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
13484
|
+
default:
|
|
13485
|
+
if (isSource || isTest) return 60;
|
|
13486
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
13487
|
+
if (inDocs) return 20;
|
|
13488
|
+
return 0;
|
|
13385
13489
|
}
|
|
13386
|
-
return value;
|
|
13387
13490
|
}
|
|
13388
|
-
function
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
throw new Error("workflow frontmatter field status is invalid");
|
|
13393
|
-
}
|
|
13394
|
-
return value;
|
|
13491
|
+
function tokenScore(text, tokens) {
|
|
13492
|
+
if (tokens.length === 0) return 0;
|
|
13493
|
+
const normalized = text.toLowerCase();
|
|
13494
|
+
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
13395
13495
|
}
|
|
13396
|
-
function
|
|
13397
|
-
const
|
|
13398
|
-
if (
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13496
|
+
function sourceTaskMatchScore(source, task) {
|
|
13497
|
+
const tokens = tokenize2(task ?? "");
|
|
13498
|
+
if (tokens.length === 0) return 0;
|
|
13499
|
+
const text = [
|
|
13500
|
+
source.title,
|
|
13501
|
+
source.path ?? "",
|
|
13502
|
+
source.symbol ?? ""
|
|
13503
|
+
].join("\n").toLowerCase();
|
|
13504
|
+
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
13505
|
+
if (matches >= 2) return 40;
|
|
13506
|
+
if (matches === 1) return 24;
|
|
13507
|
+
return 0;
|
|
13402
13508
|
}
|
|
13403
|
-
function
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
return {
|
|
13411
|
-
id: phaseId,
|
|
13412
|
-
title,
|
|
13413
|
-
instructions: optionalText4(data, "instructions") ?? "",
|
|
13414
|
-
branches: optionalStringArray(data, "branches"),
|
|
13415
|
-
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
13416
|
-
verificationGates: optionalStringArray(data, "verificationGates")
|
|
13417
|
-
};
|
|
13509
|
+
function fallbackPathRank(path25) {
|
|
13510
|
+
const normalized = normalizePath2(path25);
|
|
13511
|
+
if (normalized.startsWith("src/")) return 0;
|
|
13512
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
13513
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
13514
|
+
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
13515
|
+
return 4;
|
|
13418
13516
|
}
|
|
13419
|
-
function
|
|
13420
|
-
const
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
branches: [],
|
|
13428
|
-
expectedOutputs: [],
|
|
13429
|
-
verificationGates: []
|
|
13430
|
-
}] : [];
|
|
13431
|
-
}
|
|
13432
|
-
return headers.map((match, index) => {
|
|
13433
|
-
const title = match[1].trim();
|
|
13434
|
-
const start = (match.index ?? 0) + match[0].length;
|
|
13435
|
-
const end = index + 1 < headers.length ? headers[index + 1].index ?? body.length : body.length;
|
|
13436
|
-
return {
|
|
13437
|
-
id: slug(title) + "-" + (index + 1),
|
|
13438
|
-
title,
|
|
13439
|
-
instructions: body.slice(start, end).trim(),
|
|
13440
|
-
branches: [],
|
|
13441
|
-
expectedOutputs: [],
|
|
13442
|
-
verificationGates: []
|
|
13443
|
-
};
|
|
13444
|
-
});
|
|
13517
|
+
function rankLensPaths(paths, lens, task) {
|
|
13518
|
+
const tokens = tokenize2(task ?? "");
|
|
13519
|
+
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
13520
|
+
path: path25,
|
|
13521
|
+
index,
|
|
13522
|
+
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
13523
|
+
fallback: fallbackPathRank(path25)
|
|
13524
|
+
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
13445
13525
|
}
|
|
13446
|
-
function
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13526
|
+
function lensPathCandidates(lens) {
|
|
13527
|
+
switch (lens.id) {
|
|
13528
|
+
case "release":
|
|
13529
|
+
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
13530
|
+
case "onboarding":
|
|
13531
|
+
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
13532
|
+
case "docs":
|
|
13533
|
+
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
13534
|
+
case "test":
|
|
13535
|
+
case "bugfix":
|
|
13536
|
+
return ["tests", "test"];
|
|
13537
|
+
default:
|
|
13538
|
+
return [];
|
|
13450
13539
|
}
|
|
13451
|
-
return normalized;
|
|
13452
13540
|
}
|
|
13453
|
-
function
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13541
|
+
function scoreLensSource(source, lens, task) {
|
|
13542
|
+
const tokens = tokenize2(task ?? "");
|
|
13543
|
+
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
13544
|
+
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
13545
|
+
}
|
|
13546
|
+
function rankLensSources(sources, lens, task) {
|
|
13547
|
+
return [...sources].map((source, index) => ({
|
|
13548
|
+
source,
|
|
13549
|
+
index,
|
|
13550
|
+
score: scoreLensSource(source, lens, task)
|
|
13551
|
+
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
13552
|
+
}
|
|
13553
|
+
function lensVerificationHints(lens) {
|
|
13554
|
+
switch (lens.id) {
|
|
13555
|
+
case "bugfix":
|
|
13556
|
+
return [
|
|
13557
|
+
"run the smallest failing test or repro first",
|
|
13558
|
+
"inspect the changed code path before trusting old memory"
|
|
13559
|
+
];
|
|
13560
|
+
case "test":
|
|
13561
|
+
return [
|
|
13562
|
+
"run the exact focused test file or test name first",
|
|
13563
|
+
"inspect fixtures and harness setup before changing assertions"
|
|
13564
|
+
];
|
|
13565
|
+
case "release":
|
|
13566
|
+
return [
|
|
13567
|
+
"run build, tests, package smoke, and publish dry-run where available",
|
|
13568
|
+
"verify package metadata, changelog, and Git state before publishing"
|
|
13569
|
+
];
|
|
13570
|
+
case "onboarding":
|
|
13571
|
+
return [
|
|
13572
|
+
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
13573
|
+
"treat old implementation memories as leads until current code confirms them"
|
|
13574
|
+
];
|
|
13575
|
+
case "refactor":
|
|
13576
|
+
return [
|
|
13577
|
+
"inspect call sites and tests before editing shared code",
|
|
13578
|
+
"run the narrow affected test plus one regression smoke"
|
|
13579
|
+
];
|
|
13580
|
+
case "docs":
|
|
13581
|
+
return [
|
|
13582
|
+
"check headings, links, commands, and examples against current code",
|
|
13583
|
+
"run the smallest docs or package smoke available"
|
|
13584
|
+
];
|
|
13585
|
+
case "feature":
|
|
13586
|
+
return [
|
|
13587
|
+
"inspect the closest existing implementation pattern before adding new code",
|
|
13588
|
+
"run focused tests plus one user-flow smoke after changes"
|
|
13589
|
+
];
|
|
13590
|
+
default:
|
|
13591
|
+
return [
|
|
13592
|
+
"inspect the Start here files before editing",
|
|
13593
|
+
"run the smallest relevant test or smoke command after changes"
|
|
13594
|
+
];
|
|
13595
|
+
}
|
|
13596
|
+
}
|
|
13597
|
+
function shouldShowLensSource(source, lens, task) {
|
|
13598
|
+
if (lens.id === "general") return true;
|
|
13599
|
+
const score = scoreLensSource(source, lens, task);
|
|
13600
|
+
if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
|
|
13601
|
+
return score > 0;
|
|
13602
|
+
}
|
|
13603
|
+
var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
|
|
13604
|
+
var init_task_lens = __esm({
|
|
13605
|
+
"src/codegraph/task-lens.ts"() {
|
|
13606
|
+
"use strict";
|
|
13607
|
+
init_esm_shims();
|
|
13608
|
+
STOP_WORDS = /* @__PURE__ */ new Set([
|
|
13609
|
+
"a",
|
|
13610
|
+
"an",
|
|
13611
|
+
"and",
|
|
13612
|
+
"as",
|
|
13613
|
+
"for",
|
|
13614
|
+
"in",
|
|
13615
|
+
"into",
|
|
13616
|
+
"of",
|
|
13617
|
+
"on",
|
|
13618
|
+
"onto",
|
|
13619
|
+
"the",
|
|
13620
|
+
"this",
|
|
13621
|
+
"that",
|
|
13622
|
+
"to",
|
|
13623
|
+
"with",
|
|
13624
|
+
"work",
|
|
13625
|
+
"project",
|
|
13626
|
+
"continue",
|
|
13627
|
+
"\u7EE7\u7EED",
|
|
13628
|
+
"\u9879\u76EE"
|
|
13629
|
+
]);
|
|
13630
|
+
LENSES = {
|
|
13631
|
+
bugfix: {
|
|
13632
|
+
id: "bugfix",
|
|
13633
|
+
description: "debug the failure with current code and the smallest repro first",
|
|
13634
|
+
sourceLimit: 6,
|
|
13635
|
+
cautionLimit: 4,
|
|
13636
|
+
hideUnrelatedCautionDetails: true,
|
|
13637
|
+
hideUnrelatedReliableDetails: false
|
|
13638
|
+
},
|
|
13639
|
+
feature: {
|
|
13640
|
+
id: "feature",
|
|
13641
|
+
description: "build a scoped feature from nearby source, types, and user flow",
|
|
13642
|
+
sourceLimit: 6,
|
|
13643
|
+
cautionLimit: 3,
|
|
13644
|
+
hideUnrelatedCautionDetails: true,
|
|
13645
|
+
hideUnrelatedReliableDetails: false
|
|
13646
|
+
},
|
|
13647
|
+
release: {
|
|
13648
|
+
id: "release",
|
|
13649
|
+
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
13650
|
+
sourceLimit: 4,
|
|
13651
|
+
cautionLimit: 2,
|
|
13652
|
+
hideUnrelatedCautionDetails: true,
|
|
13653
|
+
hideUnrelatedReliableDetails: true
|
|
13654
|
+
},
|
|
13655
|
+
onboarding: {
|
|
13656
|
+
id: "onboarding",
|
|
13657
|
+
description: "understand the project shape before trusting old implementation details",
|
|
13658
|
+
sourceLimit: 4,
|
|
13659
|
+
cautionLimit: 2,
|
|
13660
|
+
hideUnrelatedCautionDetails: true,
|
|
13661
|
+
hideUnrelatedReliableDetails: true
|
|
13662
|
+
},
|
|
13663
|
+
refactor: {
|
|
13664
|
+
id: "refactor",
|
|
13665
|
+
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
13666
|
+
sourceLimit: 6,
|
|
13667
|
+
cautionLimit: 4,
|
|
13668
|
+
hideUnrelatedCautionDetails: true,
|
|
13669
|
+
hideUnrelatedReliableDetails: false
|
|
13670
|
+
},
|
|
13671
|
+
docs: {
|
|
13672
|
+
id: "docs",
|
|
13673
|
+
description: "update documentation against current code and public entry points",
|
|
13674
|
+
sourceLimit: 5,
|
|
13675
|
+
cautionLimit: 2,
|
|
13676
|
+
hideUnrelatedCautionDetails: true,
|
|
13677
|
+
hideUnrelatedReliableDetails: true
|
|
13678
|
+
},
|
|
13679
|
+
test: {
|
|
13680
|
+
id: "test",
|
|
13681
|
+
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
13682
|
+
sourceLimit: 6,
|
|
13683
|
+
cautionLimit: 3,
|
|
13684
|
+
hideUnrelatedCautionDetails: true,
|
|
13685
|
+
hideUnrelatedReliableDetails: false
|
|
13686
|
+
},
|
|
13687
|
+
general: {
|
|
13688
|
+
id: "general",
|
|
13689
|
+
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
13690
|
+
sourceLimit: 8,
|
|
13691
|
+
cautionLimit: 5,
|
|
13692
|
+
hideUnrelatedCautionDetails: false,
|
|
13693
|
+
hideUnrelatedReliableDetails: false
|
|
13694
|
+
}
|
|
13695
|
+
};
|
|
13696
|
+
KEYWORDS = {
|
|
13697
|
+
bugfix: [
|
|
13698
|
+
"bug",
|
|
13699
|
+
"crash",
|
|
13700
|
+
"incident",
|
|
13701
|
+
"debug",
|
|
13702
|
+
"error",
|
|
13703
|
+
"fail",
|
|
13704
|
+
"failing",
|
|
13705
|
+
"fix",
|
|
13706
|
+
"issue",
|
|
13707
|
+
"regression",
|
|
13708
|
+
"repro",
|
|
13709
|
+
"\u62A5\u9519",
|
|
13710
|
+
"\u5D29\u6E83",
|
|
13711
|
+
"\u6545\u969C",
|
|
13712
|
+
"\u5931\u8D25",
|
|
13713
|
+
"\u4FEE\u590D",
|
|
13714
|
+
"\u95EE\u9898"
|
|
13715
|
+
],
|
|
13716
|
+
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
13717
|
+
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
13718
|
+
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
13719
|
+
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
13720
|
+
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
13721
|
+
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
13722
|
+
};
|
|
13723
|
+
LENS_PRIORITY = [
|
|
13724
|
+
"bugfix",
|
|
13725
|
+
"release",
|
|
13726
|
+
"test",
|
|
13727
|
+
"refactor",
|
|
13728
|
+
"feature",
|
|
13729
|
+
"docs",
|
|
13730
|
+
"onboarding"
|
|
13731
|
+
];
|
|
13732
|
+
}
|
|
13733
|
+
});
|
|
13734
|
+
|
|
13735
|
+
// src/knowledge/workflows.ts
|
|
13736
|
+
var workflows_exports = {};
|
|
13737
|
+
__export(workflows_exports, {
|
|
13738
|
+
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
13739
|
+
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
13740
|
+
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
13741
|
+
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
13742
|
+
previewWorkflowAdapter: () => previewWorkflowAdapter,
|
|
13743
|
+
recordWorkflowRun: () => recordWorkflowRun,
|
|
13744
|
+
renderWorkflowMarkdown: () => renderWorkflowMarkdown,
|
|
13745
|
+
selectWorkflows: () => selectWorkflows,
|
|
13746
|
+
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13747
|
+
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13748
|
+
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13749
|
+
});
|
|
13750
|
+
import { createHash as createHash12 } from "crypto";
|
|
13751
|
+
import { promises as fs14 } from "fs";
|
|
13752
|
+
import path17 from "path";
|
|
13753
|
+
import matter11 from "gray-matter";
|
|
13754
|
+
function hash3(value) {
|
|
13755
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
13756
|
+
}
|
|
13757
|
+
function now2() {
|
|
13758
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13759
|
+
}
|
|
13760
|
+
function slug(value) {
|
|
13761
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13762
|
+
return normalized || "workflow";
|
|
13763
|
+
}
|
|
13764
|
+
function titleFromName(name) {
|
|
13765
|
+
return name.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ") || "Imported Workflow";
|
|
13766
|
+
}
|
|
13767
|
+
function requiredText(data, key) {
|
|
13768
|
+
const value = data[key];
|
|
13769
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
13770
|
+
throw new Error("workflow frontmatter requires " + key);
|
|
13771
|
+
}
|
|
13772
|
+
return value.trim();
|
|
13773
|
+
}
|
|
13774
|
+
function optionalText4(data, key) {
|
|
13775
|
+
const value = data[key];
|
|
13776
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
13777
|
+
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
13778
|
+
return value.trim() || void 0;
|
|
13779
|
+
}
|
|
13780
|
+
function optionalStringArray(data, key) {
|
|
13781
|
+
const value = data[key];
|
|
13782
|
+
if (value === void 0 || value === null) return [];
|
|
13783
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
13784
|
+
throw new Error("workflow frontmatter field " + key + " must be a string array");
|
|
13785
|
+
}
|
|
13786
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
13787
|
+
}
|
|
13788
|
+
function optionalNumber(data, key, fallback) {
|
|
13789
|
+
const value = data[key];
|
|
13790
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
13791
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
13792
|
+
throw new Error("workflow frontmatter field " + key + " must be a positive integer");
|
|
13793
|
+
}
|
|
13794
|
+
return value;
|
|
13795
|
+
}
|
|
13796
|
+
function optionalStatus(data) {
|
|
13797
|
+
const value = data.status;
|
|
13798
|
+
if (value === void 0 || value === null || value === "") return "draft";
|
|
13799
|
+
if (typeof value !== "string" || !STATUS_VALUES.includes(value)) {
|
|
13800
|
+
throw new Error("workflow frontmatter field status is invalid");
|
|
13801
|
+
}
|
|
13802
|
+
return value;
|
|
13803
|
+
}
|
|
13804
|
+
function normalizeAgents(values) {
|
|
13805
|
+
const unknown = values.filter((value) => !KNOWN_AGENTS.includes(value));
|
|
13806
|
+
if (unknown.length > 0) {
|
|
13807
|
+
throw new Error("workflow frontmatter has unsupported agent: " + unknown[0]);
|
|
13808
|
+
}
|
|
13809
|
+
return values;
|
|
13810
|
+
}
|
|
13811
|
+
function phaseFromValue(value, index) {
|
|
13812
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13813
|
+
throw new Error("workflow frontmatter phases must contain objects");
|
|
13814
|
+
}
|
|
13815
|
+
const data = value;
|
|
13816
|
+
const title = requiredText(data, "title");
|
|
13817
|
+
const phaseId = optionalText4(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
13818
|
+
return {
|
|
13819
|
+
id: phaseId,
|
|
13820
|
+
title,
|
|
13821
|
+
instructions: optionalText4(data, "instructions") ?? "",
|
|
13822
|
+
branches: optionalStringArray(data, "branches"),
|
|
13823
|
+
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
13824
|
+
verificationGates: optionalStringArray(data, "verificationGates")
|
|
13825
|
+
};
|
|
13826
|
+
}
|
|
13827
|
+
function phasesFromBody(body) {
|
|
13828
|
+
const headers = [...body.matchAll(/^##\s+(.+?)\s*$/gm)];
|
|
13829
|
+
if (headers.length === 0) {
|
|
13830
|
+
const instructions = body.trim();
|
|
13831
|
+
return instructions ? [{
|
|
13832
|
+
id: "execute",
|
|
13833
|
+
title: "Execute",
|
|
13834
|
+
instructions,
|
|
13835
|
+
branches: [],
|
|
13836
|
+
expectedOutputs: [],
|
|
13837
|
+
verificationGates: []
|
|
13838
|
+
}] : [];
|
|
13839
|
+
}
|
|
13840
|
+
return headers.map((match, index) => {
|
|
13841
|
+
const title = match[1].trim();
|
|
13842
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
13843
|
+
const end = index + 1 < headers.length ? headers[index + 1].index ?? body.length : body.length;
|
|
13844
|
+
return {
|
|
13845
|
+
id: slug(title) + "-" + (index + 1),
|
|
13846
|
+
title,
|
|
13847
|
+
instructions: body.slice(start, end).trim(),
|
|
13848
|
+
branches: [],
|
|
13849
|
+
expectedOutputs: [],
|
|
13850
|
+
verificationGates: []
|
|
13851
|
+
};
|
|
13852
|
+
});
|
|
13853
|
+
}
|
|
13854
|
+
function normalizeSourcePath(sourcePath) {
|
|
13855
|
+
const normalized = sourcePath.replace(/\\/g, "/");
|
|
13856
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path17.posix.normalize(normalized) !== normalized) {
|
|
13857
|
+
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
13858
|
+
}
|
|
13859
|
+
return normalized;
|
|
13860
|
+
}
|
|
13861
|
+
function sourceHashFor(workflow) {
|
|
13862
|
+
return hash3(JSON.stringify({
|
|
13863
|
+
id: workflow.id,
|
|
13864
|
+
title: workflow.title,
|
|
13865
|
+
description: workflow.description,
|
|
13866
|
+
status: workflow.status,
|
|
13867
|
+
version: workflow.version,
|
|
13868
|
+
taskLenses: workflow.taskLenses,
|
|
13869
|
+
triggers: workflow.triggers,
|
|
13870
|
+
assumptions: workflow.assumptions,
|
|
13871
|
+
requiredContext: workflow.requiredContext,
|
|
13872
|
+
guardrails: workflow.guardrails,
|
|
13873
|
+
allowedTools: workflow.allowedTools,
|
|
13874
|
+
phases: workflow.phases,
|
|
13875
|
+
verificationGates: workflow.verificationGates,
|
|
13876
|
+
claimIds: workflow.claimIds,
|
|
13469
13877
|
evidenceRefs: workflow.evidenceRefs,
|
|
13470
13878
|
codeRefs: workflow.codeRefs,
|
|
13471
13879
|
compatibleAgents: workflow.compatibleAgents,
|
|
@@ -13646,20 +14054,27 @@ function taskScore(workflow, task) {
|
|
|
13646
14054
|
const text = task.toLowerCase();
|
|
13647
14055
|
let score = 0;
|
|
13648
14056
|
const reasons = [];
|
|
14057
|
+
const matchedLenses = /* @__PURE__ */ new Set();
|
|
14058
|
+
const matchedTriggers = /* @__PURE__ */ new Set();
|
|
13649
14059
|
for (const lens of workflow.taskLenses) {
|
|
13650
14060
|
const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
|
|
13651
|
-
if (terms.some((term) =>
|
|
14061
|
+
if (terms.some((term) => containsTaskKeyword(task, term))) {
|
|
13652
14062
|
score += 20;
|
|
13653
14063
|
reasons.push("matches " + lens + " workflow");
|
|
14064
|
+
matchedLenses.add(lens.toLowerCase());
|
|
13654
14065
|
}
|
|
13655
14066
|
}
|
|
13656
14067
|
for (const trigger of workflow.triggers) {
|
|
13657
14068
|
const term = trigger.toLowerCase().trim();
|
|
13658
|
-
if (term.length >= 2 &&
|
|
14069
|
+
if (term.length >= 2 && containsTaskKeyword(task, term)) {
|
|
13659
14070
|
score += 8;
|
|
13660
14071
|
reasons.push('matches trigger "' + trigger + '"');
|
|
14072
|
+
matchedTriggers.add(term);
|
|
13661
14073
|
}
|
|
13662
14074
|
}
|
|
14075
|
+
const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
|
|
14076
|
+
const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
|
|
14077
|
+
if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
|
|
13663
14078
|
return { score, reasons: [...new Set(reasons)] };
|
|
13664
14079
|
}
|
|
13665
14080
|
function selectWorkflows(input) {
|
|
@@ -13677,9 +14092,28 @@ function selectWorkflows(input) {
|
|
|
13677
14092
|
return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
|
|
13678
14093
|
}
|
|
13679
14094
|
function importedWorkflowSpec(input) {
|
|
14095
|
+
let sourceMatter;
|
|
14096
|
+
try {
|
|
14097
|
+
sourceMatter = matter11(input.raw);
|
|
14098
|
+
} catch (error) {
|
|
14099
|
+
throw new Error("Malformed Windsurf workflow Markdown: " + (error instanceof Error ? error.message : String(error)));
|
|
14100
|
+
}
|
|
14101
|
+
if (sourceMatter.data.id !== void 0) {
|
|
14102
|
+
const id = requiredText(sourceMatter.data, "id");
|
|
14103
|
+
const sourcePath2 = "workflows/" + slug(id) + ".md";
|
|
14104
|
+
const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
|
|
14105
|
+
workspaceId: input.workspace.id,
|
|
14106
|
+
sourcePath: sourcePath2,
|
|
14107
|
+
contentHash: hash3(input.raw)
|
|
14108
|
+
});
|
|
14109
|
+
return materializeWorkflow({
|
|
14110
|
+
...canonical,
|
|
14111
|
+
importedFrom: input.sourcePath.replace(/\\/g, "/")
|
|
14112
|
+
});
|
|
14113
|
+
}
|
|
13680
14114
|
const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
|
|
13681
14115
|
const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
|
|
13682
|
-
const lenses = inferTaskLenses(entry.name + "\n" + entry.description
|
|
14116
|
+
const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
|
|
13683
14117
|
const createdAt = now2();
|
|
13684
14118
|
const sourcePath = "workflows/" + slug(entry.name) + ".md";
|
|
13685
14119
|
const spec = {
|
|
@@ -13900,7 +14334,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
13900
14334
|
errors: synced.errors
|
|
13901
14335
|
};
|
|
13902
14336
|
}
|
|
13903
|
-
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
|
|
14337
|
+
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
13904
14338
|
var init_workflows = __esm({
|
|
13905
14339
|
"src/knowledge/workflows.ts"() {
|
|
13906
14340
|
"use strict";
|
|
@@ -13909,6 +14343,7 @@ var init_workflows = __esm({
|
|
|
13909
14343
|
init_workflow_sync();
|
|
13910
14344
|
init_workspace();
|
|
13911
14345
|
init_workflow_store();
|
|
14346
|
+
init_task_lens();
|
|
13912
14347
|
KNOWN_AGENTS = [
|
|
13913
14348
|
"windsurf",
|
|
13914
14349
|
"cursor",
|
|
@@ -13940,6 +14375,8 @@ var init_workflows = __esm({
|
|
|
13940
14375
|
refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
|
|
13941
14376
|
test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
|
|
13942
14377
|
};
|
|
14378
|
+
GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
|
|
14379
|
+
GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
|
|
13943
14380
|
}
|
|
13944
14381
|
});
|
|
13945
14382
|
|
|
@@ -15124,7 +15561,8 @@ var init_workset = __esm({
|
|
|
15124
15561
|
// src/codegraph/current-facts.ts
|
|
15125
15562
|
var current_facts_exports = {};
|
|
15126
15563
|
__export(current_facts_exports, {
|
|
15127
|
-
collectCurrentProjectFacts: () => collectCurrentProjectFacts
|
|
15564
|
+
collectCurrentProjectFacts: () => collectCurrentProjectFacts,
|
|
15565
|
+
formatGitFact: () => formatGitFact
|
|
15128
15566
|
});
|
|
15129
15567
|
import { execFileSync } from "child_process";
|
|
15130
15568
|
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
@@ -15169,11 +15607,16 @@ function runGit2(rootPath, args) {
|
|
|
15169
15607
|
}
|
|
15170
15608
|
}
|
|
15171
15609
|
function readGitFacts(rootPath) {
|
|
15172
|
-
const
|
|
15610
|
+
const available = runGit2(rootPath, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
15611
|
+
if (!available) {
|
|
15612
|
+
return { available: false, dirty: false, detached: false };
|
|
15613
|
+
}
|
|
15614
|
+
const branch = runGit2(rootPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]) ?? runGit2(rootPath, ["branch", "--show-current"]);
|
|
15173
15615
|
const commit = runGit2(rootPath, ["rev-parse", "--short", "HEAD"]);
|
|
15174
15616
|
const latestCommit = runGit2(rootPath, ["log", "-1", "--pretty=%s"]);
|
|
15175
15617
|
const dirty = Boolean(runGit2(rootPath, ["status", "--porcelain"]));
|
|
15176
15618
|
return {
|
|
15619
|
+
available: true,
|
|
15177
15620
|
...branch ? { branch } : {},
|
|
15178
15621
|
...commit ? { commit } : {},
|
|
15179
15622
|
...latestCommit ? { latestCommit } : {},
|
|
@@ -15181,6 +15624,15 @@ function readGitFacts(rootPath) {
|
|
|
15181
15624
|
detached: !branch
|
|
15182
15625
|
};
|
|
15183
15626
|
}
|
|
15627
|
+
function formatGitFact(git) {
|
|
15628
|
+
if (!git.available) return "Git: unavailable";
|
|
15629
|
+
const parts = [];
|
|
15630
|
+
if (git.detached) parts.push("detached HEAD");
|
|
15631
|
+
else if (git.branch) parts.push("branch " + git.branch);
|
|
15632
|
+
if (git.commit) parts.push("commit " + git.commit);
|
|
15633
|
+
parts.push(git.dirty ? "dirty worktree" : "clean worktree");
|
|
15634
|
+
return "Git: " + parts.join(", ");
|
|
15635
|
+
}
|
|
15184
15636
|
function parseProgressNote(content) {
|
|
15185
15637
|
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);
|
|
15186
15638
|
const branchHint = content.match(/Branch\*\*:\s*([^\r\n]+)/i) ?? content.match(/Branch:\s*([^\r\n]+)/i);
|
|
@@ -15682,485 +16134,172 @@ var init_external_provider = __esm({
|
|
|
15682
16134
|
...typeof exitCode === "number" ? { exitCode } : {},
|
|
15683
16135
|
...timedOut ? { timedOut } : {},
|
|
15684
16136
|
...outputLimited ? { outputLimited } : {},
|
|
15685
|
-
...spawnError ? { error: spawnError } : {}
|
|
15686
|
-
});
|
|
15687
|
-
});
|
|
15688
|
-
});
|
|
15689
|
-
}
|
|
15690
|
-
};
|
|
15691
|
-
}
|
|
15692
|
-
});
|
|
15693
|
-
|
|
15694
|
-
// src/codegraph/freshness.ts
|
|
15695
|
-
function evaluateCodeRefFreshness(ref, file, symbol) {
|
|
15696
|
-
if (!file) {
|
|
15697
|
-
return { status: "stale", reason: "referenced file is no longer indexed" };
|
|
15698
|
-
}
|
|
15699
|
-
if (ref.symbolId && !symbol) {
|
|
15700
|
-
return { status: "stale", reason: "referenced symbol is no longer indexed" };
|
|
15701
|
-
}
|
|
15702
|
-
if (ref.capturedSymbolHash && symbol?.contentHash) {
|
|
15703
|
-
if (symbol.contentHash !== ref.capturedSymbolHash) {
|
|
15704
|
-
return { status: "stale", reason: "referenced symbol content changed" };
|
|
15705
|
-
}
|
|
15706
|
-
return { status: "current", reason: "referenced symbol content still matches" };
|
|
15707
|
-
}
|
|
15708
|
-
if (ref.capturedFileHash && file.contentHash !== ref.capturedFileHash) {
|
|
15709
|
-
return { status: "suspect", reason: "referenced file changed since capture" };
|
|
15710
|
-
}
|
|
15711
|
-
return { status: "current", reason: "referenced file still matches" };
|
|
15712
|
-
}
|
|
15713
|
-
var init_freshness2 = __esm({
|
|
15714
|
-
"src/codegraph/freshness.ts"() {
|
|
15715
|
-
"use strict";
|
|
15716
|
-
init_esm_shims();
|
|
15717
|
-
}
|
|
15718
|
-
});
|
|
15719
|
-
|
|
15720
|
-
// src/codegraph/project-context.ts
|
|
15721
|
-
function uniq(items) {
|
|
15722
|
-
return [...new Set(items)];
|
|
15723
|
-
}
|
|
15724
|
-
function activeObservations(observations2, projectId) {
|
|
15725
|
-
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
15726
|
-
}
|
|
15727
|
-
function countLanguages(files) {
|
|
15728
|
-
const counts = /* @__PURE__ */ new Map();
|
|
15729
|
-
for (const file of files) {
|
|
15730
|
-
const language = file.language ?? "unknown";
|
|
15731
|
-
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
15732
|
-
}
|
|
15733
|
-
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
15734
|
-
}
|
|
15735
|
-
function suggestedReadRank(path25) {
|
|
15736
|
-
const normalized = path25.replace(/\\/g, "/");
|
|
15737
|
-
if (normalized.startsWith("src/")) return 0;
|
|
15738
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
15739
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15740
|
-
return 3;
|
|
15741
|
-
}
|
|
15742
|
-
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
15743
|
-
return uniq(paths).filter((path25) => !isCodeGraphExcludedPath(path25, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
15744
|
-
}
|
|
15745
|
-
function collectGraph(store, projectId, observations2, exclude) {
|
|
15746
|
-
const files = store.listFiles(projectId);
|
|
15747
|
-
const symbols = store.listReferencedSymbols(projectId);
|
|
15748
|
-
const filesById = new Map(files.map((file) => [file.id, file]));
|
|
15749
|
-
const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
|
|
15750
|
-
const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
|
|
15751
|
-
const activeObservationIds = new Set(observations2.map((obs) => obs.id));
|
|
15752
|
-
const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
|
|
15753
|
-
const freshness = {
|
|
15754
|
-
current: 0,
|
|
15755
|
-
suspect: 0,
|
|
15756
|
-
stale: 0,
|
|
15757
|
-
unbound: 0
|
|
15758
|
-
};
|
|
15759
|
-
const sources = [];
|
|
15760
|
-
const suggestedReads = [];
|
|
15761
|
-
for (const ref of refs) {
|
|
15762
|
-
const file = ref.fileId ? filesById.get(ref.fileId) : void 0;
|
|
15763
|
-
const symbol = ref.symbolId ? symbolsById.get(ref.symbolId) : void 0;
|
|
15764
|
-
const result = evaluateCodeRefFreshness(ref, file, symbol);
|
|
15765
|
-
freshness[result.status] += 1;
|
|
15766
|
-
const observation = observationsById.get(ref.observationId);
|
|
15767
|
-
if (!observation) continue;
|
|
15768
|
-
const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
|
|
15769
|
-
if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
|
|
15770
|
-
if (excluded) continue;
|
|
15771
|
-
sources.push({
|
|
15772
|
-
observationId: observation.id,
|
|
15773
|
-
title: observation.title,
|
|
15774
|
-
type: observation.type,
|
|
15775
|
-
...file ? { path: file.path } : {},
|
|
15776
|
-
...symbol ? { symbol: symbol.name } : {},
|
|
15777
|
-
status: result.status,
|
|
15778
|
-
reason: result.reason
|
|
15779
|
-
});
|
|
15780
|
-
}
|
|
15781
|
-
if (files.length === 0) {
|
|
15782
|
-
for (const observation of observations2) {
|
|
15783
|
-
for (const path25 of observation.filesModified ?? []) {
|
|
15784
|
-
if (!path25 || isCodeGraphExcludedPath(path25, exclude)) continue;
|
|
15785
|
-
suggestedReads.push(path25);
|
|
15786
|
-
sources.push({
|
|
15787
|
-
observationId: observation.id,
|
|
15788
|
-
title: observation.title,
|
|
15789
|
-
type: observation.type,
|
|
15790
|
-
path: path25,
|
|
15791
|
-
status: "unbound",
|
|
15792
|
-
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
16137
|
+
...spawnError ? { error: spawnError } : {}
|
|
16138
|
+
});
|
|
16139
|
+
});
|
|
15793
16140
|
});
|
|
15794
|
-
freshness.unbound++;
|
|
15795
16141
|
}
|
|
16142
|
+
};
|
|
16143
|
+
}
|
|
16144
|
+
});
|
|
16145
|
+
|
|
16146
|
+
// src/codegraph/freshness.ts
|
|
16147
|
+
function evaluateCodeRefFreshness(ref, file, symbol) {
|
|
16148
|
+
if (!file) {
|
|
16149
|
+
return { status: "stale", reason: "referenced file is no longer indexed" };
|
|
16150
|
+
}
|
|
16151
|
+
if (ref.symbolId && !symbol) {
|
|
16152
|
+
return { status: "stale", reason: "referenced symbol is no longer indexed" };
|
|
16153
|
+
}
|
|
16154
|
+
if (ref.capturedSymbolHash && symbol?.contentHash) {
|
|
16155
|
+
if (symbol.contentHash !== ref.capturedSymbolHash) {
|
|
16156
|
+
return { status: "stale", reason: "referenced symbol content changed" };
|
|
15796
16157
|
}
|
|
16158
|
+
return { status: "current", reason: "referenced symbol content still matches" };
|
|
15797
16159
|
}
|
|
15798
|
-
|
|
15799
|
-
|
|
15800
|
-
|
|
15801
|
-
|
|
15802
|
-
freshness,
|
|
15803
|
-
sources,
|
|
15804
|
-
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
15805
|
-
};
|
|
15806
|
-
}
|
|
15807
|
-
function overviewFromGraph(input) {
|
|
15808
|
-
const status = input.store.status(input.project.id);
|
|
15809
|
-
return {
|
|
15810
|
-
project: input.project,
|
|
15811
|
-
code: {
|
|
15812
|
-
provider: status.provider,
|
|
15813
|
-
files: status.files,
|
|
15814
|
-
symbols: status.symbols,
|
|
15815
|
-
edges: status.edges,
|
|
15816
|
-
refs: status.refs,
|
|
15817
|
-
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
15818
|
-
languages: countLanguages(input.graph.files),
|
|
15819
|
-
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
15820
|
-
},
|
|
15821
|
-
memory: {
|
|
15822
|
-
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
15823
|
-
active: input.active.length
|
|
15824
|
-
},
|
|
15825
|
-
freshness: input.graph.freshness,
|
|
15826
|
-
suggestedReads: input.graph.suggestedReads
|
|
15827
|
-
};
|
|
15828
|
-
}
|
|
15829
|
-
function buildProjectContextExplain(input) {
|
|
15830
|
-
const active = activeObservations(input.observations, input.project.id);
|
|
15831
|
-
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
15832
|
-
const overview = overviewFromGraph({
|
|
15833
|
-
project: input.project,
|
|
15834
|
-
store: input.store,
|
|
15835
|
-
observations: input.observations,
|
|
15836
|
-
active,
|
|
15837
|
-
graph
|
|
15838
|
-
});
|
|
15839
|
-
return {
|
|
15840
|
-
project: input.project,
|
|
15841
|
-
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
15842
|
-
overview
|
|
15843
|
-
};
|
|
16160
|
+
if (ref.capturedFileHash && file.contentHash !== ref.capturedFileHash) {
|
|
16161
|
+
return { status: "suspect", reason: "referenced file changed since capture" };
|
|
16162
|
+
}
|
|
16163
|
+
return { status: "current", reason: "referenced file still matches" };
|
|
15844
16164
|
}
|
|
15845
|
-
var
|
|
15846
|
-
"src/codegraph/
|
|
16165
|
+
var init_freshness2 = __esm({
|
|
16166
|
+
"src/codegraph/freshness.ts"() {
|
|
15847
16167
|
"use strict";
|
|
15848
16168
|
init_esm_shims();
|
|
15849
|
-
init_freshness2();
|
|
15850
|
-
init_exclude();
|
|
15851
16169
|
}
|
|
15852
16170
|
});
|
|
15853
16171
|
|
|
15854
|
-
// src/codegraph/
|
|
15855
|
-
|
|
15856
|
-
|
|
15857
|
-
lensPathCandidates: () => lensPathCandidates,
|
|
15858
|
-
lensVerificationHints: () => lensVerificationHints,
|
|
15859
|
-
rankLensPaths: () => rankLensPaths,
|
|
15860
|
-
rankLensSources: () => rankLensSources,
|
|
15861
|
-
resolveTaskLens: () => resolveTaskLens,
|
|
15862
|
-
scoreLensSource: () => scoreLensSource,
|
|
15863
|
-
shouldShowLensSource: () => shouldShowLensSource
|
|
15864
|
-
});
|
|
15865
|
-
function normalizePath2(path25) {
|
|
15866
|
-
return path25.replace(/\\/g, "/");
|
|
15867
|
-
}
|
|
15868
|
-
function tokenize2(text) {
|
|
15869
|
-
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
15870
|
-
}
|
|
15871
|
-
function containsKeyword(text, keyword) {
|
|
15872
|
-
if (/^[a-z0-9_-]+$/i.test(keyword)) {
|
|
15873
|
-
return new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9_-]|$)`, "i").test(text);
|
|
15874
|
-
}
|
|
15875
|
-
return text.includes(keyword);
|
|
16172
|
+
// src/codegraph/project-context.ts
|
|
16173
|
+
function uniq(items) {
|
|
16174
|
+
return [...new Set(items)];
|
|
15876
16175
|
}
|
|
15877
|
-
function
|
|
15878
|
-
|
|
15879
|
-
if (!normalized.trim()) return LENSES.general;
|
|
15880
|
-
let best = {
|
|
15881
|
-
id: "general",
|
|
15882
|
-
score: 0,
|
|
15883
|
-
priority: Number.MAX_SAFE_INTEGER
|
|
15884
|
-
};
|
|
15885
|
-
for (const id of LENS_PRIORITY) {
|
|
15886
|
-
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
15887
|
-
const priority = LENS_PRIORITY.indexOf(id);
|
|
15888
|
-
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
15889
|
-
best = { id, score, priority };
|
|
15890
|
-
}
|
|
15891
|
-
}
|
|
15892
|
-
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
16176
|
+
function activeObservations(observations2, projectId) {
|
|
16177
|
+
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
15893
16178
|
}
|
|
15894
|
-
function
|
|
15895
|
-
const
|
|
15896
|
-
const
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
15900
|
-
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
15901
|
-
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
15902
|
-
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
15903
|
-
switch (lens.id) {
|
|
15904
|
-
case "bugfix":
|
|
15905
|
-
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
15906
|
-
case "test":
|
|
15907
|
-
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
15908
|
-
case "release":
|
|
15909
|
-
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
15910
|
-
case "onboarding":
|
|
15911
|
-
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
15912
|
-
case "docs":
|
|
15913
|
-
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
15914
|
-
case "refactor":
|
|
15915
|
-
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
15916
|
-
case "feature":
|
|
15917
|
-
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
15918
|
-
default:
|
|
15919
|
-
if (isSource || isTest) return 60;
|
|
15920
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
15921
|
-
if (inDocs) return 20;
|
|
15922
|
-
return 0;
|
|
16179
|
+
function countLanguages(files) {
|
|
16180
|
+
const counts = /* @__PURE__ */ new Map();
|
|
16181
|
+
for (const file of files) {
|
|
16182
|
+
const language = file.language ?? "unknown";
|
|
16183
|
+
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
15923
16184
|
}
|
|
16185
|
+
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
15924
16186
|
}
|
|
15925
|
-
function
|
|
15926
|
-
|
|
15927
|
-
const normalized = text.toLowerCase();
|
|
15928
|
-
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
15929
|
-
}
|
|
15930
|
-
function sourceTaskMatchScore(source, task) {
|
|
15931
|
-
const tokens = tokenize2(task ?? "");
|
|
15932
|
-
if (tokens.length === 0) return 0;
|
|
15933
|
-
const text = [
|
|
15934
|
-
source.title,
|
|
15935
|
-
source.path ?? "",
|
|
15936
|
-
source.symbol ?? ""
|
|
15937
|
-
].join("\n").toLowerCase();
|
|
15938
|
-
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
15939
|
-
if (matches >= 2) return 40;
|
|
15940
|
-
if (matches === 1) return 24;
|
|
15941
|
-
return 0;
|
|
15942
|
-
}
|
|
15943
|
-
function fallbackPathRank(path25) {
|
|
15944
|
-
const normalized = normalizePath2(path25);
|
|
16187
|
+
function suggestedReadRank(path25) {
|
|
16188
|
+
const normalized = path25.replace(/\\/g, "/");
|
|
15945
16189
|
if (normalized.startsWith("src/")) return 0;
|
|
15946
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return
|
|
16190
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
15947
16191
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15948
|
-
|
|
15949
|
-
return 4;
|
|
15950
|
-
}
|
|
15951
|
-
function rankLensPaths(paths, lens, task) {
|
|
15952
|
-
const tokens = tokenize2(task ?? "");
|
|
15953
|
-
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
15954
|
-
path: path25,
|
|
15955
|
-
index,
|
|
15956
|
-
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
15957
|
-
fallback: fallbackPathRank(path25)
|
|
15958
|
-
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
15959
|
-
}
|
|
15960
|
-
function lensPathCandidates(lens) {
|
|
15961
|
-
switch (lens.id) {
|
|
15962
|
-
case "release":
|
|
15963
|
-
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
15964
|
-
case "onboarding":
|
|
15965
|
-
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
15966
|
-
case "docs":
|
|
15967
|
-
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
15968
|
-
case "test":
|
|
15969
|
-
case "bugfix":
|
|
15970
|
-
return ["tests", "test"];
|
|
15971
|
-
default:
|
|
15972
|
-
return [];
|
|
15973
|
-
}
|
|
15974
|
-
}
|
|
15975
|
-
function scoreLensSource(source, lens, task) {
|
|
15976
|
-
const tokens = tokenize2(task ?? "");
|
|
15977
|
-
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
15978
|
-
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
16192
|
+
return 3;
|
|
15979
16193
|
}
|
|
15980
|
-
function
|
|
15981
|
-
return
|
|
15982
|
-
source,
|
|
15983
|
-
index,
|
|
15984
|
-
score: scoreLensSource(source, lens, task)
|
|
15985
|
-
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
16194
|
+
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
16195
|
+
return uniq(paths).filter((path25) => !isCodeGraphExcludedPath(path25, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
15986
16196
|
}
|
|
15987
|
-
function
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16197
|
+
function collectGraph(store, projectId, observations2, exclude) {
|
|
16198
|
+
const files = store.listFiles(projectId);
|
|
16199
|
+
const symbols = store.listReferencedSymbols(projectId);
|
|
16200
|
+
const filesById = new Map(files.map((file) => [file.id, file]));
|
|
16201
|
+
const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
|
|
16202
|
+
const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
|
|
16203
|
+
const activeObservationIds = new Set(observations2.map((obs) => obs.id));
|
|
16204
|
+
const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
|
|
16205
|
+
const freshness = {
|
|
16206
|
+
current: 0,
|
|
16207
|
+
suspect: 0,
|
|
16208
|
+
stale: 0,
|
|
16209
|
+
unbound: 0
|
|
16210
|
+
};
|
|
16211
|
+
const sources = [];
|
|
16212
|
+
const suggestedReads = [];
|
|
16213
|
+
for (const ref of refs) {
|
|
16214
|
+
const file = ref.fileId ? filesById.get(ref.fileId) : void 0;
|
|
16215
|
+
const symbol = ref.symbolId ? symbolsById.get(ref.symbolId) : void 0;
|
|
16216
|
+
const result = evaluateCodeRefFreshness(ref, file, symbol);
|
|
16217
|
+
freshness[result.status] += 1;
|
|
16218
|
+
const observation = observationsById.get(ref.observationId);
|
|
16219
|
+
if (!observation) continue;
|
|
16220
|
+
const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
|
|
16221
|
+
if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
|
|
16222
|
+
if (excluded) continue;
|
|
16223
|
+
sources.push({
|
|
16224
|
+
observationId: observation.id,
|
|
16225
|
+
title: observation.title,
|
|
16226
|
+
type: observation.type,
|
|
16227
|
+
...file ? { path: file.path } : {},
|
|
16228
|
+
...symbol ? { symbol: symbol.name } : {},
|
|
16229
|
+
status: result.status,
|
|
16230
|
+
reason: result.reason
|
|
16231
|
+
});
|
|
16232
|
+
}
|
|
16233
|
+
if (files.length === 0) {
|
|
16234
|
+
for (const observation of observations2) {
|
|
16235
|
+
for (const path25 of observation.filesModified ?? []) {
|
|
16236
|
+
if (!path25 || isCodeGraphExcludedPath(path25, exclude)) continue;
|
|
16237
|
+
suggestedReads.push(path25);
|
|
16238
|
+
sources.push({
|
|
16239
|
+
observationId: observation.id,
|
|
16240
|
+
title: observation.title,
|
|
16241
|
+
type: observation.type,
|
|
16242
|
+
path: path25,
|
|
16243
|
+
status: "unbound",
|
|
16244
|
+
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
16245
|
+
});
|
|
16246
|
+
freshness.unbound++;
|
|
16247
|
+
}
|
|
16248
|
+
}
|
|
16029
16249
|
}
|
|
16250
|
+
return {
|
|
16251
|
+
files,
|
|
16252
|
+
symbols,
|
|
16253
|
+
refs,
|
|
16254
|
+
freshness,
|
|
16255
|
+
sources,
|
|
16256
|
+
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
16257
|
+
};
|
|
16030
16258
|
}
|
|
16031
|
-
function
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16259
|
+
function overviewFromGraph(input) {
|
|
16260
|
+
const status = input.store.status(input.project.id);
|
|
16261
|
+
return {
|
|
16262
|
+
project: input.project,
|
|
16263
|
+
code: {
|
|
16264
|
+
provider: status.provider,
|
|
16265
|
+
files: status.files,
|
|
16266
|
+
symbols: status.symbols,
|
|
16267
|
+
edges: status.edges,
|
|
16268
|
+
refs: status.refs,
|
|
16269
|
+
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
16270
|
+
languages: countLanguages(input.graph.files),
|
|
16271
|
+
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
16272
|
+
},
|
|
16273
|
+
memory: {
|
|
16274
|
+
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
16275
|
+
active: input.active.length
|
|
16276
|
+
},
|
|
16277
|
+
freshness: input.graph.freshness,
|
|
16278
|
+
suggestedReads: input.graph.suggestedReads
|
|
16279
|
+
};
|
|
16036
16280
|
}
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16281
|
+
function buildProjectContextExplain(input) {
|
|
16282
|
+
const active = activeObservations(input.observations, input.project.id);
|
|
16283
|
+
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
16284
|
+
const overview = overviewFromGraph({
|
|
16285
|
+
project: input.project,
|
|
16286
|
+
store: input.store,
|
|
16287
|
+
observations: input.observations,
|
|
16288
|
+
active,
|
|
16289
|
+
graph
|
|
16290
|
+
});
|
|
16291
|
+
return {
|
|
16292
|
+
project: input.project,
|
|
16293
|
+
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
16294
|
+
overview
|
|
16295
|
+
};
|
|
16296
|
+
}
|
|
16297
|
+
var init_project_context = __esm({
|
|
16298
|
+
"src/codegraph/project-context.ts"() {
|
|
16040
16299
|
"use strict";
|
|
16041
16300
|
init_esm_shims();
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
"an",
|
|
16045
|
-
"and",
|
|
16046
|
-
"as",
|
|
16047
|
-
"for",
|
|
16048
|
-
"in",
|
|
16049
|
-
"into",
|
|
16050
|
-
"of",
|
|
16051
|
-
"on",
|
|
16052
|
-
"onto",
|
|
16053
|
-
"the",
|
|
16054
|
-
"this",
|
|
16055
|
-
"that",
|
|
16056
|
-
"to",
|
|
16057
|
-
"with",
|
|
16058
|
-
"work",
|
|
16059
|
-
"project",
|
|
16060
|
-
"continue",
|
|
16061
|
-
"\u7EE7\u7EED",
|
|
16062
|
-
"\u9879\u76EE"
|
|
16063
|
-
]);
|
|
16064
|
-
LENSES = {
|
|
16065
|
-
bugfix: {
|
|
16066
|
-
id: "bugfix",
|
|
16067
|
-
description: "debug the failure with current code and the smallest repro first",
|
|
16068
|
-
sourceLimit: 6,
|
|
16069
|
-
cautionLimit: 4,
|
|
16070
|
-
hideUnrelatedCautionDetails: true,
|
|
16071
|
-
hideUnrelatedReliableDetails: false
|
|
16072
|
-
},
|
|
16073
|
-
feature: {
|
|
16074
|
-
id: "feature",
|
|
16075
|
-
description: "build a scoped feature from nearby source, types, and user flow",
|
|
16076
|
-
sourceLimit: 6,
|
|
16077
|
-
cautionLimit: 3,
|
|
16078
|
-
hideUnrelatedCautionDetails: true,
|
|
16079
|
-
hideUnrelatedReliableDetails: false
|
|
16080
|
-
},
|
|
16081
|
-
release: {
|
|
16082
|
-
id: "release",
|
|
16083
|
-
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
16084
|
-
sourceLimit: 4,
|
|
16085
|
-
cautionLimit: 2,
|
|
16086
|
-
hideUnrelatedCautionDetails: true,
|
|
16087
|
-
hideUnrelatedReliableDetails: true
|
|
16088
|
-
},
|
|
16089
|
-
onboarding: {
|
|
16090
|
-
id: "onboarding",
|
|
16091
|
-
description: "understand the project shape before trusting old implementation details",
|
|
16092
|
-
sourceLimit: 4,
|
|
16093
|
-
cautionLimit: 2,
|
|
16094
|
-
hideUnrelatedCautionDetails: true,
|
|
16095
|
-
hideUnrelatedReliableDetails: true
|
|
16096
|
-
},
|
|
16097
|
-
refactor: {
|
|
16098
|
-
id: "refactor",
|
|
16099
|
-
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
16100
|
-
sourceLimit: 6,
|
|
16101
|
-
cautionLimit: 4,
|
|
16102
|
-
hideUnrelatedCautionDetails: true,
|
|
16103
|
-
hideUnrelatedReliableDetails: false
|
|
16104
|
-
},
|
|
16105
|
-
docs: {
|
|
16106
|
-
id: "docs",
|
|
16107
|
-
description: "update documentation against current code and public entry points",
|
|
16108
|
-
sourceLimit: 5,
|
|
16109
|
-
cautionLimit: 2,
|
|
16110
|
-
hideUnrelatedCautionDetails: true,
|
|
16111
|
-
hideUnrelatedReliableDetails: true
|
|
16112
|
-
},
|
|
16113
|
-
test: {
|
|
16114
|
-
id: "test",
|
|
16115
|
-
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
16116
|
-
sourceLimit: 6,
|
|
16117
|
-
cautionLimit: 3,
|
|
16118
|
-
hideUnrelatedCautionDetails: true,
|
|
16119
|
-
hideUnrelatedReliableDetails: false
|
|
16120
|
-
},
|
|
16121
|
-
general: {
|
|
16122
|
-
id: "general",
|
|
16123
|
-
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
16124
|
-
sourceLimit: 8,
|
|
16125
|
-
cautionLimit: 5,
|
|
16126
|
-
hideUnrelatedCautionDetails: false,
|
|
16127
|
-
hideUnrelatedReliableDetails: false
|
|
16128
|
-
}
|
|
16129
|
-
};
|
|
16130
|
-
KEYWORDS = {
|
|
16131
|
-
bugfix: [
|
|
16132
|
-
"bug",
|
|
16133
|
-
"crash",
|
|
16134
|
-
"debug",
|
|
16135
|
-
"error",
|
|
16136
|
-
"fail",
|
|
16137
|
-
"failing",
|
|
16138
|
-
"fix",
|
|
16139
|
-
"issue",
|
|
16140
|
-
"regression",
|
|
16141
|
-
"repro",
|
|
16142
|
-
"\u62A5\u9519",
|
|
16143
|
-
"\u5D29\u6E83",
|
|
16144
|
-
"\u5931\u8D25",
|
|
16145
|
-
"\u4FEE\u590D",
|
|
16146
|
-
"\u95EE\u9898"
|
|
16147
|
-
],
|
|
16148
|
-
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
16149
|
-
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
16150
|
-
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
16151
|
-
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
16152
|
-
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
16153
|
-
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
16154
|
-
};
|
|
16155
|
-
LENS_PRIORITY = [
|
|
16156
|
-
"bugfix",
|
|
16157
|
-
"release",
|
|
16158
|
-
"test",
|
|
16159
|
-
"refactor",
|
|
16160
|
-
"feature",
|
|
16161
|
-
"docs",
|
|
16162
|
-
"onboarding"
|
|
16163
|
-
];
|
|
16301
|
+
init_freshness2();
|
|
16302
|
+
init_exclude();
|
|
16164
16303
|
}
|
|
16165
16304
|
});
|
|
16166
16305
|
|
|
@@ -16459,15 +16598,7 @@ function formatCurrentFactsLines(facts) {
|
|
|
16459
16598
|
if (facts.latestChangelog) {
|
|
16460
16599
|
lines.push(`- Latest changelog: ${facts.latestChangelog.version}${facts.latestChangelog.date ? ` (${facts.latestChangelog.date})` : ""}`);
|
|
16461
16600
|
}
|
|
16462
|
-
|
|
16463
|
-
if (facts.git.detached) {
|
|
16464
|
-
gitParts.push("detached HEAD");
|
|
16465
|
-
} else if (facts.git.branch) {
|
|
16466
|
-
gitParts.push(`branch ${facts.git.branch}`);
|
|
16467
|
-
}
|
|
16468
|
-
if (facts.git.commit) gitParts.push(`commit ${facts.git.commit}`);
|
|
16469
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16470
|
-
lines.push(`- Git: ${gitParts.join(", ")}`);
|
|
16601
|
+
lines.push("- " + formatGitFact(facts.git));
|
|
16471
16602
|
if (facts.git.latestCommit) lines.push(`- Latest commit: ${facts.git.latestCommit}`);
|
|
16472
16603
|
lines.push("- Current facts above outrank progress/dev-log files when they conflict.");
|
|
16473
16604
|
if (facts.staleNotes.length > 0) {
|
|
@@ -16489,11 +16620,7 @@ function worksetFactLines(facts) {
|
|
|
16489
16620
|
if (facts.latestChangelog) {
|
|
16490
16621
|
lines.push("Latest changelog: " + facts.latestChangelog.version + (facts.latestChangelog.date ? " (" + facts.latestChangelog.date + ")" : ""));
|
|
16491
16622
|
}
|
|
16492
|
-
|
|
16493
|
-
if (facts.git.branch) gitParts.push("branch " + facts.git.branch);
|
|
16494
|
-
if (facts.git.commit) gitParts.push("commit " + facts.git.commit);
|
|
16495
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16496
|
-
lines.push("Git: " + gitParts.join(", "));
|
|
16623
|
+
lines.push(formatGitFact(facts.git));
|
|
16497
16624
|
for (const note of facts.staleNotes.slice(0, 1)) {
|
|
16498
16625
|
lines.push(
|
|
16499
16626
|
"Historical note: " + note.path + (note.branchHint ? " (branch hint " + note.branchHint + "; " + note.reason + ")" : " (" + note.reason + ")")
|
|
@@ -18975,13 +19102,8 @@ function prepareDashboardConfig(projectRoot) {
|
|
|
18975
19102
|
}
|
|
18976
19103
|
}
|
|
18977
19104
|
function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
|
|
18978
|
-
const
|
|
18979
|
-
|
|
18980
|
-
);
|
|
18981
|
-
const entities = allEntities.filter((e) => entityNames.has(e.name));
|
|
18982
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
18983
|
-
const relations = allRelations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
18984
|
-
return { entities: entities.length, relations: relations.length, entityNames };
|
|
19105
|
+
const scoped = scopeKnowledgeGraphToProject({ entities: allEntities, relations: allRelations }, projectObs);
|
|
19106
|
+
return { entities: scoped.entities.length, relations: scoped.relations.length, entityNames: scoped.entityNames };
|
|
18985
19107
|
}
|
|
18986
19108
|
async function handleApi(req, res, dataDir, projectId, projectName, baseDir, projectRoot, projectResolved, mode = "standalone", port = 3210) {
|
|
18987
19109
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -19054,13 +19176,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19054
19176
|
const gStore = getGraphStore();
|
|
19055
19177
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19056
19178
|
const graphObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19057
|
-
const
|
|
19058
|
-
|
|
19059
|
-
);
|
|
19060
|
-
const entities = graph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19061
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
19062
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
19063
|
-
sendJson(res, { entities, relations });
|
|
19179
|
+
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19180
|
+
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19064
19181
|
break;
|
|
19065
19182
|
}
|
|
19066
19183
|
case "/observations": {
|
|
@@ -19252,19 +19369,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19252
19369
|
const allObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19253
19370
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19254
19371
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19255
|
-
const
|
|
19256
|
-
const projectEntityNames = new Set(
|
|
19257
|
-
graphObs.filter((o) => o.entityName).map((o) => o.entityName)
|
|
19258
|
-
);
|
|
19259
|
-
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19260
|
-
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
19261
|
-
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
19372
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
19262
19373
|
const graph = generateKnowledgeGraph2({
|
|
19263
19374
|
projectId: effectiveProjectId,
|
|
19264
19375
|
observations: allObs,
|
|
19265
19376
|
miniSkills: skills,
|
|
19266
|
-
graphEntities:
|
|
19267
|
-
graphRelations:
|
|
19377
|
+
graphEntities: scoped.entities,
|
|
19378
|
+
graphRelations: scoped.relations
|
|
19268
19379
|
});
|
|
19269
19380
|
sendJson(res, graph);
|
|
19270
19381
|
break;
|
|
@@ -19486,16 +19597,11 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19486
19597
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19487
19598
|
const observations2 = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19488
19599
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19489
|
-
const
|
|
19490
|
-
observations2.filter((o) => (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
19491
|
-
);
|
|
19492
|
-
const exportEntities = fullGraph.entities.filter((e) => exportEntityNames.has(e.name));
|
|
19493
|
-
const exportEntitySet = new Set(exportEntities.map((e) => e.name));
|
|
19494
|
-
const exportRelations = fullGraph.relations.filter((r) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
|
|
19600
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19495
19601
|
const exportData = {
|
|
19496
19602
|
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
19497
19603
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19498
|
-
graph: { entities:
|
|
19604
|
+
graph: { entities: scoped.entities, relations: scoped.relations },
|
|
19499
19605
|
observations: observations2,
|
|
19500
19606
|
nextId: nextId2
|
|
19501
19607
|
};
|
|
@@ -19812,6 +19918,7 @@ var init_server = __esm({
|
|
|
19812
19918
|
init_dotenv_loader();
|
|
19813
19919
|
init_yaml_loader();
|
|
19814
19920
|
init_yaml_loader();
|
|
19921
|
+
init_graph_scope();
|
|
19815
19922
|
MIME_TYPES = {
|
|
19816
19923
|
".html": "text/html; charset=utf-8",
|
|
19817
19924
|
".css": "text/css; charset=utf-8",
|
|
@@ -22528,6 +22635,24 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22528
22635
|
const relationType = inferRelationType(obs);
|
|
22529
22636
|
const relations = [];
|
|
22530
22637
|
const selfName = obs.entityName.toLowerCase();
|
|
22638
|
+
const explicitRelated = [...new Set((obs.relatedEntities ?? []).map((name) => name.trim()).filter((name) => name && name.toLowerCase() !== selfName))];
|
|
22639
|
+
if (explicitRelated.length > 0) {
|
|
22640
|
+
await graphManager.createEntities(explicitRelated.map((name) => ({
|
|
22641
|
+
name,
|
|
22642
|
+
entityType: "related",
|
|
22643
|
+
observations: []
|
|
22644
|
+
})));
|
|
22645
|
+
for (const name of explicitRelated) {
|
|
22646
|
+
const matchedEntity = graphManager.findEntityByName(name);
|
|
22647
|
+
if (matchedEntity) {
|
|
22648
|
+
relations.push({
|
|
22649
|
+
from: obs.entityName,
|
|
22650
|
+
to: matchedEntity.name,
|
|
22651
|
+
relationType: "related_entity"
|
|
22652
|
+
});
|
|
22653
|
+
}
|
|
22654
|
+
}
|
|
22655
|
+
}
|
|
22531
22656
|
const candidates = [
|
|
22532
22657
|
...extracted.identifiers,
|
|
22533
22658
|
...extracted.files.map((f) => f.split("/").pop()?.replace(/\.\w+$/, "") ?? ""),
|
|
@@ -22569,6 +22694,7 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22569
22694
|
|
|
22570
22695
|
// src/server.ts
|
|
22571
22696
|
init_entity_extractor();
|
|
22697
|
+
init_graph_scope();
|
|
22572
22698
|
|
|
22573
22699
|
// src/compact/engine.ts
|
|
22574
22700
|
init_esm_shims();
|
|
@@ -26254,7 +26380,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26254
26380
|
};
|
|
26255
26381
|
const server = existingServer ?? new McpServer({
|
|
26256
26382
|
name: "memorix",
|
|
26257
|
-
version: true ? "1.2.
|
|
26383
|
+
version: true ? "1.2.1" : "1.0.1"
|
|
26258
26384
|
});
|
|
26259
26385
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26260
26386
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26986,7 +27112,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26986
27112
|
{ getResolvedConfig: getResolvedConfig2 },
|
|
26987
27113
|
{ getExternalCodeGraphContext: getExternalCodeGraphContext2 },
|
|
26988
27114
|
{ getObservationStore: getObservationStore2 },
|
|
26989
|
-
{ collectCurrentProjectFacts: collectCurrentProjectFacts2 },
|
|
27115
|
+
{ collectCurrentProjectFacts: collectCurrentProjectFacts2, formatGitFact: formatGitFact2 },
|
|
26990
27116
|
{ resolveTaskLens: resolveTaskLens2 }
|
|
26991
27117
|
] = await Promise.all([
|
|
26992
27118
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
@@ -27027,7 +27153,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27027
27153
|
if (currentFacts.latestChangelog) {
|
|
27028
27154
|
worksetFacts.push("Latest changelog: " + currentFacts.latestChangelog.version + (currentFacts.latestChangelog.date ? " (" + currentFacts.latestChangelog.date + ")" : ""));
|
|
27029
27155
|
}
|
|
27030
|
-
worksetFacts.push(
|
|
27156
|
+
worksetFacts.push(formatGitFact2(currentFacts.git));
|
|
27031
27157
|
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";
|
|
27032
27158
|
const pack = await attachTaskWorkset2({
|
|
27033
27159
|
pack: basePack,
|
|
@@ -27059,11 +27185,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27059
27185
|
"memorix_knowledge",
|
|
27060
27186
|
{
|
|
27061
27187
|
title: "Knowledge Workspace",
|
|
27062
|
-
description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace,
|
|
27188
|
+
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.",
|
|
27063
27189
|
inputSchema: {
|
|
27064
27190
|
action: z2.enum([
|
|
27065
27191
|
"workspace_init",
|
|
27066
27192
|
"status",
|
|
27193
|
+
"claim_list",
|
|
27194
|
+
"claim_review",
|
|
27067
27195
|
"compile",
|
|
27068
27196
|
"lint",
|
|
27069
27197
|
"proposal_apply",
|
|
@@ -27078,6 +27206,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27078
27206
|
path: z2.string().optional().describe("Explicit versioned workspace path, used only by workspace_init"),
|
|
27079
27207
|
proposalId: z2.string().optional().describe("Pending proposal id for proposal_apply"),
|
|
27080
27208
|
allowManualOverwrite: z2.boolean().optional().default(false).describe("Explicitly allow proposal_apply to replace a manually edited page"),
|
|
27209
|
+
claimId: z2.string().optional().describe("Source-backed claim id for claim_review"),
|
|
27210
|
+
claimReviewState: z2.enum(["approved", "rejected"]).optional().describe("Deliberate review verdict for claim_review"),
|
|
27211
|
+
reviewDetail: z2.string().max(2e3).optional().describe("Evidence check performed before approving or rejecting a claim"),
|
|
27081
27212
|
workflowId: z2.string().optional().describe("Canonical workflow id for workflow preview, apply, or run"),
|
|
27082
27213
|
agent: z2.string().optional().describe("Target agent for a workflow adapter"),
|
|
27083
27214
|
task: z2.string().optional().describe("Task text for workflow selection or a workflow run"),
|
|
@@ -27094,6 +27225,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27094
27225
|
path: workspacePath,
|
|
27095
27226
|
proposalId,
|
|
27096
27227
|
allowManualOverwrite,
|
|
27228
|
+
claimId,
|
|
27229
|
+
claimReviewState,
|
|
27230
|
+
reviewDetail,
|
|
27097
27231
|
workflowId,
|
|
27098
27232
|
agent,
|
|
27099
27233
|
task,
|
|
@@ -27116,6 +27250,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27116
27250
|
};
|
|
27117
27251
|
const [
|
|
27118
27252
|
{ ClaimStore: ClaimStore2 },
|
|
27253
|
+
{ reviewClaim: reviewClaim2 },
|
|
27119
27254
|
{ CodeGraphStore: CodeGraphStore2 },
|
|
27120
27255
|
{ initializeKnowledgeWorkspace: initializeKnowledgeWorkspace2, loadKnowledgeWorkspace: loadKnowledgeWorkspace2 },
|
|
27121
27256
|
{ KnowledgeWorkspaceStore: KnowledgeWorkspaceStore2 },
|
|
@@ -27131,6 +27266,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27131
27266
|
}
|
|
27132
27267
|
] = await Promise.all([
|
|
27133
27268
|
Promise.resolve().then(() => (init_claim_store(), claim_store_exports)),
|
|
27269
|
+
Promise.resolve().then(() => (init_claims(), claims_exports)),
|
|
27134
27270
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
27135
27271
|
Promise.resolve().then(() => (init_workspace(), workspace_exports)),
|
|
27136
27272
|
Promise.resolve().then(() => (init_workspace_store(), workspace_store_exports)),
|
|
@@ -27180,10 +27316,52 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27180
27316
|
targetPath: proposal.targetPath,
|
|
27181
27317
|
reason: proposal.reason,
|
|
27182
27318
|
createdAt: proposal.createdAt
|
|
27183
|
-
}))
|
|
27319
|
+
})),
|
|
27320
|
+
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 }))
|
|
27184
27321
|
}
|
|
27185
27322
|
});
|
|
27186
27323
|
}
|
|
27324
|
+
if (action === "claim_list") {
|
|
27325
|
+
return text({
|
|
27326
|
+
claims: claims.listClaims(project.id, { limit: 100 }).map((claim) => ({
|
|
27327
|
+
id: claim.id,
|
|
27328
|
+
subject: claim.subject,
|
|
27329
|
+
predicate: claim.predicate,
|
|
27330
|
+
objectValue: claim.objectValue,
|
|
27331
|
+
status: claim.status,
|
|
27332
|
+
reviewState: claim.reviewState,
|
|
27333
|
+
origin: claim.origin,
|
|
27334
|
+
confidence: claim.confidence,
|
|
27335
|
+
evidenceCount: claims.listEvidence(claim.id).length
|
|
27336
|
+
})),
|
|
27337
|
+
next: "Approve only after checking the linked evidence. Rejected claims stay out of retrieval and publication."
|
|
27338
|
+
});
|
|
27339
|
+
}
|
|
27340
|
+
if (action === "claim_review") {
|
|
27341
|
+
const requestedClaimId = requireText(claimId, "claimId");
|
|
27342
|
+
const detail = requireText(reviewDetail, "reviewDetail");
|
|
27343
|
+
if (!requestedClaimId || !claimReviewState || !detail) {
|
|
27344
|
+
return text({ error: "claimId, claimReviewState, and reviewDetail are required for claim_review." }, true);
|
|
27345
|
+
}
|
|
27346
|
+
const existing = claims.getClaim(requestedClaimId);
|
|
27347
|
+
if (!existing || existing.projectId !== project.id) {
|
|
27348
|
+
return text({ error: "Claim was not found for the current project." }, true);
|
|
27349
|
+
}
|
|
27350
|
+
const claim = reviewClaim2(claims, {
|
|
27351
|
+
claimId: requestedClaimId,
|
|
27352
|
+
reviewState: claimReviewState,
|
|
27353
|
+
detail
|
|
27354
|
+
});
|
|
27355
|
+
return text({
|
|
27356
|
+
claim: {
|
|
27357
|
+
id: claim.id,
|
|
27358
|
+
status: claim.status,
|
|
27359
|
+
reviewState: claim.reviewState,
|
|
27360
|
+
updatedAt: claim.updatedAt
|
|
27361
|
+
},
|
|
27362
|
+
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."
|
|
27363
|
+
});
|
|
27364
|
+
}
|
|
27187
27365
|
if (action === "compile") {
|
|
27188
27366
|
const result = await compileKnowledgeWorkspace2({ workspace, claims });
|
|
27189
27367
|
return text({
|
|
@@ -27222,7 +27400,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27222
27400
|
if (action === "workflow_import") {
|
|
27223
27401
|
const result = await importWindsurfWorkflows2({ workspace, projectRoot });
|
|
27224
27402
|
return text({
|
|
27225
|
-
imported: result.imported.map((workflow2) => ({
|
|
27403
|
+
imported: result.imported.map((workflow2) => ({
|
|
27404
|
+
id: workflow2.id,
|
|
27405
|
+
title: workflow2.title,
|
|
27406
|
+
sourcePath: workflow2.sourcePath,
|
|
27407
|
+
importedFrom: workflow2.importedFrom,
|
|
27408
|
+
verificationGates: workflow2.verificationGates
|
|
27409
|
+
})),
|
|
27226
27410
|
skipped: result.skipped
|
|
27227
27411
|
});
|
|
27228
27412
|
}
|
|
@@ -27234,7 +27418,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27234
27418
|
title: workflow2.title,
|
|
27235
27419
|
status: workflow2.status,
|
|
27236
27420
|
taskLenses: workflow2.taskLenses,
|
|
27237
|
-
sourcePath: workflow2.sourcePath
|
|
27421
|
+
sourcePath: workflow2.sourcePath,
|
|
27422
|
+
importedFrom: workflow2.importedFrom,
|
|
27423
|
+
verificationGates: workflow2.verificationGates
|
|
27238
27424
|
})),
|
|
27239
27425
|
parseErrors: synced.errors
|
|
27240
27426
|
});
|
|
@@ -28063,13 +28249,11 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
28063
28249
|
async function scopeGraphToProject(graph) {
|
|
28064
28250
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28065
28251
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28066
|
-
const
|
|
28067
|
-
|
|
28252
|
+
const scoped = scopeKnowledgeGraphToProject(
|
|
28253
|
+
graph,
|
|
28254
|
+
allObs.filter((observation) => observation.projectId === project.id)
|
|
28068
28255
|
);
|
|
28069
|
-
|
|
28070
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
28071
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
28072
|
-
return { entities, relations };
|
|
28256
|
+
return { entities: scoped.entities, relations: scoped.relations };
|
|
28073
28257
|
}
|
|
28074
28258
|
server.registerTool(
|
|
28075
28259
|
"read_graph",
|