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.
@@ -1,5 +1,5 @@
1
1
  // src/install/index.ts
2
- import * as path17 from "path";
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
- mcpCommandMatches = b.command === targetCommand && Array.isArray(b.args) && b.args.length === targetArgs.length && b.args.every((v, i) => v === targetArgs[i]);
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 path16 from "path";
1859
+ import * as path18 from "path";
1508
1860
 
1509
1861
  // src/mcp/server.ts
1510
1862
  import { McpApp, z } from "mcponce";
1511
- import * as path15 from "path";
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 path14 from "path";
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 fs6 from "fs/promises";
3272
- import * as path11 from "path";
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(path11.sep).join("/");
4279
+ return rel.split(path13.sep).join("/");
3294
4280
  }
3295
4281
  async function loadGitignorePatterns(rootDir) {
3296
4282
  try {
3297
- const gitignorePath = path11.join(rootDir, ".gitignore");
3298
- const content = await fs6.readFile(gitignorePath, "utf-8");
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 fs6.realpath(currentDir);
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 fs6.readdir(currentDir, { withFileTypes: true });
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 = path11.join(currentDir, entry.name);
3363
- const rawRelPath = path11.relative(root, fullPath);
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 = path11.extname(entry.name).toLowerCase();
4365
+ const ext = path13.extname(entry.name).toLowerCase();
3380
4366
  try {
3381
- const stat3 = await fs6.stat(fullPath);
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: stat3.size,
3388
- mtimeMs: stat3.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 = path11.resolve(this.rootDir, relativePath);
4417
+ const fullPath = path13.resolve(this.rootDir, relativePath);
3432
4418
  try {
3433
- const handle = await fs6.open(fullPath, "r");
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 fs7 from "fs/promises";
3860
- import * as path12 from "path";
3861
- import YAML2 from "yaml";
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 = path12.basename(relativePath, ".md").toLowerCase();
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 = YAML2.parse(yamlText);
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 = path12.dirname(doc.relativePath);
4282
- const resolved = dir === "." || path12.isAbsolute(rawTarget) ? rawTarget.replace(/^\.\//, "") : path12.normalize(path12.join(dir, rawTarget));
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 fs7.readFile(filePath, "utf-8");
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 execFileAsync = promisify(execFile);
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 execFileAsync("git", args, {
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 fs8 from "fs/promises";
5528
+ import * as fs10 from "fs/promises";
4543
5529
  import * as fsSync from "fs";
4544
- import * as path13 from "path";
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 = path13.join(path13.dirname(pkgJson), "out");
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
- path13.resolve(process.cwd(), "node_modules/tree-sitter-wasms/out"),
4607
- path13.resolve(process.cwd(), "../node_modules/tree-sitter-wasms/out")
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 = path13.join(wasmDir, wasmName);
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 fs8.readFile(filePath, "utf-8");
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 = path13.basename(relativePath);
4988
- const ext = path13.extname(relativePath).toLowerCase();
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: path14.basename(rootDir),
6172
+ projectName: path16.basename(rootDir),
5187
6173
  defaultScope: "project",
5188
- dataDir: path14.join(rootDir, ".bilmem")
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.${path14.basename(o.provenance.path || "doc", ".md")}.${o.provenance.line || "adr"}`;
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 execFile2 } from "child_process";
5737
- import { promisify as promisify2 } from "util";
5738
- var execFileAsync2 = promisify2(execFile2);
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 execFileAsync2(
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 rootDir = options.rootDir ? path15.resolve(options.rootDir) : process.cwd();
5815
- const storage = new Storage(rootDir);
5816
- const index = new SearchIndex();
5817
- let initialized = false;
5818
- async function ensureInitialized() {
5819
- if (!initialized) {
5820
- await storage.init();
5821
- const all = await storage.getAllKnowledge();
5822
- await index.buildIndex(all);
5823
- initialized = true;
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 ensureInitialized();
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 ensureInitialized();
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 ensureInitialized();
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 ensureInitialized();
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 ensureInitialized();
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 ensureInitialized();
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("Root directory to scan (defaults to project root)"),
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 targetDir = args.path ? path15.resolve(args.path) : rootDir;
6149
- const report = await scanRepository(targetDir, {
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 ensureInitialized();
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: path16.basename(context.projectDir) });
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
- if (needsMcpFix || needsInstFix) {
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 = path17.resolve(options.projectDir || process.cwd());
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