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/sdk.js
CHANGED
|
@@ -9066,6 +9066,36 @@ var init_session_store = __esm({
|
|
|
9066
9066
|
}
|
|
9067
9067
|
});
|
|
9068
9068
|
|
|
9069
|
+
// src/memory/graph-scope.ts
|
|
9070
|
+
function projectGraphEntityNames(observations2) {
|
|
9071
|
+
const entityNames = /* @__PURE__ */ new Set();
|
|
9072
|
+
for (const observation of observations2) {
|
|
9073
|
+
if ((observation.status ?? "active") !== "active") continue;
|
|
9074
|
+
const entityName = observation.entityName?.trim();
|
|
9075
|
+
if (entityName) entityNames.add(entityName);
|
|
9076
|
+
for (const relatedEntity of observation.relatedEntities ?? []) {
|
|
9077
|
+
const name = relatedEntity.trim();
|
|
9078
|
+
if (name) entityNames.add(name);
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
return entityNames;
|
|
9082
|
+
}
|
|
9083
|
+
function scopeKnowledgeGraphToProject(graph, observations2) {
|
|
9084
|
+
const entityNames = projectGraphEntityNames(observations2);
|
|
9085
|
+
const entities = graph.entities.filter((entity) => entityNames.has(entity.name));
|
|
9086
|
+
const visibleNames = new Set(entities.map((entity) => entity.name));
|
|
9087
|
+
const relations = graph.relations.filter(
|
|
9088
|
+
(relation) => visibleNames.has(relation.from) && visibleNames.has(relation.to)
|
|
9089
|
+
);
|
|
9090
|
+
return { entities, relations, entityNames };
|
|
9091
|
+
}
|
|
9092
|
+
var init_graph_scope = __esm({
|
|
9093
|
+
"src/memory/graph-scope.ts"() {
|
|
9094
|
+
"use strict";
|
|
9095
|
+
init_esm_shims();
|
|
9096
|
+
}
|
|
9097
|
+
});
|
|
9098
|
+
|
|
9069
9099
|
// src/memory/disclosure-policy.ts
|
|
9070
9100
|
function resolveEvidenceBasis(fields) {
|
|
9071
9101
|
if (fields.commitHash || fields.source === "git" || fields.sourceDetail === "git-ingest") {
|
|
@@ -12178,6 +12208,7 @@ __export(claims_exports, {
|
|
|
12178
12208
|
buildClaimIdentity: () => buildClaimIdentity,
|
|
12179
12209
|
deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
|
|
12180
12210
|
requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
|
|
12211
|
+
reviewClaim: () => reviewClaim,
|
|
12181
12212
|
selectClaimsForTask: () => selectClaimsForTask,
|
|
12182
12213
|
supersedeClaim: () => supersedeClaim,
|
|
12183
12214
|
writeClaim: () => writeClaim
|
|
@@ -12364,6 +12395,39 @@ function writeClaim(store, input) {
|
|
|
12364
12395
|
};
|
|
12365
12396
|
});
|
|
12366
12397
|
}
|
|
12398
|
+
function reviewClaim(store, input) {
|
|
12399
|
+
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
12400
|
+
throw new Error("A claim review must approve or reject the claim");
|
|
12401
|
+
}
|
|
12402
|
+
const detail = compactText(input.detail, "review detail");
|
|
12403
|
+
return store.transaction(() => {
|
|
12404
|
+
const claim = store.getClaim(input.claimId);
|
|
12405
|
+
if (!claim) throw new Error("Claim was not found");
|
|
12406
|
+
if (input.reviewState === "approved" && claim.status === "superseded") {
|
|
12407
|
+
throw new Error("A superseded claim cannot be approved");
|
|
12408
|
+
}
|
|
12409
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
12410
|
+
const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
|
|
12411
|
+
const updated = {
|
|
12412
|
+
...claim,
|
|
12413
|
+
status: nextStatus,
|
|
12414
|
+
reviewState: input.reviewState,
|
|
12415
|
+
updatedAt: now3
|
|
12416
|
+
};
|
|
12417
|
+
store.updateClaim(updated);
|
|
12418
|
+
store.recordEvent({
|
|
12419
|
+
projectId: claim.projectId,
|
|
12420
|
+
claimId: claim.id,
|
|
12421
|
+
kind: "reviewed",
|
|
12422
|
+
fromStatus: claim.status,
|
|
12423
|
+
toStatus: updated.status,
|
|
12424
|
+
detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
|
|
12425
|
+
createdAt: now3
|
|
12426
|
+
});
|
|
12427
|
+
reconcileConflicts(store, claim.projectId, claim.conflictKey, now3);
|
|
12428
|
+
return store.getClaim(claim.id) ?? updated;
|
|
12429
|
+
});
|
|
12430
|
+
}
|
|
12367
12431
|
function supersedeClaim(store, input) {
|
|
12368
12432
|
const evidence = validateEvidence(input.evidence);
|
|
12369
12433
|
return store.transaction(() => {
|
|
@@ -12452,7 +12516,7 @@ function deriveLowRiskClaimsFromObservation(store, observation, codeStore) {
|
|
|
12452
12516
|
scope: "project",
|
|
12453
12517
|
confidence,
|
|
12454
12518
|
observedAt: observation.updatedAt ?? observation.createdAt,
|
|
12455
|
-
reviewState: "approved",
|
|
12519
|
+
reviewState: isGit ? "approved" : "needs-review",
|
|
12456
12520
|
origin: isGit ? "git" : "derived",
|
|
12457
12521
|
evidence
|
|
12458
12522
|
});
|
|
@@ -13328,148 +13392,492 @@ var init_workflow_store = __esm({
|
|
|
13328
13392
|
}
|
|
13329
13393
|
});
|
|
13330
13394
|
|
|
13331
|
-
// src/
|
|
13332
|
-
var
|
|
13333
|
-
__export(
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13343
|
-
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13344
|
-
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13395
|
+
// src/codegraph/task-lens.ts
|
|
13396
|
+
var task_lens_exports = {};
|
|
13397
|
+
__export(task_lens_exports, {
|
|
13398
|
+
containsTaskKeyword: () => containsTaskKeyword,
|
|
13399
|
+
lensPathCandidates: () => lensPathCandidates,
|
|
13400
|
+
lensVerificationHints: () => lensVerificationHints,
|
|
13401
|
+
rankLensPaths: () => rankLensPaths,
|
|
13402
|
+
rankLensSources: () => rankLensSources,
|
|
13403
|
+
resolveTaskLens: () => resolveTaskLens,
|
|
13404
|
+
scoreLensSource: () => scoreLensSource,
|
|
13405
|
+
shouldShowLensSource: () => shouldShowLensSource
|
|
13345
13406
|
});
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
import path17 from "path";
|
|
13349
|
-
import matter11 from "gray-matter";
|
|
13350
|
-
function hash3(value) {
|
|
13351
|
-
return createHash12("sha256").update(value).digest("hex");
|
|
13352
|
-
}
|
|
13353
|
-
function now2() {
|
|
13354
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13355
|
-
}
|
|
13356
|
-
function slug(value) {
|
|
13357
|
-
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13358
|
-
return normalized || "workflow";
|
|
13407
|
+
function normalizePath2(path25) {
|
|
13408
|
+
return path25.replace(/\\/g, "/");
|
|
13359
13409
|
}
|
|
13360
|
-
function
|
|
13361
|
-
return
|
|
13410
|
+
function tokenize2(text) {
|
|
13411
|
+
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
13362
13412
|
}
|
|
13363
|
-
function
|
|
13364
|
-
const
|
|
13365
|
-
|
|
13366
|
-
|
|
13413
|
+
function containsTaskKeyword(text, keyword) {
|
|
13414
|
+
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");
|
|
13415
|
+
let match;
|
|
13416
|
+
while ((match = matcher.exec(text)) !== null) {
|
|
13417
|
+
const keywordIndex = match.index + (match[1]?.length ?? 0);
|
|
13418
|
+
if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
|
|
13367
13419
|
}
|
|
13368
|
-
return
|
|
13420
|
+
return false;
|
|
13369
13421
|
}
|
|
13370
|
-
function
|
|
13371
|
-
const
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13422
|
+
function isNegatedTaskKeyword(text, keywordIndex) {
|
|
13423
|
+
const boundary = Math.max(
|
|
13424
|
+
text.lastIndexOf(".", keywordIndex - 1),
|
|
13425
|
+
text.lastIndexOf("!", keywordIndex - 1),
|
|
13426
|
+
text.lastIndexOf("?", keywordIndex - 1),
|
|
13427
|
+
text.lastIndexOf(";", keywordIndex - 1),
|
|
13428
|
+
text.lastIndexOf("\n", keywordIndex - 1),
|
|
13429
|
+
text.lastIndexOf("\u3002", keywordIndex - 1),
|
|
13430
|
+
text.lastIndexOf("\uFF01", keywordIndex - 1),
|
|
13431
|
+
text.lastIndexOf("\uFF1F", keywordIndex - 1),
|
|
13432
|
+
text.lastIndexOf("\uFF1B", keywordIndex - 1)
|
|
13433
|
+
);
|
|
13434
|
+
let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
|
|
13435
|
+
const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
|
|
13436
|
+
let contrastMatch;
|
|
13437
|
+
let contrastEnd = -1;
|
|
13438
|
+
while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
|
|
13439
|
+
if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
|
|
13440
|
+
if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
|
|
13441
|
+
if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
|
|
13442
|
+
if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
|
|
13443
|
+
const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
|
|
13444
|
+
const chineseClause = prefix.slice(chineseClauseStart + 1);
|
|
13445
|
+
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);
|
|
13375
13446
|
}
|
|
13376
|
-
function
|
|
13377
|
-
const
|
|
13378
|
-
if (
|
|
13379
|
-
|
|
13380
|
-
|
|
13447
|
+
function resolveTaskLens(task) {
|
|
13448
|
+
const normalized = (task ?? "").toLowerCase();
|
|
13449
|
+
if (!normalized.trim()) return LENSES.general;
|
|
13450
|
+
let best = {
|
|
13451
|
+
id: "general",
|
|
13452
|
+
score: 0,
|
|
13453
|
+
priority: Number.MAX_SAFE_INTEGER
|
|
13454
|
+
};
|
|
13455
|
+
for (const id of LENS_PRIORITY) {
|
|
13456
|
+
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsTaskKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
13457
|
+
const priority = LENS_PRIORITY.indexOf(id);
|
|
13458
|
+
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
13459
|
+
best = { id, score, priority };
|
|
13460
|
+
}
|
|
13381
13461
|
}
|
|
13382
|
-
return [
|
|
13462
|
+
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
13383
13463
|
}
|
|
13384
|
-
function
|
|
13385
|
-
const
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13464
|
+
function pathKindScore(path25, lens) {
|
|
13465
|
+
const normalized = normalizePath2(path25).toLowerCase();
|
|
13466
|
+
const name = normalized.split("/").pop() ?? normalized;
|
|
13467
|
+
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
13468
|
+
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
13469
|
+
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
13470
|
+
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
13471
|
+
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
13472
|
+
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
13473
|
+
switch (lens.id) {
|
|
13474
|
+
case "bugfix":
|
|
13475
|
+
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
13476
|
+
case "test":
|
|
13477
|
+
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
13478
|
+
case "release":
|
|
13479
|
+
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
13480
|
+
case "onboarding":
|
|
13481
|
+
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
13482
|
+
case "docs":
|
|
13483
|
+
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
13484
|
+
case "refactor":
|
|
13485
|
+
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
13486
|
+
case "feature":
|
|
13487
|
+
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
13488
|
+
default:
|
|
13489
|
+
if (isSource || isTest) return 60;
|
|
13490
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
13491
|
+
if (inDocs) return 20;
|
|
13492
|
+
return 0;
|
|
13389
13493
|
}
|
|
13390
|
-
return value;
|
|
13391
13494
|
}
|
|
13392
|
-
function
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
throw new Error("workflow frontmatter field status is invalid");
|
|
13397
|
-
}
|
|
13398
|
-
return value;
|
|
13495
|
+
function tokenScore(text, tokens) {
|
|
13496
|
+
if (tokens.length === 0) return 0;
|
|
13497
|
+
const normalized = text.toLowerCase();
|
|
13498
|
+
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
13399
13499
|
}
|
|
13400
|
-
function
|
|
13401
|
-
const
|
|
13402
|
-
if (
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13500
|
+
function sourceTaskMatchScore(source, task) {
|
|
13501
|
+
const tokens = tokenize2(task ?? "");
|
|
13502
|
+
if (tokens.length === 0) return 0;
|
|
13503
|
+
const text = [
|
|
13504
|
+
source.title,
|
|
13505
|
+
source.path ?? "",
|
|
13506
|
+
source.symbol ?? ""
|
|
13507
|
+
].join("\n").toLowerCase();
|
|
13508
|
+
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
13509
|
+
if (matches >= 2) return 40;
|
|
13510
|
+
if (matches === 1) return 24;
|
|
13511
|
+
return 0;
|
|
13406
13512
|
}
|
|
13407
|
-
function
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
return {
|
|
13415
|
-
id: phaseId,
|
|
13416
|
-
title,
|
|
13417
|
-
instructions: optionalText4(data, "instructions") ?? "",
|
|
13418
|
-
branches: optionalStringArray(data, "branches"),
|
|
13419
|
-
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
13420
|
-
verificationGates: optionalStringArray(data, "verificationGates")
|
|
13421
|
-
};
|
|
13513
|
+
function fallbackPathRank(path25) {
|
|
13514
|
+
const normalized = normalizePath2(path25);
|
|
13515
|
+
if (normalized.startsWith("src/")) return 0;
|
|
13516
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
13517
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
13518
|
+
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
13519
|
+
return 4;
|
|
13422
13520
|
}
|
|
13423
|
-
function
|
|
13424
|
-
const
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
branches: [],
|
|
13432
|
-
expectedOutputs: [],
|
|
13433
|
-
verificationGates: []
|
|
13434
|
-
}] : [];
|
|
13435
|
-
}
|
|
13436
|
-
return headers.map((match, index) => {
|
|
13437
|
-
const title = match[1].trim();
|
|
13438
|
-
const start = (match.index ?? 0) + match[0].length;
|
|
13439
|
-
const end = index + 1 < headers.length ? headers[index + 1].index ?? body.length : body.length;
|
|
13440
|
-
return {
|
|
13441
|
-
id: slug(title) + "-" + (index + 1),
|
|
13442
|
-
title,
|
|
13443
|
-
instructions: body.slice(start, end).trim(),
|
|
13444
|
-
branches: [],
|
|
13445
|
-
expectedOutputs: [],
|
|
13446
|
-
verificationGates: []
|
|
13447
|
-
};
|
|
13448
|
-
});
|
|
13521
|
+
function rankLensPaths(paths, lens, task) {
|
|
13522
|
+
const tokens = tokenize2(task ?? "");
|
|
13523
|
+
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
13524
|
+
path: path25,
|
|
13525
|
+
index,
|
|
13526
|
+
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
13527
|
+
fallback: fallbackPathRank(path25)
|
|
13528
|
+
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
13449
13529
|
}
|
|
13450
|
-
function
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13530
|
+
function lensPathCandidates(lens) {
|
|
13531
|
+
switch (lens.id) {
|
|
13532
|
+
case "release":
|
|
13533
|
+
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
13534
|
+
case "onboarding":
|
|
13535
|
+
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
13536
|
+
case "docs":
|
|
13537
|
+
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
13538
|
+
case "test":
|
|
13539
|
+
case "bugfix":
|
|
13540
|
+
return ["tests", "test"];
|
|
13541
|
+
default:
|
|
13542
|
+
return [];
|
|
13454
13543
|
}
|
|
13455
|
-
return normalized;
|
|
13456
13544
|
}
|
|
13457
|
-
function
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13545
|
+
function scoreLensSource(source, lens, task) {
|
|
13546
|
+
const tokens = tokenize2(task ?? "");
|
|
13547
|
+
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
13548
|
+
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
13549
|
+
}
|
|
13550
|
+
function rankLensSources(sources, lens, task) {
|
|
13551
|
+
return [...sources].map((source, index) => ({
|
|
13552
|
+
source,
|
|
13553
|
+
index,
|
|
13554
|
+
score: scoreLensSource(source, lens, task)
|
|
13555
|
+
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
13556
|
+
}
|
|
13557
|
+
function lensVerificationHints(lens) {
|
|
13558
|
+
switch (lens.id) {
|
|
13559
|
+
case "bugfix":
|
|
13560
|
+
return [
|
|
13561
|
+
"run the smallest failing test or repro first",
|
|
13562
|
+
"inspect the changed code path before trusting old memory"
|
|
13563
|
+
];
|
|
13564
|
+
case "test":
|
|
13565
|
+
return [
|
|
13566
|
+
"run the exact focused test file or test name first",
|
|
13567
|
+
"inspect fixtures and harness setup before changing assertions"
|
|
13568
|
+
];
|
|
13569
|
+
case "release":
|
|
13570
|
+
return [
|
|
13571
|
+
"run build, tests, package smoke, and publish dry-run where available",
|
|
13572
|
+
"verify package metadata, changelog, and Git state before publishing"
|
|
13573
|
+
];
|
|
13574
|
+
case "onboarding":
|
|
13575
|
+
return [
|
|
13576
|
+
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
13577
|
+
"treat old implementation memories as leads until current code confirms them"
|
|
13578
|
+
];
|
|
13579
|
+
case "refactor":
|
|
13580
|
+
return [
|
|
13581
|
+
"inspect call sites and tests before editing shared code",
|
|
13582
|
+
"run the narrow affected test plus one regression smoke"
|
|
13583
|
+
];
|
|
13584
|
+
case "docs":
|
|
13585
|
+
return [
|
|
13586
|
+
"check headings, links, commands, and examples against current code",
|
|
13587
|
+
"run the smallest docs or package smoke available"
|
|
13588
|
+
];
|
|
13589
|
+
case "feature":
|
|
13590
|
+
return [
|
|
13591
|
+
"inspect the closest existing implementation pattern before adding new code",
|
|
13592
|
+
"run focused tests plus one user-flow smoke after changes"
|
|
13593
|
+
];
|
|
13594
|
+
default:
|
|
13595
|
+
return [
|
|
13596
|
+
"inspect the Start here files before editing",
|
|
13597
|
+
"run the smallest relevant test or smoke command after changes"
|
|
13598
|
+
];
|
|
13599
|
+
}
|
|
13600
|
+
}
|
|
13601
|
+
function shouldShowLensSource(source, lens, task) {
|
|
13602
|
+
if (lens.id === "general") return true;
|
|
13603
|
+
const score = scoreLensSource(source, lens, task);
|
|
13604
|
+
if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
|
|
13605
|
+
return score > 0;
|
|
13606
|
+
}
|
|
13607
|
+
var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
|
|
13608
|
+
var init_task_lens = __esm({
|
|
13609
|
+
"src/codegraph/task-lens.ts"() {
|
|
13610
|
+
"use strict";
|
|
13611
|
+
init_esm_shims();
|
|
13612
|
+
STOP_WORDS = /* @__PURE__ */ new Set([
|
|
13613
|
+
"a",
|
|
13614
|
+
"an",
|
|
13615
|
+
"and",
|
|
13616
|
+
"as",
|
|
13617
|
+
"for",
|
|
13618
|
+
"in",
|
|
13619
|
+
"into",
|
|
13620
|
+
"of",
|
|
13621
|
+
"on",
|
|
13622
|
+
"onto",
|
|
13623
|
+
"the",
|
|
13624
|
+
"this",
|
|
13625
|
+
"that",
|
|
13626
|
+
"to",
|
|
13627
|
+
"with",
|
|
13628
|
+
"work",
|
|
13629
|
+
"project",
|
|
13630
|
+
"continue",
|
|
13631
|
+
"\u7EE7\u7EED",
|
|
13632
|
+
"\u9879\u76EE"
|
|
13633
|
+
]);
|
|
13634
|
+
LENSES = {
|
|
13635
|
+
bugfix: {
|
|
13636
|
+
id: "bugfix",
|
|
13637
|
+
description: "debug the failure with current code and the smallest repro first",
|
|
13638
|
+
sourceLimit: 6,
|
|
13639
|
+
cautionLimit: 4,
|
|
13640
|
+
hideUnrelatedCautionDetails: true,
|
|
13641
|
+
hideUnrelatedReliableDetails: false
|
|
13642
|
+
},
|
|
13643
|
+
feature: {
|
|
13644
|
+
id: "feature",
|
|
13645
|
+
description: "build a scoped feature from nearby source, types, and user flow",
|
|
13646
|
+
sourceLimit: 6,
|
|
13647
|
+
cautionLimit: 3,
|
|
13648
|
+
hideUnrelatedCautionDetails: true,
|
|
13649
|
+
hideUnrelatedReliableDetails: false
|
|
13650
|
+
},
|
|
13651
|
+
release: {
|
|
13652
|
+
id: "release",
|
|
13653
|
+
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
13654
|
+
sourceLimit: 4,
|
|
13655
|
+
cautionLimit: 2,
|
|
13656
|
+
hideUnrelatedCautionDetails: true,
|
|
13657
|
+
hideUnrelatedReliableDetails: true
|
|
13658
|
+
},
|
|
13659
|
+
onboarding: {
|
|
13660
|
+
id: "onboarding",
|
|
13661
|
+
description: "understand the project shape before trusting old implementation details",
|
|
13662
|
+
sourceLimit: 4,
|
|
13663
|
+
cautionLimit: 2,
|
|
13664
|
+
hideUnrelatedCautionDetails: true,
|
|
13665
|
+
hideUnrelatedReliableDetails: true
|
|
13666
|
+
},
|
|
13667
|
+
refactor: {
|
|
13668
|
+
id: "refactor",
|
|
13669
|
+
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
13670
|
+
sourceLimit: 6,
|
|
13671
|
+
cautionLimit: 4,
|
|
13672
|
+
hideUnrelatedCautionDetails: true,
|
|
13673
|
+
hideUnrelatedReliableDetails: false
|
|
13674
|
+
},
|
|
13675
|
+
docs: {
|
|
13676
|
+
id: "docs",
|
|
13677
|
+
description: "update documentation against current code and public entry points",
|
|
13678
|
+
sourceLimit: 5,
|
|
13679
|
+
cautionLimit: 2,
|
|
13680
|
+
hideUnrelatedCautionDetails: true,
|
|
13681
|
+
hideUnrelatedReliableDetails: true
|
|
13682
|
+
},
|
|
13683
|
+
test: {
|
|
13684
|
+
id: "test",
|
|
13685
|
+
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
13686
|
+
sourceLimit: 6,
|
|
13687
|
+
cautionLimit: 3,
|
|
13688
|
+
hideUnrelatedCautionDetails: true,
|
|
13689
|
+
hideUnrelatedReliableDetails: false
|
|
13690
|
+
},
|
|
13691
|
+
general: {
|
|
13692
|
+
id: "general",
|
|
13693
|
+
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
13694
|
+
sourceLimit: 8,
|
|
13695
|
+
cautionLimit: 5,
|
|
13696
|
+
hideUnrelatedCautionDetails: false,
|
|
13697
|
+
hideUnrelatedReliableDetails: false
|
|
13698
|
+
}
|
|
13699
|
+
};
|
|
13700
|
+
KEYWORDS = {
|
|
13701
|
+
bugfix: [
|
|
13702
|
+
"bug",
|
|
13703
|
+
"crash",
|
|
13704
|
+
"incident",
|
|
13705
|
+
"debug",
|
|
13706
|
+
"error",
|
|
13707
|
+
"fail",
|
|
13708
|
+
"failing",
|
|
13709
|
+
"fix",
|
|
13710
|
+
"issue",
|
|
13711
|
+
"regression",
|
|
13712
|
+
"repro",
|
|
13713
|
+
"\u62A5\u9519",
|
|
13714
|
+
"\u5D29\u6E83",
|
|
13715
|
+
"\u6545\u969C",
|
|
13716
|
+
"\u5931\u8D25",
|
|
13717
|
+
"\u4FEE\u590D",
|
|
13718
|
+
"\u95EE\u9898"
|
|
13719
|
+
],
|
|
13720
|
+
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
13721
|
+
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
13722
|
+
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
13723
|
+
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
13724
|
+
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
13725
|
+
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
13726
|
+
};
|
|
13727
|
+
LENS_PRIORITY = [
|
|
13728
|
+
"bugfix",
|
|
13729
|
+
"release",
|
|
13730
|
+
"test",
|
|
13731
|
+
"refactor",
|
|
13732
|
+
"feature",
|
|
13733
|
+
"docs",
|
|
13734
|
+
"onboarding"
|
|
13735
|
+
];
|
|
13736
|
+
}
|
|
13737
|
+
});
|
|
13738
|
+
|
|
13739
|
+
// src/knowledge/workflows.ts
|
|
13740
|
+
var workflows_exports = {};
|
|
13741
|
+
__export(workflows_exports, {
|
|
13742
|
+
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
13743
|
+
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
13744
|
+
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
13745
|
+
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
13746
|
+
previewWorkflowAdapter: () => previewWorkflowAdapter,
|
|
13747
|
+
recordWorkflowRun: () => recordWorkflowRun,
|
|
13748
|
+
renderWorkflowMarkdown: () => renderWorkflowMarkdown,
|
|
13749
|
+
selectWorkflows: () => selectWorkflows,
|
|
13750
|
+
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13751
|
+
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13752
|
+
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13753
|
+
});
|
|
13754
|
+
import { createHash as createHash12 } from "crypto";
|
|
13755
|
+
import { promises as fs14 } from "fs";
|
|
13756
|
+
import path17 from "path";
|
|
13757
|
+
import matter11 from "gray-matter";
|
|
13758
|
+
function hash3(value) {
|
|
13759
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
13760
|
+
}
|
|
13761
|
+
function now2() {
|
|
13762
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13763
|
+
}
|
|
13764
|
+
function slug(value) {
|
|
13765
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13766
|
+
return normalized || "workflow";
|
|
13767
|
+
}
|
|
13768
|
+
function titleFromName(name) {
|
|
13769
|
+
return name.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ") || "Imported Workflow";
|
|
13770
|
+
}
|
|
13771
|
+
function requiredText(data, key) {
|
|
13772
|
+
const value = data[key];
|
|
13773
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
13774
|
+
throw new Error("workflow frontmatter requires " + key);
|
|
13775
|
+
}
|
|
13776
|
+
return value.trim();
|
|
13777
|
+
}
|
|
13778
|
+
function optionalText4(data, key) {
|
|
13779
|
+
const value = data[key];
|
|
13780
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
13781
|
+
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
13782
|
+
return value.trim() || void 0;
|
|
13783
|
+
}
|
|
13784
|
+
function optionalStringArray(data, key) {
|
|
13785
|
+
const value = data[key];
|
|
13786
|
+
if (value === void 0 || value === null) return [];
|
|
13787
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
13788
|
+
throw new Error("workflow frontmatter field " + key + " must be a string array");
|
|
13789
|
+
}
|
|
13790
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
13791
|
+
}
|
|
13792
|
+
function optionalNumber(data, key, fallback) {
|
|
13793
|
+
const value = data[key];
|
|
13794
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
13795
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
13796
|
+
throw new Error("workflow frontmatter field " + key + " must be a positive integer");
|
|
13797
|
+
}
|
|
13798
|
+
return value;
|
|
13799
|
+
}
|
|
13800
|
+
function optionalStatus(data) {
|
|
13801
|
+
const value = data.status;
|
|
13802
|
+
if (value === void 0 || value === null || value === "") return "draft";
|
|
13803
|
+
if (typeof value !== "string" || !STATUS_VALUES.includes(value)) {
|
|
13804
|
+
throw new Error("workflow frontmatter field status is invalid");
|
|
13805
|
+
}
|
|
13806
|
+
return value;
|
|
13807
|
+
}
|
|
13808
|
+
function normalizeAgents(values) {
|
|
13809
|
+
const unknown = values.filter((value) => !KNOWN_AGENTS.includes(value));
|
|
13810
|
+
if (unknown.length > 0) {
|
|
13811
|
+
throw new Error("workflow frontmatter has unsupported agent: " + unknown[0]);
|
|
13812
|
+
}
|
|
13813
|
+
return values;
|
|
13814
|
+
}
|
|
13815
|
+
function phaseFromValue(value, index) {
|
|
13816
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13817
|
+
throw new Error("workflow frontmatter phases must contain objects");
|
|
13818
|
+
}
|
|
13819
|
+
const data = value;
|
|
13820
|
+
const title = requiredText(data, "title");
|
|
13821
|
+
const phaseId = optionalText4(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
13822
|
+
return {
|
|
13823
|
+
id: phaseId,
|
|
13824
|
+
title,
|
|
13825
|
+
instructions: optionalText4(data, "instructions") ?? "",
|
|
13826
|
+
branches: optionalStringArray(data, "branches"),
|
|
13827
|
+
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
13828
|
+
verificationGates: optionalStringArray(data, "verificationGates")
|
|
13829
|
+
};
|
|
13830
|
+
}
|
|
13831
|
+
function phasesFromBody(body) {
|
|
13832
|
+
const headers = [...body.matchAll(/^##\s+(.+?)\s*$/gm)];
|
|
13833
|
+
if (headers.length === 0) {
|
|
13834
|
+
const instructions = body.trim();
|
|
13835
|
+
return instructions ? [{
|
|
13836
|
+
id: "execute",
|
|
13837
|
+
title: "Execute",
|
|
13838
|
+
instructions,
|
|
13839
|
+
branches: [],
|
|
13840
|
+
expectedOutputs: [],
|
|
13841
|
+
verificationGates: []
|
|
13842
|
+
}] : [];
|
|
13843
|
+
}
|
|
13844
|
+
return headers.map((match, index) => {
|
|
13845
|
+
const title = match[1].trim();
|
|
13846
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
13847
|
+
const end = index + 1 < headers.length ? headers[index + 1].index ?? body.length : body.length;
|
|
13848
|
+
return {
|
|
13849
|
+
id: slug(title) + "-" + (index + 1),
|
|
13850
|
+
title,
|
|
13851
|
+
instructions: body.slice(start, end).trim(),
|
|
13852
|
+
branches: [],
|
|
13853
|
+
expectedOutputs: [],
|
|
13854
|
+
verificationGates: []
|
|
13855
|
+
};
|
|
13856
|
+
});
|
|
13857
|
+
}
|
|
13858
|
+
function normalizeSourcePath(sourcePath) {
|
|
13859
|
+
const normalized = sourcePath.replace(/\\/g, "/");
|
|
13860
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path17.posix.normalize(normalized) !== normalized) {
|
|
13861
|
+
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
13862
|
+
}
|
|
13863
|
+
return normalized;
|
|
13864
|
+
}
|
|
13865
|
+
function sourceHashFor(workflow) {
|
|
13866
|
+
return hash3(JSON.stringify({
|
|
13867
|
+
id: workflow.id,
|
|
13868
|
+
title: workflow.title,
|
|
13869
|
+
description: workflow.description,
|
|
13870
|
+
status: workflow.status,
|
|
13871
|
+
version: workflow.version,
|
|
13872
|
+
taskLenses: workflow.taskLenses,
|
|
13873
|
+
triggers: workflow.triggers,
|
|
13874
|
+
assumptions: workflow.assumptions,
|
|
13875
|
+
requiredContext: workflow.requiredContext,
|
|
13876
|
+
guardrails: workflow.guardrails,
|
|
13877
|
+
allowedTools: workflow.allowedTools,
|
|
13878
|
+
phases: workflow.phases,
|
|
13879
|
+
verificationGates: workflow.verificationGates,
|
|
13880
|
+
claimIds: workflow.claimIds,
|
|
13473
13881
|
evidenceRefs: workflow.evidenceRefs,
|
|
13474
13882
|
codeRefs: workflow.codeRefs,
|
|
13475
13883
|
compatibleAgents: workflow.compatibleAgents,
|
|
@@ -13650,20 +14058,27 @@ function taskScore(workflow, task) {
|
|
|
13650
14058
|
const text = task.toLowerCase();
|
|
13651
14059
|
let score = 0;
|
|
13652
14060
|
const reasons = [];
|
|
14061
|
+
const matchedLenses = /* @__PURE__ */ new Set();
|
|
14062
|
+
const matchedTriggers = /* @__PURE__ */ new Set();
|
|
13653
14063
|
for (const lens of workflow.taskLenses) {
|
|
13654
14064
|
const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
|
|
13655
|
-
if (terms.some((term) =>
|
|
14065
|
+
if (terms.some((term) => containsTaskKeyword(task, term))) {
|
|
13656
14066
|
score += 20;
|
|
13657
14067
|
reasons.push("matches " + lens + " workflow");
|
|
14068
|
+
matchedLenses.add(lens.toLowerCase());
|
|
13658
14069
|
}
|
|
13659
14070
|
}
|
|
13660
14071
|
for (const trigger of workflow.triggers) {
|
|
13661
14072
|
const term = trigger.toLowerCase().trim();
|
|
13662
|
-
if (term.length >= 2 &&
|
|
14073
|
+
if (term.length >= 2 && containsTaskKeyword(task, term)) {
|
|
13663
14074
|
score += 8;
|
|
13664
14075
|
reasons.push('matches trigger "' + trigger + '"');
|
|
14076
|
+
matchedTriggers.add(term);
|
|
13665
14077
|
}
|
|
13666
14078
|
}
|
|
14079
|
+
const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
|
|
14080
|
+
const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
|
|
14081
|
+
if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
|
|
13667
14082
|
return { score, reasons: [...new Set(reasons)] };
|
|
13668
14083
|
}
|
|
13669
14084
|
function selectWorkflows(input) {
|
|
@@ -13681,9 +14096,28 @@ function selectWorkflows(input) {
|
|
|
13681
14096
|
return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
|
|
13682
14097
|
}
|
|
13683
14098
|
function importedWorkflowSpec(input) {
|
|
14099
|
+
let sourceMatter;
|
|
14100
|
+
try {
|
|
14101
|
+
sourceMatter = matter11(input.raw);
|
|
14102
|
+
} catch (error) {
|
|
14103
|
+
throw new Error("Malformed Windsurf workflow Markdown: " + (error instanceof Error ? error.message : String(error)));
|
|
14104
|
+
}
|
|
14105
|
+
if (sourceMatter.data.id !== void 0) {
|
|
14106
|
+
const id = requiredText(sourceMatter.data, "id");
|
|
14107
|
+
const sourcePath2 = "workflows/" + slug(id) + ".md";
|
|
14108
|
+
const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
|
|
14109
|
+
workspaceId: input.workspace.id,
|
|
14110
|
+
sourcePath: sourcePath2,
|
|
14111
|
+
contentHash: hash3(input.raw)
|
|
14112
|
+
});
|
|
14113
|
+
return materializeWorkflow({
|
|
14114
|
+
...canonical,
|
|
14115
|
+
importedFrom: input.sourcePath.replace(/\\/g, "/")
|
|
14116
|
+
});
|
|
14117
|
+
}
|
|
13684
14118
|
const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
|
|
13685
14119
|
const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
|
|
13686
|
-
const lenses = inferTaskLenses(entry.name + "\n" + entry.description
|
|
14120
|
+
const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
|
|
13687
14121
|
const createdAt = now2();
|
|
13688
14122
|
const sourcePath = "workflows/" + slug(entry.name) + ".md";
|
|
13689
14123
|
const spec = {
|
|
@@ -13904,7 +14338,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
13904
14338
|
errors: synced.errors
|
|
13905
14339
|
};
|
|
13906
14340
|
}
|
|
13907
|
-
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
|
|
14341
|
+
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
13908
14342
|
var init_workflows = __esm({
|
|
13909
14343
|
"src/knowledge/workflows.ts"() {
|
|
13910
14344
|
"use strict";
|
|
@@ -13913,6 +14347,7 @@ var init_workflows = __esm({
|
|
|
13913
14347
|
init_workflow_sync();
|
|
13914
14348
|
init_workspace();
|
|
13915
14349
|
init_workflow_store();
|
|
14350
|
+
init_task_lens();
|
|
13916
14351
|
KNOWN_AGENTS = [
|
|
13917
14352
|
"windsurf",
|
|
13918
14353
|
"cursor",
|
|
@@ -13944,6 +14379,8 @@ var init_workflows = __esm({
|
|
|
13944
14379
|
refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
|
|
13945
14380
|
test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
|
|
13946
14381
|
};
|
|
14382
|
+
GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
|
|
14383
|
+
GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
|
|
13947
14384
|
}
|
|
13948
14385
|
});
|
|
13949
14386
|
|
|
@@ -15128,7 +15565,8 @@ var init_workset = __esm({
|
|
|
15128
15565
|
// src/codegraph/current-facts.ts
|
|
15129
15566
|
var current_facts_exports = {};
|
|
15130
15567
|
__export(current_facts_exports, {
|
|
15131
|
-
collectCurrentProjectFacts: () => collectCurrentProjectFacts
|
|
15568
|
+
collectCurrentProjectFacts: () => collectCurrentProjectFacts,
|
|
15569
|
+
formatGitFact: () => formatGitFact
|
|
15132
15570
|
});
|
|
15133
15571
|
import { execFileSync } from "child_process";
|
|
15134
15572
|
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
@@ -15173,11 +15611,16 @@ function runGit2(rootPath, args) {
|
|
|
15173
15611
|
}
|
|
15174
15612
|
}
|
|
15175
15613
|
function readGitFacts(rootPath) {
|
|
15176
|
-
const
|
|
15614
|
+
const available = runGit2(rootPath, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
15615
|
+
if (!available) {
|
|
15616
|
+
return { available: false, dirty: false, detached: false };
|
|
15617
|
+
}
|
|
15618
|
+
const branch = runGit2(rootPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]) ?? runGit2(rootPath, ["branch", "--show-current"]);
|
|
15177
15619
|
const commit = runGit2(rootPath, ["rev-parse", "--short", "HEAD"]);
|
|
15178
15620
|
const latestCommit = runGit2(rootPath, ["log", "-1", "--pretty=%s"]);
|
|
15179
15621
|
const dirty = Boolean(runGit2(rootPath, ["status", "--porcelain"]));
|
|
15180
15622
|
return {
|
|
15623
|
+
available: true,
|
|
15181
15624
|
...branch ? { branch } : {},
|
|
15182
15625
|
...commit ? { commit } : {},
|
|
15183
15626
|
...latestCommit ? { latestCommit } : {},
|
|
@@ -15185,6 +15628,15 @@ function readGitFacts(rootPath) {
|
|
|
15185
15628
|
detached: !branch
|
|
15186
15629
|
};
|
|
15187
15630
|
}
|
|
15631
|
+
function formatGitFact(git) {
|
|
15632
|
+
if (!git.available) return "Git: unavailable";
|
|
15633
|
+
const parts = [];
|
|
15634
|
+
if (git.detached) parts.push("detached HEAD");
|
|
15635
|
+
else if (git.branch) parts.push("branch " + git.branch);
|
|
15636
|
+
if (git.commit) parts.push("commit " + git.commit);
|
|
15637
|
+
parts.push(git.dirty ? "dirty worktree" : "clean worktree");
|
|
15638
|
+
return "Git: " + parts.join(", ");
|
|
15639
|
+
}
|
|
15188
15640
|
function parseProgressNote(content) {
|
|
15189
15641
|
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);
|
|
15190
15642
|
const branchHint = content.match(/Branch\*\*:\s*([^\r\n]+)/i) ?? content.match(/Branch:\s*([^\r\n]+)/i);
|
|
@@ -15686,485 +16138,172 @@ var init_external_provider = __esm({
|
|
|
15686
16138
|
...typeof exitCode === "number" ? { exitCode } : {},
|
|
15687
16139
|
...timedOut ? { timedOut } : {},
|
|
15688
16140
|
...outputLimited ? { outputLimited } : {},
|
|
15689
|
-
...spawnError ? { error: spawnError } : {}
|
|
15690
|
-
});
|
|
15691
|
-
});
|
|
15692
|
-
});
|
|
15693
|
-
}
|
|
15694
|
-
};
|
|
15695
|
-
}
|
|
15696
|
-
});
|
|
15697
|
-
|
|
15698
|
-
// src/codegraph/freshness.ts
|
|
15699
|
-
function evaluateCodeRefFreshness(ref, file, symbol) {
|
|
15700
|
-
if (!file) {
|
|
15701
|
-
return { status: "stale", reason: "referenced file is no longer indexed" };
|
|
15702
|
-
}
|
|
15703
|
-
if (ref.symbolId && !symbol) {
|
|
15704
|
-
return { status: "stale", reason: "referenced symbol is no longer indexed" };
|
|
15705
|
-
}
|
|
15706
|
-
if (ref.capturedSymbolHash && symbol?.contentHash) {
|
|
15707
|
-
if (symbol.contentHash !== ref.capturedSymbolHash) {
|
|
15708
|
-
return { status: "stale", reason: "referenced symbol content changed" };
|
|
15709
|
-
}
|
|
15710
|
-
return { status: "current", reason: "referenced symbol content still matches" };
|
|
15711
|
-
}
|
|
15712
|
-
if (ref.capturedFileHash && file.contentHash !== ref.capturedFileHash) {
|
|
15713
|
-
return { status: "suspect", reason: "referenced file changed since capture" };
|
|
15714
|
-
}
|
|
15715
|
-
return { status: "current", reason: "referenced file still matches" };
|
|
15716
|
-
}
|
|
15717
|
-
var init_freshness2 = __esm({
|
|
15718
|
-
"src/codegraph/freshness.ts"() {
|
|
15719
|
-
"use strict";
|
|
15720
|
-
init_esm_shims();
|
|
15721
|
-
}
|
|
15722
|
-
});
|
|
15723
|
-
|
|
15724
|
-
// src/codegraph/project-context.ts
|
|
15725
|
-
function uniq(items) {
|
|
15726
|
-
return [...new Set(items)];
|
|
15727
|
-
}
|
|
15728
|
-
function activeObservations(observations2, projectId) {
|
|
15729
|
-
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
15730
|
-
}
|
|
15731
|
-
function countLanguages(files) {
|
|
15732
|
-
const counts = /* @__PURE__ */ new Map();
|
|
15733
|
-
for (const file of files) {
|
|
15734
|
-
const language = file.language ?? "unknown";
|
|
15735
|
-
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
15736
|
-
}
|
|
15737
|
-
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
15738
|
-
}
|
|
15739
|
-
function suggestedReadRank(path25) {
|
|
15740
|
-
const normalized = path25.replace(/\\/g, "/");
|
|
15741
|
-
if (normalized.startsWith("src/")) return 0;
|
|
15742
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
15743
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15744
|
-
return 3;
|
|
15745
|
-
}
|
|
15746
|
-
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
15747
|
-
return uniq(paths).filter((path25) => !isCodeGraphExcludedPath(path25, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
15748
|
-
}
|
|
15749
|
-
function collectGraph(store, projectId, observations2, exclude) {
|
|
15750
|
-
const files = store.listFiles(projectId);
|
|
15751
|
-
const symbols = store.listReferencedSymbols(projectId);
|
|
15752
|
-
const filesById = new Map(files.map((file) => [file.id, file]));
|
|
15753
|
-
const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
|
|
15754
|
-
const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
|
|
15755
|
-
const activeObservationIds = new Set(observations2.map((obs) => obs.id));
|
|
15756
|
-
const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
|
|
15757
|
-
const freshness = {
|
|
15758
|
-
current: 0,
|
|
15759
|
-
suspect: 0,
|
|
15760
|
-
stale: 0,
|
|
15761
|
-
unbound: 0
|
|
15762
|
-
};
|
|
15763
|
-
const sources = [];
|
|
15764
|
-
const suggestedReads = [];
|
|
15765
|
-
for (const ref of refs) {
|
|
15766
|
-
const file = ref.fileId ? filesById.get(ref.fileId) : void 0;
|
|
15767
|
-
const symbol = ref.symbolId ? symbolsById.get(ref.symbolId) : void 0;
|
|
15768
|
-
const result = evaluateCodeRefFreshness(ref, file, symbol);
|
|
15769
|
-
freshness[result.status] += 1;
|
|
15770
|
-
const observation = observationsById.get(ref.observationId);
|
|
15771
|
-
if (!observation) continue;
|
|
15772
|
-
const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
|
|
15773
|
-
if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
|
|
15774
|
-
if (excluded) continue;
|
|
15775
|
-
sources.push({
|
|
15776
|
-
observationId: observation.id,
|
|
15777
|
-
title: observation.title,
|
|
15778
|
-
type: observation.type,
|
|
15779
|
-
...file ? { path: file.path } : {},
|
|
15780
|
-
...symbol ? { symbol: symbol.name } : {},
|
|
15781
|
-
status: result.status,
|
|
15782
|
-
reason: result.reason
|
|
15783
|
-
});
|
|
15784
|
-
}
|
|
15785
|
-
if (files.length === 0) {
|
|
15786
|
-
for (const observation of observations2) {
|
|
15787
|
-
for (const path25 of observation.filesModified ?? []) {
|
|
15788
|
-
if (!path25 || isCodeGraphExcludedPath(path25, exclude)) continue;
|
|
15789
|
-
suggestedReads.push(path25);
|
|
15790
|
-
sources.push({
|
|
15791
|
-
observationId: observation.id,
|
|
15792
|
-
title: observation.title,
|
|
15793
|
-
type: observation.type,
|
|
15794
|
-
path: path25,
|
|
15795
|
-
status: "unbound",
|
|
15796
|
-
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
16141
|
+
...spawnError ? { error: spawnError } : {}
|
|
16142
|
+
});
|
|
16143
|
+
});
|
|
15797
16144
|
});
|
|
15798
|
-
freshness.unbound++;
|
|
15799
16145
|
}
|
|
16146
|
+
};
|
|
16147
|
+
}
|
|
16148
|
+
});
|
|
16149
|
+
|
|
16150
|
+
// src/codegraph/freshness.ts
|
|
16151
|
+
function evaluateCodeRefFreshness(ref, file, symbol) {
|
|
16152
|
+
if (!file) {
|
|
16153
|
+
return { status: "stale", reason: "referenced file is no longer indexed" };
|
|
16154
|
+
}
|
|
16155
|
+
if (ref.symbolId && !symbol) {
|
|
16156
|
+
return { status: "stale", reason: "referenced symbol is no longer indexed" };
|
|
16157
|
+
}
|
|
16158
|
+
if (ref.capturedSymbolHash && symbol?.contentHash) {
|
|
16159
|
+
if (symbol.contentHash !== ref.capturedSymbolHash) {
|
|
16160
|
+
return { status: "stale", reason: "referenced symbol content changed" };
|
|
15800
16161
|
}
|
|
16162
|
+
return { status: "current", reason: "referenced symbol content still matches" };
|
|
15801
16163
|
}
|
|
15802
|
-
|
|
15803
|
-
|
|
15804
|
-
|
|
15805
|
-
|
|
15806
|
-
freshness,
|
|
15807
|
-
sources,
|
|
15808
|
-
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
15809
|
-
};
|
|
15810
|
-
}
|
|
15811
|
-
function overviewFromGraph(input) {
|
|
15812
|
-
const status = input.store.status(input.project.id);
|
|
15813
|
-
return {
|
|
15814
|
-
project: input.project,
|
|
15815
|
-
code: {
|
|
15816
|
-
provider: status.provider,
|
|
15817
|
-
files: status.files,
|
|
15818
|
-
symbols: status.symbols,
|
|
15819
|
-
edges: status.edges,
|
|
15820
|
-
refs: status.refs,
|
|
15821
|
-
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
15822
|
-
languages: countLanguages(input.graph.files),
|
|
15823
|
-
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
15824
|
-
},
|
|
15825
|
-
memory: {
|
|
15826
|
-
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
15827
|
-
active: input.active.length
|
|
15828
|
-
},
|
|
15829
|
-
freshness: input.graph.freshness,
|
|
15830
|
-
suggestedReads: input.graph.suggestedReads
|
|
15831
|
-
};
|
|
15832
|
-
}
|
|
15833
|
-
function buildProjectContextExplain(input) {
|
|
15834
|
-
const active = activeObservations(input.observations, input.project.id);
|
|
15835
|
-
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
15836
|
-
const overview = overviewFromGraph({
|
|
15837
|
-
project: input.project,
|
|
15838
|
-
store: input.store,
|
|
15839
|
-
observations: input.observations,
|
|
15840
|
-
active,
|
|
15841
|
-
graph
|
|
15842
|
-
});
|
|
15843
|
-
return {
|
|
15844
|
-
project: input.project,
|
|
15845
|
-
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
15846
|
-
overview
|
|
15847
|
-
};
|
|
16164
|
+
if (ref.capturedFileHash && file.contentHash !== ref.capturedFileHash) {
|
|
16165
|
+
return { status: "suspect", reason: "referenced file changed since capture" };
|
|
16166
|
+
}
|
|
16167
|
+
return { status: "current", reason: "referenced file still matches" };
|
|
15848
16168
|
}
|
|
15849
|
-
var
|
|
15850
|
-
"src/codegraph/
|
|
16169
|
+
var init_freshness2 = __esm({
|
|
16170
|
+
"src/codegraph/freshness.ts"() {
|
|
15851
16171
|
"use strict";
|
|
15852
16172
|
init_esm_shims();
|
|
15853
|
-
init_freshness2();
|
|
15854
|
-
init_exclude();
|
|
15855
16173
|
}
|
|
15856
16174
|
});
|
|
15857
16175
|
|
|
15858
|
-
// src/codegraph/
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
lensPathCandidates: () => lensPathCandidates,
|
|
15862
|
-
lensVerificationHints: () => lensVerificationHints,
|
|
15863
|
-
rankLensPaths: () => rankLensPaths,
|
|
15864
|
-
rankLensSources: () => rankLensSources,
|
|
15865
|
-
resolveTaskLens: () => resolveTaskLens,
|
|
15866
|
-
scoreLensSource: () => scoreLensSource,
|
|
15867
|
-
shouldShowLensSource: () => shouldShowLensSource
|
|
15868
|
-
});
|
|
15869
|
-
function normalizePath2(path25) {
|
|
15870
|
-
return path25.replace(/\\/g, "/");
|
|
15871
|
-
}
|
|
15872
|
-
function tokenize2(text) {
|
|
15873
|
-
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
15874
|
-
}
|
|
15875
|
-
function containsKeyword(text, keyword) {
|
|
15876
|
-
if (/^[a-z0-9_-]+$/i.test(keyword)) {
|
|
15877
|
-
return new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9_-]|$)`, "i").test(text);
|
|
15878
|
-
}
|
|
15879
|
-
return text.includes(keyword);
|
|
16176
|
+
// src/codegraph/project-context.ts
|
|
16177
|
+
function uniq(items) {
|
|
16178
|
+
return [...new Set(items)];
|
|
15880
16179
|
}
|
|
15881
|
-
function
|
|
15882
|
-
|
|
15883
|
-
if (!normalized.trim()) return LENSES.general;
|
|
15884
|
-
let best = {
|
|
15885
|
-
id: "general",
|
|
15886
|
-
score: 0,
|
|
15887
|
-
priority: Number.MAX_SAFE_INTEGER
|
|
15888
|
-
};
|
|
15889
|
-
for (const id of LENS_PRIORITY) {
|
|
15890
|
-
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
15891
|
-
const priority = LENS_PRIORITY.indexOf(id);
|
|
15892
|
-
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
15893
|
-
best = { id, score, priority };
|
|
15894
|
-
}
|
|
15895
|
-
}
|
|
15896
|
-
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
16180
|
+
function activeObservations(observations2, projectId) {
|
|
16181
|
+
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
15897
16182
|
}
|
|
15898
|
-
function
|
|
15899
|
-
const
|
|
15900
|
-
const
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
15904
|
-
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
15905
|
-
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
15906
|
-
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
15907
|
-
switch (lens.id) {
|
|
15908
|
-
case "bugfix":
|
|
15909
|
-
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
15910
|
-
case "test":
|
|
15911
|
-
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
15912
|
-
case "release":
|
|
15913
|
-
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
15914
|
-
case "onboarding":
|
|
15915
|
-
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
15916
|
-
case "docs":
|
|
15917
|
-
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
15918
|
-
case "refactor":
|
|
15919
|
-
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
15920
|
-
case "feature":
|
|
15921
|
-
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
15922
|
-
default:
|
|
15923
|
-
if (isSource || isTest) return 60;
|
|
15924
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
15925
|
-
if (inDocs) return 20;
|
|
15926
|
-
return 0;
|
|
16183
|
+
function countLanguages(files) {
|
|
16184
|
+
const counts = /* @__PURE__ */ new Map();
|
|
16185
|
+
for (const file of files) {
|
|
16186
|
+
const language = file.language ?? "unknown";
|
|
16187
|
+
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
15927
16188
|
}
|
|
16189
|
+
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
15928
16190
|
}
|
|
15929
|
-
function
|
|
15930
|
-
|
|
15931
|
-
const normalized = text.toLowerCase();
|
|
15932
|
-
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
15933
|
-
}
|
|
15934
|
-
function sourceTaskMatchScore(source, task) {
|
|
15935
|
-
const tokens = tokenize2(task ?? "");
|
|
15936
|
-
if (tokens.length === 0) return 0;
|
|
15937
|
-
const text = [
|
|
15938
|
-
source.title,
|
|
15939
|
-
source.path ?? "",
|
|
15940
|
-
source.symbol ?? ""
|
|
15941
|
-
].join("\n").toLowerCase();
|
|
15942
|
-
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
15943
|
-
if (matches >= 2) return 40;
|
|
15944
|
-
if (matches === 1) return 24;
|
|
15945
|
-
return 0;
|
|
15946
|
-
}
|
|
15947
|
-
function fallbackPathRank(path25) {
|
|
15948
|
-
const normalized = normalizePath2(path25);
|
|
16191
|
+
function suggestedReadRank(path25) {
|
|
16192
|
+
const normalized = path25.replace(/\\/g, "/");
|
|
15949
16193
|
if (normalized.startsWith("src/")) return 0;
|
|
15950
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return
|
|
16194
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
15951
16195
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15952
|
-
|
|
15953
|
-
return 4;
|
|
15954
|
-
}
|
|
15955
|
-
function rankLensPaths(paths, lens, task) {
|
|
15956
|
-
const tokens = tokenize2(task ?? "");
|
|
15957
|
-
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
15958
|
-
path: path25,
|
|
15959
|
-
index,
|
|
15960
|
-
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
15961
|
-
fallback: fallbackPathRank(path25)
|
|
15962
|
-
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
15963
|
-
}
|
|
15964
|
-
function lensPathCandidates(lens) {
|
|
15965
|
-
switch (lens.id) {
|
|
15966
|
-
case "release":
|
|
15967
|
-
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
15968
|
-
case "onboarding":
|
|
15969
|
-
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
15970
|
-
case "docs":
|
|
15971
|
-
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
15972
|
-
case "test":
|
|
15973
|
-
case "bugfix":
|
|
15974
|
-
return ["tests", "test"];
|
|
15975
|
-
default:
|
|
15976
|
-
return [];
|
|
15977
|
-
}
|
|
15978
|
-
}
|
|
15979
|
-
function scoreLensSource(source, lens, task) {
|
|
15980
|
-
const tokens = tokenize2(task ?? "");
|
|
15981
|
-
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
15982
|
-
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
16196
|
+
return 3;
|
|
15983
16197
|
}
|
|
15984
|
-
function
|
|
15985
|
-
return
|
|
15986
|
-
source,
|
|
15987
|
-
index,
|
|
15988
|
-
score: scoreLensSource(source, lens, task)
|
|
15989
|
-
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
16198
|
+
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
16199
|
+
return uniq(paths).filter((path25) => !isCodeGraphExcludedPath(path25, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
15990
16200
|
}
|
|
15991
|
-
function
|
|
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
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16201
|
+
function collectGraph(store, projectId, observations2, exclude) {
|
|
16202
|
+
const files = store.listFiles(projectId);
|
|
16203
|
+
const symbols = store.listReferencedSymbols(projectId);
|
|
16204
|
+
const filesById = new Map(files.map((file) => [file.id, file]));
|
|
16205
|
+
const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
|
|
16206
|
+
const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
|
|
16207
|
+
const activeObservationIds = new Set(observations2.map((obs) => obs.id));
|
|
16208
|
+
const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
|
|
16209
|
+
const freshness = {
|
|
16210
|
+
current: 0,
|
|
16211
|
+
suspect: 0,
|
|
16212
|
+
stale: 0,
|
|
16213
|
+
unbound: 0
|
|
16214
|
+
};
|
|
16215
|
+
const sources = [];
|
|
16216
|
+
const suggestedReads = [];
|
|
16217
|
+
for (const ref of refs) {
|
|
16218
|
+
const file = ref.fileId ? filesById.get(ref.fileId) : void 0;
|
|
16219
|
+
const symbol = ref.symbolId ? symbolsById.get(ref.symbolId) : void 0;
|
|
16220
|
+
const result = evaluateCodeRefFreshness(ref, file, symbol);
|
|
16221
|
+
freshness[result.status] += 1;
|
|
16222
|
+
const observation = observationsById.get(ref.observationId);
|
|
16223
|
+
if (!observation) continue;
|
|
16224
|
+
const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
|
|
16225
|
+
if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
|
|
16226
|
+
if (excluded) continue;
|
|
16227
|
+
sources.push({
|
|
16228
|
+
observationId: observation.id,
|
|
16229
|
+
title: observation.title,
|
|
16230
|
+
type: observation.type,
|
|
16231
|
+
...file ? { path: file.path } : {},
|
|
16232
|
+
...symbol ? { symbol: symbol.name } : {},
|
|
16233
|
+
status: result.status,
|
|
16234
|
+
reason: result.reason
|
|
16235
|
+
});
|
|
16236
|
+
}
|
|
16237
|
+
if (files.length === 0) {
|
|
16238
|
+
for (const observation of observations2) {
|
|
16239
|
+
for (const path25 of observation.filesModified ?? []) {
|
|
16240
|
+
if (!path25 || isCodeGraphExcludedPath(path25, exclude)) continue;
|
|
16241
|
+
suggestedReads.push(path25);
|
|
16242
|
+
sources.push({
|
|
16243
|
+
observationId: observation.id,
|
|
16244
|
+
title: observation.title,
|
|
16245
|
+
type: observation.type,
|
|
16246
|
+
path: path25,
|
|
16247
|
+
status: "unbound",
|
|
16248
|
+
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
16249
|
+
});
|
|
16250
|
+
freshness.unbound++;
|
|
16251
|
+
}
|
|
16252
|
+
}
|
|
16033
16253
|
}
|
|
16254
|
+
return {
|
|
16255
|
+
files,
|
|
16256
|
+
symbols,
|
|
16257
|
+
refs,
|
|
16258
|
+
freshness,
|
|
16259
|
+
sources,
|
|
16260
|
+
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
16261
|
+
};
|
|
16034
16262
|
}
|
|
16035
|
-
function
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16263
|
+
function overviewFromGraph(input) {
|
|
16264
|
+
const status = input.store.status(input.project.id);
|
|
16265
|
+
return {
|
|
16266
|
+
project: input.project,
|
|
16267
|
+
code: {
|
|
16268
|
+
provider: status.provider,
|
|
16269
|
+
files: status.files,
|
|
16270
|
+
symbols: status.symbols,
|
|
16271
|
+
edges: status.edges,
|
|
16272
|
+
refs: status.refs,
|
|
16273
|
+
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
16274
|
+
languages: countLanguages(input.graph.files),
|
|
16275
|
+
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
16276
|
+
},
|
|
16277
|
+
memory: {
|
|
16278
|
+
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
16279
|
+
active: input.active.length
|
|
16280
|
+
},
|
|
16281
|
+
freshness: input.graph.freshness,
|
|
16282
|
+
suggestedReads: input.graph.suggestedReads
|
|
16283
|
+
};
|
|
16040
16284
|
}
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16285
|
+
function buildProjectContextExplain(input) {
|
|
16286
|
+
const active = activeObservations(input.observations, input.project.id);
|
|
16287
|
+
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
16288
|
+
const overview = overviewFromGraph({
|
|
16289
|
+
project: input.project,
|
|
16290
|
+
store: input.store,
|
|
16291
|
+
observations: input.observations,
|
|
16292
|
+
active,
|
|
16293
|
+
graph
|
|
16294
|
+
});
|
|
16295
|
+
return {
|
|
16296
|
+
project: input.project,
|
|
16297
|
+
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
16298
|
+
overview
|
|
16299
|
+
};
|
|
16300
|
+
}
|
|
16301
|
+
var init_project_context = __esm({
|
|
16302
|
+
"src/codegraph/project-context.ts"() {
|
|
16044
16303
|
"use strict";
|
|
16045
16304
|
init_esm_shims();
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
"an",
|
|
16049
|
-
"and",
|
|
16050
|
-
"as",
|
|
16051
|
-
"for",
|
|
16052
|
-
"in",
|
|
16053
|
-
"into",
|
|
16054
|
-
"of",
|
|
16055
|
-
"on",
|
|
16056
|
-
"onto",
|
|
16057
|
-
"the",
|
|
16058
|
-
"this",
|
|
16059
|
-
"that",
|
|
16060
|
-
"to",
|
|
16061
|
-
"with",
|
|
16062
|
-
"work",
|
|
16063
|
-
"project",
|
|
16064
|
-
"continue",
|
|
16065
|
-
"\u7EE7\u7EED",
|
|
16066
|
-
"\u9879\u76EE"
|
|
16067
|
-
]);
|
|
16068
|
-
LENSES = {
|
|
16069
|
-
bugfix: {
|
|
16070
|
-
id: "bugfix",
|
|
16071
|
-
description: "debug the failure with current code and the smallest repro first",
|
|
16072
|
-
sourceLimit: 6,
|
|
16073
|
-
cautionLimit: 4,
|
|
16074
|
-
hideUnrelatedCautionDetails: true,
|
|
16075
|
-
hideUnrelatedReliableDetails: false
|
|
16076
|
-
},
|
|
16077
|
-
feature: {
|
|
16078
|
-
id: "feature",
|
|
16079
|
-
description: "build a scoped feature from nearby source, types, and user flow",
|
|
16080
|
-
sourceLimit: 6,
|
|
16081
|
-
cautionLimit: 3,
|
|
16082
|
-
hideUnrelatedCautionDetails: true,
|
|
16083
|
-
hideUnrelatedReliableDetails: false
|
|
16084
|
-
},
|
|
16085
|
-
release: {
|
|
16086
|
-
id: "release",
|
|
16087
|
-
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
16088
|
-
sourceLimit: 4,
|
|
16089
|
-
cautionLimit: 2,
|
|
16090
|
-
hideUnrelatedCautionDetails: true,
|
|
16091
|
-
hideUnrelatedReliableDetails: true
|
|
16092
|
-
},
|
|
16093
|
-
onboarding: {
|
|
16094
|
-
id: "onboarding",
|
|
16095
|
-
description: "understand the project shape before trusting old implementation details",
|
|
16096
|
-
sourceLimit: 4,
|
|
16097
|
-
cautionLimit: 2,
|
|
16098
|
-
hideUnrelatedCautionDetails: true,
|
|
16099
|
-
hideUnrelatedReliableDetails: true
|
|
16100
|
-
},
|
|
16101
|
-
refactor: {
|
|
16102
|
-
id: "refactor",
|
|
16103
|
-
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
16104
|
-
sourceLimit: 6,
|
|
16105
|
-
cautionLimit: 4,
|
|
16106
|
-
hideUnrelatedCautionDetails: true,
|
|
16107
|
-
hideUnrelatedReliableDetails: false
|
|
16108
|
-
},
|
|
16109
|
-
docs: {
|
|
16110
|
-
id: "docs",
|
|
16111
|
-
description: "update documentation against current code and public entry points",
|
|
16112
|
-
sourceLimit: 5,
|
|
16113
|
-
cautionLimit: 2,
|
|
16114
|
-
hideUnrelatedCautionDetails: true,
|
|
16115
|
-
hideUnrelatedReliableDetails: true
|
|
16116
|
-
},
|
|
16117
|
-
test: {
|
|
16118
|
-
id: "test",
|
|
16119
|
-
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
16120
|
-
sourceLimit: 6,
|
|
16121
|
-
cautionLimit: 3,
|
|
16122
|
-
hideUnrelatedCautionDetails: true,
|
|
16123
|
-
hideUnrelatedReliableDetails: false
|
|
16124
|
-
},
|
|
16125
|
-
general: {
|
|
16126
|
-
id: "general",
|
|
16127
|
-
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
16128
|
-
sourceLimit: 8,
|
|
16129
|
-
cautionLimit: 5,
|
|
16130
|
-
hideUnrelatedCautionDetails: false,
|
|
16131
|
-
hideUnrelatedReliableDetails: false
|
|
16132
|
-
}
|
|
16133
|
-
};
|
|
16134
|
-
KEYWORDS = {
|
|
16135
|
-
bugfix: [
|
|
16136
|
-
"bug",
|
|
16137
|
-
"crash",
|
|
16138
|
-
"debug",
|
|
16139
|
-
"error",
|
|
16140
|
-
"fail",
|
|
16141
|
-
"failing",
|
|
16142
|
-
"fix",
|
|
16143
|
-
"issue",
|
|
16144
|
-
"regression",
|
|
16145
|
-
"repro",
|
|
16146
|
-
"\u62A5\u9519",
|
|
16147
|
-
"\u5D29\u6E83",
|
|
16148
|
-
"\u5931\u8D25",
|
|
16149
|
-
"\u4FEE\u590D",
|
|
16150
|
-
"\u95EE\u9898"
|
|
16151
|
-
],
|
|
16152
|
-
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
16153
|
-
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
16154
|
-
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
16155
|
-
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
16156
|
-
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
16157
|
-
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
16158
|
-
};
|
|
16159
|
-
LENS_PRIORITY = [
|
|
16160
|
-
"bugfix",
|
|
16161
|
-
"release",
|
|
16162
|
-
"test",
|
|
16163
|
-
"refactor",
|
|
16164
|
-
"feature",
|
|
16165
|
-
"docs",
|
|
16166
|
-
"onboarding"
|
|
16167
|
-
];
|
|
16305
|
+
init_freshness2();
|
|
16306
|
+
init_exclude();
|
|
16168
16307
|
}
|
|
16169
16308
|
});
|
|
16170
16309
|
|
|
@@ -16463,15 +16602,7 @@ function formatCurrentFactsLines(facts) {
|
|
|
16463
16602
|
if (facts.latestChangelog) {
|
|
16464
16603
|
lines.push(`- Latest changelog: ${facts.latestChangelog.version}${facts.latestChangelog.date ? ` (${facts.latestChangelog.date})` : ""}`);
|
|
16465
16604
|
}
|
|
16466
|
-
|
|
16467
|
-
if (facts.git.detached) {
|
|
16468
|
-
gitParts.push("detached HEAD");
|
|
16469
|
-
} else if (facts.git.branch) {
|
|
16470
|
-
gitParts.push(`branch ${facts.git.branch}`);
|
|
16471
|
-
}
|
|
16472
|
-
if (facts.git.commit) gitParts.push(`commit ${facts.git.commit}`);
|
|
16473
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16474
|
-
lines.push(`- Git: ${gitParts.join(", ")}`);
|
|
16605
|
+
lines.push("- " + formatGitFact(facts.git));
|
|
16475
16606
|
if (facts.git.latestCommit) lines.push(`- Latest commit: ${facts.git.latestCommit}`);
|
|
16476
16607
|
lines.push("- Current facts above outrank progress/dev-log files when they conflict.");
|
|
16477
16608
|
if (facts.staleNotes.length > 0) {
|
|
@@ -16493,11 +16624,7 @@ function worksetFactLines(facts) {
|
|
|
16493
16624
|
if (facts.latestChangelog) {
|
|
16494
16625
|
lines.push("Latest changelog: " + facts.latestChangelog.version + (facts.latestChangelog.date ? " (" + facts.latestChangelog.date + ")" : ""));
|
|
16495
16626
|
}
|
|
16496
|
-
|
|
16497
|
-
if (facts.git.branch) gitParts.push("branch " + facts.git.branch);
|
|
16498
|
-
if (facts.git.commit) gitParts.push("commit " + facts.git.commit);
|
|
16499
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16500
|
-
lines.push("Git: " + gitParts.join(", "));
|
|
16627
|
+
lines.push(formatGitFact(facts.git));
|
|
16501
16628
|
for (const note of facts.staleNotes.slice(0, 1)) {
|
|
16502
16629
|
lines.push(
|
|
16503
16630
|
"Historical note: " + note.path + (note.branchHint ? " (branch hint " + note.branchHint + "; " + note.reason + ")" : " (" + note.reason + ")")
|
|
@@ -18979,13 +19106,8 @@ function prepareDashboardConfig(projectRoot) {
|
|
|
18979
19106
|
}
|
|
18980
19107
|
}
|
|
18981
19108
|
function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
|
|
18982
|
-
const
|
|
18983
|
-
|
|
18984
|
-
);
|
|
18985
|
-
const entities = allEntities.filter((e) => entityNames.has(e.name));
|
|
18986
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
18987
|
-
const relations = allRelations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
18988
|
-
return { entities: entities.length, relations: relations.length, entityNames };
|
|
19109
|
+
const scoped = scopeKnowledgeGraphToProject({ entities: allEntities, relations: allRelations }, projectObs);
|
|
19110
|
+
return { entities: scoped.entities.length, relations: scoped.relations.length, entityNames: scoped.entityNames };
|
|
18989
19111
|
}
|
|
18990
19112
|
async function handleApi(req, res, dataDir, projectId, projectName, baseDir, projectRoot, projectResolved, mode = "standalone", port = 3210) {
|
|
18991
19113
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -19058,13 +19180,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19058
19180
|
const gStore = getGraphStore();
|
|
19059
19181
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19060
19182
|
const graphObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19061
|
-
const
|
|
19062
|
-
|
|
19063
|
-
);
|
|
19064
|
-
const entities = graph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19065
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
19066
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
19067
|
-
sendJson(res, { entities, relations });
|
|
19183
|
+
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19184
|
+
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19068
19185
|
break;
|
|
19069
19186
|
}
|
|
19070
19187
|
case "/observations": {
|
|
@@ -19256,19 +19373,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19256
19373
|
const allObs = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19257
19374
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19258
19375
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19259
|
-
const
|
|
19260
|
-
const projectEntityNames = new Set(
|
|
19261
|
-
graphObs.filter((o) => o.entityName).map((o) => o.entityName)
|
|
19262
|
-
);
|
|
19263
|
-
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19264
|
-
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
19265
|
-
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
19376
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
19266
19377
|
const graph = generateKnowledgeGraph2({
|
|
19267
19378
|
projectId: effectiveProjectId,
|
|
19268
19379
|
observations: allObs,
|
|
19269
19380
|
miniSkills: skills,
|
|
19270
|
-
graphEntities:
|
|
19271
|
-
graphRelations:
|
|
19381
|
+
graphEntities: scoped.entities,
|
|
19382
|
+
graphRelations: scoped.relations
|
|
19272
19383
|
});
|
|
19273
19384
|
sendJson(res, graph);
|
|
19274
19385
|
break;
|
|
@@ -19490,16 +19601,11 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19490
19601
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19491
19602
|
const observations2 = await getObservationStore().loadByProject(effectiveProjectId, { status: "active" });
|
|
19492
19603
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19493
|
-
const
|
|
19494
|
-
observations2.filter((o) => (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
19495
|
-
);
|
|
19496
|
-
const exportEntities = fullGraph.entities.filter((e) => exportEntityNames.has(e.name));
|
|
19497
|
-
const exportEntitySet = new Set(exportEntities.map((e) => e.name));
|
|
19498
|
-
const exportRelations = fullGraph.relations.filter((r) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
|
|
19604
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19499
19605
|
const exportData = {
|
|
19500
19606
|
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
19501
19607
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19502
|
-
graph: { entities:
|
|
19608
|
+
graph: { entities: scoped.entities, relations: scoped.relations },
|
|
19503
19609
|
observations: observations2,
|
|
19504
19610
|
nextId: nextId2
|
|
19505
19611
|
};
|
|
@@ -19816,6 +19922,7 @@ var init_server = __esm({
|
|
|
19816
19922
|
init_dotenv_loader();
|
|
19817
19923
|
init_yaml_loader();
|
|
19818
19924
|
init_yaml_loader();
|
|
19925
|
+
init_graph_scope();
|
|
19819
19926
|
MIME_TYPES = {
|
|
19820
19927
|
".html": "text/html; charset=utf-8",
|
|
19821
19928
|
".css": "text/css; charset=utf-8",
|
|
@@ -22443,6 +22550,24 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22443
22550
|
const relationType = inferRelationType(obs);
|
|
22444
22551
|
const relations = [];
|
|
22445
22552
|
const selfName = obs.entityName.toLowerCase();
|
|
22553
|
+
const explicitRelated = [...new Set((obs.relatedEntities ?? []).map((name) => name.trim()).filter((name) => name && name.toLowerCase() !== selfName))];
|
|
22554
|
+
if (explicitRelated.length > 0) {
|
|
22555
|
+
await graphManager.createEntities(explicitRelated.map((name) => ({
|
|
22556
|
+
name,
|
|
22557
|
+
entityType: "related",
|
|
22558
|
+
observations: []
|
|
22559
|
+
})));
|
|
22560
|
+
for (const name of explicitRelated) {
|
|
22561
|
+
const matchedEntity = graphManager.findEntityByName(name);
|
|
22562
|
+
if (matchedEntity) {
|
|
22563
|
+
relations.push({
|
|
22564
|
+
from: obs.entityName,
|
|
22565
|
+
to: matchedEntity.name,
|
|
22566
|
+
relationType: "related_entity"
|
|
22567
|
+
});
|
|
22568
|
+
}
|
|
22569
|
+
}
|
|
22570
|
+
}
|
|
22446
22571
|
const candidates = [
|
|
22447
22572
|
...extracted.identifiers,
|
|
22448
22573
|
...extracted.files.map((f) => f.split("/").pop()?.replace(/\.\w+$/, "") ?? ""),
|
|
@@ -22484,6 +22609,7 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22484
22609
|
|
|
22485
22610
|
// src/server.ts
|
|
22486
22611
|
init_entity_extractor();
|
|
22612
|
+
init_graph_scope();
|
|
22487
22613
|
|
|
22488
22614
|
// src/compact/engine.ts
|
|
22489
22615
|
init_esm_shims();
|
|
@@ -26169,7 +26295,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26169
26295
|
};
|
|
26170
26296
|
const server = existingServer ?? new McpServer({
|
|
26171
26297
|
name: "memorix",
|
|
26172
|
-
version: true ? "1.2.
|
|
26298
|
+
version: true ? "1.2.1" : "1.0.1"
|
|
26173
26299
|
});
|
|
26174
26300
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26175
26301
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26901,7 +27027,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26901
27027
|
{ getResolvedConfig: getResolvedConfig2 },
|
|
26902
27028
|
{ getExternalCodeGraphContext: getExternalCodeGraphContext2 },
|
|
26903
27029
|
{ getObservationStore: getObservationStore2 },
|
|
26904
|
-
{ collectCurrentProjectFacts: collectCurrentProjectFacts2 },
|
|
27030
|
+
{ collectCurrentProjectFacts: collectCurrentProjectFacts2, formatGitFact: formatGitFact2 },
|
|
26905
27031
|
{ resolveTaskLens: resolveTaskLens2 }
|
|
26906
27032
|
] = await Promise.all([
|
|
26907
27033
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
@@ -26942,7 +27068,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26942
27068
|
if (currentFacts.latestChangelog) {
|
|
26943
27069
|
worksetFacts.push("Latest changelog: " + currentFacts.latestChangelog.version + (currentFacts.latestChangelog.date ? " (" + currentFacts.latestChangelog.date + ")" : ""));
|
|
26944
27070
|
}
|
|
26945
|
-
worksetFacts.push(
|
|
27071
|
+
worksetFacts.push(formatGitFact2(currentFacts.git));
|
|
26946
27072
|
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";
|
|
26947
27073
|
const pack = await attachTaskWorkset2({
|
|
26948
27074
|
pack: basePack,
|
|
@@ -26974,11 +27100,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26974
27100
|
"memorix_knowledge",
|
|
26975
27101
|
{
|
|
26976
27102
|
title: "Knowledge Workspace",
|
|
26977
|
-
description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace,
|
|
27103
|
+
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.",
|
|
26978
27104
|
inputSchema: {
|
|
26979
27105
|
action: z2.enum([
|
|
26980
27106
|
"workspace_init",
|
|
26981
27107
|
"status",
|
|
27108
|
+
"claim_list",
|
|
27109
|
+
"claim_review",
|
|
26982
27110
|
"compile",
|
|
26983
27111
|
"lint",
|
|
26984
27112
|
"proposal_apply",
|
|
@@ -26993,6 +27121,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26993
27121
|
path: z2.string().optional().describe("Explicit versioned workspace path, used only by workspace_init"),
|
|
26994
27122
|
proposalId: z2.string().optional().describe("Pending proposal id for proposal_apply"),
|
|
26995
27123
|
allowManualOverwrite: z2.boolean().optional().default(false).describe("Explicitly allow proposal_apply to replace a manually edited page"),
|
|
27124
|
+
claimId: z2.string().optional().describe("Source-backed claim id for claim_review"),
|
|
27125
|
+
claimReviewState: z2.enum(["approved", "rejected"]).optional().describe("Deliberate review verdict for claim_review"),
|
|
27126
|
+
reviewDetail: z2.string().max(2e3).optional().describe("Evidence check performed before approving or rejecting a claim"),
|
|
26996
27127
|
workflowId: z2.string().optional().describe("Canonical workflow id for workflow preview, apply, or run"),
|
|
26997
27128
|
agent: z2.string().optional().describe("Target agent for a workflow adapter"),
|
|
26998
27129
|
task: z2.string().optional().describe("Task text for workflow selection or a workflow run"),
|
|
@@ -27009,6 +27140,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27009
27140
|
path: workspacePath,
|
|
27010
27141
|
proposalId,
|
|
27011
27142
|
allowManualOverwrite,
|
|
27143
|
+
claimId,
|
|
27144
|
+
claimReviewState,
|
|
27145
|
+
reviewDetail,
|
|
27012
27146
|
workflowId,
|
|
27013
27147
|
agent,
|
|
27014
27148
|
task,
|
|
@@ -27031,6 +27165,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27031
27165
|
};
|
|
27032
27166
|
const [
|
|
27033
27167
|
{ ClaimStore: ClaimStore2 },
|
|
27168
|
+
{ reviewClaim: reviewClaim2 },
|
|
27034
27169
|
{ CodeGraphStore: CodeGraphStore2 },
|
|
27035
27170
|
{ initializeKnowledgeWorkspace: initializeKnowledgeWorkspace2, loadKnowledgeWorkspace: loadKnowledgeWorkspace2 },
|
|
27036
27171
|
{ KnowledgeWorkspaceStore: KnowledgeWorkspaceStore2 },
|
|
@@ -27046,6 +27181,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27046
27181
|
}
|
|
27047
27182
|
] = await Promise.all([
|
|
27048
27183
|
Promise.resolve().then(() => (init_claim_store(), claim_store_exports)),
|
|
27184
|
+
Promise.resolve().then(() => (init_claims(), claims_exports)),
|
|
27049
27185
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
27050
27186
|
Promise.resolve().then(() => (init_workspace(), workspace_exports)),
|
|
27051
27187
|
Promise.resolve().then(() => (init_workspace_store(), workspace_store_exports)),
|
|
@@ -27095,10 +27231,52 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27095
27231
|
targetPath: proposal.targetPath,
|
|
27096
27232
|
reason: proposal.reason,
|
|
27097
27233
|
createdAt: proposal.createdAt
|
|
27098
|
-
}))
|
|
27234
|
+
})),
|
|
27235
|
+
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 }))
|
|
27099
27236
|
}
|
|
27100
27237
|
});
|
|
27101
27238
|
}
|
|
27239
|
+
if (action === "claim_list") {
|
|
27240
|
+
return text({
|
|
27241
|
+
claims: claims.listClaims(project.id, { limit: 100 }).map((claim) => ({
|
|
27242
|
+
id: claim.id,
|
|
27243
|
+
subject: claim.subject,
|
|
27244
|
+
predicate: claim.predicate,
|
|
27245
|
+
objectValue: claim.objectValue,
|
|
27246
|
+
status: claim.status,
|
|
27247
|
+
reviewState: claim.reviewState,
|
|
27248
|
+
origin: claim.origin,
|
|
27249
|
+
confidence: claim.confidence,
|
|
27250
|
+
evidenceCount: claims.listEvidence(claim.id).length
|
|
27251
|
+
})),
|
|
27252
|
+
next: "Approve only after checking the linked evidence. Rejected claims stay out of retrieval and publication."
|
|
27253
|
+
});
|
|
27254
|
+
}
|
|
27255
|
+
if (action === "claim_review") {
|
|
27256
|
+
const requestedClaimId = requireText(claimId, "claimId");
|
|
27257
|
+
const detail = requireText(reviewDetail, "reviewDetail");
|
|
27258
|
+
if (!requestedClaimId || !claimReviewState || !detail) {
|
|
27259
|
+
return text({ error: "claimId, claimReviewState, and reviewDetail are required for claim_review." }, true);
|
|
27260
|
+
}
|
|
27261
|
+
const existing = claims.getClaim(requestedClaimId);
|
|
27262
|
+
if (!existing || existing.projectId !== project.id) {
|
|
27263
|
+
return text({ error: "Claim was not found for the current project." }, true);
|
|
27264
|
+
}
|
|
27265
|
+
const claim = reviewClaim2(claims, {
|
|
27266
|
+
claimId: requestedClaimId,
|
|
27267
|
+
reviewState: claimReviewState,
|
|
27268
|
+
detail
|
|
27269
|
+
});
|
|
27270
|
+
return text({
|
|
27271
|
+
claim: {
|
|
27272
|
+
id: claim.id,
|
|
27273
|
+
status: claim.status,
|
|
27274
|
+
reviewState: claim.reviewState,
|
|
27275
|
+
updatedAt: claim.updatedAt
|
|
27276
|
+
},
|
|
27277
|
+
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."
|
|
27278
|
+
});
|
|
27279
|
+
}
|
|
27102
27280
|
if (action === "compile") {
|
|
27103
27281
|
const result = await compileKnowledgeWorkspace2({ workspace, claims });
|
|
27104
27282
|
return text({
|
|
@@ -27137,7 +27315,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27137
27315
|
if (action === "workflow_import") {
|
|
27138
27316
|
const result = await importWindsurfWorkflows2({ workspace, projectRoot });
|
|
27139
27317
|
return text({
|
|
27140
|
-
imported: result.imported.map((workflow2) => ({
|
|
27318
|
+
imported: result.imported.map((workflow2) => ({
|
|
27319
|
+
id: workflow2.id,
|
|
27320
|
+
title: workflow2.title,
|
|
27321
|
+
sourcePath: workflow2.sourcePath,
|
|
27322
|
+
importedFrom: workflow2.importedFrom,
|
|
27323
|
+
verificationGates: workflow2.verificationGates
|
|
27324
|
+
})),
|
|
27141
27325
|
skipped: result.skipped
|
|
27142
27326
|
});
|
|
27143
27327
|
}
|
|
@@ -27149,7 +27333,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27149
27333
|
title: workflow2.title,
|
|
27150
27334
|
status: workflow2.status,
|
|
27151
27335
|
taskLenses: workflow2.taskLenses,
|
|
27152
|
-
sourcePath: workflow2.sourcePath
|
|
27336
|
+
sourcePath: workflow2.sourcePath,
|
|
27337
|
+
importedFrom: workflow2.importedFrom,
|
|
27338
|
+
verificationGates: workflow2.verificationGates
|
|
27153
27339
|
})),
|
|
27154
27340
|
parseErrors: synced.errors
|
|
27155
27341
|
});
|
|
@@ -27978,13 +28164,11 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27978
28164
|
async function scopeGraphToProject(graph) {
|
|
27979
28165
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27980
28166
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
27981
|
-
const
|
|
27982
|
-
|
|
28167
|
+
const scoped = scopeKnowledgeGraphToProject(
|
|
28168
|
+
graph,
|
|
28169
|
+
allObs.filter((observation) => observation.projectId === project.id)
|
|
27983
28170
|
);
|
|
27984
|
-
|
|
27985
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
27986
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
27987
|
-
return { entities, relations };
|
|
28171
|
+
return { entities: scoped.entities, relations: scoped.relations };
|
|
27988
28172
|
}
|
|
27989
28173
|
server.registerTool(
|
|
27990
28174
|
"read_graph",
|