bilmem 0.1.0 → 0.2.0
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/README.md +163 -20
- package/dist/{canonical-CglQQ0xc.d.ts → canonical-JS6HUWGc.d.ts} +24 -1
- package/dist/cli/index.js +1433 -200
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.d.ts +52 -5
- package/dist/core/index.js +623 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{filesystem-B3qAej0l.d.ts → filesystem-CrAkhr85.d.ts} +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +1451 -303
- package/dist/index.js.map +1 -1
- package/dist/install/index.d.ts +59 -3
- package/dist/install/index.js +1245 -111
- package/dist/install/index.js.map +1 -1
- package/dist/jev/index.d.ts +1 -1
- package/dist/mcp/index.d.ts +12 -1
- package/dist/mcp/index.js +846 -97
- package/dist/mcp/index.js.map +1 -1
- package/dist/rules/index.d.ts +2 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/scanner/index.d.ts +2 -2
- package/dist/scanner/index.js +6 -1
- package/dist/scanner/index.js.map +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/stats/index.d.ts +3 -3
- package/dist/stats/index.js +45 -1
- package/dist/stats/index.js.map +1 -1
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.js +6 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/{types-Nxq5Gzay.d.ts → types-DNlvHsj9.d.ts} +120 -3
- package/package.json +1 -1
package/dist/install/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/install/index.ts
|
|
2
|
-
import * as
|
|
2
|
+
import * as path19 from "path";
|
|
3
3
|
import * as readline from "readline/promises";
|
|
4
4
|
|
|
5
5
|
// src/storage/canonical.ts
|
|
@@ -829,6 +829,8 @@ function getAgentInstructionText(version = CURRENT_INTEGRATION_VERSION) {
|
|
|
829
829
|
## Bilmem Knowledge Runtime
|
|
830
830
|
Bilmem is available through MCP.
|
|
831
831
|
|
|
832
|
+
Before substantial implementation work, use \`bilmem_preflight\` to surface existing constraints, relevant lessons, stale knowledge, and important unknowns. Do not run it for trivial edits or typo fixes.
|
|
833
|
+
|
|
832
834
|
Use \`bilmem_resolve\` when:
|
|
833
835
|
- uncertain about project-specific behavior,
|
|
834
836
|
- multiple plausible approaches exist,
|
|
@@ -852,6 +854,159 @@ function extractInstructionVersion(content) {
|
|
|
852
854
|
function hasBilmemInstruction(content) {
|
|
853
855
|
return content.includes(BILMEM_START_MARKER) && content.includes(BILMEM_END_MARKER);
|
|
854
856
|
}
|
|
857
|
+
function getBilmemSkillContent(version = CURRENT_INTEGRATION_VERSION) {
|
|
858
|
+
return `---
|
|
859
|
+
name: bilmem
|
|
860
|
+
description: >-
|
|
861
|
+
Epistemic runtime and project knowledge engine. Use when you need to inspect existing constraints or lessons before starting work (bilmem_preflight), resolve epistemic uncertainty or architectural decisions (bilmem_resolve), query verified knowledge (bilmem_lookup), record new observations (bilmem_observe), extract lessons from trajectories (bilmem_learn), track unknowns (bilmem_unknown), or check knowledge reuse and effectiveness (bilmem_stats).
|
|
862
|
+
---
|
|
863
|
+
<!-- bilmem-version: ${version} -->
|
|
864
|
+
|
|
865
|
+
# Bilmem Epistemic Knowledge Runtime
|
|
866
|
+
|
|
867
|
+
Bilmem is an epistemic knowledge engine designed to answer:
|
|
868
|
+
> **Do I know enough to act, what do I know does not apply, what am I missing, and what is the cheapest next step to resolve uncertainty?**
|
|
869
|
+
|
|
870
|
+
Unlike traditional code graphs or vector memory, Bilmem focuses on verified facts, contextual applicability (via JEV positive/negative cues), staged learning from trajectories, and tracking unknowns as first-class citizens.
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
## \u26A1 Core Epistemic Commitments
|
|
875
|
+
|
|
876
|
+
1. **Cheaper than not knowing:** Querying Bilmem or resolving uncertainty is significantly cheaper than re-investigating, guessing, or recovering from incorrect architectural assumptions.
|
|
877
|
+
2. **Never guess project invariants:** If uncertain whether caching is enabled in production, how auth tokens rotate, or why a specific pattern exists, resolve it through Bilmem instead of guessing.
|
|
878
|
+
3. **No queries on trivial edits:** Do not query Bilmem for simple typo fixes, formatting, or obvious local syntax changes.
|
|
879
|
+
4. **Receipts & Honest Confidence:** Bilmem uses explainable confidence tiers (\`low\`, \`medium\`, \`high\`) and returns receipts explaining why knowledge was accepted or rejected.
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
## \u{1F680} Progressive Disclosure: Tool Runbook
|
|
884
|
+
|
|
885
|
+
> [!TIP]
|
|
886
|
+
> **Project Directory Target (\`path\` parameter)**:
|
|
887
|
+
> In global MCP environments or multi-workspace IDEs, always pass the \`"path"\` parameter with your project root (e.g. \`"path": "/path/to/project"\` or active workspace directory) to target the proper repository and prevent root filesystem permission issues.
|
|
888
|
+
|
|
889
|
+
### 1. Before Substantial Implementation: \`bilmem_preflight\`
|
|
890
|
+
Run \`bilmem_preflight\` with your proposed goal before modifying architecture, core logic, or adding features:
|
|
891
|
+
\`\`\`json
|
|
892
|
+
{
|
|
893
|
+
"path": "/path/to/project",
|
|
894
|
+
"goal": "Add OAuth login with Google",
|
|
895
|
+
"positive": ["auth", "oauth", "session"],
|
|
896
|
+
"negative": ["stateless-jwt"]
|
|
897
|
+
}
|
|
898
|
+
\`\`\`
|
|
899
|
+
**Returns:**
|
|
900
|
+
- Applicable verified constraints & architectural rules.
|
|
901
|
+
- Relevant lessons from past implementations or regressions.
|
|
902
|
+
- Past failures in similar domains.
|
|
903
|
+
- Blocking unknowns that must be investigated first.
|
|
904
|
+
- Stale dependencies that need review.
|
|
905
|
+
- Next recommended investigation actions.
|
|
906
|
+
|
|
907
|
+
### 2. When Uncertain or Resolving Contradictions: \`bilmem_resolve\`
|
|
908
|
+
Use \`bilmem_resolve\` when encountering ambiguous codebase behavior, multiple plausible paths, or conflicting assumptions:
|
|
909
|
+
\`\`\`json
|
|
910
|
+
{
|
|
911
|
+
"path": "/path/to/project",
|
|
912
|
+
"question": "How are user sessions renewed and validated?",
|
|
913
|
+
"positive": ["server-session", "postgres", "redis"],
|
|
914
|
+
"negative": ["client-cookie-only"]
|
|
915
|
+
}
|
|
916
|
+
\`\`\`
|
|
917
|
+
**Returns:**
|
|
918
|
+
- \`state\`: \`resolved\` (sufficient facts known), \`needs_investigation\`, or \`contradiction\`.
|
|
919
|
+
- \`known\`: Verified statements applicable to the current context.
|
|
920
|
+
- \`rejected\`: Inapplicable knowledge units filtered out by negative cues.
|
|
921
|
+
- \`missing\`: Critical gaps that need observation before proceeding.
|
|
922
|
+
- \`next\`: The cheapest concrete investigation step.
|
|
923
|
+
|
|
924
|
+
### 3. Exact Knowledge Retrieval: \`bilmem_lookup\`
|
|
925
|
+
Quickly look up specific conventions, lessons, or rules:
|
|
926
|
+
\`\`\`json
|
|
927
|
+
{
|
|
928
|
+
"path": "/path/to/project",
|
|
929
|
+
"query": "session timeout configuration",
|
|
930
|
+
"limit": 5
|
|
931
|
+
}
|
|
932
|
+
\`\`\`
|
|
933
|
+
|
|
934
|
+
### 4. Recording Fresh Discoveries: \`bilmem_observe\`
|
|
935
|
+
Record verified discoveries, configuration invariants, or environment realities:
|
|
936
|
+
\`\`\`json
|
|
937
|
+
{
|
|
938
|
+
"path": "/path/to/project",
|
|
939
|
+
"claim": "Production session cookies require SameSite=Strict and Secure flags",
|
|
940
|
+
"source": "file:src/auth/session.ts#L42",
|
|
941
|
+
"positive": ["production", "cookie", "auth"],
|
|
942
|
+
"negative": ["development"]
|
|
943
|
+
}
|
|
944
|
+
\`\`\`
|
|
945
|
+
|
|
946
|
+
### 5. Learning from Corrections & Trajectories: \`bilmem_learn\`
|
|
947
|
+
After resolving a difficult bug, fixing a regression, or receiving a critical user correction, distill the trajectory:
|
|
948
|
+
\`\`\`json
|
|
949
|
+
{
|
|
950
|
+
"path": "/path/to/project",
|
|
951
|
+
"goal": "Fix refresh token rotation loop",
|
|
952
|
+
"outcome": "success",
|
|
953
|
+
"userCorrection": {
|
|
954
|
+
"topic": "session-auth",
|
|
955
|
+
"incorrectAssumption": "Token refresh does not require a concurrent mutex",
|
|
956
|
+
"correctFact": "Token refresh requires a concurrency mutex to avoid 401 race conditions"
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
\`\`\`
|
|
960
|
+
Bilmem runs staged reconciliation: extracts observations, creates candidate lessons, evaluates polar contradictions, and reconciles into durable knowledge.
|
|
961
|
+
|
|
962
|
+
### 6. Recording Known Unknowns: \`bilmem_unknown\`
|
|
963
|
+
When you realize critical context is missing, record an unknown instead of making a silent assumption:
|
|
964
|
+
\`\`\`json
|
|
965
|
+
{
|
|
966
|
+
"path": "/path/to/project",
|
|
967
|
+
"action": "record",
|
|
968
|
+
"id": "staging.redis-cache",
|
|
969
|
+
"question": "Does the staging database share the Redis cache cluster with production?"
|
|
970
|
+
}
|
|
971
|
+
\`\`\`
|
|
972
|
+
|
|
973
|
+
### 7. Auditing Effectiveness & Reuse: \`bilmem_stats\`
|
|
974
|
+
Verify how well Bilmem is reducing repeated investigation:
|
|
975
|
+
\`\`\`json
|
|
976
|
+
{
|
|
977
|
+
"path": "/path/to/project",
|
|
978
|
+
"since": "7d"
|
|
979
|
+
}
|
|
980
|
+
\`\`\`
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
## \u{1F4BB} CLI Quick Reference
|
|
985
|
+
|
|
986
|
+
All tools are also available directly on the command line:
|
|
987
|
+
|
|
988
|
+
| Command | Purpose |
|
|
989
|
+
| :--- | :--- |
|
|
990
|
+
| \`bilmem preflight "<goal>"\` | Pre-implementation readiness & constraint analysis |
|
|
991
|
+
| \`bilmem resolve "<question>"\` | Epistemic resolution with context cues & receipts |
|
|
992
|
+
| \`bilmem lookup "<query>"\` | Fast contextual lookup of verified knowledge |
|
|
993
|
+
| \`bilmem observe "<claim>" --source <src>\` | Record verified evidence or invariant |
|
|
994
|
+
| \`bilmem learn --trajectory "<text>"\` | Reconcile candidate lessons into knowledge |
|
|
995
|
+
| \`bilmem scan [--dry-run]\` | Conservative filesystem & convention discovery |
|
|
996
|
+
| \`bilmem stats [--since 7d]\` | Measure knowledge reuse and effectiveness |
|
|
997
|
+
| \`bilmem doctor [--fix]\` | Verify and repair integrations and storage |
|
|
998
|
+
`;
|
|
999
|
+
}
|
|
1000
|
+
function hasBilmemSkill(content) {
|
|
1001
|
+
return content.includes("name: bilmem") && content.includes("---");
|
|
1002
|
+
}
|
|
1003
|
+
function extractSkillVersion(content) {
|
|
1004
|
+
const match = content.match(/<!--\s*bilmem-version:\s*(\d+)\s*-->/);
|
|
1005
|
+
if (match && match[1]) {
|
|
1006
|
+
return parseInt(match[1], 10);
|
|
1007
|
+
}
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
855
1010
|
|
|
856
1011
|
// src/install/atomic.ts
|
|
857
1012
|
async function writeAtomic(filePath, content) {
|
|
@@ -1020,6 +1175,42 @@ async function pathExists(p) {
|
|
|
1020
1175
|
return false;
|
|
1021
1176
|
}
|
|
1022
1177
|
}
|
|
1178
|
+
async function updateSkillFile(filePath, content, dryRun = false) {
|
|
1179
|
+
let existingContent = "";
|
|
1180
|
+
let exists = false;
|
|
1181
|
+
try {
|
|
1182
|
+
existingContent = await fs4.readFile(filePath, "utf-8");
|
|
1183
|
+
exists = true;
|
|
1184
|
+
} catch (err) {
|
|
1185
|
+
if (err.code !== "ENOENT") throw err;
|
|
1186
|
+
}
|
|
1187
|
+
if (exists && existingContent.trim() === content.trim()) {
|
|
1188
|
+
return { changed: false, created: false, originalExisted: true };
|
|
1189
|
+
}
|
|
1190
|
+
if (!dryRun) {
|
|
1191
|
+
await writeAtomic(filePath, content.trimEnd() + "\n");
|
|
1192
|
+
}
|
|
1193
|
+
return { changed: true, created: !exists, originalExisted: exists };
|
|
1194
|
+
}
|
|
1195
|
+
async function removeSkillFile(filePath, dryRun = false) {
|
|
1196
|
+
try {
|
|
1197
|
+
await fs4.access(filePath);
|
|
1198
|
+
} catch {
|
|
1199
|
+
return { removed: false, fileDeleted: false };
|
|
1200
|
+
}
|
|
1201
|
+
if (!dryRun) {
|
|
1202
|
+
try {
|
|
1203
|
+
await fs4.unlink(filePath);
|
|
1204
|
+
const parentDir = path4.dirname(filePath);
|
|
1205
|
+
const remaining = await fs4.readdir(parentDir);
|
|
1206
|
+
if (remaining.length === 0) {
|
|
1207
|
+
await fs4.rmdir(parentDir);
|
|
1208
|
+
}
|
|
1209
|
+
} catch {
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return { removed: true, fileDeleted: true };
|
|
1213
|
+
}
|
|
1023
1214
|
async function isExecutableInPath(command) {
|
|
1024
1215
|
const envPath = process.env.PATH;
|
|
1025
1216
|
if (!envPath) return false;
|
|
@@ -1085,6 +1276,7 @@ async function getIntegrationRecord(rootDir, agentId) {
|
|
|
1085
1276
|
|
|
1086
1277
|
// src/install/adapters/base.ts
|
|
1087
1278
|
var BaseAgentAdapter = class {
|
|
1279
|
+
aliases;
|
|
1088
1280
|
supportedScopes = ["project", "global"];
|
|
1089
1281
|
async inspect(context) {
|
|
1090
1282
|
const configPath = this.resolveConfigPath(context);
|
|
@@ -1098,7 +1290,9 @@ var BaseAgentAdapter = class {
|
|
|
1098
1290
|
if (read.exists && !read.error && read.data?.mcpServers?.bilmem) {
|
|
1099
1291
|
mcpConfigured = true;
|
|
1100
1292
|
const b = read.data.mcpServers.bilmem;
|
|
1101
|
-
|
|
1293
|
+
const targetMatch = b.command === targetCommand && Array.isArray(b.args) && b.args.length === targetArgs.length && b.args.every((v, i) => v === targetArgs[i]);
|
|
1294
|
+
const wrapperMatch = Array.isArray(b.args) && b.args.some((arg) => typeof arg === "string" && arg.includes("bilmem"));
|
|
1295
|
+
mcpCommandMatches = Boolean(targetMatch || wrapperMatch);
|
|
1102
1296
|
}
|
|
1103
1297
|
let instructionInstalled = false;
|
|
1104
1298
|
let instructionVersion;
|
|
@@ -1111,6 +1305,20 @@ var BaseAgentAdapter = class {
|
|
|
1111
1305
|
}
|
|
1112
1306
|
} catch {
|
|
1113
1307
|
}
|
|
1308
|
+
let skillInstalled = false;
|
|
1309
|
+
let skillVersion;
|
|
1310
|
+
const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
|
|
1311
|
+
if (skillPath) {
|
|
1312
|
+
try {
|
|
1313
|
+
const skillContent = await fs5.readFile(skillPath, "utf-8");
|
|
1314
|
+
skillInstalled = hasBilmemSkill(skillContent);
|
|
1315
|
+
const extracted = extractSkillVersion(skillContent);
|
|
1316
|
+
if (extracted !== null) {
|
|
1317
|
+
skillVersion = extracted;
|
|
1318
|
+
}
|
|
1319
|
+
} catch {
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1114
1322
|
return {
|
|
1115
1323
|
id: this.id,
|
|
1116
1324
|
name: this.name,
|
|
@@ -1120,9 +1328,12 @@ var BaseAgentAdapter = class {
|
|
|
1120
1328
|
mcpCommandMatches,
|
|
1121
1329
|
instructionInstalled,
|
|
1122
1330
|
instructionVersion,
|
|
1331
|
+
skillInstalled: skillPath ? skillInstalled : void 0,
|
|
1332
|
+
skillVersion: skillPath ? skillVersion : void 0,
|
|
1123
1333
|
currentInstructionVersion: CURRENT_INTEGRATION_VERSION,
|
|
1124
1334
|
configPath,
|
|
1125
1335
|
instructionPath,
|
|
1336
|
+
skillPath,
|
|
1126
1337
|
details: {
|
|
1127
1338
|
rawConfigExisted: read.exists,
|
|
1128
1339
|
configError: read.error
|
|
@@ -1167,6 +1378,19 @@ var BaseAgentAdapter = class {
|
|
|
1167
1378
|
if (instRes.changed) {
|
|
1168
1379
|
actionsTaken.push(`Installed/updated agent instruction block in ${instructionPath}`);
|
|
1169
1380
|
}
|
|
1381
|
+
let skillChanged = false;
|
|
1382
|
+
const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
|
|
1383
|
+
if (skillPath) {
|
|
1384
|
+
const skillRes = await updateSkillFile(
|
|
1385
|
+
skillPath,
|
|
1386
|
+
getBilmemSkillContent(CURRENT_INTEGRATION_VERSION),
|
|
1387
|
+
options.dryRun ?? false
|
|
1388
|
+
);
|
|
1389
|
+
skillChanged = skillRes.changed;
|
|
1390
|
+
if (skillRes.changed) {
|
|
1391
|
+
actionsTaken.push(`Installed/updated Antigravity skill in ${skillPath}`);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1170
1394
|
if (context.scope === "project") {
|
|
1171
1395
|
await recordIntegration(
|
|
1172
1396
|
context.projectDir,
|
|
@@ -1177,6 +1401,8 @@ var BaseAgentAdapter = class {
|
|
|
1177
1401
|
mcpConfigPath: path6.relative(context.projectDir, configPath),
|
|
1178
1402
|
instructions: true,
|
|
1179
1403
|
instructionPath: path6.relative(context.projectDir, instructionPath),
|
|
1404
|
+
skill: skillPath ? true : void 0,
|
|
1405
|
+
skillPath: skillPath ? path6.relative(context.projectDir, skillPath) : void 0,
|
|
1180
1406
|
version: CURRENT_INTEGRATION_VERSION,
|
|
1181
1407
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1182
1408
|
},
|
|
@@ -1187,7 +1413,7 @@ var BaseAgentAdapter = class {
|
|
|
1187
1413
|
if (!resolvable && !options.dryRun) {
|
|
1188
1414
|
warnings.push(`Executable "${targetCommand}" is not in system PATH. Ensure it can be run by ${this.name}.`);
|
|
1189
1415
|
}
|
|
1190
|
-
const changesMade = mcpRes.changed || instRes.changed || projectInitialized;
|
|
1416
|
+
const changesMade = mcpRes.changed || instRes.changed || skillChanged || projectInitialized;
|
|
1191
1417
|
if (!changesMade) {
|
|
1192
1418
|
actionsTaken.push("Already up to date");
|
|
1193
1419
|
}
|
|
@@ -1198,6 +1424,8 @@ var BaseAgentAdapter = class {
|
|
|
1198
1424
|
scope: options.scope,
|
|
1199
1425
|
mcpChanged: mcpRes.changed,
|
|
1200
1426
|
instructionChanged: instRes.changed,
|
|
1427
|
+
skillChanged,
|
|
1428
|
+
skillPath,
|
|
1201
1429
|
projectInitialized,
|
|
1202
1430
|
dryRun: options.dryRun,
|
|
1203
1431
|
actionsTaken,
|
|
@@ -1217,10 +1445,19 @@ var BaseAgentAdapter = class {
|
|
|
1217
1445
|
if (instRes.removed) {
|
|
1218
1446
|
actionsTaken.push(`Removed Bilmem instruction block from ${instructionPath}`);
|
|
1219
1447
|
}
|
|
1448
|
+
let skillRemoved = false;
|
|
1449
|
+
const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
|
|
1450
|
+
if (skillPath) {
|
|
1451
|
+
const skillRes = await removeSkillFile(skillPath, options.dryRun ?? false);
|
|
1452
|
+
skillRemoved = skillRes.removed;
|
|
1453
|
+
if (skillRes.removed) {
|
|
1454
|
+
actionsTaken.push(`Removed Antigravity skill from ${skillPath}`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1220
1457
|
if (context.scope === "project") {
|
|
1221
1458
|
await removeIntegration(context.projectDir, this.id, options.dryRun ?? false);
|
|
1222
1459
|
}
|
|
1223
|
-
const anyRemoved = mcpRes.removed || instRes.removed;
|
|
1460
|
+
const anyRemoved = mcpRes.removed || instRes.removed || skillRemoved;
|
|
1224
1461
|
if (!anyRemoved) {
|
|
1225
1462
|
actionsTaken.push("No Bilmem configuration found to remove");
|
|
1226
1463
|
}
|
|
@@ -1231,6 +1468,8 @@ var BaseAgentAdapter = class {
|
|
|
1231
1468
|
scope: options.scope,
|
|
1232
1469
|
mcpRemoved: mcpRes.removed,
|
|
1233
1470
|
instructionRemoved: instRes.removed,
|
|
1471
|
+
skillRemoved,
|
|
1472
|
+
skillPath,
|
|
1234
1473
|
dryRun: options.dryRun,
|
|
1235
1474
|
actionsTaken,
|
|
1236
1475
|
message: anyRemoved ? `Successfully uninstalled Bilmem from ${this.name}` : `No Bilmem configuration found for ${this.name}`
|
|
@@ -1242,6 +1481,7 @@ var BaseAgentAdapter = class {
|
|
|
1242
1481
|
const recommendations = [];
|
|
1243
1482
|
let mcpValid = false;
|
|
1244
1483
|
let instructionValid = false;
|
|
1484
|
+
let skillValid = true;
|
|
1245
1485
|
if (!state.mcpConfigured) {
|
|
1246
1486
|
issues.push(`MCP server "bilmem" is not configured in ${state.configPath}`);
|
|
1247
1487
|
recommendations.push(`Run "bilmem install ${this.id}" to configure MCP`);
|
|
@@ -1262,17 +1502,31 @@ var BaseAgentAdapter = class {
|
|
|
1262
1502
|
} else {
|
|
1263
1503
|
instructionValid = true;
|
|
1264
1504
|
}
|
|
1505
|
+
if (state.skillPath) {
|
|
1506
|
+
if (!state.skillInstalled) {
|
|
1507
|
+
skillValid = false;
|
|
1508
|
+
issues.push(`Skill file missing from ${state.skillPath}`);
|
|
1509
|
+
recommendations.push(`Run "bilmem install ${this.id}" to install the skill`);
|
|
1510
|
+
} else if (state.skillVersion !== CURRENT_INTEGRATION_VERSION) {
|
|
1511
|
+
skillValid = false;
|
|
1512
|
+
issues.push(
|
|
1513
|
+
`Skill file version (${state.skillVersion || "legacy"}) is outdated (current: ${CURRENT_INTEGRATION_VERSION})`
|
|
1514
|
+
);
|
|
1515
|
+
recommendations.push(`Run "bilmem doctor --fix" to update the skill`);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1265
1518
|
const targetCommand = context.bilmemCommand || "bilmem";
|
|
1266
1519
|
const commandResolvable = await isExecutableInPath(targetCommand);
|
|
1267
1520
|
if (!commandResolvable) {
|
|
1268
1521
|
issues.push(`Command "${targetCommand}" is not found in PATH`);
|
|
1269
1522
|
recommendations.push(`Ensure "${targetCommand}" is installed globally or in your PATH`);
|
|
1270
1523
|
}
|
|
1271
|
-
const valid = mcpValid && instructionValid && commandResolvable;
|
|
1524
|
+
const valid = mcpValid && instructionValid && skillValid && commandResolvable;
|
|
1272
1525
|
return {
|
|
1273
1526
|
valid,
|
|
1274
1527
|
mcpValid,
|
|
1275
1528
|
instructionValid,
|
|
1529
|
+
skillValid,
|
|
1276
1530
|
commandResolvable,
|
|
1277
1531
|
issues,
|
|
1278
1532
|
recommendations
|
|
@@ -1479,12 +1733,110 @@ var GeminiAdapter = class extends BaseAgentAdapter {
|
|
|
1479
1733
|
}
|
|
1480
1734
|
};
|
|
1481
1735
|
|
|
1736
|
+
// src/install/adapters/antigravity.ts
|
|
1737
|
+
import * as path11 from "path";
|
|
1738
|
+
import * as os5 from "os";
|
|
1739
|
+
import * as fs6 from "fs";
|
|
1740
|
+
function resolveAntigravityGlobalDir(context) {
|
|
1741
|
+
const home = context.globalDir || os5.homedir();
|
|
1742
|
+
if (path11.basename(home) === "config" && path11.basename(path11.dirname(home)) === ".gemini") {
|
|
1743
|
+
return home;
|
|
1744
|
+
}
|
|
1745
|
+
return path11.join(home, ".gemini", "config");
|
|
1746
|
+
}
|
|
1747
|
+
var AntigravityAdapter = class extends BaseAgentAdapter {
|
|
1748
|
+
id = "antigravity";
|
|
1749
|
+
name = "Antigravity";
|
|
1750
|
+
resolveConfigPath(context) {
|
|
1751
|
+
if (context.scope === "project") {
|
|
1752
|
+
const rootMcp = path11.join(context.projectDir, "mcp_config.json");
|
|
1753
|
+
if (fs6.existsSync(rootMcp)) {
|
|
1754
|
+
return rootMcp;
|
|
1755
|
+
}
|
|
1756
|
+
return path11.join(context.projectDir, ".agents", "mcp_config.json");
|
|
1757
|
+
}
|
|
1758
|
+
const globalRoot = resolveAntigravityGlobalDir(context);
|
|
1759
|
+
return path11.join(globalRoot, "mcp_config.json");
|
|
1760
|
+
}
|
|
1761
|
+
resolveSkillPath(context) {
|
|
1762
|
+
if (context.scope === "project") {
|
|
1763
|
+
return path11.join(context.projectDir, ".agents", "skills", "bilmem", "SKILL.md");
|
|
1764
|
+
}
|
|
1765
|
+
const globalRoot = resolveAntigravityGlobalDir(context);
|
|
1766
|
+
return path11.join(globalRoot, "skills", "bilmem", "SKILL.md");
|
|
1767
|
+
}
|
|
1768
|
+
resolveInstructionPath(context) {
|
|
1769
|
+
if (context.scope === "project") {
|
|
1770
|
+
const geminiMd = path11.join(context.projectDir, "GEMINI.md");
|
|
1771
|
+
const agentsMd = path11.join(context.projectDir, "AGENTS.md");
|
|
1772
|
+
const rulesBilmem = path11.join(context.projectDir, ".agents", "rules", "bilmem.md");
|
|
1773
|
+
if (fs6.existsSync(rulesBilmem)) {
|
|
1774
|
+
return rulesBilmem;
|
|
1775
|
+
}
|
|
1776
|
+
if (fs6.existsSync(geminiMd)) {
|
|
1777
|
+
return geminiMd;
|
|
1778
|
+
}
|
|
1779
|
+
if (fs6.existsSync(agentsMd)) {
|
|
1780
|
+
return agentsMd;
|
|
1781
|
+
}
|
|
1782
|
+
return path11.join(context.projectDir, ".agents", "rules", "bilmem.md");
|
|
1783
|
+
}
|
|
1784
|
+
const globalRoot = resolveAntigravityGlobalDir(context);
|
|
1785
|
+
return path11.join(globalRoot, "rules", "bilmem.md");
|
|
1786
|
+
}
|
|
1787
|
+
async detect(context) {
|
|
1788
|
+
const reasons = [];
|
|
1789
|
+
const dotAgents = path11.join(context.projectDir, ".agents");
|
|
1790
|
+
const skillsDir = path11.join(context.projectDir, ".agents", "skills");
|
|
1791
|
+
const agentsMcp = path11.join(context.projectDir, ".agents", "mcp_config.json");
|
|
1792
|
+
const rootMcp = path11.join(context.projectDir, "mcp_config.json");
|
|
1793
|
+
const geminiMd = path11.join(context.projectDir, "GEMINI.md");
|
|
1794
|
+
const agentsMd = path11.join(context.projectDir, "AGENTS.md");
|
|
1795
|
+
const hasDotAgents = await pathExists(dotAgents);
|
|
1796
|
+
const hasSkillsDir = await pathExists(skillsDir);
|
|
1797
|
+
const hasAgentsMcp = await pathExists(agentsMcp);
|
|
1798
|
+
const hasRootMcp = await pathExists(rootMcp);
|
|
1799
|
+
const hasGeminiMd = await pathExists(geminiMd);
|
|
1800
|
+
const hasAgentsMd = await pathExists(agentsMd);
|
|
1801
|
+
if (hasDotAgents) reasons.push("Found .agents/ directory");
|
|
1802
|
+
if (hasSkillsDir) reasons.push("Found .agents/skills/ directory");
|
|
1803
|
+
if (hasAgentsMcp) reasons.push("Found .agents/mcp_config.json");
|
|
1804
|
+
if (hasRootMcp) reasons.push("Found mcp_config.json file");
|
|
1805
|
+
if (hasGeminiMd) reasons.push("Found GEMINI.md rule file");
|
|
1806
|
+
if (hasAgentsMd) reasons.push("Found AGENTS.md rule file");
|
|
1807
|
+
const home = context.globalDir || os5.homedir();
|
|
1808
|
+
const globalConfig = resolveAntigravityGlobalDir(context);
|
|
1809
|
+
const globalIde = path11.join(home, ".gemini", "antigravity-ide");
|
|
1810
|
+
const globalCli = path11.join(home, ".gemini", "antigravity-cli");
|
|
1811
|
+
const globalAgy = path11.join(home, ".gemini", "antigravity");
|
|
1812
|
+
const hasGlobalConfig = await pathExists(globalConfig);
|
|
1813
|
+
const hasGlobalIde = await pathExists(globalIde);
|
|
1814
|
+
const hasGlobalCli = await pathExists(globalCli);
|
|
1815
|
+
const hasGlobalAgy = await pathExists(globalAgy);
|
|
1816
|
+
if (hasGlobalConfig) reasons.push(`Found Antigravity global config at ${globalConfig}`);
|
|
1817
|
+
if (hasGlobalIde) reasons.push(`Found Antigravity IDE at ${globalIde}`);
|
|
1818
|
+
if (hasGlobalCli) reasons.push(`Found Antigravity CLI at ${globalCli}`);
|
|
1819
|
+
if (hasGlobalAgy) reasons.push(`Found Antigravity directory at ${globalAgy}`);
|
|
1820
|
+
const hasAgyExec = await isExecutableInPath("agy");
|
|
1821
|
+
if (hasAgyExec) reasons.push('Found "agy" command in PATH');
|
|
1822
|
+
const detected = reasons.length > 0;
|
|
1823
|
+
const confidence = hasDotAgents || hasSkillsDir || hasAgentsMcp || hasRootMcp || hasGeminiMd || hasAgentsMd ? "high" : hasGlobalConfig || hasGlobalIde || hasGlobalCli || hasGlobalAgy || hasAgyExec ? "medium" : "low";
|
|
1824
|
+
return {
|
|
1825
|
+
detected,
|
|
1826
|
+
confidence: detected ? confidence : "low",
|
|
1827
|
+
reasons,
|
|
1828
|
+
supportedScopes: this.supportedScopes
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1482
1833
|
// src/install/registry.ts
|
|
1483
1834
|
var adapters = [
|
|
1484
1835
|
new CursorAdapter(),
|
|
1485
1836
|
new ClaudeAdapter(),
|
|
1486
1837
|
new CodexAdapter(),
|
|
1487
|
-
new GeminiAdapter()
|
|
1838
|
+
new GeminiAdapter(),
|
|
1839
|
+
new AntigravityAdapter()
|
|
1488
1840
|
];
|
|
1489
1841
|
function getAllAdapters() {
|
|
1490
1842
|
return [...adapters];
|
|
@@ -1504,11 +1856,11 @@ async function detectAgents(context) {
|
|
|
1504
1856
|
}
|
|
1505
1857
|
|
|
1506
1858
|
// src/install/doctor.ts
|
|
1507
|
-
import * as
|
|
1859
|
+
import * as path18 from "path";
|
|
1508
1860
|
|
|
1509
1861
|
// src/mcp/server.ts
|
|
1510
1862
|
import { McpApp, z } from "mcponce";
|
|
1511
|
-
import * as
|
|
1863
|
+
import * as path17 from "path";
|
|
1512
1864
|
|
|
1513
1865
|
// src/search/indexer.ts
|
|
1514
1866
|
import { create, insert, remove, search } from "@orama/orama";
|
|
@@ -1883,6 +2235,7 @@ function formatWhyHistory(knowledge) {
|
|
|
1883
2235
|
// src/core/resolver.ts
|
|
1884
2236
|
var RELATIVE_COSTS = {
|
|
1885
2237
|
USE_KNOWLEDGE: 1,
|
|
2238
|
+
REVALIDATE: 2,
|
|
1886
2239
|
INSPECT_TEST: 2,
|
|
1887
2240
|
INSPECT_FILE: 2,
|
|
1888
2241
|
INSPECT_SYMBOL: 2.5,
|
|
@@ -2223,6 +2576,634 @@ async function resolveEpistemicQuery(options, storage, retriever) {
|
|
|
2223
2576
|
};
|
|
2224
2577
|
}
|
|
2225
2578
|
|
|
2579
|
+
// src/core/preflight.ts
|
|
2580
|
+
import { execFile } from "child_process";
|
|
2581
|
+
import { promisify } from "util";
|
|
2582
|
+
import * as path12 from "path";
|
|
2583
|
+
import * as fs7 from "fs/promises";
|
|
2584
|
+
import YAML2 from "yaml";
|
|
2585
|
+
|
|
2586
|
+
// src/core/dependencies.ts
|
|
2587
|
+
function normalizeSourceKey(source) {
|
|
2588
|
+
return source.replace(/^(file|code|test|doc):/, "").trim().toLowerCase();
|
|
2589
|
+
}
|
|
2590
|
+
function checkKnowledgeStaleness(knowledge, changedSources) {
|
|
2591
|
+
const normChanged = new Set(changedSources.map(normalizeSourceKey));
|
|
2592
|
+
const allDeps = [
|
|
2593
|
+
...(knowledge.depends_on ?? []).map(normalizeSourceKey),
|
|
2594
|
+
...knowledge.evidence.map(normalizeSourceKey)
|
|
2595
|
+
];
|
|
2596
|
+
const changed = [];
|
|
2597
|
+
const unchanged = [];
|
|
2598
|
+
for (const dep of allDeps) {
|
|
2599
|
+
if (!dep) continue;
|
|
2600
|
+
const isChanged = normChanged.has(dep) || Array.from(normChanged).some((c) => dep.includes(c) || c.includes(dep));
|
|
2601
|
+
if (isChanged) {
|
|
2602
|
+
changed.push(dep);
|
|
2603
|
+
} else {
|
|
2604
|
+
unchanged.push(dep);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
if (changed.length > 0) {
|
|
2608
|
+
const reason = `Supporting dependency changed: ${changed.join(", ")}. Unchanged evidence: ${unchanged.join(", ") || "none"}`;
|
|
2609
|
+
return {
|
|
2610
|
+
knowledgeId: knowledge.id,
|
|
2611
|
+
affected: true,
|
|
2612
|
+
status: "needs_revalidation",
|
|
2613
|
+
changedSources: changed,
|
|
2614
|
+
unchangedSources: unchanged,
|
|
2615
|
+
reason
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
knowledgeId: knowledge.id,
|
|
2620
|
+
affected: false,
|
|
2621
|
+
status: knowledge.status,
|
|
2622
|
+
changedSources: [],
|
|
2623
|
+
unchangedSources: allDeps
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
function markStaleKnowledge(knowledgeList, changedSources) {
|
|
2627
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2628
|
+
let affectedCount = 0;
|
|
2629
|
+
const updatedKnowledge = [];
|
|
2630
|
+
for (const k of knowledgeList) {
|
|
2631
|
+
if (k.status === "superseded" || k.status === "invalid" || k.status === "invalidated") {
|
|
2632
|
+
updatedKnowledge.push(k);
|
|
2633
|
+
continue;
|
|
2634
|
+
}
|
|
2635
|
+
const res = checkKnowledgeStaleness(k, changedSources);
|
|
2636
|
+
if (res.affected) {
|
|
2637
|
+
affectedCount++;
|
|
2638
|
+
const history = k.history ?? [];
|
|
2639
|
+
const updated = {
|
|
2640
|
+
...k,
|
|
2641
|
+
status: "needs_revalidation",
|
|
2642
|
+
updatedAt: now,
|
|
2643
|
+
history: [
|
|
2644
|
+
...history,
|
|
2645
|
+
{
|
|
2646
|
+
timestamp: now,
|
|
2647
|
+
status: "needs_revalidation",
|
|
2648
|
+
action: "source_changed",
|
|
2649
|
+
reason: res.reason
|
|
2650
|
+
}
|
|
2651
|
+
]
|
|
2652
|
+
};
|
|
2653
|
+
updatedKnowledge.push(updated);
|
|
2654
|
+
} else {
|
|
2655
|
+
updatedKnowledge.push(k);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
return { updatedKnowledge, affectedCount };
|
|
2659
|
+
}
|
|
2660
|
+
function checkKnowledgeListStaleness(knowledgeList, changedSources) {
|
|
2661
|
+
const { updatedKnowledge, affectedCount } = markStaleKnowledge(knowledgeList, changedSources);
|
|
2662
|
+
const affectedKnowledge = updatedKnowledge.filter((k) => k.status === "needs_revalidation");
|
|
2663
|
+
return { affectedKnowledge, updatedKnowledge, affectedCount };
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// src/core/preflight.ts
|
|
2667
|
+
var execFileAsync = promisify(execFile);
|
|
2668
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
2669
|
+
"add",
|
|
2670
|
+
"implement",
|
|
2671
|
+
"create",
|
|
2672
|
+
"support",
|
|
2673
|
+
"with",
|
|
2674
|
+
"the",
|
|
2675
|
+
"a",
|
|
2676
|
+
"an",
|
|
2677
|
+
"for",
|
|
2678
|
+
"in",
|
|
2679
|
+
"to",
|
|
2680
|
+
"and",
|
|
2681
|
+
"of",
|
|
2682
|
+
"on",
|
|
2683
|
+
"from",
|
|
2684
|
+
"using",
|
|
2685
|
+
"via",
|
|
2686
|
+
"into",
|
|
2687
|
+
"by",
|
|
2688
|
+
"our",
|
|
2689
|
+
"new",
|
|
2690
|
+
"use",
|
|
2691
|
+
"make",
|
|
2692
|
+
"allow",
|
|
2693
|
+
"enable",
|
|
2694
|
+
"provide",
|
|
2695
|
+
"setup",
|
|
2696
|
+
"set"
|
|
2697
|
+
]);
|
|
2698
|
+
var DOMAIN_KEYWORDS = {
|
|
2699
|
+
authentication: ["auth", "oauth", "login", "logout", "session", "jwt", "token", "credential", "sso", "password"],
|
|
2700
|
+
session: ["session", "cookie", "cookies", "expiry", "refresh", "token"],
|
|
2701
|
+
"user-identity": ["user", "account", "profile", "identity", "member", "role", "permission"],
|
|
2702
|
+
database: ["db", "database", "sql", "postgres", "sqlite", "migration", "schema", "table", "query"],
|
|
2703
|
+
storage: ["storage", "s3", "bucket", "file", "upload", "disk", "blob"],
|
|
2704
|
+
api: ["api", "endpoint", "http", "rest", "route", "controller", "middleware", "graphql"],
|
|
2705
|
+
caching: ["cache", "redis", "memcached", "ttl", "invalidation"],
|
|
2706
|
+
billing: ["payment", "stripe", "checkout", "billing", "subscription", "invoice", "credit"],
|
|
2707
|
+
"data-processing": ["import", "export", "csv", "bulk", "batch", "stream", "pipeline"]
|
|
2708
|
+
};
|
|
2709
|
+
function tokenizeGoal(goal) {
|
|
2710
|
+
return goal.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
|
|
2711
|
+
}
|
|
2712
|
+
function inferLikelyDomains(tokens, knowledgeTags) {
|
|
2713
|
+
const domains = /* @__PURE__ */ new Set();
|
|
2714
|
+
for (const token of tokens) {
|
|
2715
|
+
for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
|
|
2716
|
+
if (keywords.includes(token) || token.includes(domain)) {
|
|
2717
|
+
domains.add(domain);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
for (const tag of knowledgeTags) {
|
|
2722
|
+
const norm = tag.toLowerCase().trim();
|
|
2723
|
+
if (DOMAIN_KEYWORDS[norm]) {
|
|
2724
|
+
domains.add(norm);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return Array.from(domains);
|
|
2728
|
+
}
|
|
2729
|
+
function classifyUnknown(unknown, goalTokens, domainCues) {
|
|
2730
|
+
const text = `${unknown.id} ${unknown.statement || ""} ${unknown.question || unknown.next?.question || ""}`.toLowerCase();
|
|
2731
|
+
if (unknown.blocking === true) {
|
|
2732
|
+
const isDecision2 = /\b(should|whether|how to handle|what happens when|policy|strategy|linking|collision|merge|choice)\b/i.test(text);
|
|
2733
|
+
return {
|
|
2734
|
+
classification: "BLOCKING",
|
|
2735
|
+
isDecision: isDecision2 || true,
|
|
2736
|
+
reason: unknown.reason || "Blocking unknown identified for this goal"
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
if (unknown.blocking === false) {
|
|
2740
|
+
const isDecision2 = /\b(should|whether|how to handle|what happens when|policy|strategy|choice)\b/i.test(text);
|
|
2741
|
+
return {
|
|
2742
|
+
classification: "NON_BLOCKING",
|
|
2743
|
+
isDecision: isDecision2,
|
|
2744
|
+
reason: "Operational or instrumentation detail not blocking core functionality"
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
const isNonBlocking = /\b(analytics|telemetry|metric|event name|logging level|log format|button color|styling|theme)\b/i.test(text);
|
|
2748
|
+
if (isNonBlocking) {
|
|
2749
|
+
return {
|
|
2750
|
+
classification: "NON_BLOCKING",
|
|
2751
|
+
isDecision: /\b(should|which name|choice)\b/i.test(text),
|
|
2752
|
+
reason: "Operational or instrumentation detail not blocking core functionality"
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
const isDecision = /\b(should|whether|how to handle|what happens when|policy|strategy|linking|collision|merge)\b/i.test(text);
|
|
2756
|
+
const isBlockingCandidate = /\b(link|linking|collision|duplicate|merge|security|permission|storage-engine|protocol|auth-flow|credentials)\b/i.test(text);
|
|
2757
|
+
const overlapsGoal = goalTokens.some((t) => text.includes(t)) || domainCues.some((d) => text.includes(d));
|
|
2758
|
+
if (isBlockingCandidate && (overlapsGoal || unknown.reason === "UNRESOLVABLE_LOCALLY")) {
|
|
2759
|
+
return {
|
|
2760
|
+
classification: "BLOCKING",
|
|
2761
|
+
isDecision: true,
|
|
2762
|
+
reason: "Unresolved architectural or behavioral decision critical to goal implementation"
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
return {
|
|
2766
|
+
classification: "IMPORTANT",
|
|
2767
|
+
isDecision,
|
|
2768
|
+
reason: isDecision ? "Product or behavior decision requiring attention" : "Important implementation detail"
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
function prioritizeQuestions(rawQuestions, blockingUnknowns) {
|
|
2772
|
+
const consolidated = /* @__PURE__ */ new Map();
|
|
2773
|
+
for (const rq of rawQuestions) {
|
|
2774
|
+
const qText = rq.question.trim();
|
|
2775
|
+
if (!qText) continue;
|
|
2776
|
+
const topic = rq.targetTopic || qText.toLowerCase().replace(/[^a-z0-9]/g, " ").slice(0, 30);
|
|
2777
|
+
let expectedResolutionValue = 1;
|
|
2778
|
+
if (rq.isBlocking) expectedResolutionValue += 2;
|
|
2779
|
+
if (rq.outcomeScore && rq.outcomeScore > 0) expectedResolutionValue += 1;
|
|
2780
|
+
if (rq.outcomeScore && rq.outcomeScore < 0) expectedResolutionValue -= 0.5;
|
|
2781
|
+
const interactionCost = 1;
|
|
2782
|
+
const priorityScore = Math.max(0.1, expectedResolutionValue) / interactionCost;
|
|
2783
|
+
const existing = consolidated.get(topic);
|
|
2784
|
+
if (!existing || priorityScore > (existing.priorityScore ?? 0)) {
|
|
2785
|
+
consolidated.set(topic, {
|
|
2786
|
+
question: qText,
|
|
2787
|
+
reason: rq.reason,
|
|
2788
|
+
targetTopic: rq.targetTopic,
|
|
2789
|
+
expectedResolutionValue,
|
|
2790
|
+
interactionCost,
|
|
2791
|
+
priorityScore
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return Array.from(consolidated.values()).sort((a, b) => (b.priorityScore ?? 0) - (a.priorityScore ?? 0));
|
|
2796
|
+
}
|
|
2797
|
+
async function executePreflight(options, storage, retriever, index) {
|
|
2798
|
+
const goal = options.goal.trim();
|
|
2799
|
+
const explicitPositive = (options.positive ?? []).map((p) => p.trim().toLowerCase());
|
|
2800
|
+
const negative = (options.negative ?? []).map((n) => n.trim().toLowerCase());
|
|
2801
|
+
const scope = options.scope ?? "project";
|
|
2802
|
+
const goalTokens = tokenizeGoal(goal);
|
|
2803
|
+
const allKnowledge = await storage.getAllKnowledge();
|
|
2804
|
+
const allTags = allKnowledge.flatMap((k) => k.tags ?? []);
|
|
2805
|
+
const likelyDomains = inferLikelyDomains(goalTokens, allTags);
|
|
2806
|
+
const contextualPositive = Array.from(/* @__PURE__ */ new Set([...explicitPositive, ...likelyDomains, ...goalTokens]));
|
|
2807
|
+
let candidatePool = [];
|
|
2808
|
+
if (retriever) {
|
|
2809
|
+
const jevCandidates = await retriever.lookup({
|
|
2810
|
+
query: goal,
|
|
2811
|
+
positive: contextualPositive,
|
|
2812
|
+
negative,
|
|
2813
|
+
scope,
|
|
2814
|
+
limit: options.maxCandidates ?? 25,
|
|
2815
|
+
includeRejected: true
|
|
2816
|
+
});
|
|
2817
|
+
for (const r of jevCandidates) {
|
|
2818
|
+
if (r.knowledge) candidatePool.push(r.knowledge);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (candidatePool.length === 0) {
|
|
2822
|
+
candidatePool = allKnowledge.filter((k) => k.status !== "invalid" && k.status !== "superseded");
|
|
2823
|
+
} else {
|
|
2824
|
+
for (const k of allKnowledge) {
|
|
2825
|
+
if (!candidatePool.some((c) => c.id === k.id) && k.status !== "invalid" && k.status !== "superseded") {
|
|
2826
|
+
const matchesToken = goalTokens.some((t) => k.id.toLowerCase().includes(t) || k.statement.toLowerCase().includes(t));
|
|
2827
|
+
const matchesCues = k.positive.some((p) => contextualPositive.includes(p));
|
|
2828
|
+
if (matchesToken || matchesCues) {
|
|
2829
|
+
candidatePool.push(k);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
const jevResults = rerankCandidates(candidatePool, {
|
|
2835
|
+
query: goal,
|
|
2836
|
+
positive: contextualPositive,
|
|
2837
|
+
negative,
|
|
2838
|
+
scope
|
|
2839
|
+
});
|
|
2840
|
+
const matchingItems = [];
|
|
2841
|
+
const rejectedItems = [];
|
|
2842
|
+
for (const res of jevResults) {
|
|
2843
|
+
if (res.status === "REJECT") {
|
|
2844
|
+
const reason = res.explanation.negativeConflicts.length > 0 ? `Inapplicable: ${res.explanation.negativeConflicts.join(", ")} conflicts with current context` : res.explanation.reasons[0] || "Context mismatch";
|
|
2845
|
+
rejectedItems.push({ id: res.knowledge.id, reason });
|
|
2846
|
+
} else {
|
|
2847
|
+
matchingItems.push(res.knowledge);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
if (options.deep) {
|
|
2851
|
+
try {
|
|
2852
|
+
const { stdout: diffOutput } = await execFileAsync(
|
|
2853
|
+
"git",
|
|
2854
|
+
["diff", "--name-only", "HEAD~1"],
|
|
2855
|
+
{ cwd: storage.getBaseDir() ? path12.dirname(storage.getBaseDir()) : process.cwd() }
|
|
2856
|
+
);
|
|
2857
|
+
const changedFiles = diffOutput.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
2858
|
+
if (changedFiles.length > 0) {
|
|
2859
|
+
const staleness = checkKnowledgeListStaleness(matchingItems, changedFiles);
|
|
2860
|
+
for (const affected of staleness.affectedKnowledge) {
|
|
2861
|
+
const matchIdx = matchingItems.findIndex((m) => m.id === affected.id);
|
|
2862
|
+
if (matchIdx >= 0) {
|
|
2863
|
+
matchingItems[matchIdx] = affected;
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
} catch {
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
const constraints = [];
|
|
2871
|
+
const lessons = [];
|
|
2872
|
+
const pastFailures = [];
|
|
2873
|
+
const stale = [];
|
|
2874
|
+
const known = [];
|
|
2875
|
+
let conflictedCount = 0;
|
|
2876
|
+
const CONSTRAINT_PATTERN = /\b(must|must not|never|only|cannot|require|required|boundary|strictly|prohibited|forbidden)\b/i;
|
|
2877
|
+
const FAILURE_PATTERN = /(fail|bug|error|crash|duplicate write|race condition|pressure|leak|revert|timeout)/i;
|
|
2878
|
+
const rootDir = storage.getBaseDir() ? path12.dirname(storage.getBaseDir()) : process.cwd();
|
|
2879
|
+
for (const k of matchingItems) {
|
|
2880
|
+
const matchesContradiction = k.status === "contradicted" || k.contradicts && k.contradicts.some((cid) => matchingItems.some((m) => m.id === cid));
|
|
2881
|
+
if (matchesContradiction) {
|
|
2882
|
+
conflictedCount++;
|
|
2883
|
+
}
|
|
2884
|
+
let isStale = k.status === "needs_revalidation" || k.status === "stale";
|
|
2885
|
+
let staleReason = k.history?.[k.history.length - 1]?.reason || "Supporting source changed after last validation";
|
|
2886
|
+
const changedDeps = [];
|
|
2887
|
+
const candidateDeps = [...k.depends_on ?? [], ...k.evidence ?? []];
|
|
2888
|
+
const timestampStr = k.updated_at ?? k.updatedAt ?? k.createdAt;
|
|
2889
|
+
const knowledgeTime = new Date(timestampStr).getTime();
|
|
2890
|
+
for (const dep of candidateDeps) {
|
|
2891
|
+
const cleanPath = dep.replace(/^(file|code|test|doc):/, "").trim();
|
|
2892
|
+
if (!cleanPath) continue;
|
|
2893
|
+
const absPath = path12.isAbsolute(cleanPath) ? cleanPath : path12.resolve(rootDir, cleanPath);
|
|
2894
|
+
try {
|
|
2895
|
+
const stat4 = await fs7.stat(absPath);
|
|
2896
|
+
if (stat4.mtimeMs > knowledgeTime + 500) {
|
|
2897
|
+
isStale = true;
|
|
2898
|
+
changedDeps.push(cleanPath);
|
|
2899
|
+
staleReason = `Supporting source changed after last validation (${cleanPath})`;
|
|
2900
|
+
}
|
|
2901
|
+
} catch {
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
if (isStale) {
|
|
2905
|
+
stale.push({
|
|
2906
|
+
id: k.id,
|
|
2907
|
+
statement: k.statement,
|
|
2908
|
+
reason: staleReason,
|
|
2909
|
+
changedSources: changedDeps.length > 0 ? changedDeps : k.depends_on
|
|
2910
|
+
});
|
|
2911
|
+
continue;
|
|
2912
|
+
}
|
|
2913
|
+
const isConstraint = k.type === "rule" || k.type === "decision" || k.type === "convention" || CONSTRAINT_PATTERN.test(k.statement);
|
|
2914
|
+
if (isConstraint) {
|
|
2915
|
+
constraints.push({
|
|
2916
|
+
id: k.id,
|
|
2917
|
+
statement: k.statement,
|
|
2918
|
+
type: k.type,
|
|
2919
|
+
confidence: k.confidence,
|
|
2920
|
+
confidenceTier: k.confidenceTier,
|
|
2921
|
+
source: k.evidence?.[0]
|
|
2922
|
+
});
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
const isFailure = k.type === "failure" || k.statement.startsWith("[FAILURE]") || k.statement.startsWith("[REVERT]") || FAILURE_PATTERN.test(k.statement) || k.metrics.failed_outcomes > 0;
|
|
2926
|
+
if (isFailure) {
|
|
2927
|
+
pastFailures.push({
|
|
2928
|
+
id: k.id,
|
|
2929
|
+
statement: k.statement,
|
|
2930
|
+
context: k.context || {
|
|
2931
|
+
positive: k.positive,
|
|
2932
|
+
negative: k.negative
|
|
2933
|
+
},
|
|
2934
|
+
failure: k.statement,
|
|
2935
|
+
lesson: k.lesson || k.reasoning || k.resolves?.[0],
|
|
2936
|
+
evidence: k.evidence
|
|
2937
|
+
});
|
|
2938
|
+
continue;
|
|
2939
|
+
}
|
|
2940
|
+
const isLesson = k.type === "lesson" && (k.id.includes("lesson") || k.tags?.includes("lesson") || k.statement.toLowerCase().includes("lesson")) || k.statement.toLowerCase().startsWith("lesson:") || k.statement.toLowerCase().includes("lesson:");
|
|
2941
|
+
if (isLesson) {
|
|
2942
|
+
lessons.push({
|
|
2943
|
+
id: k.id,
|
|
2944
|
+
statement: k.statement,
|
|
2945
|
+
confidence: k.confidence,
|
|
2946
|
+
domain: k.tags?.[0],
|
|
2947
|
+
evidence: k.evidence
|
|
2948
|
+
});
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
if (k.status === "active" || k.stage === "active" || k.stage === "probation" || !k.status) {
|
|
2952
|
+
known.push({
|
|
2953
|
+
id: k.id,
|
|
2954
|
+
statement: k.statement,
|
|
2955
|
+
confidence: k.confidence,
|
|
2956
|
+
confidenceTier: k.confidenceTier,
|
|
2957
|
+
stage: k.stage
|
|
2958
|
+
});
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
const unknownsFromStorage = await storage.getAllUnknowns();
|
|
2962
|
+
const unknownKnowledge = allKnowledge.filter((k) => k.type === "unknown");
|
|
2963
|
+
const unifiedUnknowns = [];
|
|
2964
|
+
const seenUnknownIds = /* @__PURE__ */ new Set();
|
|
2965
|
+
for (const u of unknownsFromStorage) {
|
|
2966
|
+
seenUnknownIds.add(u.id);
|
|
2967
|
+
unifiedUnknowns.push({
|
|
2968
|
+
id: u.id,
|
|
2969
|
+
statement: u.statement || u.id,
|
|
2970
|
+
reason: u.reason,
|
|
2971
|
+
question: u.question || u.next?.question,
|
|
2972
|
+
next: u.next,
|
|
2973
|
+
blocking: u.blocking,
|
|
2974
|
+
positive: u.positive,
|
|
2975
|
+
negative: u.negative,
|
|
2976
|
+
checked: u.checked
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
for (const k of unknownKnowledge) {
|
|
2980
|
+
if (!seenUnknownIds.has(k.id)) {
|
|
2981
|
+
seenUnknownIds.add(k.id);
|
|
2982
|
+
unifiedUnknowns.push({
|
|
2983
|
+
id: k.id,
|
|
2984
|
+
statement: k.statement,
|
|
2985
|
+
reason: k.history?.[0]?.reason,
|
|
2986
|
+
question: k.question || (k.statement.includes("?") ? k.statement : void 0),
|
|
2987
|
+
next: {
|
|
2988
|
+
type: "ask",
|
|
2989
|
+
target: k.id,
|
|
2990
|
+
question: k.question || k.statement
|
|
2991
|
+
},
|
|
2992
|
+
blocking: k.blocking,
|
|
2993
|
+
positive: k.positive,
|
|
2994
|
+
negative: k.negative,
|
|
2995
|
+
checked: k.depends_on || k.evidence
|
|
2996
|
+
});
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
const unknowns = [];
|
|
3000
|
+
const missing = [];
|
|
3001
|
+
const rawQuestions = [];
|
|
3002
|
+
for (const u of unifiedUnknowns) {
|
|
3003
|
+
const text = `${u.id} ${u.statement || ""} ${u.question || ""}`.toLowerCase();
|
|
3004
|
+
const overlaps = goalTokens.some((t) => text.includes(t)) || likelyDomains.some((d) => text.includes(d)) || u.positive && u.positive.some((p) => contextualPositive.includes(p));
|
|
3005
|
+
if (u.negative && u.negative.some((n) => contextualPositive.includes(n))) {
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
3008
|
+
if (overlaps) {
|
|
3009
|
+
const { classification, isDecision, reason } = classifyUnknown(u, goalTokens, likelyDomains);
|
|
3010
|
+
const isBlocking = classification === "BLOCKING";
|
|
3011
|
+
unknowns.push({
|
|
3012
|
+
id: u.id,
|
|
3013
|
+
statement: u.statement,
|
|
3014
|
+
question: u.question || u.statement,
|
|
3015
|
+
classification,
|
|
3016
|
+
blocking: isBlocking,
|
|
3017
|
+
reason
|
|
3018
|
+
});
|
|
3019
|
+
if (isDecision) {
|
|
3020
|
+
missing.push({
|
|
3021
|
+
type: "decision",
|
|
3022
|
+
subject: u.id,
|
|
3023
|
+
question: u.question || u.statement,
|
|
3024
|
+
reason
|
|
3025
|
+
});
|
|
3026
|
+
} else {
|
|
3027
|
+
missing.push({
|
|
3028
|
+
type: "evidence",
|
|
3029
|
+
subject: u.id,
|
|
3030
|
+
question: u.question || u.statement,
|
|
3031
|
+
reason
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
const qText = u.question || (u.statement && u.statement.includes("?") ? u.statement : void 0);
|
|
3035
|
+
if (qText) {
|
|
3036
|
+
rawQuestions.push({
|
|
3037
|
+
question: qText,
|
|
3038
|
+
reason: `Unknown: ${u.id} (${reason})`,
|
|
3039
|
+
targetTopic: u.id,
|
|
3040
|
+
isBlocking
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
const questionKnowledge = allKnowledge.filter((k) => k.type === "question");
|
|
3046
|
+
for (const qk of questionKnowledge) {
|
|
3047
|
+
const matchesCues = qk.positive.some((p) => contextualPositive.includes(p));
|
|
3048
|
+
const conflicts = qk.negative.some((n) => negative.includes(n));
|
|
3049
|
+
if (matchesCues && !conflicts && qk.question) {
|
|
3050
|
+
rawQuestions.push({
|
|
3051
|
+
question: qk.question,
|
|
3052
|
+
reason: `Learned question for [${qk.positive.join(", ")}] uncertainty`,
|
|
3053
|
+
targetTopic: qk.resolves?.[0] || qk.id,
|
|
3054
|
+
outcomeScore: (qk.metrics?.successful_outcomes ?? 0) - (qk.metrics?.failed_outcomes ?? 0)
|
|
3055
|
+
});
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
const questions = prioritizeQuestions(rawQuestions, unknowns);
|
|
3059
|
+
const receipts = await storage.getAllReceipts();
|
|
3060
|
+
const alreadyInvestigated = [];
|
|
3061
|
+
const investigatedSources = /* @__PURE__ */ new Set();
|
|
3062
|
+
for (const r of receipts) {
|
|
3063
|
+
const qTokens = r.question.toLowerCase().split(/\s+/).filter(Boolean);
|
|
3064
|
+
const overlapsGoal = qTokens.some((t) => goalTokens.includes(t));
|
|
3065
|
+
const overlapsCues = r.positive && r.positive.some((p) => contextualPositive.includes(p));
|
|
3066
|
+
if (overlapsGoal || overlapsCues) {
|
|
3067
|
+
for (const checked of r.checked) {
|
|
3068
|
+
investigatedSources.add(checked.toLowerCase());
|
|
3069
|
+
alreadyInvestigated.push({
|
|
3070
|
+
source: checked,
|
|
3071
|
+
result: r.findings[0] || r.rejected[0]?.reason || "Did not establish requested behavior",
|
|
3072
|
+
nextSuggestion: "ASK_USER"
|
|
3073
|
+
});
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
let readiness = "READY";
|
|
3078
|
+
const totalRelevantKnowledge = known.length + constraints.length + lessons.length + pastFailures.length + stale.length;
|
|
3079
|
+
const hasBlockingUnknown = unknowns.some((u) => u.blocking);
|
|
3080
|
+
const hasMissingDecision = missing.some((m) => m.type === "decision");
|
|
3081
|
+
if (totalRelevantKnowledge === 0 && unknowns.length === 0) {
|
|
3082
|
+
readiness = "INSUFFICIENT_KNOWLEDGE";
|
|
3083
|
+
} else if (conflictedCount > 0) {
|
|
3084
|
+
readiness = "CONFLICTED";
|
|
3085
|
+
} else if (hasBlockingUnknown || hasMissingDecision) {
|
|
3086
|
+
readiness = "NEEDS_CLARIFICATION";
|
|
3087
|
+
} else if (stale.length > 0) {
|
|
3088
|
+
readiness = "NEEDS_REVALIDATION";
|
|
3089
|
+
} else {
|
|
3090
|
+
readiness = "READY";
|
|
3091
|
+
}
|
|
3092
|
+
const next = [];
|
|
3093
|
+
for (const m of missing) {
|
|
3094
|
+
if (m.type === "decision") {
|
|
3095
|
+
next.push({
|
|
3096
|
+
type: "ASK_USER",
|
|
3097
|
+
target: m.subject,
|
|
3098
|
+
reason: m.question || m.reason,
|
|
3099
|
+
relativeCost: 1,
|
|
3100
|
+
expectedInfoGain: 0.95
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
for (const s of stale) {
|
|
3105
|
+
next.push({
|
|
3106
|
+
type: "REVALIDATE",
|
|
3107
|
+
target: s.id,
|
|
3108
|
+
reason: s.reason,
|
|
3109
|
+
relativeCost: 2,
|
|
3110
|
+
expectedInfoGain: 0.85
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
for (const m of missing) {
|
|
3114
|
+
if (m.type === "evidence") {
|
|
3115
|
+
const wasChecked = investigatedSources.has(m.subject.toLowerCase()) || alreadyInvestigated.length > 0;
|
|
3116
|
+
if (wasChecked) {
|
|
3117
|
+
if (!next.some((n) => n.type === "ASK_USER")) {
|
|
3118
|
+
next.push({
|
|
3119
|
+
type: "ASK_USER",
|
|
3120
|
+
target: m.subject,
|
|
3121
|
+
reason: `Already investigated (${Array.from(investigatedSources).join(", ")}); did not establish requested behavior. Clarification required.`,
|
|
3122
|
+
relativeCost: 1,
|
|
3123
|
+
expectedInfoGain: 0.9
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
} else {
|
|
3127
|
+
const isFilePath = m.subject.includes("/") || m.subject.includes(".");
|
|
3128
|
+
next.push({
|
|
3129
|
+
type: isFilePath ? "INSPECT_FILE" : "SEARCH_REPOSITORY",
|
|
3130
|
+
target: m.subject,
|
|
3131
|
+
reason: m.reason,
|
|
3132
|
+
relativeCost: isFilePath ? 2 : 2.5,
|
|
3133
|
+
expectedInfoGain: 0.8
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
if (alreadyInvestigated.length > 0 && !next.some((n) => n.type === "ASK_USER")) {
|
|
3139
|
+
next.push({
|
|
3140
|
+
type: "ASK_USER",
|
|
3141
|
+
target: alreadyInvestigated[0].source,
|
|
3142
|
+
reason: `Already investigated ${alreadyInvestigated[0].source}; clarification required`,
|
|
3143
|
+
relativeCost: 1,
|
|
3144
|
+
expectedInfoGain: 0.9
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
if (next.length === 0 && known.length > 0) {
|
|
3148
|
+
next.push({
|
|
3149
|
+
type: "USE_KNOWLEDGE",
|
|
3150
|
+
target: known[0].id,
|
|
3151
|
+
reason: "Proceed with implementation using verified knowledge and constraints",
|
|
3152
|
+
relativeCost: 1,
|
|
3153
|
+
expectedInfoGain: 1
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
const knowledgeImpact = {
|
|
3157
|
+
relevantKnowledge: totalRelevantKnowledge,
|
|
3158
|
+
validated: known.filter((k) => k.confidenceTier === "high").length + constraints.length,
|
|
3159
|
+
needsRevalidation: stale.length,
|
|
3160
|
+
conflicted: conflictedCount,
|
|
3161
|
+
blockingUnknowns: unknowns.filter((u) => u.blocking).length,
|
|
3162
|
+
relevantFailures: pastFailures.length
|
|
3163
|
+
};
|
|
3164
|
+
const similarWork = [];
|
|
3165
|
+
for (const l of lessons) {
|
|
3166
|
+
similarWork.push({
|
|
3167
|
+
topic: l.domain || l.id,
|
|
3168
|
+
lesson: l.statement
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
for (const f of pastFailures) {
|
|
3172
|
+
similarWork.push({
|
|
3173
|
+
topic: f.id,
|
|
3174
|
+
failure: f.statement
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
await storage.appendMetricEvent({
|
|
3178
|
+
type: "preflight",
|
|
3179
|
+
goal,
|
|
3180
|
+
readiness,
|
|
3181
|
+
blockingUnknownsCount: knowledgeImpact.blockingUnknowns,
|
|
3182
|
+
staleKnowledgeCount: stale.length,
|
|
3183
|
+
pastFailuresCount: pastFailures.length,
|
|
3184
|
+
questionsCount: questions.length,
|
|
3185
|
+
repeatedChecksAvoidedCount: alreadyInvestigated.length,
|
|
3186
|
+
timestamp: Date.now()
|
|
3187
|
+
});
|
|
3188
|
+
return {
|
|
3189
|
+
goal,
|
|
3190
|
+
readiness,
|
|
3191
|
+
known,
|
|
3192
|
+
constraints,
|
|
3193
|
+
lessons,
|
|
3194
|
+
past_failures: pastFailures,
|
|
3195
|
+
unknowns,
|
|
3196
|
+
stale,
|
|
3197
|
+
missing,
|
|
3198
|
+
questions,
|
|
3199
|
+
likely_domains: likelyDomains,
|
|
3200
|
+
similar_work: similarWork.length > 0 ? similarWork.slice(0, 5) : void 0,
|
|
3201
|
+
already_investigated: alreadyInvestigated.length > 0 ? alreadyInvestigated : void 0,
|
|
3202
|
+
knowledge_impact: knowledgeImpact,
|
|
3203
|
+
next
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
|
|
2226
3207
|
// src/core/knowledge.ts
|
|
2227
3208
|
import { createHash } from "crypto";
|
|
2228
3209
|
|
|
@@ -2369,7 +3350,7 @@ function createKnowledge(params) {
|
|
|
2369
3350
|
type: params.type ?? "lesson",
|
|
2370
3351
|
status,
|
|
2371
3352
|
stage: initialStage,
|
|
2372
|
-
statement: params.statement.trim(),
|
|
3353
|
+
statement: (params.statement ?? params.question ?? params.id).trim(),
|
|
2373
3354
|
positive: Array.from(new Set(params.positive?.map((p) => p.trim().toLowerCase()) ?? [])),
|
|
2374
3355
|
negative: Array.from(new Set(params.negative?.map((n) => n.trim().toLowerCase()) ?? [])),
|
|
2375
3356
|
relations: Array.from(new Set(params.relations?.map((r) => r.trim()) ?? [])),
|
|
@@ -2399,6 +3380,11 @@ function createKnowledge(params) {
|
|
|
2399
3380
|
},
|
|
2400
3381
|
history,
|
|
2401
3382
|
value: 0,
|
|
3383
|
+
lesson: params.lesson,
|
|
3384
|
+
failure_details: params.failure_details,
|
|
3385
|
+
context: params.context,
|
|
3386
|
+
contradicts: params.contradicts ? Array.from(new Set(params.contradicts)) : void 0,
|
|
3387
|
+
blocking: params.blocking,
|
|
2402
3388
|
createdAt: now,
|
|
2403
3389
|
updatedAt: now
|
|
2404
3390
|
};
|
|
@@ -3177,7 +4163,7 @@ function processUserCorrection(params) {
|
|
|
3177
4163
|
}
|
|
3178
4164
|
|
|
3179
4165
|
// src/scanner/engine.ts
|
|
3180
|
-
import * as
|
|
4166
|
+
import * as path16 from "path";
|
|
3181
4167
|
|
|
3182
4168
|
// src/core/relations.ts
|
|
3183
4169
|
var EvidenceGraph = class _EvidenceGraph {
|
|
@@ -3268,8 +4254,8 @@ var EvidenceGraph = class _EvidenceGraph {
|
|
|
3268
4254
|
};
|
|
3269
4255
|
|
|
3270
4256
|
// src/scanner/filesystem.ts
|
|
3271
|
-
import * as
|
|
3272
|
-
import * as
|
|
4257
|
+
import * as fs8 from "fs/promises";
|
|
4258
|
+
import * as path13 from "path";
|
|
3273
4259
|
var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
3274
4260
|
".git",
|
|
3275
4261
|
".bilmem",
|
|
@@ -3290,12 +4276,12 @@ var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
3290
4276
|
"temp"
|
|
3291
4277
|
]);
|
|
3292
4278
|
function normalizeRelPath(rel) {
|
|
3293
|
-
return rel.split(
|
|
4279
|
+
return rel.split(path13.sep).join("/");
|
|
3294
4280
|
}
|
|
3295
4281
|
async function loadGitignorePatterns(rootDir) {
|
|
3296
4282
|
try {
|
|
3297
|
-
const gitignorePath =
|
|
3298
|
-
const content = await
|
|
4283
|
+
const gitignorePath = path13.join(rootDir, ".gitignore");
|
|
4284
|
+
const content = await fs8.readFile(gitignorePath, "utf-8");
|
|
3299
4285
|
const regexes = [];
|
|
3300
4286
|
for (const rawLine of content.split("\n")) {
|
|
3301
4287
|
const line = rawLine.trim();
|
|
@@ -3343,7 +4329,7 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
|
|
|
3343
4329
|
return;
|
|
3344
4330
|
}
|
|
3345
4331
|
try {
|
|
3346
|
-
const real = await
|
|
4332
|
+
const real = await fs8.realpath(currentDir);
|
|
3347
4333
|
if (visitedRealPaths.has(real)) {
|
|
3348
4334
|
return;
|
|
3349
4335
|
}
|
|
@@ -3353,14 +4339,14 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
|
|
|
3353
4339
|
}
|
|
3354
4340
|
let entries;
|
|
3355
4341
|
try {
|
|
3356
|
-
entries = await
|
|
4342
|
+
entries = await fs8.readdir(currentDir, { withFileTypes: true });
|
|
3357
4343
|
} catch {
|
|
3358
4344
|
return;
|
|
3359
4345
|
}
|
|
3360
4346
|
for (const entry of entries) {
|
|
3361
4347
|
if (fileList.length >= maxFiles) break;
|
|
3362
|
-
const fullPath =
|
|
3363
|
-
const rawRelPath =
|
|
4348
|
+
const fullPath = path13.join(currentDir, entry.name);
|
|
4349
|
+
const rawRelPath = path13.relative(root, fullPath);
|
|
3364
4350
|
const relPath = normalizeRelPath(rawRelPath);
|
|
3365
4351
|
const lowerName = entry.name.toLowerCase();
|
|
3366
4352
|
if (gitignoreRules.some((rule) => rule.test(relPath))) {
|
|
@@ -3376,16 +4362,16 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
|
|
|
3376
4362
|
if (!includeHidden && entry.name.startsWith(".") && lowerName !== ".env.example") {
|
|
3377
4363
|
continue;
|
|
3378
4364
|
}
|
|
3379
|
-
const ext =
|
|
4365
|
+
const ext = path13.extname(entry.name).toLowerCase();
|
|
3380
4366
|
try {
|
|
3381
|
-
const
|
|
4367
|
+
const stat4 = await fs8.stat(fullPath);
|
|
3382
4368
|
const fileInfo = {
|
|
3383
4369
|
path: fullPath,
|
|
3384
4370
|
relativePath: relPath,
|
|
3385
4371
|
name: entry.name,
|
|
3386
4372
|
ext,
|
|
3387
|
-
size:
|
|
3388
|
-
mtimeMs:
|
|
4373
|
+
size: stat4.size,
|
|
4374
|
+
mtimeMs: stat4.mtimeMs,
|
|
3389
4375
|
isDirectory: false
|
|
3390
4376
|
};
|
|
3391
4377
|
if (!options.filter || options.filter(fileInfo)) {
|
|
@@ -3428,9 +4414,9 @@ var ProjectContext = class {
|
|
|
3428
4414
|
if (this.textCache.has(key)) {
|
|
3429
4415
|
return this.textCache.get(key);
|
|
3430
4416
|
}
|
|
3431
|
-
const fullPath =
|
|
4417
|
+
const fullPath = path13.resolve(this.rootDir, relativePath);
|
|
3432
4418
|
try {
|
|
3433
|
-
const handle = await
|
|
4419
|
+
const handle = await fs8.open(fullPath, "r");
|
|
3434
4420
|
const buffer = Buffer.alloc(maxBytes);
|
|
3435
4421
|
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
3436
4422
|
await handle.close();
|
|
@@ -3856,12 +4842,12 @@ async function analyzeProjectStructure(files, rootDir, registry = defaultRegistr
|
|
|
3856
4842
|
}
|
|
3857
4843
|
|
|
3858
4844
|
// src/scanner/markdown.ts
|
|
3859
|
-
import * as
|
|
3860
|
-
import * as
|
|
3861
|
-
import
|
|
4845
|
+
import * as fs9 from "fs/promises";
|
|
4846
|
+
import * as path14 from "path";
|
|
4847
|
+
import YAML3 from "yaml";
|
|
3862
4848
|
function parseMarkdownDocument(content, filePath, relativePath) {
|
|
3863
4849
|
const lines = content.split("\n");
|
|
3864
|
-
const baseName =
|
|
4850
|
+
const baseName = path14.basename(relativePath, ".md").toLowerCase();
|
|
3865
4851
|
let frontmatter = {};
|
|
3866
4852
|
const headings = [];
|
|
3867
4853
|
const alerts = [];
|
|
@@ -3890,7 +4876,7 @@ function parseMarkdownDocument(content, filePath, relativePath) {
|
|
|
3890
4876
|
bodyStartIndex = i + 1;
|
|
3891
4877
|
try {
|
|
3892
4878
|
const yamlText = frontmatterLines.join("\n");
|
|
3893
|
-
const parsed =
|
|
4879
|
+
const parsed = YAML3.parse(yamlText);
|
|
3894
4880
|
if (parsed && typeof parsed === "object") {
|
|
3895
4881
|
frontmatter = parsed;
|
|
3896
4882
|
}
|
|
@@ -4278,8 +5264,8 @@ var BUILTIN_MARKDOWN_EXTRACTORS = [
|
|
|
4278
5264
|
if (!target.startsWith("http://") && !target.startsWith("https://") && !target.startsWith("mailto:") && (target.startsWith(".") || target.includes("/") || /\.(md|ts|js|json|ya?ml|toml|rs|go|py)$/i.test(target))) {
|
|
4279
5265
|
const rawTarget = target.split("#")[0].split("?")[0].trim();
|
|
4280
5266
|
if (rawTarget) {
|
|
4281
|
-
const dir =
|
|
4282
|
-
const resolved = dir === "." ||
|
|
5267
|
+
const dir = path14.dirname(doc.relativePath);
|
|
5268
|
+
const resolved = dir === "." || path14.isAbsolute(rawTarget) ? rawTarget.replace(/^\.\//, "") : path14.normalize(path14.join(dir, rawTarget));
|
|
4283
5269
|
relations.push({
|
|
4284
5270
|
from: doc.relativePath,
|
|
4285
5271
|
to: resolved,
|
|
@@ -4332,7 +5318,7 @@ for (const ext of BUILTIN_MARKDOWN_EXTRACTORS) {
|
|
|
4332
5318
|
async function parseMarkdownFile(filePath, relativePath, registry = defaultMarkdownRegistry) {
|
|
4333
5319
|
let content = "";
|
|
4334
5320
|
try {
|
|
4335
|
-
content = await
|
|
5321
|
+
content = await fs9.readFile(filePath, "utf-8");
|
|
4336
5322
|
} catch {
|
|
4337
5323
|
return { observations: [], relations: [] };
|
|
4338
5324
|
}
|
|
@@ -4341,11 +5327,11 @@ async function parseMarkdownFile(filePath, relativePath, registry = defaultMarkd
|
|
|
4341
5327
|
}
|
|
4342
5328
|
|
|
4343
5329
|
// src/scanner/git.ts
|
|
4344
|
-
import { execFile } from "child_process";
|
|
4345
|
-
import { promisify } from "util";
|
|
4346
|
-
var
|
|
5330
|
+
import { execFile as execFile2 } from "child_process";
|
|
5331
|
+
import { promisify as promisify2 } from "util";
|
|
5332
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
4347
5333
|
async function runGit(args, cwd) {
|
|
4348
|
-
const { stdout } = await
|
|
5334
|
+
const { stdout } = await execFileAsync2("git", args, {
|
|
4349
5335
|
cwd,
|
|
4350
5336
|
maxBuffer: 10 * 1024 * 1024
|
|
4351
5337
|
});
|
|
@@ -4539,9 +5525,9 @@ async function scanGitHistory(cwd, options = {}) {
|
|
|
4539
5525
|
}
|
|
4540
5526
|
|
|
4541
5527
|
// src/scanner/source.ts
|
|
4542
|
-
import * as
|
|
5528
|
+
import * as fs10 from "fs/promises";
|
|
4543
5529
|
import * as fsSync from "fs";
|
|
4544
|
-
import * as
|
|
5530
|
+
import * as path15 from "path";
|
|
4545
5531
|
import { createRequire } from "module";
|
|
4546
5532
|
import Parser from "web-tree-sitter";
|
|
4547
5533
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -4595,7 +5581,7 @@ function getWasmDir() {
|
|
|
4595
5581
|
try {
|
|
4596
5582
|
const req = createRequire(import.meta.url);
|
|
4597
5583
|
const pkgJson = req.resolve("tree-sitter-wasms/package.json");
|
|
4598
|
-
const outDir =
|
|
5584
|
+
const outDir = path15.join(path15.dirname(pkgJson), "out");
|
|
4599
5585
|
if (fsSync.existsSync(outDir)) {
|
|
4600
5586
|
wasmBaseDir = outDir;
|
|
4601
5587
|
return wasmBaseDir;
|
|
@@ -4603,8 +5589,8 @@ function getWasmDir() {
|
|
|
4603
5589
|
} catch {
|
|
4604
5590
|
}
|
|
4605
5591
|
const candidateDirs = [
|
|
4606
|
-
|
|
4607
|
-
|
|
5592
|
+
path15.resolve(process.cwd(), "node_modules/tree-sitter-wasms/out"),
|
|
5593
|
+
path15.resolve(process.cwd(), "../node_modules/tree-sitter-wasms/out")
|
|
4608
5594
|
];
|
|
4609
5595
|
for (const dir of candidateDirs) {
|
|
4610
5596
|
if (fsSync.existsSync(dir)) {
|
|
@@ -4617,7 +5603,7 @@ function getWasmDir() {
|
|
|
4617
5603
|
async function getParserForWasm(wasmName) {
|
|
4618
5604
|
const wasmDir = getWasmDir();
|
|
4619
5605
|
if (!wasmDir) return null;
|
|
4620
|
-
const wasmPath =
|
|
5606
|
+
const wasmPath = path15.join(wasmDir, wasmName);
|
|
4621
5607
|
if (!fsSync.existsSync(wasmPath)) return null;
|
|
4622
5608
|
try {
|
|
4623
5609
|
if (!parserInitialized) {
|
|
@@ -4976,7 +5962,7 @@ async function analyzeSourceFile(filePath, relativePath) {
|
|
|
4976
5962
|
const relations = [];
|
|
4977
5963
|
let content = "";
|
|
4978
5964
|
try {
|
|
4979
|
-
content = await
|
|
5965
|
+
content = await fs10.readFile(filePath, "utf-8");
|
|
4980
5966
|
} catch {
|
|
4981
5967
|
return { observations, relations };
|
|
4982
5968
|
}
|
|
@@ -4984,8 +5970,8 @@ async function analyzeSourceFile(filePath, relativePath) {
|
|
|
4984
5970
|
return { observations, relations };
|
|
4985
5971
|
}
|
|
4986
5972
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4987
|
-
const baseName =
|
|
4988
|
-
const ext =
|
|
5973
|
+
const baseName = path15.basename(relativePath);
|
|
5974
|
+
const ext = path15.extname(relativePath).toLowerCase();
|
|
4989
5975
|
const wasmName = EXT_TO_WASM[ext];
|
|
4990
5976
|
if (wasmName) {
|
|
4991
5977
|
try {
|
|
@@ -5183,9 +6169,9 @@ async function scanRepository(rootDir = process.cwd(), options = {}) {
|
|
|
5183
6169
|
const plannedActions = [];
|
|
5184
6170
|
const config = dryRun ? await storage.isInitialized() ? await storage.getConfig() : {
|
|
5185
6171
|
schemaVersion: 1,
|
|
5186
|
-
projectName:
|
|
6172
|
+
projectName: path16.basename(rootDir),
|
|
5187
6173
|
defaultScope: "project",
|
|
5188
|
-
dataDir:
|
|
6174
|
+
dataDir: path16.join(rootDir, ".bilmem")
|
|
5189
6175
|
} : await storage.init();
|
|
5190
6176
|
const incremental = options.incremental ?? false;
|
|
5191
6177
|
const lastScanCommit = incremental ? config.lastScanCommit : void 0;
|
|
@@ -5258,7 +6244,7 @@ async function scanRepository(rootDir = process.cwd(), options = {}) {
|
|
|
5258
6244
|
];
|
|
5259
6245
|
for (const o of mdObs) {
|
|
5260
6246
|
if (o.source.includes(":adr") || o.claim.startsWith("[WARNING]") || o.claim.startsWith("[CAUTION]")) {
|
|
5261
|
-
const id = `decision.${
|
|
6247
|
+
const id = `decision.${path16.basename(o.provenance.path || "doc", ".md")}.${o.provenance.line || "adr"}`;
|
|
5262
6248
|
candidateLessons.push({
|
|
5263
6249
|
id,
|
|
5264
6250
|
type: "decision",
|
|
@@ -5492,8 +6478,34 @@ async function computeStats(storage, events, options) {
|
|
|
5492
6478
|
}
|
|
5493
6479
|
};
|
|
5494
6480
|
const actions = {};
|
|
6481
|
+
const preflight = {
|
|
6482
|
+
runs: 0,
|
|
6483
|
+
ready: 0,
|
|
6484
|
+
needsClarification: 0,
|
|
6485
|
+
needsRevalidation: 0,
|
|
6486
|
+
conflicted: 0,
|
|
6487
|
+
insufficientKnowledge: 0,
|
|
6488
|
+
blockingUnknownsFound: 0,
|
|
6489
|
+
staleKnowledgeFound: 0,
|
|
6490
|
+
pastFailuresSurfaced: 0,
|
|
6491
|
+
questionsSuggested: 0,
|
|
6492
|
+
repeatedChecksAvoided: 0
|
|
6493
|
+
};
|
|
5495
6494
|
for (const e of events) {
|
|
5496
6495
|
switch (e.type) {
|
|
6496
|
+
case "preflight":
|
|
6497
|
+
preflight.runs++;
|
|
6498
|
+
if (e.readiness === "READY") preflight.ready++;
|
|
6499
|
+
else if (e.readiness === "NEEDS_CLARIFICATION") preflight.needsClarification++;
|
|
6500
|
+
else if (e.readiness === "NEEDS_REVALIDATION") preflight.needsRevalidation++;
|
|
6501
|
+
else if (e.readiness === "CONFLICTED") preflight.conflicted++;
|
|
6502
|
+
else if (e.readiness === "INSUFFICIENT_KNOWLEDGE") preflight.insufficientKnowledge++;
|
|
6503
|
+
preflight.blockingUnknownsFound += e.blockingUnknownsCount || 0;
|
|
6504
|
+
preflight.staleKnowledgeFound += e.staleKnowledgeCount || 0;
|
|
6505
|
+
preflight.pastFailuresSurfaced += e.pastFailuresCount || 0;
|
|
6506
|
+
preflight.questionsSuggested += e.questionsCount || 0;
|
|
6507
|
+
preflight.repeatedChecksAvoided += e.repeatedChecksAvoidedCount || 0;
|
|
6508
|
+
break;
|
|
5497
6509
|
case "knowledge_created":
|
|
5498
6510
|
learning.new++;
|
|
5499
6511
|
break;
|
|
@@ -5664,7 +6676,8 @@ async function computeStats(storage, events, options) {
|
|
|
5664
6676
|
questionResolutionRate,
|
|
5665
6677
|
jevRejectionRate
|
|
5666
6678
|
},
|
|
5667
|
-
health
|
|
6679
|
+
health,
|
|
6680
|
+
preflight: preflight.runs > 0 ? preflight : void 0
|
|
5668
6681
|
};
|
|
5669
6682
|
}
|
|
5670
6683
|
async function computeKnowledgeStats(storage, knowledgeId, events) {
|
|
@@ -5733,9 +6746,9 @@ async function computeKnowledgeStats(storage, knowledgeId, events) {
|
|
|
5733
6746
|
}
|
|
5734
6747
|
|
|
5735
6748
|
// src/stats/filter.ts
|
|
5736
|
-
import { execFile as
|
|
5737
|
-
import { promisify as
|
|
5738
|
-
var
|
|
6749
|
+
import { execFile as execFile3 } from "child_process";
|
|
6750
|
+
import { promisify as promisify3 } from "util";
|
|
6751
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
5739
6752
|
async function resolveSinceTimestamp(since, projectDir = process.cwd()) {
|
|
5740
6753
|
const trimmed = since.trim();
|
|
5741
6754
|
const durationMatch = trimmed.match(/^(\d+)\s*([smhdw])$/i);
|
|
@@ -5767,7 +6780,7 @@ async function resolveSinceTimestamp(since, projectDir = process.cwd()) {
|
|
|
5767
6780
|
return parsedDate;
|
|
5768
6781
|
}
|
|
5769
6782
|
try {
|
|
5770
|
-
const { stdout } = await
|
|
6783
|
+
const { stdout } = await execFileAsync3(
|
|
5771
6784
|
"git",
|
|
5772
6785
|
["log", "-1", "--format=%ct", trimmed],
|
|
5773
6786
|
{ cwd: projectDir }
|
|
@@ -5811,17 +6824,56 @@ async function runStats(options = {}) {
|
|
|
5811
6824
|
|
|
5812
6825
|
// src/mcp/server.ts
|
|
5813
6826
|
function createBilmemMcpApp(options = {}) {
|
|
5814
|
-
const
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
6827
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
6828
|
+
let lastActiveDir = options.rootDir ? path17.resolve(options.rootDir) : null;
|
|
6829
|
+
function isSystemRoot(dir) {
|
|
6830
|
+
const resolved = path17.resolve(dir);
|
|
6831
|
+
return resolved === "/" || path17.parse(resolved).root === resolved;
|
|
6832
|
+
}
|
|
6833
|
+
function resolveTargetDir(requestedPath) {
|
|
6834
|
+
if (requestedPath && requestedPath.trim()) {
|
|
6835
|
+
const resolved = path17.resolve(requestedPath.trim());
|
|
6836
|
+
lastActiveDir = resolved;
|
|
6837
|
+
return resolved;
|
|
6838
|
+
}
|
|
6839
|
+
if (lastActiveDir) {
|
|
6840
|
+
return lastActiveDir;
|
|
6841
|
+
}
|
|
6842
|
+
if (process.env.BILMEM_PROJECT_DIR) {
|
|
6843
|
+
const envDir = path17.resolve(process.env.BILMEM_PROJECT_DIR);
|
|
6844
|
+
lastActiveDir = envDir;
|
|
6845
|
+
return envDir;
|
|
6846
|
+
}
|
|
6847
|
+
const cwd = process.cwd();
|
|
6848
|
+
if (cwd && !isSystemRoot(cwd)) {
|
|
6849
|
+
lastActiveDir = cwd;
|
|
6850
|
+
return cwd;
|
|
6851
|
+
}
|
|
6852
|
+
throw new Error(
|
|
6853
|
+
`No project directory specified and the MCP server was started in system root ('/'). Please provide the 'path' parameter in your tool call (e.g. { path: "/path/to/project", ... }).`
|
|
6854
|
+
);
|
|
6855
|
+
}
|
|
6856
|
+
async function getProjectContext(requestedPath) {
|
|
6857
|
+
const targetDir = resolveTargetDir(requestedPath);
|
|
6858
|
+
let ctx = contexts.get(targetDir);
|
|
6859
|
+
if (!ctx) {
|
|
6860
|
+
const storage = new Storage(targetDir);
|
|
6861
|
+
const index = new SearchIndex();
|
|
6862
|
+
ctx = {
|
|
6863
|
+
rootDir: targetDir,
|
|
6864
|
+
storage,
|
|
6865
|
+
index,
|
|
6866
|
+
initialized: false
|
|
6867
|
+
};
|
|
6868
|
+
contexts.set(targetDir, ctx);
|
|
5824
6869
|
}
|
|
6870
|
+
if (!ctx.initialized) {
|
|
6871
|
+
await ctx.storage.init();
|
|
6872
|
+
const all = await ctx.storage.getAllKnowledge();
|
|
6873
|
+
await ctx.index.buildIndex(all);
|
|
6874
|
+
ctx.initialized = true;
|
|
6875
|
+
}
|
|
6876
|
+
return ctx;
|
|
5825
6877
|
}
|
|
5826
6878
|
const app = new McpApp({
|
|
5827
6879
|
name: options.name ?? "bilmem",
|
|
@@ -5831,6 +6883,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5831
6883
|
name: "bilmem_resolve",
|
|
5832
6884
|
description: "Determine if enough is known to answer or act, what contextually does not apply, what is missing, and the cheapest next step to resolve uncertainty.",
|
|
5833
6885
|
inputSchema: {
|
|
6886
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
5834
6887
|
question: z.string().describe("The question, goal, or uncertainty to resolve"),
|
|
5835
6888
|
positive: z.array(z.string()).optional().describe("Applicable context cues (e.g. server-session, node, postgres)"),
|
|
5836
6889
|
negative: z.array(z.string()).optional().describe("Inapplicable context cues (e.g. stateless-jwt, browser, sqlite)"),
|
|
@@ -5838,8 +6891,8 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5838
6891
|
maxDepth: z.number().optional().describe("Max candidates to inspect (default: 5)")
|
|
5839
6892
|
},
|
|
5840
6893
|
handler: async (args) => {
|
|
5841
|
-
await
|
|
5842
|
-
const retriever = new Retriever(index, storage);
|
|
6894
|
+
const ctx = await getProjectContext(args.path);
|
|
6895
|
+
const retriever = new Retriever(ctx.index, ctx.storage);
|
|
5843
6896
|
const res = await resolveEpistemicQuery(
|
|
5844
6897
|
{
|
|
5845
6898
|
question: args.question,
|
|
@@ -5848,7 +6901,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5848
6901
|
scope: args.scope,
|
|
5849
6902
|
maxDepth: args.maxDepth
|
|
5850
6903
|
},
|
|
5851
|
-
storage,
|
|
6904
|
+
ctx.storage,
|
|
5852
6905
|
retriever
|
|
5853
6906
|
);
|
|
5854
6907
|
return {
|
|
@@ -5865,10 +6918,53 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5865
6918
|
};
|
|
5866
6919
|
}
|
|
5867
6920
|
});
|
|
6921
|
+
app.tool({
|
|
6922
|
+
name: "bilmem_preflight",
|
|
6923
|
+
description: "Determine what must be known before beginning a proposed goal (surfaces constraints, relevant lessons, past failures, stale knowledge, and blocking unknowns).",
|
|
6924
|
+
inputSchema: {
|
|
6925
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
6926
|
+
goal: z.string().describe("The proposed goal, feature, or work to analyze before implementation"),
|
|
6927
|
+
positive: z.array(z.string()).optional().describe("Applicable context cues (e.g. authentication, postgres)"),
|
|
6928
|
+
negative: z.array(z.string()).optional().describe("Inapplicable context cues (e.g. browser, sqlite)"),
|
|
6929
|
+
deep: z.boolean().optional().describe("Check targeted git diff / unindexed files for staleness")
|
|
6930
|
+
},
|
|
6931
|
+
handler: async (args) => {
|
|
6932
|
+
const ctx = await getProjectContext(args.path);
|
|
6933
|
+
const retriever = new Retriever(ctx.index, ctx.storage);
|
|
6934
|
+
const res = await executePreflight(
|
|
6935
|
+
{
|
|
6936
|
+
goal: args.goal,
|
|
6937
|
+
positive: args.positive,
|
|
6938
|
+
negative: args.negative,
|
|
6939
|
+
deep: args.deep
|
|
6940
|
+
},
|
|
6941
|
+
ctx.storage,
|
|
6942
|
+
retriever,
|
|
6943
|
+
ctx.index
|
|
6944
|
+
);
|
|
6945
|
+
return {
|
|
6946
|
+
goal: res.goal,
|
|
6947
|
+
readiness: res.readiness,
|
|
6948
|
+
known: res.known.slice(0, 5).map((k) => k.statement),
|
|
6949
|
+
constraints: res.constraints.map((c) => c.statement),
|
|
6950
|
+
lessons: res.lessons.slice(0, 5).map((l) => l.statement),
|
|
6951
|
+
pastFailures: res.past_failures.slice(0, 5).map((f) => f.statement),
|
|
6952
|
+
blockingUnknowns: res.unknowns.filter((u) => u.blocking).map((u) => u.question || u.statement || u.id),
|
|
6953
|
+
stale: res.stale.map((s) => s.id),
|
|
6954
|
+
questions: res.questions.slice(0, 5).map((q) => q.question),
|
|
6955
|
+
next: res.next.slice(0, 3).map((n) => ({
|
|
6956
|
+
type: n.type,
|
|
6957
|
+
target: n.target,
|
|
6958
|
+
reason: n.reason
|
|
6959
|
+
}))
|
|
6960
|
+
};
|
|
6961
|
+
}
|
|
6962
|
+
});
|
|
5868
6963
|
app.tool({
|
|
5869
6964
|
name: "bilmem_lookup",
|
|
5870
6965
|
description: "Lookup reusable knowledge, conventions, and lessons using JEV contextual reranking. Returns compact, highly applicable knowledge units.",
|
|
5871
6966
|
inputSchema: {
|
|
6967
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
5872
6968
|
query: z.string().optional().describe("Query text, intent keywords, or problem description"),
|
|
5873
6969
|
positive: z.array(z.string()).optional().describe("Contextual positive cues (e.g. node, esm, server-session)"),
|
|
5874
6970
|
negative: z.array(z.string()).optional().describe("Contextual negative cues (e.g. commonjs, browser)"),
|
|
@@ -5876,8 +6972,8 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5876
6972
|
limit: z.number().optional().describe("Maximum number of results to return (default 5)")
|
|
5877
6973
|
},
|
|
5878
6974
|
handler: async (args) => {
|
|
5879
|
-
await
|
|
5880
|
-
const retriever = new Retriever(index, storage);
|
|
6975
|
+
const ctx = await getProjectContext(args.path);
|
|
6976
|
+
const retriever = new Retriever(ctx.index, ctx.storage);
|
|
5881
6977
|
const results = await retriever.lookup({
|
|
5882
6978
|
query: args.query,
|
|
5883
6979
|
positive: args.positive,
|
|
@@ -5904,6 +7000,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5904
7000
|
name: "bilmem_observe",
|
|
5905
7001
|
description: "Record a new observation from code, git, test execution, or environment. Preserves provenance.",
|
|
5906
7002
|
inputSchema: {
|
|
7003
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
5907
7004
|
claim: z.string().describe("What was observed"),
|
|
5908
7005
|
source: z.string().describe("Source identifier (e.g. file:src/auth.ts#L42, git:commit:81ac23, user:correction)"),
|
|
5909
7006
|
confidence: z.number().optional().describe("Confidence 0.0 - 1.0 (default: 0.85)"),
|
|
@@ -5912,7 +7009,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5912
7009
|
details: z.record(z.any()).optional().describe("Optional structured metadata")
|
|
5913
7010
|
},
|
|
5914
7011
|
handler: async (args) => {
|
|
5915
|
-
await
|
|
7012
|
+
const ctx = await getProjectContext(args.path);
|
|
5916
7013
|
const obs = createObservation({
|
|
5917
7014
|
source: args.source,
|
|
5918
7015
|
claim: args.claim,
|
|
@@ -5926,10 +7023,10 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5926
7023
|
negative: args.negative,
|
|
5927
7024
|
details: args.details
|
|
5928
7025
|
});
|
|
5929
|
-
await storage.saveObservation(obs);
|
|
7026
|
+
await ctx.storage.saveObservation(obs);
|
|
5930
7027
|
const evidence = createEvidence(obs, "supports");
|
|
5931
|
-
await storage.saveEvidence(evidence);
|
|
5932
|
-
const existing = await storage.getAllKnowledge();
|
|
7028
|
+
await ctx.storage.saveEvidence(evidence);
|
|
7029
|
+
const existing = await ctx.storage.getAllKnowledge();
|
|
5933
7030
|
const rec = reconcileKnowledge(
|
|
5934
7031
|
{
|
|
5935
7032
|
statement: obs.claim,
|
|
@@ -5950,10 +7047,10 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5950
7047
|
evidence: [evidence.id, obs.source]
|
|
5951
7048
|
},
|
|
5952
7049
|
existing,
|
|
5953
|
-
storage
|
|
7050
|
+
ctx.storage
|
|
5954
7051
|
);
|
|
5955
|
-
await storage.saveKnowledge(updatedKnowledge);
|
|
5956
|
-
await index.upsert(updatedKnowledge);
|
|
7052
|
+
await ctx.storage.saveKnowledge(updatedKnowledge);
|
|
7053
|
+
await ctx.index.upsert(updatedKnowledge);
|
|
5957
7054
|
return {
|
|
5958
7055
|
observationId: obs.id,
|
|
5959
7056
|
evidenceId: evidence.id,
|
|
@@ -5967,6 +7064,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5967
7064
|
name: "bilmem_learn",
|
|
5968
7065
|
description: "Learn from a completed task trajectory or explicit user correction. Converts experiences into refined, reusable lessons.",
|
|
5969
7066
|
inputSchema: {
|
|
7067
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
5970
7068
|
goal: z.string().describe("The task or goal that was pursued"),
|
|
5971
7069
|
taskId: z.string().optional().describe("Task identifier"),
|
|
5972
7070
|
outcome: z.enum(["success", "failure", "reverted", "neutral"]).describe("Outcome of the task"),
|
|
@@ -5982,16 +7080,16 @@ function createBilmemMcpApp(options = {}) {
|
|
|
5982
7080
|
}).optional().describe("Explicit user correction, if any occurred")
|
|
5983
7081
|
},
|
|
5984
7082
|
handler: async (args) => {
|
|
5985
|
-
await
|
|
7083
|
+
const ctx = await getProjectContext(args.path);
|
|
5986
7084
|
if (args.userCorrection) {
|
|
5987
7085
|
const { observation, knowledge } = processUserCorrection(args.userCorrection);
|
|
5988
|
-
await storage.saveObservation(observation);
|
|
5989
|
-
await storage.saveEvidence(createEvidence(observation, "supports"));
|
|
5990
|
-
const existing = await storage.getAllKnowledge();
|
|
7086
|
+
await ctx.storage.saveObservation(observation);
|
|
7087
|
+
await ctx.storage.saveEvidence(createEvidence(observation, "supports"));
|
|
7088
|
+
const existing = await ctx.storage.getAllKnowledge();
|
|
5991
7089
|
const rec = reconcileKnowledge(knowledge, existing);
|
|
5992
|
-
const { updatedKnowledge } = applyReconciliation(rec, knowledge, existing, storage);
|
|
5993
|
-
await storage.saveKnowledge(updatedKnowledge);
|
|
5994
|
-
await index.upsert(updatedKnowledge);
|
|
7090
|
+
const { updatedKnowledge } = applyReconciliation(rec, knowledge, existing, ctx.storage);
|
|
7091
|
+
await ctx.storage.saveKnowledge(updatedKnowledge);
|
|
7092
|
+
await ctx.index.upsert(updatedKnowledge);
|
|
5995
7093
|
return {
|
|
5996
7094
|
type: "user_correction",
|
|
5997
7095
|
knowledgeId: updatedKnowledge.id,
|
|
@@ -6007,20 +7105,20 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6007
7105
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6008
7106
|
finalOutcome: args.outcome
|
|
6009
7107
|
};
|
|
6010
|
-
const existing = await storage.getAllKnowledge();
|
|
7108
|
+
const existing = await ctx.storage.getAllKnowledge();
|
|
6011
7109
|
const learningResult = learnFromTrajectory(trajectory, existing);
|
|
6012
7110
|
for (const obs of learningResult.observations) {
|
|
6013
|
-
await storage.saveObservation(obs);
|
|
7111
|
+
await ctx.storage.saveObservation(obs);
|
|
6014
7112
|
}
|
|
6015
7113
|
for (const ev of learningResult.evidence) {
|
|
6016
|
-
await storage.saveEvidence(ev);
|
|
7114
|
+
await ctx.storage.saveEvidence(ev);
|
|
6017
7115
|
}
|
|
6018
7116
|
let updatedKnowledge;
|
|
6019
7117
|
let action = "NEW";
|
|
6020
7118
|
for (const item of learningResult.reconciliations) {
|
|
6021
7119
|
if (item.appliedKnowledge) {
|
|
6022
|
-
await storage.saveKnowledge(item.appliedKnowledge);
|
|
6023
|
-
await index.upsert(item.appliedKnowledge);
|
|
7120
|
+
await ctx.storage.saveKnowledge(item.appliedKnowledge);
|
|
7121
|
+
await ctx.index.upsert(item.appliedKnowledge);
|
|
6024
7122
|
updatedKnowledge = item.appliedKnowledge;
|
|
6025
7123
|
action = item.action;
|
|
6026
7124
|
}
|
|
@@ -6032,9 +7130,9 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6032
7130
|
result: args.outcome,
|
|
6033
7131
|
context: { positive: updatedKnowledge.positive, negative: updatedKnowledge.negative }
|
|
6034
7132
|
});
|
|
6035
|
-
await storage.appendOutcome(out);
|
|
7133
|
+
await ctx.storage.appendOutcome(out);
|
|
6036
7134
|
applyOutcomeToKnowledge(updatedKnowledge, out);
|
|
6037
|
-
await storage.saveKnowledge(updatedKnowledge);
|
|
7135
|
+
await ctx.storage.saveKnowledge(updatedKnowledge);
|
|
6038
7136
|
return {
|
|
6039
7137
|
type: "trajectory_lesson",
|
|
6040
7138
|
action,
|
|
@@ -6056,12 +7154,13 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6056
7154
|
name: "bilmem_explain",
|
|
6057
7155
|
description: "Explain why a knowledge ID exists: statement, evidence provenance, confidence, support/contradiction history, and applicable context.",
|
|
6058
7156
|
inputSchema: {
|
|
7157
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
6059
7158
|
id: z.string().describe("The knowledge ID to explain"),
|
|
6060
7159
|
history: z.boolean().optional().describe("Whether to format lifecycle transition history (--history)")
|
|
6061
7160
|
},
|
|
6062
7161
|
handler: async (args) => {
|
|
6063
|
-
await
|
|
6064
|
-
const k = await storage.getKnowledge(args.id);
|
|
7162
|
+
const ctx = await getProjectContext(args.path);
|
|
7163
|
+
const k = await ctx.storage.getKnowledge(args.id);
|
|
6065
7164
|
if (!k) {
|
|
6066
7165
|
return { error: `Knowledge not found: "${args.id}"` };
|
|
6067
7166
|
}
|
|
@@ -6095,6 +7194,7 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6095
7194
|
name: "bilmem_unknown",
|
|
6096
7195
|
description: "Query or record first-class unknowns (NOT_OBSERVED, INSUFFICIENT_EVIDENCE, CONFLICTING_EVIDENCE, UNRESOLVABLE_LOCALLY) to prevent repeating useless investigations.",
|
|
6097
7196
|
inputSchema: {
|
|
7197
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
6098
7198
|
action: z.enum(["query", "check", "record", "resolve"]).describe("Action to perform"),
|
|
6099
7199
|
id: z.string().optional().describe("Unknown ID or query subject"),
|
|
6100
7200
|
reason: z.enum(["NOT_OBSERVED", "INSUFFICIENT_EVIDENCE", "CONFLICTING_EVIDENCE", "STALE", "UNRESOLVABLE_LOCALLY"]).optional(),
|
|
@@ -6103,16 +7203,16 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6103
7203
|
question: z.string().optional().describe("Clarifying question for the user or next agent")
|
|
6104
7204
|
},
|
|
6105
7205
|
handler: async (args) => {
|
|
6106
|
-
await
|
|
7206
|
+
const ctx = await getProjectContext(args.path);
|
|
6107
7207
|
if (args.action === "query") {
|
|
6108
|
-
const list = await storage.getAllUnknowns();
|
|
7208
|
+
const list = await ctx.storage.getAllUnknowns();
|
|
6109
7209
|
return {
|
|
6110
7210
|
count: list.length,
|
|
6111
7211
|
unknowns: list
|
|
6112
7212
|
};
|
|
6113
7213
|
}
|
|
6114
7214
|
if (args.action === "check") {
|
|
6115
|
-
const list = await storage.getAllUnknowns();
|
|
7215
|
+
const list = await ctx.storage.getAllUnknowns();
|
|
6116
7216
|
const checkResult = hasBeenInvestigated(list, args.id || "");
|
|
6117
7217
|
return checkResult;
|
|
6118
7218
|
}
|
|
@@ -6126,12 +7226,12 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6126
7226
|
checked: args.checked ?? [],
|
|
6127
7227
|
next: args.question ? { type: "ask", question: args.question } : void 0
|
|
6128
7228
|
});
|
|
6129
|
-
await storage.saveUnknown(record);
|
|
7229
|
+
await ctx.storage.saveUnknown(record);
|
|
6130
7230
|
return { success: true, recorded: record };
|
|
6131
7231
|
}
|
|
6132
7232
|
if (args.action === "resolve") {
|
|
6133
7233
|
if (!args.id) throw new Error("ID is required to resolve an unknown");
|
|
6134
|
-
const deleted = await storage.deleteUnknown(args.id);
|
|
7234
|
+
const deleted = await ctx.storage.deleteUnknown(args.id);
|
|
6135
7235
|
return { success: deleted, resolvedId: args.id };
|
|
6136
7236
|
}
|
|
6137
7237
|
return { error: "Unknown action" };
|
|
@@ -6141,16 +7241,16 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6141
7241
|
name: "bilmem_scan",
|
|
6142
7242
|
description: "Trigger a language-independent scan of the repository filesystem, markdown documentation, and git history.",
|
|
6143
7243
|
inputSchema: {
|
|
6144
|
-
path: z.string().optional().describe("
|
|
7244
|
+
path: z.string().optional().describe("Project root directory to scan (defaults to active workspace or current working directory)"),
|
|
6145
7245
|
incremental: z.boolean().optional().describe("Whether to scan incrementally from last scanned git commit")
|
|
6146
7246
|
},
|
|
6147
7247
|
handler: async (args) => {
|
|
6148
|
-
const
|
|
6149
|
-
const report = await scanRepository(
|
|
7248
|
+
const ctx = await getProjectContext(args.path);
|
|
7249
|
+
const report = await scanRepository(ctx.rootDir, {
|
|
6150
7250
|
incremental: args.incremental ?? false
|
|
6151
7251
|
});
|
|
6152
|
-
const all = await storage.getAllKnowledge();
|
|
6153
|
-
await index.buildIndex(all);
|
|
7252
|
+
const all = await ctx.storage.getAllKnowledge();
|
|
7253
|
+
await ctx.index.buildIndex(all);
|
|
6154
7254
|
return report;
|
|
6155
7255
|
}
|
|
6156
7256
|
});
|
|
@@ -6158,13 +7258,14 @@ function createBilmemMcpApp(options = {}) {
|
|
|
6158
7258
|
name: "bilmem_stats",
|
|
6159
7259
|
description: "Inspect local-first statistics, knowledge health, and effectiveness metrics (measuring reuse, investigation avoidance, and JEV accuracy). Read-only.",
|
|
6160
7260
|
inputSchema: {
|
|
7261
|
+
path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
|
|
6161
7262
|
since: z.string().optional().describe("Time window (e.g. 24h, 7d, 30d) or Git revision (e.g. HEAD~20, commit-sha)"),
|
|
6162
7263
|
knowledgeId: z.string().optional().describe("Specific knowledge ID to inspect single-item lifecycle and diagnostics")
|
|
6163
7264
|
},
|
|
6164
7265
|
handler: async (args) => {
|
|
6165
|
-
await
|
|
7266
|
+
const ctx = await getProjectContext(args.path);
|
|
6166
7267
|
const res = await runStats({
|
|
6167
|
-
rootDir,
|
|
7268
|
+
rootDir: ctx.rootDir,
|
|
6168
7269
|
since: args.since,
|
|
6169
7270
|
knowledgeId: args.knowledgeId
|
|
6170
7271
|
});
|
|
@@ -6269,6 +7370,26 @@ async function runDoctor(context) {
|
|
|
6269
7370
|
canFix = true;
|
|
6270
7371
|
}
|
|
6271
7372
|
}
|
|
7373
|
+
let skillStatus = "none";
|
|
7374
|
+
let skillDetails;
|
|
7375
|
+
if (state.skillPath) {
|
|
7376
|
+
if (state.skillInstalled) {
|
|
7377
|
+
if (state.skillVersion === CURRENT_INTEGRATION_VERSION) {
|
|
7378
|
+
skillStatus = "ok";
|
|
7379
|
+
} else {
|
|
7380
|
+
skillStatus = "outdated";
|
|
7381
|
+
skillDetails = `Version ${state.skillVersion || "legacy"} (current: ${CURRENT_INTEGRATION_VERSION})`;
|
|
7382
|
+
issuesCount++;
|
|
7383
|
+
canFix = true;
|
|
7384
|
+
}
|
|
7385
|
+
} else {
|
|
7386
|
+
skillStatus = "missing";
|
|
7387
|
+
if (state.detected) {
|
|
7388
|
+
issuesCount++;
|
|
7389
|
+
canFix = true;
|
|
7390
|
+
}
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
6272
7393
|
let cmdStatus = "ok";
|
|
6273
7394
|
let cmdDetails;
|
|
6274
7395
|
if (!commandResolvable) {
|
|
@@ -6282,9 +7403,11 @@ async function runDoctor(context) {
|
|
|
6282
7403
|
scope: context.scope,
|
|
6283
7404
|
mcp: { status: mcpStatus, details: mcpDetails },
|
|
6284
7405
|
instructions: { status: instStatus, details: instDetails },
|
|
7406
|
+
skill: state.skillPath ? { status: skillStatus, details: skillDetails } : void 0,
|
|
6285
7407
|
command: { status: cmdStatus, details: cmdDetails },
|
|
6286
7408
|
configPath: state.configPath,
|
|
6287
|
-
instructionPath: state.instructionPath
|
|
7409
|
+
instructionPath: state.instructionPath,
|
|
7410
|
+
skillPath: state.skillPath
|
|
6288
7411
|
});
|
|
6289
7412
|
}
|
|
6290
7413
|
if (!core.storage.initialized) {
|
|
@@ -6304,18 +7427,19 @@ async function runDoctorFix(context, dryRun = false) {
|
|
|
6304
7427
|
const agentsFixed = {};
|
|
6305
7428
|
if (!await storage.isInitialized()) {
|
|
6306
7429
|
if (!dryRun) {
|
|
6307
|
-
await storage.init({ projectName:
|
|
7430
|
+
await storage.init({ projectName: path18.basename(context.projectDir) });
|
|
6308
7431
|
}
|
|
6309
7432
|
coreFixed.push("Initialized .bilmem/ storage repository");
|
|
6310
7433
|
}
|
|
6311
7434
|
const adapters2 = getAllAdapters();
|
|
6312
7435
|
for (const adapter of adapters2) {
|
|
6313
7436
|
const state = await adapter.inspect(context);
|
|
6314
|
-
const shouldFix = state.detected || state.mcpConfigured || state.instructionInstalled;
|
|
7437
|
+
const shouldFix = state.detected || state.mcpConfigured || state.instructionInstalled || Boolean(state.skillInstalled);
|
|
6315
7438
|
if (!shouldFix) continue;
|
|
6316
7439
|
const needsMcpFix = !state.mcpConfigured || !state.mcpCommandMatches;
|
|
6317
7440
|
const needsInstFix = !state.instructionInstalled || state.instructionVersion !== CURRENT_INTEGRATION_VERSION;
|
|
6318
|
-
|
|
7441
|
+
const needsSkillFix = Boolean(state.skillPath) && (!state.skillInstalled || state.skillVersion !== CURRENT_INTEGRATION_VERSION);
|
|
7442
|
+
if (needsMcpFix || needsInstFix || needsSkillFix) {
|
|
6319
7443
|
const fixResult = await adapter.install(context, {
|
|
6320
7444
|
scope: context.scope,
|
|
6321
7445
|
dryRun
|
|
@@ -6422,6 +7546,10 @@ function formatDoctorReport(report) {
|
|
|
6422
7546
|
lines.push(` MCP ${mcpMark}${agent.mcp.details ? ` (${agent.mcp.details})` : ""}`);
|
|
6423
7547
|
const instMark = agent.instructions.status === "ok" ? "\u2713" : agent.instructions.status === "outdated" ? "!" : agent.instructions.status === "missing" ? "\u2717" : "\u25CB";
|
|
6424
7548
|
lines.push(` Instructions ${instMark}${agent.instructions.details ? ` (${agent.instructions.details})` : ""}`);
|
|
7549
|
+
if (agent.skillPath) {
|
|
7550
|
+
const skillMark = agent.skill?.status === "ok" ? "\u2713" : agent.skill?.status === "outdated" ? "!" : agent.skill?.status === "missing" ? "\u2717" : "\u25CB";
|
|
7551
|
+
lines.push(` Skill ${skillMark}${agent.skill?.details ? ` (${agent.skill?.details})` : ""}`);
|
|
7552
|
+
}
|
|
6425
7553
|
const cmdMark = agent.command.status === "ok" ? "\u2713" : "!";
|
|
6426
7554
|
lines.push(` Connection ${cmdMark}${agent.command.details ? ` (${agent.command.details})` : ""}`);
|
|
6427
7555
|
}
|
|
@@ -6466,7 +7594,7 @@ function formatDoctorFixResult(fixed) {
|
|
|
6466
7594
|
|
|
6467
7595
|
// src/install/index.ts
|
|
6468
7596
|
function createInstallContext(options) {
|
|
6469
|
-
const projectDir =
|
|
7597
|
+
const projectDir = path19.resolve(options.projectDir || process.cwd());
|
|
6470
7598
|
const scope = options.global || options.scope === "global" ? "global" : "project";
|
|
6471
7599
|
return {
|
|
6472
7600
|
projectDir,
|
|
@@ -6487,7 +7615,7 @@ async function handleInstallCommand(target, options = {}) {
|
|
|
6487
7615
|
if (target) {
|
|
6488
7616
|
const adapter = getAdapter(target);
|
|
6489
7617
|
if (!adapter) {
|
|
6490
|
-
const valid = getAllAdapters().map((a) => a.id).join(", ");
|
|
7618
|
+
const valid = [...getAllAdapters().map((a) => a.id), "gemini"].join(", ");
|
|
6491
7619
|
throw new Error(`Unknown agent/editor target "${target}". Supported targets: ${valid}`);
|
|
6492
7620
|
}
|
|
6493
7621
|
const result = await adapter.install(context, installOptions);
|
|
@@ -6569,7 +7697,7 @@ async function handleUninstallCommand(target, options = {}) {
|
|
|
6569
7697
|
const context = createInstallContext(options);
|
|
6570
7698
|
const adapter = getAdapter(target);
|
|
6571
7699
|
if (!adapter) {
|
|
6572
|
-
const valid = getAllAdapters().map((a) => a.id).join(", ");
|
|
7700
|
+
const valid = [...getAllAdapters().map((a) => a.id), "gemini"].join(", ");
|
|
6573
7701
|
throw new Error(`Unknown agent/editor target "${target}". Supported targets: ${valid}`);
|
|
6574
7702
|
}
|
|
6575
7703
|
const uninstallOptions = {
|
|
@@ -6590,6 +7718,7 @@ async function handleDoctorCommand(options = {}) {
|
|
|
6590
7718
|
return { report };
|
|
6591
7719
|
}
|
|
6592
7720
|
export {
|
|
7721
|
+
AntigravityAdapter,
|
|
6593
7722
|
BILMEM_END_MARKER,
|
|
6594
7723
|
BILMEM_START_MARKER,
|
|
6595
7724
|
BaseAgentAdapter,
|
|
@@ -6601,6 +7730,7 @@ export {
|
|
|
6601
7730
|
createInstallContext,
|
|
6602
7731
|
detectAgents,
|
|
6603
7732
|
extractInstructionVersion,
|
|
7733
|
+
extractSkillVersion,
|
|
6604
7734
|
formatDoctorFixResult,
|
|
6605
7735
|
formatDoctorReport,
|
|
6606
7736
|
formatInstallResult,
|
|
@@ -6608,12 +7738,14 @@ export {
|
|
|
6608
7738
|
getAdapter,
|
|
6609
7739
|
getAgentInstructionText,
|
|
6610
7740
|
getAllAdapters,
|
|
7741
|
+
getBilmemSkillContent,
|
|
6611
7742
|
getIntegrationRecord,
|
|
6612
7743
|
getIntegrationsFilePath,
|
|
6613
7744
|
handleDoctorCommand,
|
|
6614
7745
|
handleInstallCommand,
|
|
6615
7746
|
handleUninstallCommand,
|
|
6616
7747
|
hasBilmemInstruction,
|
|
7748
|
+
hasBilmemSkill,
|
|
6617
7749
|
isExecutableInPath,
|
|
6618
7750
|
loadIntegrationsMetadata,
|
|
6619
7751
|
pathExists,
|
|
@@ -6621,12 +7753,14 @@ export {
|
|
|
6621
7753
|
removeIntegration,
|
|
6622
7754
|
removeMarkdownInstructionFile,
|
|
6623
7755
|
removeMcpJsonConfig,
|
|
7756
|
+
removeSkillFile,
|
|
6624
7757
|
runDoctor,
|
|
6625
7758
|
runDoctorFix,
|
|
6626
7759
|
safeReadJson,
|
|
6627
7760
|
saveIntegrationsMetadata,
|
|
6628
7761
|
updateMarkdownInstructionFile,
|
|
6629
7762
|
updateMcpJsonConfig,
|
|
7763
|
+
updateSkillFile,
|
|
6630
7764
|
writeAtomic
|
|
6631
7765
|
};
|
|
6632
7766
|
//# sourceMappingURL=index.js.map
|