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/dist/index.js CHANGED
@@ -316,7 +316,7 @@ function createKnowledge(params) {
316
316
  type: params.type ?? "lesson",
317
317
  status,
318
318
  stage: initialStage,
319
- statement: params.statement.trim(),
319
+ statement: (params.statement ?? params.question ?? params.id).trim(),
320
320
  positive: Array.from(new Set(params.positive?.map((p) => p.trim().toLowerCase()) ?? [])),
321
321
  negative: Array.from(new Set(params.negative?.map((n) => n.trim().toLowerCase()) ?? [])),
322
322
  relations: Array.from(new Set(params.relations?.map((r) => r.trim()) ?? [])),
@@ -346,6 +346,11 @@ function createKnowledge(params) {
346
346
  },
347
347
  history,
348
348
  value: 0,
349
+ lesson: params.lesson,
350
+ failure_details: params.failure_details,
351
+ context: params.context,
352
+ contradicts: params.contradicts ? Array.from(new Set(params.contradicts)) : void 0,
353
+ blocking: params.blocking,
349
354
  createdAt: now,
350
355
  updatedAt: now
351
356
  };
@@ -1710,6 +1715,7 @@ var Retriever = class {
1710
1715
  // src/core/resolver.ts
1711
1716
  var RELATIVE_COSTS = {
1712
1717
  USE_KNOWLEDGE: 1,
1718
+ REVALIDATE: 2,
1713
1719
  INSPECT_TEST: 2,
1714
1720
  INSPECT_FILE: 2,
1715
1721
  INSPECT_SYMBOL: 2.5,
@@ -2050,14 +2056,624 @@ async function resolveEpistemicQuery(options, storage, retriever) {
2050
2056
  };
2051
2057
  }
2052
2058
 
2059
+ // src/core/preflight.ts
2060
+ import { execFile } from "child_process";
2061
+ import { promisify } from "util";
2062
+ import * as path from "path";
2063
+ import * as fs from "fs/promises";
2064
+ import YAML from "yaml";
2065
+ var execFileAsync = promisify(execFile);
2066
+ var STOP_WORDS = /* @__PURE__ */ new Set([
2067
+ "add",
2068
+ "implement",
2069
+ "create",
2070
+ "support",
2071
+ "with",
2072
+ "the",
2073
+ "a",
2074
+ "an",
2075
+ "for",
2076
+ "in",
2077
+ "to",
2078
+ "and",
2079
+ "of",
2080
+ "on",
2081
+ "from",
2082
+ "using",
2083
+ "via",
2084
+ "into",
2085
+ "by",
2086
+ "our",
2087
+ "new",
2088
+ "use",
2089
+ "make",
2090
+ "allow",
2091
+ "enable",
2092
+ "provide",
2093
+ "setup",
2094
+ "set"
2095
+ ]);
2096
+ var DOMAIN_KEYWORDS = {
2097
+ authentication: ["auth", "oauth", "login", "logout", "session", "jwt", "token", "credential", "sso", "password"],
2098
+ session: ["session", "cookie", "cookies", "expiry", "refresh", "token"],
2099
+ "user-identity": ["user", "account", "profile", "identity", "member", "role", "permission"],
2100
+ database: ["db", "database", "sql", "postgres", "sqlite", "migration", "schema", "table", "query"],
2101
+ storage: ["storage", "s3", "bucket", "file", "upload", "disk", "blob"],
2102
+ api: ["api", "endpoint", "http", "rest", "route", "controller", "middleware", "graphql"],
2103
+ caching: ["cache", "redis", "memcached", "ttl", "invalidation"],
2104
+ billing: ["payment", "stripe", "checkout", "billing", "subscription", "invoice", "credit"],
2105
+ "data-processing": ["import", "export", "csv", "bulk", "batch", "stream", "pipeline"]
2106
+ };
2107
+ function tokenizeGoal(goal) {
2108
+ return goal.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
2109
+ }
2110
+ function inferLikelyDomains(tokens, knowledgeTags) {
2111
+ const domains = /* @__PURE__ */ new Set();
2112
+ for (const token of tokens) {
2113
+ for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
2114
+ if (keywords.includes(token) || token.includes(domain)) {
2115
+ domains.add(domain);
2116
+ }
2117
+ }
2118
+ }
2119
+ for (const tag of knowledgeTags) {
2120
+ const norm = tag.toLowerCase().trim();
2121
+ if (DOMAIN_KEYWORDS[norm]) {
2122
+ domains.add(norm);
2123
+ }
2124
+ }
2125
+ return Array.from(domains);
2126
+ }
2127
+ function classifyUnknown(unknown, goalTokens, domainCues) {
2128
+ const text = `${unknown.id} ${unknown.statement || ""} ${unknown.question || unknown.next?.question || ""}`.toLowerCase();
2129
+ if (unknown.blocking === true) {
2130
+ const isDecision2 = /\b(should|whether|how to handle|what happens when|policy|strategy|linking|collision|merge|choice)\b/i.test(text);
2131
+ return {
2132
+ classification: "BLOCKING",
2133
+ isDecision: isDecision2 || true,
2134
+ reason: unknown.reason || "Blocking unknown identified for this goal"
2135
+ };
2136
+ }
2137
+ if (unknown.blocking === false) {
2138
+ const isDecision2 = /\b(should|whether|how to handle|what happens when|policy|strategy|choice)\b/i.test(text);
2139
+ return {
2140
+ classification: "NON_BLOCKING",
2141
+ isDecision: isDecision2,
2142
+ reason: "Operational or instrumentation detail not blocking core functionality"
2143
+ };
2144
+ }
2145
+ const isNonBlocking = /\b(analytics|telemetry|metric|event name|logging level|log format|button color|styling|theme)\b/i.test(text);
2146
+ if (isNonBlocking) {
2147
+ return {
2148
+ classification: "NON_BLOCKING",
2149
+ isDecision: /\b(should|which name|choice)\b/i.test(text),
2150
+ reason: "Operational or instrumentation detail not blocking core functionality"
2151
+ };
2152
+ }
2153
+ const isDecision = /\b(should|whether|how to handle|what happens when|policy|strategy|linking|collision|merge)\b/i.test(text);
2154
+ const isBlockingCandidate = /\b(link|linking|collision|duplicate|merge|security|permission|storage-engine|protocol|auth-flow|credentials)\b/i.test(text);
2155
+ const overlapsGoal = goalTokens.some((t) => text.includes(t)) || domainCues.some((d) => text.includes(d));
2156
+ if (isBlockingCandidate && (overlapsGoal || unknown.reason === "UNRESOLVABLE_LOCALLY")) {
2157
+ return {
2158
+ classification: "BLOCKING",
2159
+ isDecision: true,
2160
+ reason: "Unresolved architectural or behavioral decision critical to goal implementation"
2161
+ };
2162
+ }
2163
+ return {
2164
+ classification: "IMPORTANT",
2165
+ isDecision,
2166
+ reason: isDecision ? "Product or behavior decision requiring attention" : "Important implementation detail"
2167
+ };
2168
+ }
2169
+ function prioritizeQuestions(rawQuestions, blockingUnknowns) {
2170
+ const consolidated = /* @__PURE__ */ new Map();
2171
+ for (const rq of rawQuestions) {
2172
+ const qText = rq.question.trim();
2173
+ if (!qText) continue;
2174
+ const topic = rq.targetTopic || qText.toLowerCase().replace(/[^a-z0-9]/g, " ").slice(0, 30);
2175
+ let expectedResolutionValue = 1;
2176
+ if (rq.isBlocking) expectedResolutionValue += 2;
2177
+ if (rq.outcomeScore && rq.outcomeScore > 0) expectedResolutionValue += 1;
2178
+ if (rq.outcomeScore && rq.outcomeScore < 0) expectedResolutionValue -= 0.5;
2179
+ const interactionCost = 1;
2180
+ const priorityScore = Math.max(0.1, expectedResolutionValue) / interactionCost;
2181
+ const existing = consolidated.get(topic);
2182
+ if (!existing || priorityScore > (existing.priorityScore ?? 0)) {
2183
+ consolidated.set(topic, {
2184
+ question: qText,
2185
+ reason: rq.reason,
2186
+ targetTopic: rq.targetTopic,
2187
+ expectedResolutionValue,
2188
+ interactionCost,
2189
+ priorityScore
2190
+ });
2191
+ }
2192
+ }
2193
+ return Array.from(consolidated.values()).sort((a, b) => (b.priorityScore ?? 0) - (a.priorityScore ?? 0));
2194
+ }
2195
+ async function executePreflight(options, storage, retriever, index) {
2196
+ const goal = options.goal.trim();
2197
+ const explicitPositive = (options.positive ?? []).map((p) => p.trim().toLowerCase());
2198
+ const negative = (options.negative ?? []).map((n) => n.trim().toLowerCase());
2199
+ const scope = options.scope ?? "project";
2200
+ const goalTokens = tokenizeGoal(goal);
2201
+ const allKnowledge = await storage.getAllKnowledge();
2202
+ const allTags = allKnowledge.flatMap((k) => k.tags ?? []);
2203
+ const likelyDomains = inferLikelyDomains(goalTokens, allTags);
2204
+ const contextualPositive = Array.from(/* @__PURE__ */ new Set([...explicitPositive, ...likelyDomains, ...goalTokens]));
2205
+ let candidatePool = [];
2206
+ if (retriever) {
2207
+ const jevCandidates = await retriever.lookup({
2208
+ query: goal,
2209
+ positive: contextualPositive,
2210
+ negative,
2211
+ scope,
2212
+ limit: options.maxCandidates ?? 25,
2213
+ includeRejected: true
2214
+ });
2215
+ for (const r of jevCandidates) {
2216
+ if (r.knowledge) candidatePool.push(r.knowledge);
2217
+ }
2218
+ }
2219
+ if (candidatePool.length === 0) {
2220
+ candidatePool = allKnowledge.filter((k) => k.status !== "invalid" && k.status !== "superseded");
2221
+ } else {
2222
+ for (const k of allKnowledge) {
2223
+ if (!candidatePool.some((c) => c.id === k.id) && k.status !== "invalid" && k.status !== "superseded") {
2224
+ const matchesToken = goalTokens.some((t) => k.id.toLowerCase().includes(t) || k.statement.toLowerCase().includes(t));
2225
+ const matchesCues = k.positive.some((p) => contextualPositive.includes(p));
2226
+ if (matchesToken || matchesCues) {
2227
+ candidatePool.push(k);
2228
+ }
2229
+ }
2230
+ }
2231
+ }
2232
+ const jevResults = rerankCandidates(candidatePool, {
2233
+ query: goal,
2234
+ positive: contextualPositive,
2235
+ negative,
2236
+ scope
2237
+ });
2238
+ const matchingItems = [];
2239
+ const rejectedItems = [];
2240
+ for (const res of jevResults) {
2241
+ if (res.status === "REJECT") {
2242
+ const reason = res.explanation.negativeConflicts.length > 0 ? `Inapplicable: ${res.explanation.negativeConflicts.join(", ")} conflicts with current context` : res.explanation.reasons[0] || "Context mismatch";
2243
+ rejectedItems.push({ id: res.knowledge.id, reason });
2244
+ } else {
2245
+ matchingItems.push(res.knowledge);
2246
+ }
2247
+ }
2248
+ if (options.deep) {
2249
+ try {
2250
+ const { stdout: diffOutput } = await execFileAsync(
2251
+ "git",
2252
+ ["diff", "--name-only", "HEAD~1"],
2253
+ { cwd: storage.getBaseDir() ? path.dirname(storage.getBaseDir()) : process.cwd() }
2254
+ );
2255
+ const changedFiles = diffOutput.split("\n").map((f) => f.trim()).filter(Boolean);
2256
+ if (changedFiles.length > 0) {
2257
+ const staleness = checkKnowledgeListStaleness(matchingItems, changedFiles);
2258
+ for (const affected of staleness.affectedKnowledge) {
2259
+ const matchIdx = matchingItems.findIndex((m) => m.id === affected.id);
2260
+ if (matchIdx >= 0) {
2261
+ matchingItems[matchIdx] = affected;
2262
+ }
2263
+ }
2264
+ }
2265
+ } catch {
2266
+ }
2267
+ }
2268
+ const constraints = [];
2269
+ const lessons = [];
2270
+ const pastFailures = [];
2271
+ const stale = [];
2272
+ const known = [];
2273
+ let conflictedCount = 0;
2274
+ const CONSTRAINT_PATTERN = /\b(must|must not|never|only|cannot|require|required|boundary|strictly|prohibited|forbidden)\b/i;
2275
+ const FAILURE_PATTERN = /(fail|bug|error|crash|duplicate write|race condition|pressure|leak|revert|timeout)/i;
2276
+ const rootDir = storage.getBaseDir() ? path.dirname(storage.getBaseDir()) : process.cwd();
2277
+ for (const k of matchingItems) {
2278
+ const matchesContradiction = k.status === "contradicted" || k.contradicts && k.contradicts.some((cid) => matchingItems.some((m) => m.id === cid));
2279
+ if (matchesContradiction) {
2280
+ conflictedCount++;
2281
+ }
2282
+ let isStale = k.status === "needs_revalidation" || k.status === "stale";
2283
+ let staleReason = k.history?.[k.history.length - 1]?.reason || "Supporting source changed after last validation";
2284
+ const changedDeps = [];
2285
+ const candidateDeps = [...k.depends_on ?? [], ...k.evidence ?? []];
2286
+ const timestampStr = k.updated_at ?? k.updatedAt ?? k.createdAt;
2287
+ const knowledgeTime = new Date(timestampStr).getTime();
2288
+ for (const dep of candidateDeps) {
2289
+ const cleanPath = dep.replace(/^(file|code|test|doc):/, "").trim();
2290
+ if (!cleanPath) continue;
2291
+ const absPath = path.isAbsolute(cleanPath) ? cleanPath : path.resolve(rootDir, cleanPath);
2292
+ try {
2293
+ const stat4 = await fs.stat(absPath);
2294
+ if (stat4.mtimeMs > knowledgeTime + 500) {
2295
+ isStale = true;
2296
+ changedDeps.push(cleanPath);
2297
+ staleReason = `Supporting source changed after last validation (${cleanPath})`;
2298
+ }
2299
+ } catch {
2300
+ }
2301
+ }
2302
+ if (isStale) {
2303
+ stale.push({
2304
+ id: k.id,
2305
+ statement: k.statement,
2306
+ reason: staleReason,
2307
+ changedSources: changedDeps.length > 0 ? changedDeps : k.depends_on
2308
+ });
2309
+ continue;
2310
+ }
2311
+ const isConstraint = k.type === "rule" || k.type === "decision" || k.type === "convention" || CONSTRAINT_PATTERN.test(k.statement);
2312
+ if (isConstraint) {
2313
+ constraints.push({
2314
+ id: k.id,
2315
+ statement: k.statement,
2316
+ type: k.type,
2317
+ confidence: k.confidence,
2318
+ confidenceTier: k.confidenceTier,
2319
+ source: k.evidence?.[0]
2320
+ });
2321
+ continue;
2322
+ }
2323
+ const isFailure = k.type === "failure" || k.statement.startsWith("[FAILURE]") || k.statement.startsWith("[REVERT]") || FAILURE_PATTERN.test(k.statement) || k.metrics.failed_outcomes > 0;
2324
+ if (isFailure) {
2325
+ pastFailures.push({
2326
+ id: k.id,
2327
+ statement: k.statement,
2328
+ context: k.context || {
2329
+ positive: k.positive,
2330
+ negative: k.negative
2331
+ },
2332
+ failure: k.statement,
2333
+ lesson: k.lesson || k.reasoning || k.resolves?.[0],
2334
+ evidence: k.evidence
2335
+ });
2336
+ continue;
2337
+ }
2338
+ 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:");
2339
+ if (isLesson) {
2340
+ lessons.push({
2341
+ id: k.id,
2342
+ statement: k.statement,
2343
+ confidence: k.confidence,
2344
+ domain: k.tags?.[0],
2345
+ evidence: k.evidence
2346
+ });
2347
+ continue;
2348
+ }
2349
+ if (k.status === "active" || k.stage === "active" || k.stage === "probation" || !k.status) {
2350
+ known.push({
2351
+ id: k.id,
2352
+ statement: k.statement,
2353
+ confidence: k.confidence,
2354
+ confidenceTier: k.confidenceTier,
2355
+ stage: k.stage
2356
+ });
2357
+ }
2358
+ }
2359
+ const unknownsFromStorage = await storage.getAllUnknowns();
2360
+ const unknownKnowledge = allKnowledge.filter((k) => k.type === "unknown");
2361
+ const unifiedUnknowns = [];
2362
+ const seenUnknownIds = /* @__PURE__ */ new Set();
2363
+ for (const u of unknownsFromStorage) {
2364
+ seenUnknownIds.add(u.id);
2365
+ unifiedUnknowns.push({
2366
+ id: u.id,
2367
+ statement: u.statement || u.id,
2368
+ reason: u.reason,
2369
+ question: u.question || u.next?.question,
2370
+ next: u.next,
2371
+ blocking: u.blocking,
2372
+ positive: u.positive,
2373
+ negative: u.negative,
2374
+ checked: u.checked
2375
+ });
2376
+ }
2377
+ for (const k of unknownKnowledge) {
2378
+ if (!seenUnknownIds.has(k.id)) {
2379
+ seenUnknownIds.add(k.id);
2380
+ unifiedUnknowns.push({
2381
+ id: k.id,
2382
+ statement: k.statement,
2383
+ reason: k.history?.[0]?.reason,
2384
+ question: k.question || (k.statement.includes("?") ? k.statement : void 0),
2385
+ next: {
2386
+ type: "ask",
2387
+ target: k.id,
2388
+ question: k.question || k.statement
2389
+ },
2390
+ blocking: k.blocking,
2391
+ positive: k.positive,
2392
+ negative: k.negative,
2393
+ checked: k.depends_on || k.evidence
2394
+ });
2395
+ }
2396
+ }
2397
+ const unknowns = [];
2398
+ const missing = [];
2399
+ const rawQuestions = [];
2400
+ for (const u of unifiedUnknowns) {
2401
+ const text = `${u.id} ${u.statement || ""} ${u.question || ""}`.toLowerCase();
2402
+ const overlaps = goalTokens.some((t) => text.includes(t)) || likelyDomains.some((d) => text.includes(d)) || u.positive && u.positive.some((p) => contextualPositive.includes(p));
2403
+ if (u.negative && u.negative.some((n) => contextualPositive.includes(n))) {
2404
+ continue;
2405
+ }
2406
+ if (overlaps) {
2407
+ const { classification, isDecision, reason } = classifyUnknown(u, goalTokens, likelyDomains);
2408
+ const isBlocking = classification === "BLOCKING";
2409
+ unknowns.push({
2410
+ id: u.id,
2411
+ statement: u.statement,
2412
+ question: u.question || u.statement,
2413
+ classification,
2414
+ blocking: isBlocking,
2415
+ reason
2416
+ });
2417
+ if (isDecision) {
2418
+ missing.push({
2419
+ type: "decision",
2420
+ subject: u.id,
2421
+ question: u.question || u.statement,
2422
+ reason
2423
+ });
2424
+ } else {
2425
+ missing.push({
2426
+ type: "evidence",
2427
+ subject: u.id,
2428
+ question: u.question || u.statement,
2429
+ reason
2430
+ });
2431
+ }
2432
+ const qText = u.question || (u.statement && u.statement.includes("?") ? u.statement : void 0);
2433
+ if (qText) {
2434
+ rawQuestions.push({
2435
+ question: qText,
2436
+ reason: `Unknown: ${u.id} (${reason})`,
2437
+ targetTopic: u.id,
2438
+ isBlocking
2439
+ });
2440
+ }
2441
+ }
2442
+ }
2443
+ const questionKnowledge = allKnowledge.filter((k) => k.type === "question");
2444
+ for (const qk of questionKnowledge) {
2445
+ const matchesCues = qk.positive.some((p) => contextualPositive.includes(p));
2446
+ const conflicts = qk.negative.some((n) => negative.includes(n));
2447
+ if (matchesCues && !conflicts && qk.question) {
2448
+ rawQuestions.push({
2449
+ question: qk.question,
2450
+ reason: `Learned question for [${qk.positive.join(", ")}] uncertainty`,
2451
+ targetTopic: qk.resolves?.[0] || qk.id,
2452
+ outcomeScore: (qk.metrics?.successful_outcomes ?? 0) - (qk.metrics?.failed_outcomes ?? 0)
2453
+ });
2454
+ }
2455
+ }
2456
+ const questions = prioritizeQuestions(rawQuestions, unknowns);
2457
+ const receipts = await storage.getAllReceipts();
2458
+ const alreadyInvestigated = [];
2459
+ const investigatedSources = /* @__PURE__ */ new Set();
2460
+ for (const r of receipts) {
2461
+ const qTokens = r.question.toLowerCase().split(/\s+/).filter(Boolean);
2462
+ const overlapsGoal = qTokens.some((t) => goalTokens.includes(t));
2463
+ const overlapsCues = r.positive && r.positive.some((p) => contextualPositive.includes(p));
2464
+ if (overlapsGoal || overlapsCues) {
2465
+ for (const checked of r.checked) {
2466
+ investigatedSources.add(checked.toLowerCase());
2467
+ alreadyInvestigated.push({
2468
+ source: checked,
2469
+ result: r.findings[0] || r.rejected[0]?.reason || "Did not establish requested behavior",
2470
+ nextSuggestion: "ASK_USER"
2471
+ });
2472
+ }
2473
+ }
2474
+ }
2475
+ let readiness = "READY";
2476
+ const totalRelevantKnowledge = known.length + constraints.length + lessons.length + pastFailures.length + stale.length;
2477
+ const hasBlockingUnknown = unknowns.some((u) => u.blocking);
2478
+ const hasMissingDecision = missing.some((m) => m.type === "decision");
2479
+ if (totalRelevantKnowledge === 0 && unknowns.length === 0) {
2480
+ readiness = "INSUFFICIENT_KNOWLEDGE";
2481
+ } else if (conflictedCount > 0) {
2482
+ readiness = "CONFLICTED";
2483
+ } else if (hasBlockingUnknown || hasMissingDecision) {
2484
+ readiness = "NEEDS_CLARIFICATION";
2485
+ } else if (stale.length > 0) {
2486
+ readiness = "NEEDS_REVALIDATION";
2487
+ } else {
2488
+ readiness = "READY";
2489
+ }
2490
+ const next = [];
2491
+ for (const m of missing) {
2492
+ if (m.type === "decision") {
2493
+ next.push({
2494
+ type: "ASK_USER",
2495
+ target: m.subject,
2496
+ reason: m.question || m.reason,
2497
+ relativeCost: 1,
2498
+ expectedInfoGain: 0.95
2499
+ });
2500
+ }
2501
+ }
2502
+ for (const s of stale) {
2503
+ next.push({
2504
+ type: "REVALIDATE",
2505
+ target: s.id,
2506
+ reason: s.reason,
2507
+ relativeCost: 2,
2508
+ expectedInfoGain: 0.85
2509
+ });
2510
+ }
2511
+ for (const m of missing) {
2512
+ if (m.type === "evidence") {
2513
+ const wasChecked = investigatedSources.has(m.subject.toLowerCase()) || alreadyInvestigated.length > 0;
2514
+ if (wasChecked) {
2515
+ if (!next.some((n) => n.type === "ASK_USER")) {
2516
+ next.push({
2517
+ type: "ASK_USER",
2518
+ target: m.subject,
2519
+ reason: `Already investigated (${Array.from(investigatedSources).join(", ")}); did not establish requested behavior. Clarification required.`,
2520
+ relativeCost: 1,
2521
+ expectedInfoGain: 0.9
2522
+ });
2523
+ }
2524
+ } else {
2525
+ const isFilePath = m.subject.includes("/") || m.subject.includes(".");
2526
+ next.push({
2527
+ type: isFilePath ? "INSPECT_FILE" : "SEARCH_REPOSITORY",
2528
+ target: m.subject,
2529
+ reason: m.reason,
2530
+ relativeCost: isFilePath ? 2 : 2.5,
2531
+ expectedInfoGain: 0.8
2532
+ });
2533
+ }
2534
+ }
2535
+ }
2536
+ if (alreadyInvestigated.length > 0 && !next.some((n) => n.type === "ASK_USER")) {
2537
+ next.push({
2538
+ type: "ASK_USER",
2539
+ target: alreadyInvestigated[0].source,
2540
+ reason: `Already investigated ${alreadyInvestigated[0].source}; clarification required`,
2541
+ relativeCost: 1,
2542
+ expectedInfoGain: 0.9
2543
+ });
2544
+ }
2545
+ if (next.length === 0 && known.length > 0) {
2546
+ next.push({
2547
+ type: "USE_KNOWLEDGE",
2548
+ target: known[0].id,
2549
+ reason: "Proceed with implementation using verified knowledge and constraints",
2550
+ relativeCost: 1,
2551
+ expectedInfoGain: 1
2552
+ });
2553
+ }
2554
+ const knowledgeImpact = {
2555
+ relevantKnowledge: totalRelevantKnowledge,
2556
+ validated: known.filter((k) => k.confidenceTier === "high").length + constraints.length,
2557
+ needsRevalidation: stale.length,
2558
+ conflicted: conflictedCount,
2559
+ blockingUnknowns: unknowns.filter((u) => u.blocking).length,
2560
+ relevantFailures: pastFailures.length
2561
+ };
2562
+ const similarWork = [];
2563
+ for (const l of lessons) {
2564
+ similarWork.push({
2565
+ topic: l.domain || l.id,
2566
+ lesson: l.statement
2567
+ });
2568
+ }
2569
+ for (const f of pastFailures) {
2570
+ similarWork.push({
2571
+ topic: f.id,
2572
+ failure: f.statement
2573
+ });
2574
+ }
2575
+ await storage.appendMetricEvent({
2576
+ type: "preflight",
2577
+ goal,
2578
+ readiness,
2579
+ blockingUnknownsCount: knowledgeImpact.blockingUnknowns,
2580
+ staleKnowledgeCount: stale.length,
2581
+ pastFailuresCount: pastFailures.length,
2582
+ questionsCount: questions.length,
2583
+ repeatedChecksAvoidedCount: alreadyInvestigated.length,
2584
+ timestamp: Date.now()
2585
+ });
2586
+ return {
2587
+ goal,
2588
+ readiness,
2589
+ known,
2590
+ constraints,
2591
+ lessons,
2592
+ past_failures: pastFailures,
2593
+ unknowns,
2594
+ stale,
2595
+ missing,
2596
+ questions,
2597
+ likely_domains: likelyDomains,
2598
+ similar_work: similarWork.length > 0 ? similarWork.slice(0, 5) : void 0,
2599
+ already_investigated: alreadyInvestigated.length > 0 ? alreadyInvestigated : void 0,
2600
+ knowledge_impact: knowledgeImpact,
2601
+ next
2602
+ };
2603
+ }
2604
+ function formatPreflightReport(result) {
2605
+ const plainObject = {
2606
+ goal: result.goal,
2607
+ readiness: result.readiness
2608
+ };
2609
+ if (result.known.length > 0) {
2610
+ plainObject.known = result.known.map((k) => ({
2611
+ id: k.id,
2612
+ statement: k.statement,
2613
+ confidence: k.confidenceTier || (typeof k.confidence === "number" ? k.confidence >= 0.9 ? "high" : k.confidence >= 0.7 ? "medium" : "low" : k.confidence)
2614
+ }));
2615
+ }
2616
+ if (result.constraints.length > 0) {
2617
+ plainObject.constraints = result.constraints.map((c) => ({
2618
+ id: c.id,
2619
+ statement: c.statement
2620
+ }));
2621
+ }
2622
+ if (result.lessons.length > 0) {
2623
+ plainObject.lessons = result.lessons.map((l) => ({
2624
+ id: l.id,
2625
+ statement: l.statement
2626
+ }));
2627
+ }
2628
+ if (result.past_failures.length > 0) {
2629
+ plainObject.past_failures = result.past_failures.map((f) => ({
2630
+ id: f.id,
2631
+ statement: f.statement
2632
+ }));
2633
+ }
2634
+ if (result.unknowns.length > 0) {
2635
+ plainObject.unknowns = result.unknowns.map((u) => ({
2636
+ id: u.id,
2637
+ question: u.question || u.statement,
2638
+ blocking: u.blocking
2639
+ }));
2640
+ }
2641
+ if (result.stale.length > 0) {
2642
+ plainObject.stale = result.stale.map((s) => ({
2643
+ id: s.id,
2644
+ reason: s.reason
2645
+ }));
2646
+ }
2647
+ if (result.questions.length > 0) {
2648
+ plainObject.questions = result.questions.map((q) => ({
2649
+ question: q.question,
2650
+ reason: q.reason
2651
+ }));
2652
+ }
2653
+ if (result.likely_domains.length > 0) {
2654
+ plainObject.likely_domains = result.likely_domains;
2655
+ }
2656
+ if (result.already_investigated && result.already_investigated.length > 0) {
2657
+ plainObject.already_investigated = result.already_investigated;
2658
+ }
2659
+ if (result.next.length > 0) {
2660
+ plainObject.next = result.next.map((n) => ({
2661
+ type: n.type,
2662
+ target: n.target,
2663
+ reason: n.reason
2664
+ }));
2665
+ }
2666
+ return YAML.stringify(plainObject);
2667
+ }
2668
+
2053
2669
  // src/storage/canonical.ts
2054
- import * as fs3 from "fs/promises";
2055
- import * as path3 from "path";
2670
+ import * as fs4 from "fs/promises";
2671
+ import * as path4 from "path";
2056
2672
 
2057
2673
  // src/rules/loader.ts
2058
- import YAML from "yaml";
2059
- import * as fs from "fs/promises";
2060
- import * as path from "path";
2674
+ import YAML2 from "yaml";
2675
+ import * as fs2 from "fs/promises";
2676
+ import * as path2 from "path";
2061
2677
  function validateRuleDefinition(rule, index = 0) {
2062
2678
  const errors = [];
2063
2679
  if (!rule || typeof rule !== "object") {
@@ -2126,7 +2742,7 @@ function validateRuleSet(data) {
2126
2742
  function parseRuleSetYaml(content, sourcePath = "inline.yml") {
2127
2743
  let parsed;
2128
2744
  try {
2129
- parsed = YAML.parse(content);
2745
+ parsed = YAML2.parse(content);
2130
2746
  } catch (err) {
2131
2747
  throw new Error(`YAML syntax error in ${sourcePath}: ${err.message}`);
2132
2748
  }
@@ -2137,7 +2753,7 @@ function parseRuleSetYaml(content, sourcePath = "inline.yml") {
2137
2753
  }
2138
2754
  if (Array.isArray(parsed)) {
2139
2755
  return {
2140
- name: path.basename(sourcePath, path.extname(sourcePath)),
2756
+ name: path2.basename(sourcePath, path2.extname(sourcePath)),
2141
2757
  rules: parsed
2142
2758
  };
2143
2759
  }
@@ -2149,7 +2765,7 @@ function parseRuleSetYaml(content, sourcePath = "inline.yml") {
2149
2765
  }
2150
2766
  const set = parsed;
2151
2767
  return {
2152
- name: set.name ?? path.basename(sourcePath, path.extname(sourcePath)),
2768
+ name: set.name ?? path2.basename(sourcePath, path2.extname(sourcePath)),
2153
2769
  version: set.version ?? 1,
2154
2770
  scope: set.scope ?? "project",
2155
2771
  description: set.description,
@@ -2178,36 +2794,36 @@ function stringifyRuleSetYaml(ruleSet) {
2178
2794
  if (r.details && Object.keys(r.details).length > 0) ruleObj.details = r.details;
2179
2795
  return ruleObj;
2180
2796
  });
2181
- return YAML.stringify(payload, { indent: 2, lineWidth: 100 });
2797
+ return YAML2.stringify(payload, { indent: 2, lineWidth: 100 });
2182
2798
  }
2183
2799
  async function loadRuleSetFile(filePath) {
2184
- const content = await fs.readFile(filePath, "utf-8");
2800
+ const content = await fs2.readFile(filePath, "utf-8");
2185
2801
  return parseRuleSetYaml(content, filePath);
2186
2802
  }
2187
2803
  async function findRuleFiles(rootDir, storageRulesDir) {
2188
2804
  const candidates = [];
2189
- const rulesDir = storageRulesDir || path.resolve(rootDir, ".bilmem", "rules");
2805
+ const rulesDir = storageRulesDir || path2.resolve(rootDir, ".bilmem", "rules");
2190
2806
  try {
2191
- const entries = await fs.readdir(rulesDir, { withFileTypes: true });
2807
+ const entries = await fs2.readdir(rulesDir, { withFileTypes: true });
2192
2808
  for (const entry of entries) {
2193
2809
  if (entry.isFile() && (entry.name.endsWith(".yml") || entry.name.endsWith(".yaml"))) {
2194
- candidates.push(path.join(rulesDir, entry.name));
2810
+ candidates.push(path2.join(rulesDir, entry.name));
2195
2811
  }
2196
2812
  }
2197
2813
  } catch {
2198
2814
  }
2199
2815
  for (const name of ["rules.yml", "rules.yaml"]) {
2200
- const p = path.resolve(rootDir, ".bilmem", name);
2816
+ const p = path2.resolve(rootDir, ".bilmem", name);
2201
2817
  try {
2202
- const st = await fs.stat(p);
2818
+ const st = await fs2.stat(p);
2203
2819
  if (st.isFile()) candidates.push(p);
2204
2820
  } catch {
2205
2821
  }
2206
2822
  }
2207
2823
  for (const name of ["bilmem.rules.yml", "bilmem.rules.yaml", "rules.yml", "rules.yaml"]) {
2208
- const p = path.resolve(rootDir, name);
2824
+ const p = path2.resolve(rootDir, name);
2209
2825
  try {
2210
- const st = await fs.stat(p);
2826
+ const st = await fs2.stat(p);
2211
2827
  if (st.isFile()) candidates.push(p);
2212
2828
  } catch {
2213
2829
  }
@@ -2231,17 +2847,17 @@ async function loadAllProjectRuleSets(rootDir, storageRulesDir) {
2231
2847
  }
2232
2848
 
2233
2849
  // src/stats/events.ts
2234
- import * as fs2 from "fs/promises";
2235
- import * as path2 from "path";
2850
+ import * as fs3 from "fs/promises";
2851
+ import * as path3 from "path";
2236
2852
  function getEventsFilePath(rootDir) {
2237
- return path2.join(rootDir, ".bilmem", "events.jsonl");
2853
+ return path3.join(rootDir, ".bilmem", "events.jsonl");
2238
2854
  }
2239
2855
  async function appendMetricEvent(rootDir, event) {
2240
2856
  const filePath = getEventsFilePath(rootDir);
2241
2857
  try {
2242
- await fs2.mkdir(path2.dirname(filePath), { recursive: true });
2858
+ await fs3.mkdir(path3.dirname(filePath), { recursive: true });
2243
2859
  const line = JSON.stringify(event) + "\n";
2244
- await fs2.appendFile(filePath, line, "utf-8");
2860
+ await fs3.appendFile(filePath, line, "utf-8");
2245
2861
  } catch {
2246
2862
  }
2247
2863
  }
@@ -2249,7 +2865,7 @@ async function readMetricEvents(rootDir, filter) {
2249
2865
  const filePath = getEventsFilePath(rootDir);
2250
2866
  let raw;
2251
2867
  try {
2252
- raw = await fs2.readFile(filePath, "utf-8");
2868
+ raw = await fs3.readFile(filePath, "utf-8");
2253
2869
  } catch (err) {
2254
2870
  if (err.code === "ENOENT") {
2255
2871
  return [];
@@ -2288,8 +2904,8 @@ async function readMetricEvents(rootDir, filter) {
2288
2904
  async function resetMetricEvents(rootDir) {
2289
2905
  const filePath = getEventsFilePath(rootDir);
2290
2906
  try {
2291
- await fs2.mkdir(path2.dirname(filePath), { recursive: true });
2292
- await fs2.writeFile(filePath, "", "utf-8");
2907
+ await fs3.mkdir(path3.dirname(filePath), { recursive: true });
2908
+ await fs3.writeFile(filePath, "", "utf-8");
2293
2909
  } catch (err) {
2294
2910
  if (err.code !== "ENOENT") throw err;
2295
2911
  }
@@ -2326,62 +2942,62 @@ var Storage = class {
2326
2942
  receiptsJsonlFile;
2327
2943
  eventsFile;
2328
2944
  constructor(rootDir = process.cwd()) {
2329
- this.baseDir = path3.resolve(rootDir, ".bilmem");
2330
- this.knowledgeDir = path3.join(this.baseDir, "knowledge");
2331
- this.evidenceDir = path3.join(this.baseDir, "evidence");
2332
- this.observationDir = path3.join(this.baseDir, "observations");
2333
- this.unknownDir = path3.join(this.baseDir, "unknowns");
2334
- this.rulesDir = path3.join(this.baseDir, "rules");
2335
- this.receiptsDir = path3.join(this.baseDir, "receipts");
2336
- this.configFile = path3.join(this.baseDir, "config.json");
2337
- this.relationsFile = path3.join(this.baseDir, "relations.json");
2338
- this.outcomesFile = path3.join(this.baseDir, "outcomes.jsonl");
2339
- this.metricsFile = path3.join(this.baseDir, "metrics.json");
2340
- this.revisionsFile = path3.join(this.baseDir, "revisions.jsonl");
2341
- this.knowledgeJsonlFile = path3.join(this.baseDir, "knowledge.jsonl");
2342
- this.evidenceJsonlFile = path3.join(this.baseDir, "evidence.jsonl");
2343
- this.observationJsonlFile = path3.join(this.baseDir, "observations.jsonl");
2344
- this.unknownsJsonlFile = path3.join(this.baseDir, "unknowns.jsonl");
2345
- this.receiptsJsonlFile = path3.join(this.baseDir, "receipts.jsonl");
2346
- this.eventsFile = path3.join(this.baseDir, "events.jsonl");
2945
+ this.baseDir = path4.resolve(rootDir, ".bilmem");
2946
+ this.knowledgeDir = path4.join(this.baseDir, "knowledge");
2947
+ this.evidenceDir = path4.join(this.baseDir, "evidence");
2948
+ this.observationDir = path4.join(this.baseDir, "observations");
2949
+ this.unknownDir = path4.join(this.baseDir, "unknowns");
2950
+ this.rulesDir = path4.join(this.baseDir, "rules");
2951
+ this.receiptsDir = path4.join(this.baseDir, "receipts");
2952
+ this.configFile = path4.join(this.baseDir, "config.json");
2953
+ this.relationsFile = path4.join(this.baseDir, "relations.json");
2954
+ this.outcomesFile = path4.join(this.baseDir, "outcomes.jsonl");
2955
+ this.metricsFile = path4.join(this.baseDir, "metrics.json");
2956
+ this.revisionsFile = path4.join(this.baseDir, "revisions.jsonl");
2957
+ this.knowledgeJsonlFile = path4.join(this.baseDir, "knowledge.jsonl");
2958
+ this.evidenceJsonlFile = path4.join(this.baseDir, "evidence.jsonl");
2959
+ this.observationJsonlFile = path4.join(this.baseDir, "observations.jsonl");
2960
+ this.unknownsJsonlFile = path4.join(this.baseDir, "unknowns.jsonl");
2961
+ this.receiptsJsonlFile = path4.join(this.baseDir, "receipts.jsonl");
2962
+ this.eventsFile = path4.join(this.baseDir, "events.jsonl");
2347
2963
  }
2348
2964
  getBaseDir() {
2349
2965
  return this.baseDir;
2350
2966
  }
2351
2967
  async isInitialized() {
2352
2968
  try {
2353
- await fs3.access(this.configFile);
2969
+ await fs4.access(this.configFile);
2354
2970
  return true;
2355
2971
  } catch {
2356
2972
  return false;
2357
2973
  }
2358
2974
  }
2359
2975
  async init(options = {}) {
2360
- await fs3.mkdir(this.knowledgeDir, { recursive: true });
2361
- await fs3.mkdir(this.evidenceDir, { recursive: true });
2362
- await fs3.mkdir(this.observationDir, { recursive: true });
2363
- await fs3.mkdir(this.unknownDir, { recursive: true });
2364
- await fs3.mkdir(this.rulesDir, { recursive: true });
2365
- await fs3.mkdir(this.receiptsDir, { recursive: true });
2976
+ await fs4.mkdir(this.knowledgeDir, { recursive: true });
2977
+ await fs4.mkdir(this.evidenceDir, { recursive: true });
2978
+ await fs4.mkdir(this.observationDir, { recursive: true });
2979
+ await fs4.mkdir(this.unknownDir, { recursive: true });
2980
+ await fs4.mkdir(this.rulesDir, { recursive: true });
2981
+ await fs4.mkdir(this.receiptsDir, { recursive: true });
2366
2982
  let config;
2367
2983
  if (await this.isInitialized()) {
2368
2984
  config = await this.getConfig();
2369
2985
  } else {
2370
2986
  config = {
2371
2987
  schemaVersion: 1,
2372
- projectName: options.projectName ?? path3.basename(path3.dirname(this.baseDir)) ?? "project",
2988
+ projectName: options.projectName ?? path4.basename(path4.dirname(this.baseDir)) ?? "project",
2373
2989
  defaultScope: options.defaultScope ?? "project",
2374
2990
  dataDir: this.baseDir
2375
2991
  };
2376
2992
  await this.saveConfig(config);
2377
2993
  }
2378
2994
  try {
2379
- await fs3.access(this.relationsFile);
2995
+ await fs4.access(this.relationsFile);
2380
2996
  } catch {
2381
- await fs3.writeFile(this.relationsFile, JSON.stringify([], null, 2), "utf-8");
2997
+ await fs4.writeFile(this.relationsFile, JSON.stringify([], null, 2), "utf-8");
2382
2998
  }
2383
2999
  try {
2384
- await fs3.access(this.metricsFile);
3000
+ await fs4.access(this.metricsFile);
2385
3001
  } catch {
2386
3002
  const defaultMetrics = {
2387
3003
  knowledgeHits: 0,
@@ -2395,17 +3011,17 @@ var Storage = class {
2395
3011
  knowledgePromoted: 0,
2396
3012
  knowledgeSuperseded: 0
2397
3013
  };
2398
- await fs3.writeFile(this.metricsFile, JSON.stringify(defaultMetrics, null, 2), "utf-8");
3014
+ await fs4.writeFile(this.metricsFile, JSON.stringify(defaultMetrics, null, 2), "utf-8");
2399
3015
  }
2400
3016
  return config;
2401
3017
  }
2402
3018
  // --- Config ---
2403
3019
  async getConfig() {
2404
- const raw = await fs3.readFile(this.configFile, "utf-8");
3020
+ const raw = await fs4.readFile(this.configFile, "utf-8");
2405
3021
  return JSON.parse(raw);
2406
3022
  }
2407
3023
  async saveConfig(config) {
2408
- await fs3.writeFile(this.configFile, JSON.stringify(config, null, 2), "utf-8");
3024
+ await fs4.writeFile(this.configFile, JSON.stringify(config, null, 2), "utf-8");
2409
3025
  }
2410
3026
  async updateConfig(updates) {
2411
3027
  const current = await this.getConfig();
@@ -2415,17 +3031,17 @@ var Storage = class {
2415
3031
  }
2416
3032
  // --- Knowledge ---
2417
3033
  async saveKnowledge(knowledge) {
2418
- await fs3.mkdir(this.knowledgeDir, { recursive: true });
3034
+ await fs4.mkdir(this.knowledgeDir, { recursive: true });
2419
3035
  const filename = `${sanitizeFileName(knowledge.id)}.json`;
2420
- const filePath = path3.join(this.knowledgeDir, filename);
2421
- await fs3.writeFile(filePath, JSON.stringify(knowledge, null, 2), "utf-8");
2422
- await fs3.appendFile(this.knowledgeJsonlFile, JSON.stringify(knowledge) + "\n", "utf-8");
3036
+ const filePath = path4.join(this.knowledgeDir, filename);
3037
+ await fs4.writeFile(filePath, JSON.stringify(knowledge, null, 2), "utf-8");
3038
+ await fs4.appendFile(this.knowledgeJsonlFile, JSON.stringify(knowledge) + "\n", "utf-8");
2423
3039
  }
2424
3040
  async getKnowledge(id) {
2425
3041
  const filename = `${sanitizeFileName(id)}.json`;
2426
- const filePath = path3.join(this.knowledgeDir, filename);
3042
+ const filePath = path4.join(this.knowledgeDir, filename);
2427
3043
  try {
2428
- const raw = await fs3.readFile(filePath, "utf-8");
3044
+ const raw = await fs4.readFile(filePath, "utf-8");
2429
3045
  return JSON.parse(raw);
2430
3046
  } catch {
2431
3047
  return void 0;
@@ -2433,11 +3049,11 @@ var Storage = class {
2433
3049
  }
2434
3050
  async getAllKnowledge() {
2435
3051
  try {
2436
- const files = await fs3.readdir(this.knowledgeDir);
3052
+ const files = await fs4.readdir(this.knowledgeDir);
2437
3053
  const items = [];
2438
3054
  for (const file of files) {
2439
3055
  if (!file.endsWith(".json")) continue;
2440
- const raw = await fs3.readFile(path3.join(this.knowledgeDir, file), "utf-8");
3056
+ const raw = await fs4.readFile(path4.join(this.knowledgeDir, file), "utf-8");
2441
3057
  try {
2442
3058
  items.push(JSON.parse(raw));
2443
3059
  } catch {
@@ -2450,9 +3066,9 @@ var Storage = class {
2450
3066
  }
2451
3067
  async deleteKnowledge(id) {
2452
3068
  const filename = `${sanitizeFileName(id)}.json`;
2453
- const filePath = path3.join(this.knowledgeDir, filename);
3069
+ const filePath = path4.join(this.knowledgeDir, filename);
2454
3070
  try {
2455
- await fs3.unlink(filePath);
3071
+ await fs4.unlink(filePath);
2456
3072
  return true;
2457
3073
  } catch {
2458
3074
  return false;
@@ -2460,17 +3076,17 @@ var Storage = class {
2460
3076
  }
2461
3077
  // --- Evidence ---
2462
3078
  async saveEvidence(evidence) {
2463
- await fs3.mkdir(this.evidenceDir, { recursive: true });
3079
+ await fs4.mkdir(this.evidenceDir, { recursive: true });
2464
3080
  const filename = `${sanitizeFileName(evidence.id)}.json`;
2465
- const filePath = path3.join(this.evidenceDir, filename);
2466
- await fs3.writeFile(filePath, JSON.stringify(evidence, null, 2), "utf-8");
2467
- await fs3.appendFile(this.evidenceJsonlFile, JSON.stringify(evidence) + "\n", "utf-8");
3081
+ const filePath = path4.join(this.evidenceDir, filename);
3082
+ await fs4.writeFile(filePath, JSON.stringify(evidence, null, 2), "utf-8");
3083
+ await fs4.appendFile(this.evidenceJsonlFile, JSON.stringify(evidence) + "\n", "utf-8");
2468
3084
  }
2469
3085
  async getEvidence(id) {
2470
3086
  const filename = `${sanitizeFileName(id)}.json`;
2471
- const filePath = path3.join(this.evidenceDir, filename);
3087
+ const filePath = path4.join(this.evidenceDir, filename);
2472
3088
  try {
2473
- const raw = await fs3.readFile(filePath, "utf-8");
3089
+ const raw = await fs4.readFile(filePath, "utf-8");
2474
3090
  return JSON.parse(raw);
2475
3091
  } catch {
2476
3092
  return void 0;
@@ -2478,11 +3094,11 @@ var Storage = class {
2478
3094
  }
2479
3095
  async getAllEvidence() {
2480
3096
  try {
2481
- const files = await fs3.readdir(this.evidenceDir);
3097
+ const files = await fs4.readdir(this.evidenceDir);
2482
3098
  const items = [];
2483
3099
  for (const file of files) {
2484
3100
  if (!file.endsWith(".json")) continue;
2485
- const raw = await fs3.readFile(path3.join(this.evidenceDir, file), "utf-8");
3101
+ const raw = await fs4.readFile(path4.join(this.evidenceDir, file), "utf-8");
2486
3102
  try {
2487
3103
  items.push(JSON.parse(raw));
2488
3104
  } catch {
@@ -2495,17 +3111,17 @@ var Storage = class {
2495
3111
  }
2496
3112
  // --- Observations ---
2497
3113
  async saveObservation(obs) {
2498
- await fs3.mkdir(this.observationDir, { recursive: true });
3114
+ await fs4.mkdir(this.observationDir, { recursive: true });
2499
3115
  const filename = `${sanitizeFileName(obs.id)}.json`;
2500
- const filePath = path3.join(this.observationDir, filename);
2501
- await fs3.writeFile(filePath, JSON.stringify(obs, null, 2), "utf-8");
2502
- await fs3.appendFile(this.observationJsonlFile, JSON.stringify(obs) + "\n", "utf-8");
3116
+ const filePath = path4.join(this.observationDir, filename);
3117
+ await fs4.writeFile(filePath, JSON.stringify(obs, null, 2), "utf-8");
3118
+ await fs4.appendFile(this.observationJsonlFile, JSON.stringify(obs) + "\n", "utf-8");
2503
3119
  }
2504
3120
  async getObservation(id) {
2505
3121
  const filename = `${sanitizeFileName(id)}.json`;
2506
- const filePath = path3.join(this.observationDir, filename);
3122
+ const filePath = path4.join(this.observationDir, filename);
2507
3123
  try {
2508
- const raw = await fs3.readFile(filePath, "utf-8");
3124
+ const raw = await fs4.readFile(filePath, "utf-8");
2509
3125
  return JSON.parse(raw);
2510
3126
  } catch {
2511
3127
  return void 0;
@@ -2513,11 +3129,11 @@ var Storage = class {
2513
3129
  }
2514
3130
  async getAllObservations() {
2515
3131
  try {
2516
- const files = await fs3.readdir(this.observationDir);
3132
+ const files = await fs4.readdir(this.observationDir);
2517
3133
  const items = [];
2518
3134
  for (const file of files) {
2519
3135
  if (!file.endsWith(".json")) continue;
2520
- const raw = await fs3.readFile(path3.join(this.observationDir, file), "utf-8");
3136
+ const raw = await fs4.readFile(path4.join(this.observationDir, file), "utf-8");
2521
3137
  try {
2522
3138
  items.push(JSON.parse(raw));
2523
3139
  } catch {
@@ -2530,9 +3146,9 @@ var Storage = class {
2530
3146
  }
2531
3147
  async deleteObservation(id) {
2532
3148
  const filename = `${sanitizeFileName(id)}.json`;
2533
- const filePath = path3.join(this.observationDir, filename);
3149
+ const filePath = path4.join(this.observationDir, filename);
2534
3150
  try {
2535
- await fs3.unlink(filePath);
3151
+ await fs4.unlink(filePath);
2536
3152
  return true;
2537
3153
  } catch {
2538
3154
  return false;
@@ -2540,17 +3156,17 @@ var Storage = class {
2540
3156
  }
2541
3157
  // --- Unknowns ---
2542
3158
  async saveUnknown(record) {
2543
- await fs3.mkdir(this.unknownDir, { recursive: true });
3159
+ await fs4.mkdir(this.unknownDir, { recursive: true });
2544
3160
  const filename = `${sanitizeFileName(record.id)}.json`;
2545
- const filePath = path3.join(this.unknownDir, filename);
2546
- await fs3.writeFile(filePath, JSON.stringify(record, null, 2), "utf-8");
2547
- await fs3.appendFile(this.unknownsJsonlFile, JSON.stringify(record) + "\n", "utf-8");
3161
+ const filePath = path4.join(this.unknownDir, filename);
3162
+ await fs4.writeFile(filePath, JSON.stringify(record, null, 2), "utf-8");
3163
+ await fs4.appendFile(this.unknownsJsonlFile, JSON.stringify(record) + "\n", "utf-8");
2548
3164
  }
2549
3165
  async getUnknown(id) {
2550
3166
  const filename = `${sanitizeFileName(id)}.json`;
2551
- const filePath = path3.join(this.unknownDir, filename);
3167
+ const filePath = path4.join(this.unknownDir, filename);
2552
3168
  try {
2553
- const raw = await fs3.readFile(filePath, "utf-8");
3169
+ const raw = await fs4.readFile(filePath, "utf-8");
2554
3170
  return JSON.parse(raw);
2555
3171
  } catch {
2556
3172
  return void 0;
@@ -2558,11 +3174,11 @@ var Storage = class {
2558
3174
  }
2559
3175
  async getAllUnknowns() {
2560
3176
  try {
2561
- const files = await fs3.readdir(this.unknownDir);
3177
+ const files = await fs4.readdir(this.unknownDir);
2562
3178
  const items = [];
2563
3179
  for (const file of files) {
2564
3180
  if (!file.endsWith(".json")) continue;
2565
- const raw = await fs3.readFile(path3.join(this.unknownDir, file), "utf-8");
3181
+ const raw = await fs4.readFile(path4.join(this.unknownDir, file), "utf-8");
2566
3182
  try {
2567
3183
  items.push(JSON.parse(raw));
2568
3184
  } catch {
@@ -2575,9 +3191,9 @@ var Storage = class {
2575
3191
  }
2576
3192
  async deleteUnknown(id) {
2577
3193
  const filename = `${sanitizeFileName(id)}.json`;
2578
- const filePath = path3.join(this.unknownDir, filename);
3194
+ const filePath = path4.join(this.unknownDir, filename);
2579
3195
  try {
2580
- await fs3.unlink(filePath);
3196
+ await fs4.unlink(filePath);
2581
3197
  return true;
2582
3198
  } catch {
2583
3199
  return false;
@@ -2586,7 +3202,7 @@ var Storage = class {
2586
3202
  // --- Outcomes (Append-oriented log) ---
2587
3203
  async appendOutcome(outcome) {
2588
3204
  const line = JSON.stringify(outcome) + "\n";
2589
- await fs3.appendFile(this.outcomesFile, line, "utf-8");
3205
+ await fs4.appendFile(this.outcomesFile, line, "utf-8");
2590
3206
  if (outcome.knowledgeId) {
2591
3207
  await this.appendMetricEvent({
2592
3208
  type: "outcome",
@@ -2605,7 +3221,7 @@ var Storage = class {
2605
3221
  }
2606
3222
  async getAllOutcomes() {
2607
3223
  try {
2608
- const raw = await fs3.readFile(this.outcomesFile, "utf-8");
3224
+ const raw = await fs4.readFile(this.outcomesFile, "utf-8");
2609
3225
  const lines = raw.split("\n").filter((l) => l.trim().length > 0);
2610
3226
  return lines.map((l) => JSON.parse(l));
2611
3227
  } catch {
@@ -2615,30 +3231,30 @@ var Storage = class {
2615
3231
  // --- Metric Events (Append-only local log) ---
2616
3232
  async appendMetricEvent(event) {
2617
3233
  try {
2618
- await fs3.mkdir(this.baseDir, { recursive: true });
2619
- await fs3.appendFile(this.eventsFile, JSON.stringify(event) + "\n", "utf-8");
3234
+ await fs4.mkdir(this.baseDir, { recursive: true });
3235
+ await fs4.appendFile(this.eventsFile, JSON.stringify(event) + "\n", "utf-8");
2620
3236
  } catch {
2621
3237
  }
2622
3238
  }
2623
3239
  async getMetricEvents(filter) {
2624
- return readMetricEvents(path3.dirname(this.baseDir), filter);
3240
+ return readMetricEvents(path4.dirname(this.baseDir), filter);
2625
3241
  }
2626
3242
  async resetMetricEvents() {
2627
- return resetMetricEvents(path3.dirname(this.baseDir));
3243
+ return resetMetricEvents(path4.dirname(this.baseDir));
2628
3244
  }
2629
3245
  // --- Investigation Receipts ---
2630
3246
  async saveReceipt(receipt) {
2631
- await fs3.mkdir(this.receiptsDir, { recursive: true });
3247
+ await fs4.mkdir(this.receiptsDir, { recursive: true });
2632
3248
  const filename = `${sanitizeFileName(receipt.id)}.json`;
2633
- const filePath = path3.join(this.receiptsDir, filename);
2634
- await fs3.writeFile(filePath, JSON.stringify(receipt, null, 2), "utf-8");
2635
- await fs3.appendFile(this.receiptsJsonlFile, JSON.stringify(receipt) + "\n", "utf-8");
3249
+ const filePath = path4.join(this.receiptsDir, filename);
3250
+ await fs4.writeFile(filePath, JSON.stringify(receipt, null, 2), "utf-8");
3251
+ await fs4.appendFile(this.receiptsJsonlFile, JSON.stringify(receipt) + "\n", "utf-8");
2636
3252
  }
2637
3253
  async getReceipt(id) {
2638
3254
  const filename = `${sanitizeFileName(id)}.json`;
2639
- const filePath = path3.join(this.receiptsDir, filename);
3255
+ const filePath = path4.join(this.receiptsDir, filename);
2640
3256
  try {
2641
- const raw = await fs3.readFile(filePath, "utf-8");
3257
+ const raw = await fs4.readFile(filePath, "utf-8");
2642
3258
  return JSON.parse(raw);
2643
3259
  } catch {
2644
3260
  return void 0;
@@ -2646,11 +3262,11 @@ var Storage = class {
2646
3262
  }
2647
3263
  async getAllReceipts() {
2648
3264
  try {
2649
- const files = await fs3.readdir(this.receiptsDir);
3265
+ const files = await fs4.readdir(this.receiptsDir);
2650
3266
  const items = [];
2651
3267
  for (const file of files) {
2652
3268
  if (!file.endsWith(".json")) continue;
2653
- const raw = await fs3.readFile(path3.join(this.receiptsDir, file), "utf-8");
3269
+ const raw = await fs4.readFile(path4.join(this.receiptsDir, file), "utf-8");
2654
3270
  try {
2655
3271
  items.push(JSON.parse(raw));
2656
3272
  } catch {
@@ -2664,19 +3280,19 @@ var Storage = class {
2664
3280
  // --- Relations ---
2665
3281
  async getRelations() {
2666
3282
  try {
2667
- const raw = await fs3.readFile(this.relationsFile, "utf-8");
3283
+ const raw = await fs4.readFile(this.relationsFile, "utf-8");
2668
3284
  return JSON.parse(raw);
2669
3285
  } catch {
2670
3286
  return [];
2671
3287
  }
2672
3288
  }
2673
3289
  async saveRelations(relations) {
2674
- await fs3.writeFile(this.relationsFile, JSON.stringify(relations, null, 2), "utf-8");
3290
+ await fs4.writeFile(this.relationsFile, JSON.stringify(relations, null, 2), "utf-8");
2675
3291
  }
2676
3292
  // --- Metrics ---
2677
3293
  async getMetrics() {
2678
3294
  try {
2679
- const raw = await fs3.readFile(this.metricsFile, "utf-8");
3295
+ const raw = await fs4.readFile(this.metricsFile, "utf-8");
2680
3296
  return JSON.parse(raw);
2681
3297
  } catch {
2682
3298
  return {
@@ -2694,14 +3310,14 @@ var Storage = class {
2694
3310
  }
2695
3311
  }
2696
3312
  async saveMetrics(metrics) {
2697
- await fs3.writeFile(this.metricsFile, JSON.stringify(metrics, null, 2), "utf-8");
3313
+ await fs4.writeFile(this.metricsFile, JSON.stringify(metrics, null, 2), "utf-8");
2698
3314
  }
2699
3315
  // --- Rules ---
2700
3316
  getRulesDir() {
2701
3317
  return this.rulesDir;
2702
3318
  }
2703
3319
  async loadRuleSets() {
2704
- const files = await findRuleFiles(path3.dirname(this.baseDir), this.rulesDir);
3320
+ const files = await findRuleFiles(path4.dirname(this.baseDir), this.rulesDir);
2705
3321
  const sets = [];
2706
3322
  for (const f of files) {
2707
3323
  try {
@@ -2712,23 +3328,23 @@ var Storage = class {
2712
3328
  return sets;
2713
3329
  }
2714
3330
  async saveRuleSet(ruleSet, fileName = "default.yml") {
2715
- await fs3.mkdir(this.rulesDir, { recursive: true });
3331
+ await fs4.mkdir(this.rulesDir, { recursive: true });
2716
3332
  const cleanName = fileName.endsWith(".yml") || fileName.endsWith(".yaml") ? fileName : `${fileName}.yml`;
2717
- const targetPath = path3.join(this.rulesDir, cleanName);
3333
+ const targetPath = path4.join(this.rulesDir, cleanName);
2718
3334
  const yaml = stringifyRuleSetYaml(ruleSet);
2719
- await fs3.writeFile(targetPath, yaml, "utf-8");
3335
+ await fs4.writeFile(targetPath, yaml, "utf-8");
2720
3336
  return targetPath;
2721
3337
  }
2722
3338
  async addRule(rule, fileName = "default.yml") {
2723
- await fs3.mkdir(this.rulesDir, { recursive: true });
3339
+ await fs4.mkdir(this.rulesDir, { recursive: true });
2724
3340
  const cleanName = fileName.endsWith(".yml") || fileName.endsWith(".yaml") ? fileName : `${fileName}.yml`;
2725
- const targetPath = path3.join(this.rulesDir, cleanName);
3341
+ const targetPath = path4.join(this.rulesDir, cleanName);
2726
3342
  let existingSet;
2727
3343
  try {
2728
3344
  existingSet = await loadRuleSetFile(targetPath);
2729
3345
  } catch {
2730
3346
  existingSet = {
2731
- name: path3.basename(cleanName, path3.extname(cleanName)),
3347
+ name: path4.basename(cleanName, path4.extname(cleanName)),
2732
3348
  rules: []
2733
3349
  };
2734
3350
  }
@@ -2739,17 +3355,17 @@ var Storage = class {
2739
3355
  existingSet.rules.push(rule);
2740
3356
  }
2741
3357
  const yaml = stringifyRuleSetYaml(existingSet);
2742
- await fs3.writeFile(targetPath, yaml, "utf-8");
3358
+ await fs4.writeFile(targetPath, yaml, "utf-8");
2743
3359
  return targetPath;
2744
3360
  }
2745
3361
  // --- Revisions & Incremental Learning Diff ---
2746
3362
  async appendRevision(snapshot) {
2747
3363
  const line = JSON.stringify(snapshot) + "\n";
2748
- await fs3.appendFile(this.revisionsFile, line, "utf-8");
3364
+ await fs4.appendFile(this.revisionsFile, line, "utf-8");
2749
3365
  }
2750
3366
  async getRevisions() {
2751
3367
  try {
2752
- const raw = await fs3.readFile(this.revisionsFile, "utf-8");
3368
+ const raw = await fs4.readFile(this.revisionsFile, "utf-8");
2753
3369
  const lines = raw.split("\n").filter((l) => l.trim().length > 0);
2754
3370
  return lines.map((l) => JSON.parse(l));
2755
3371
  } catch {
@@ -2888,7 +3504,7 @@ function formatKnowledgeDiff(diff) {
2888
3504
  }
2889
3505
 
2890
3506
  // src/storage/portable.ts
2891
- import YAML2 from "yaml";
3507
+ import YAML3 from "yaml";
2892
3508
  function exportKnowledgeToYaml(items, options = {}) {
2893
3509
  let filtered = items;
2894
3510
  if (options.scope) {
@@ -2919,10 +3535,10 @@ function exportKnowledgeToYaml(items, options = {}) {
2919
3535
  }
2920
3536
  }))
2921
3537
  };
2922
- return YAML2.stringify(payload, { indent: 2, lineWidth: 100 });
3538
+ return YAML3.stringify(payload, { indent: 2, lineWidth: 100 });
2923
3539
  }
2924
3540
  async function importKnowledgeFromYaml(yamlContent, storage) {
2925
- const parsed = YAML2.parse(yamlContent);
3541
+ const parsed = YAML3.parse(yamlContent);
2926
3542
  if (!parsed || typeof parsed !== "object") {
2927
3543
  throw new Error("Invalid YAML: root must be an object");
2928
3544
  }
@@ -2981,10 +3597,10 @@ async function dumpToYaml(storage) {
2981
3597
  relations,
2982
3598
  metrics
2983
3599
  };
2984
- return YAML2.stringify(dump, { indent: 2, lineWidth: 120 });
3600
+ return YAML3.stringify(dump, { indent: 2, lineWidth: 120 });
2985
3601
  }
2986
3602
  async function restoreFromYaml(yamlContent, storage) {
2987
- const parsed = YAML2.parse(yamlContent);
3603
+ const parsed = YAML3.parse(yamlContent);
2988
3604
  if (!parsed || parsed.schemaVersion !== 1) {
2989
3605
  throw new Error(`Unsupported snapshot schema version: ${parsed?.schemaVersion}`);
2990
3606
  }
@@ -3143,8 +3759,8 @@ async function compact(storage, options = {}) {
3143
3759
  }
3144
3760
 
3145
3761
  // src/scanner/filesystem.ts
3146
- import * as fs4 from "fs/promises";
3147
- import * as path4 from "path";
3762
+ import * as fs5 from "fs/promises";
3763
+ import * as path5 from "path";
3148
3764
  var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
3149
3765
  ".git",
3150
3766
  ".bilmem",
@@ -3165,12 +3781,12 @@ var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
3165
3781
  "temp"
3166
3782
  ]);
3167
3783
  function normalizeRelPath(rel) {
3168
- return rel.split(path4.sep).join("/");
3784
+ return rel.split(path5.sep).join("/");
3169
3785
  }
3170
3786
  async function loadGitignorePatterns(rootDir) {
3171
3787
  try {
3172
- const gitignorePath = path4.join(rootDir, ".gitignore");
3173
- const content = await fs4.readFile(gitignorePath, "utf-8");
3788
+ const gitignorePath = path5.join(rootDir, ".gitignore");
3789
+ const content = await fs5.readFile(gitignorePath, "utf-8");
3174
3790
  const regexes = [];
3175
3791
  for (const rawLine of content.split("\n")) {
3176
3792
  const line = rawLine.trim();
@@ -3218,7 +3834,7 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
3218
3834
  return;
3219
3835
  }
3220
3836
  try {
3221
- const real = await fs4.realpath(currentDir);
3837
+ const real = await fs5.realpath(currentDir);
3222
3838
  if (visitedRealPaths.has(real)) {
3223
3839
  return;
3224
3840
  }
@@ -3228,14 +3844,14 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
3228
3844
  }
3229
3845
  let entries;
3230
3846
  try {
3231
- entries = await fs4.readdir(currentDir, { withFileTypes: true });
3847
+ entries = await fs5.readdir(currentDir, { withFileTypes: true });
3232
3848
  } catch {
3233
3849
  return;
3234
3850
  }
3235
3851
  for (const entry of entries) {
3236
3852
  if (fileList.length >= maxFiles) break;
3237
- const fullPath = path4.join(currentDir, entry.name);
3238
- const rawRelPath = path4.relative(root, fullPath);
3853
+ const fullPath = path5.join(currentDir, entry.name);
3854
+ const rawRelPath = path5.relative(root, fullPath);
3239
3855
  const relPath = normalizeRelPath(rawRelPath);
3240
3856
  const lowerName = entry.name.toLowerCase();
3241
3857
  if (gitignoreRules.some((rule) => rule.test(relPath))) {
@@ -3251,16 +3867,16 @@ async function walkDirectory(dir, optionsOrRoot, legacyFileList) {
3251
3867
  if (!includeHidden && entry.name.startsWith(".") && lowerName !== ".env.example") {
3252
3868
  continue;
3253
3869
  }
3254
- const ext = path4.extname(entry.name).toLowerCase();
3870
+ const ext = path5.extname(entry.name).toLowerCase();
3255
3871
  try {
3256
- const stat3 = await fs4.stat(fullPath);
3872
+ const stat4 = await fs5.stat(fullPath);
3257
3873
  const fileInfo = {
3258
3874
  path: fullPath,
3259
3875
  relativePath: relPath,
3260
3876
  name: entry.name,
3261
3877
  ext,
3262
- size: stat3.size,
3263
- mtimeMs: stat3.mtimeMs,
3878
+ size: stat4.size,
3879
+ mtimeMs: stat4.mtimeMs,
3264
3880
  isDirectory: false
3265
3881
  };
3266
3882
  if (!options.filter || options.filter(fileInfo)) {
@@ -3303,9 +3919,9 @@ var ProjectContext = class {
3303
3919
  if (this.textCache.has(key)) {
3304
3920
  return this.textCache.get(key);
3305
3921
  }
3306
- const fullPath = path4.resolve(this.rootDir, relativePath);
3922
+ const fullPath = path5.resolve(this.rootDir, relativePath);
3307
3923
  try {
3308
- const handle = await fs4.open(fullPath, "r");
3924
+ const handle = await fs5.open(fullPath, "r");
3309
3925
  const buffer = Buffer.alloc(maxBytes);
3310
3926
  const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
3311
3927
  await handle.close();
@@ -3734,12 +4350,12 @@ async function analyzeProjectStructure(files, rootDir, registry = defaultRegistr
3734
4350
  }
3735
4351
 
3736
4352
  // src/scanner/markdown.ts
3737
- import * as fs5 from "fs/promises";
3738
- import * as path5 from "path";
3739
- import YAML3 from "yaml";
4353
+ import * as fs6 from "fs/promises";
4354
+ import * as path6 from "path";
4355
+ import YAML4 from "yaml";
3740
4356
  function parseMarkdownDocument(content, filePath, relativePath) {
3741
4357
  const lines = content.split("\n");
3742
- const baseName = path5.basename(relativePath, ".md").toLowerCase();
4358
+ const baseName = path6.basename(relativePath, ".md").toLowerCase();
3743
4359
  let frontmatter = {};
3744
4360
  const headings = [];
3745
4361
  const alerts = [];
@@ -3768,7 +4384,7 @@ function parseMarkdownDocument(content, filePath, relativePath) {
3768
4384
  bodyStartIndex = i + 1;
3769
4385
  try {
3770
4386
  const yamlText = frontmatterLines.join("\n");
3771
- const parsed = YAML3.parse(yamlText);
4387
+ const parsed = YAML4.parse(yamlText);
3772
4388
  if (parsed && typeof parsed === "object") {
3773
4389
  frontmatter = parsed;
3774
4390
  }
@@ -4156,8 +4772,8 @@ var BUILTIN_MARKDOWN_EXTRACTORS = [
4156
4772
  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))) {
4157
4773
  const rawTarget = target.split("#")[0].split("?")[0].trim();
4158
4774
  if (rawTarget) {
4159
- const dir = path5.dirname(doc.relativePath);
4160
- const resolved = dir === "." || path5.isAbsolute(rawTarget) ? rawTarget.replace(/^\.\//, "") : path5.normalize(path5.join(dir, rawTarget));
4775
+ const dir = path6.dirname(doc.relativePath);
4776
+ const resolved = dir === "." || path6.isAbsolute(rawTarget) ? rawTarget.replace(/^\.\//, "") : path6.normalize(path6.join(dir, rawTarget));
4161
4777
  relations.push({
4162
4778
  from: doc.relativePath,
4163
4779
  to: resolved,
@@ -4213,7 +4829,7 @@ function registerMarkdownExtractor(extractor) {
4213
4829
  async function parseMarkdownFile(filePath, relativePath, registry = defaultMarkdownRegistry) {
4214
4830
  let content = "";
4215
4831
  try {
4216
- content = await fs5.readFile(filePath, "utf-8");
4832
+ content = await fs6.readFile(filePath, "utf-8");
4217
4833
  } catch {
4218
4834
  return { observations: [], relations: [] };
4219
4835
  }
@@ -4222,11 +4838,11 @@ async function parseMarkdownFile(filePath, relativePath, registry = defaultMarkd
4222
4838
  }
4223
4839
 
4224
4840
  // src/scanner/git.ts
4225
- import { execFile } from "child_process";
4226
- import { promisify } from "util";
4227
- var execFileAsync = promisify(execFile);
4841
+ import { execFile as execFile2 } from "child_process";
4842
+ import { promisify as promisify2 } from "util";
4843
+ var execFileAsync2 = promisify2(execFile2);
4228
4844
  async function runGit(args, cwd) {
4229
- const { stdout } = await execFileAsync("git", args, {
4845
+ const { stdout } = await execFileAsync2("git", args, {
4230
4846
  cwd,
4231
4847
  maxBuffer: 10 * 1024 * 1024
4232
4848
  });
@@ -4420,9 +5036,9 @@ async function scanGitHistory(cwd, options = {}) {
4420
5036
  }
4421
5037
 
4422
5038
  // src/scanner/source.ts
4423
- import * as fs6 from "fs/promises";
5039
+ import * as fs7 from "fs/promises";
4424
5040
  import * as fsSync from "fs";
4425
- import * as path6 from "path";
5041
+ import * as path7 from "path";
4426
5042
  import { createRequire } from "module";
4427
5043
  import Parser from "web-tree-sitter";
4428
5044
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -4476,7 +5092,7 @@ function getWasmDir() {
4476
5092
  try {
4477
5093
  const req = createRequire(import.meta.url);
4478
5094
  const pkgJson = req.resolve("tree-sitter-wasms/package.json");
4479
- const outDir = path6.join(path6.dirname(pkgJson), "out");
5095
+ const outDir = path7.join(path7.dirname(pkgJson), "out");
4480
5096
  if (fsSync.existsSync(outDir)) {
4481
5097
  wasmBaseDir = outDir;
4482
5098
  return wasmBaseDir;
@@ -4484,8 +5100,8 @@ function getWasmDir() {
4484
5100
  } catch {
4485
5101
  }
4486
5102
  const candidateDirs = [
4487
- path6.resolve(process.cwd(), "node_modules/tree-sitter-wasms/out"),
4488
- path6.resolve(process.cwd(), "../node_modules/tree-sitter-wasms/out")
5103
+ path7.resolve(process.cwd(), "node_modules/tree-sitter-wasms/out"),
5104
+ path7.resolve(process.cwd(), "../node_modules/tree-sitter-wasms/out")
4489
5105
  ];
4490
5106
  for (const dir of candidateDirs) {
4491
5107
  if (fsSync.existsSync(dir)) {
@@ -4498,7 +5114,7 @@ function getWasmDir() {
4498
5114
  async function getParserForWasm(wasmName) {
4499
5115
  const wasmDir = getWasmDir();
4500
5116
  if (!wasmDir) return null;
4501
- const wasmPath = path6.join(wasmDir, wasmName);
5117
+ const wasmPath = path7.join(wasmDir, wasmName);
4502
5118
  if (!fsSync.existsSync(wasmPath)) return null;
4503
5119
  try {
4504
5120
  if (!parserInitialized) {
@@ -4857,7 +5473,7 @@ async function analyzeSourceFile(filePath, relativePath) {
4857
5473
  const relations = [];
4858
5474
  let content = "";
4859
5475
  try {
4860
- content = await fs6.readFile(filePath, "utf-8");
5476
+ content = await fs7.readFile(filePath, "utf-8");
4861
5477
  } catch {
4862
5478
  return { observations, relations };
4863
5479
  }
@@ -4865,8 +5481,8 @@ async function analyzeSourceFile(filePath, relativePath) {
4865
5481
  return { observations, relations };
4866
5482
  }
4867
5483
  const now = (/* @__PURE__ */ new Date()).toISOString();
4868
- const baseName = path6.basename(relativePath);
4869
- const ext = path6.extname(relativePath).toLowerCase();
5484
+ const baseName = path7.basename(relativePath);
5485
+ const ext = path7.extname(relativePath).toLowerCase();
4870
5486
  const wasmName = EXT_TO_WASM[ext];
4871
5487
  if (wasmName) {
4872
5488
  try {
@@ -4892,7 +5508,7 @@ async function analyzeSourceFile(filePath, relativePath) {
4892
5508
  }
4893
5509
 
4894
5510
  // src/scanner/engine.ts
4895
- import * as path7 from "path";
5511
+ import * as path8 from "path";
4896
5512
 
4897
5513
  // src/rules/evaluator.ts
4898
5514
  function globToRegex(glob) {
@@ -5074,9 +5690,9 @@ async function scanRepository(rootDir = process.cwd(), options = {}) {
5074
5690
  const plannedActions = [];
5075
5691
  const config = dryRun ? await storage.isInitialized() ? await storage.getConfig() : {
5076
5692
  schemaVersion: 1,
5077
- projectName: path7.basename(rootDir),
5693
+ projectName: path8.basename(rootDir),
5078
5694
  defaultScope: "project",
5079
- dataDir: path7.join(rootDir, ".bilmem")
5695
+ dataDir: path8.join(rootDir, ".bilmem")
5080
5696
  } : await storage.init();
5081
5697
  const incremental = options.incremental ?? false;
5082
5698
  const lastScanCommit = incremental ? config.lastScanCommit : void 0;
@@ -5149,7 +5765,7 @@ async function scanRepository(rootDir = process.cwd(), options = {}) {
5149
5765
  ];
5150
5766
  for (const o of mdObs) {
5151
5767
  if (o.source.includes(":adr") || o.claim.startsWith("[WARNING]") || o.claim.startsWith("[CAUTION]")) {
5152
- const id = `decision.${path7.basename(o.provenance.path || "doc", ".md")}.${o.provenance.line || "adr"}`;
5768
+ const id = `decision.${path8.basename(o.provenance.path || "doc", ".md")}.${o.provenance.line || "adr"}`;
5153
5769
  candidateLessons.push({
5154
5770
  id,
5155
5771
  type: "decision",
@@ -5262,7 +5878,7 @@ async function scanRepository(rootDir = process.cwd(), options = {}) {
5262
5878
 
5263
5879
  // src/mcp/server.ts
5264
5880
  import { McpApp, z } from "mcponce";
5265
- import * as path8 from "path";
5881
+ import * as path9 from "path";
5266
5882
 
5267
5883
  // src/stats/aggregator.ts
5268
5884
  function formatPercentage(ratio) {
@@ -5387,8 +6003,34 @@ async function computeStats(storage, events, options) {
5387
6003
  }
5388
6004
  };
5389
6005
  const actions = {};
6006
+ const preflight = {
6007
+ runs: 0,
6008
+ ready: 0,
6009
+ needsClarification: 0,
6010
+ needsRevalidation: 0,
6011
+ conflicted: 0,
6012
+ insufficientKnowledge: 0,
6013
+ blockingUnknownsFound: 0,
6014
+ staleKnowledgeFound: 0,
6015
+ pastFailuresSurfaced: 0,
6016
+ questionsSuggested: 0,
6017
+ repeatedChecksAvoided: 0
6018
+ };
5390
6019
  for (const e of events) {
5391
6020
  switch (e.type) {
6021
+ case "preflight":
6022
+ preflight.runs++;
6023
+ if (e.readiness === "READY") preflight.ready++;
6024
+ else if (e.readiness === "NEEDS_CLARIFICATION") preflight.needsClarification++;
6025
+ else if (e.readiness === "NEEDS_REVALIDATION") preflight.needsRevalidation++;
6026
+ else if (e.readiness === "CONFLICTED") preflight.conflicted++;
6027
+ else if (e.readiness === "INSUFFICIENT_KNOWLEDGE") preflight.insufficientKnowledge++;
6028
+ preflight.blockingUnknownsFound += e.blockingUnknownsCount || 0;
6029
+ preflight.staleKnowledgeFound += e.staleKnowledgeCount || 0;
6030
+ preflight.pastFailuresSurfaced += e.pastFailuresCount || 0;
6031
+ preflight.questionsSuggested += e.questionsCount || 0;
6032
+ preflight.repeatedChecksAvoided += e.repeatedChecksAvoidedCount || 0;
6033
+ break;
5392
6034
  case "knowledge_created":
5393
6035
  learning.new++;
5394
6036
  break;
@@ -5559,7 +6201,8 @@ async function computeStats(storage, events, options) {
5559
6201
  questionResolutionRate,
5560
6202
  jevRejectionRate
5561
6203
  },
5562
- health
6204
+ health,
6205
+ preflight: preflight.runs > 0 ? preflight : void 0
5563
6206
  };
5564
6207
  }
5565
6208
  async function computeKnowledgeStats(storage, knowledgeId, events) {
@@ -5628,9 +6271,9 @@ async function computeKnowledgeStats(storage, knowledgeId, events) {
5628
6271
  }
5629
6272
 
5630
6273
  // src/stats/filter.ts
5631
- import { execFile as execFile2 } from "child_process";
5632
- import { promisify as promisify2 } from "util";
5633
- var execFileAsync2 = promisify2(execFile2);
6274
+ import { execFile as execFile3 } from "child_process";
6275
+ import { promisify as promisify3 } from "util";
6276
+ var execFileAsync3 = promisify3(execFile3);
5634
6277
  async function resolveSinceTimestamp(since, projectDir = process.cwd()) {
5635
6278
  const trimmed = since.trim();
5636
6279
  const durationMatch = trimmed.match(/^(\d+)\s*([smhdw])$/i);
@@ -5662,7 +6305,7 @@ async function resolveSinceTimestamp(since, projectDir = process.cwd()) {
5662
6305
  return parsedDate;
5663
6306
  }
5664
6307
  try {
5665
- const { stdout } = await execFileAsync2(
6308
+ const { stdout } = await execFileAsync3(
5666
6309
  "git",
5667
6310
  ["log", "-1", "--format=%ct", trimmed],
5668
6311
  { cwd: projectDir }
@@ -5758,6 +6401,23 @@ function formatStatsReport(report) {
5758
6401
  lines.push(padRow("Frequently rejected", report.health.frequentlyRejected));
5759
6402
  lines.push(padRow("Frequently misleading", report.health.frequentlyMisleading));
5760
6403
  lines.push(padRow("Unused", report.health.unused));
6404
+ if (report.preflight && report.preflight.runs > 0) {
6405
+ lines.push("");
6406
+ lines.push("Preflight");
6407
+ lines.push(padRow("Runs", report.preflight.runs));
6408
+ lines.push(padRow("Ready", report.preflight.ready));
6409
+ lines.push(padRow("Needs clarification", report.preflight.needsClarification));
6410
+ lines.push(padRow("Needs revalidation", report.preflight.needsRevalidation));
6411
+ lines.push(padRow("Conflicted", report.preflight.conflicted));
6412
+ lines.push(padRow("Insufficient knowledge", report.preflight.insufficientKnowledge));
6413
+ lines.push("");
6414
+ lines.push("Before implementation");
6415
+ lines.push(padRow("Blocking unknowns found", report.preflight.blockingUnknownsFound));
6416
+ lines.push(padRow("Stale knowledge found", report.preflight.staleKnowledgeFound));
6417
+ lines.push(padRow("Past failures surfaced", report.preflight.pastFailuresSurfaced));
6418
+ lines.push(padRow("Questions suggested", report.preflight.questionsSuggested));
6419
+ lines.push(padRow("Repeated checks avoided", report.preflight.repeatedChecksAvoided));
6420
+ }
5761
6421
  return lines.join("\n");
5762
6422
  }
5763
6423
  function formatKnowledgeStatsReport(report) {
@@ -5827,17 +6487,56 @@ async function runStats(options = {}) {
5827
6487
 
5828
6488
  // src/mcp/server.ts
5829
6489
  function createBilmemMcpApp(options = {}) {
5830
- const rootDir = options.rootDir ? path8.resolve(options.rootDir) : process.cwd();
5831
- const storage = new Storage(rootDir);
5832
- const index = new SearchIndex();
5833
- let initialized = false;
5834
- async function ensureInitialized() {
5835
- if (!initialized) {
5836
- await storage.init();
5837
- const all = await storage.getAllKnowledge();
5838
- await index.buildIndex(all);
5839
- initialized = true;
6490
+ const contexts = /* @__PURE__ */ new Map();
6491
+ let lastActiveDir = options.rootDir ? path9.resolve(options.rootDir) : null;
6492
+ function isSystemRoot(dir) {
6493
+ const resolved = path9.resolve(dir);
6494
+ return resolved === "/" || path9.parse(resolved).root === resolved;
6495
+ }
6496
+ function resolveTargetDir(requestedPath) {
6497
+ if (requestedPath && requestedPath.trim()) {
6498
+ const resolved = path9.resolve(requestedPath.trim());
6499
+ lastActiveDir = resolved;
6500
+ return resolved;
6501
+ }
6502
+ if (lastActiveDir) {
6503
+ return lastActiveDir;
6504
+ }
6505
+ if (process.env.BILMEM_PROJECT_DIR) {
6506
+ const envDir = path9.resolve(process.env.BILMEM_PROJECT_DIR);
6507
+ lastActiveDir = envDir;
6508
+ return envDir;
6509
+ }
6510
+ const cwd = process.cwd();
6511
+ if (cwd && !isSystemRoot(cwd)) {
6512
+ lastActiveDir = cwd;
6513
+ return cwd;
6514
+ }
6515
+ throw new Error(
6516
+ `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", ... }).`
6517
+ );
6518
+ }
6519
+ async function getProjectContext(requestedPath) {
6520
+ const targetDir = resolveTargetDir(requestedPath);
6521
+ let ctx = contexts.get(targetDir);
6522
+ if (!ctx) {
6523
+ const storage = new Storage(targetDir);
6524
+ const index = new SearchIndex();
6525
+ ctx = {
6526
+ rootDir: targetDir,
6527
+ storage,
6528
+ index,
6529
+ initialized: false
6530
+ };
6531
+ contexts.set(targetDir, ctx);
5840
6532
  }
6533
+ if (!ctx.initialized) {
6534
+ await ctx.storage.init();
6535
+ const all = await ctx.storage.getAllKnowledge();
6536
+ await ctx.index.buildIndex(all);
6537
+ ctx.initialized = true;
6538
+ }
6539
+ return ctx;
5841
6540
  }
5842
6541
  const app = new McpApp({
5843
6542
  name: options.name ?? "bilmem",
@@ -5847,6 +6546,7 @@ function createBilmemMcpApp(options = {}) {
5847
6546
  name: "bilmem_resolve",
5848
6547
  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.",
5849
6548
  inputSchema: {
6549
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
5850
6550
  question: z.string().describe("The question, goal, or uncertainty to resolve"),
5851
6551
  positive: z.array(z.string()).optional().describe("Applicable context cues (e.g. server-session, node, postgres)"),
5852
6552
  negative: z.array(z.string()).optional().describe("Inapplicable context cues (e.g. stateless-jwt, browser, sqlite)"),
@@ -5854,8 +6554,8 @@ function createBilmemMcpApp(options = {}) {
5854
6554
  maxDepth: z.number().optional().describe("Max candidates to inspect (default: 5)")
5855
6555
  },
5856
6556
  handler: async (args) => {
5857
- await ensureInitialized();
5858
- const retriever = new Retriever(index, storage);
6557
+ const ctx = await getProjectContext(args.path);
6558
+ const retriever = new Retriever(ctx.index, ctx.storage);
5859
6559
  const res = await resolveEpistemicQuery(
5860
6560
  {
5861
6561
  question: args.question,
@@ -5864,7 +6564,7 @@ function createBilmemMcpApp(options = {}) {
5864
6564
  scope: args.scope,
5865
6565
  maxDepth: args.maxDepth
5866
6566
  },
5867
- storage,
6567
+ ctx.storage,
5868
6568
  retriever
5869
6569
  );
5870
6570
  return {
@@ -5881,10 +6581,53 @@ function createBilmemMcpApp(options = {}) {
5881
6581
  };
5882
6582
  }
5883
6583
  });
6584
+ app.tool({
6585
+ name: "bilmem_preflight",
6586
+ description: "Determine what must be known before beginning a proposed goal (surfaces constraints, relevant lessons, past failures, stale knowledge, and blocking unknowns).",
6587
+ inputSchema: {
6588
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
6589
+ goal: z.string().describe("The proposed goal, feature, or work to analyze before implementation"),
6590
+ positive: z.array(z.string()).optional().describe("Applicable context cues (e.g. authentication, postgres)"),
6591
+ negative: z.array(z.string()).optional().describe("Inapplicable context cues (e.g. browser, sqlite)"),
6592
+ deep: z.boolean().optional().describe("Check targeted git diff / unindexed files for staleness")
6593
+ },
6594
+ handler: async (args) => {
6595
+ const ctx = await getProjectContext(args.path);
6596
+ const retriever = new Retriever(ctx.index, ctx.storage);
6597
+ const res = await executePreflight(
6598
+ {
6599
+ goal: args.goal,
6600
+ positive: args.positive,
6601
+ negative: args.negative,
6602
+ deep: args.deep
6603
+ },
6604
+ ctx.storage,
6605
+ retriever,
6606
+ ctx.index
6607
+ );
6608
+ return {
6609
+ goal: res.goal,
6610
+ readiness: res.readiness,
6611
+ known: res.known.slice(0, 5).map((k) => k.statement),
6612
+ constraints: res.constraints.map((c) => c.statement),
6613
+ lessons: res.lessons.slice(0, 5).map((l) => l.statement),
6614
+ pastFailures: res.past_failures.slice(0, 5).map((f) => f.statement),
6615
+ blockingUnknowns: res.unknowns.filter((u) => u.blocking).map((u) => u.question || u.statement || u.id),
6616
+ stale: res.stale.map((s) => s.id),
6617
+ questions: res.questions.slice(0, 5).map((q) => q.question),
6618
+ next: res.next.slice(0, 3).map((n) => ({
6619
+ type: n.type,
6620
+ target: n.target,
6621
+ reason: n.reason
6622
+ }))
6623
+ };
6624
+ }
6625
+ });
5884
6626
  app.tool({
5885
6627
  name: "bilmem_lookup",
5886
6628
  description: "Lookup reusable knowledge, conventions, and lessons using JEV contextual reranking. Returns compact, highly applicable knowledge units.",
5887
6629
  inputSchema: {
6630
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
5888
6631
  query: z.string().optional().describe("Query text, intent keywords, or problem description"),
5889
6632
  positive: z.array(z.string()).optional().describe("Contextual positive cues (e.g. node, esm, server-session)"),
5890
6633
  negative: z.array(z.string()).optional().describe("Contextual negative cues (e.g. commonjs, browser)"),
@@ -5892,8 +6635,8 @@ function createBilmemMcpApp(options = {}) {
5892
6635
  limit: z.number().optional().describe("Maximum number of results to return (default 5)")
5893
6636
  },
5894
6637
  handler: async (args) => {
5895
- await ensureInitialized();
5896
- const retriever = new Retriever(index, storage);
6638
+ const ctx = await getProjectContext(args.path);
6639
+ const retriever = new Retriever(ctx.index, ctx.storage);
5897
6640
  const results = await retriever.lookup({
5898
6641
  query: args.query,
5899
6642
  positive: args.positive,
@@ -5920,6 +6663,7 @@ function createBilmemMcpApp(options = {}) {
5920
6663
  name: "bilmem_observe",
5921
6664
  description: "Record a new observation from code, git, test execution, or environment. Preserves provenance.",
5922
6665
  inputSchema: {
6666
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
5923
6667
  claim: z.string().describe("What was observed"),
5924
6668
  source: z.string().describe("Source identifier (e.g. file:src/auth.ts#L42, git:commit:81ac23, user:correction)"),
5925
6669
  confidence: z.number().optional().describe("Confidence 0.0 - 1.0 (default: 0.85)"),
@@ -5928,7 +6672,7 @@ function createBilmemMcpApp(options = {}) {
5928
6672
  details: z.record(z.any()).optional().describe("Optional structured metadata")
5929
6673
  },
5930
6674
  handler: async (args) => {
5931
- await ensureInitialized();
6675
+ const ctx = await getProjectContext(args.path);
5932
6676
  const obs = createObservation({
5933
6677
  source: args.source,
5934
6678
  claim: args.claim,
@@ -5942,10 +6686,10 @@ function createBilmemMcpApp(options = {}) {
5942
6686
  negative: args.negative,
5943
6687
  details: args.details
5944
6688
  });
5945
- await storage.saveObservation(obs);
6689
+ await ctx.storage.saveObservation(obs);
5946
6690
  const evidence = createEvidence(obs, "supports");
5947
- await storage.saveEvidence(evidence);
5948
- const existing = await storage.getAllKnowledge();
6691
+ await ctx.storage.saveEvidence(evidence);
6692
+ const existing = await ctx.storage.getAllKnowledge();
5949
6693
  const rec = reconcileKnowledge(
5950
6694
  {
5951
6695
  statement: obs.claim,
@@ -5966,10 +6710,10 @@ function createBilmemMcpApp(options = {}) {
5966
6710
  evidence: [evidence.id, obs.source]
5967
6711
  },
5968
6712
  existing,
5969
- storage
6713
+ ctx.storage
5970
6714
  );
5971
- await storage.saveKnowledge(updatedKnowledge);
5972
- await index.upsert(updatedKnowledge);
6715
+ await ctx.storage.saveKnowledge(updatedKnowledge);
6716
+ await ctx.index.upsert(updatedKnowledge);
5973
6717
  return {
5974
6718
  observationId: obs.id,
5975
6719
  evidenceId: evidence.id,
@@ -5983,6 +6727,7 @@ function createBilmemMcpApp(options = {}) {
5983
6727
  name: "bilmem_learn",
5984
6728
  description: "Learn from a completed task trajectory or explicit user correction. Converts experiences into refined, reusable lessons.",
5985
6729
  inputSchema: {
6730
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
5986
6731
  goal: z.string().describe("The task or goal that was pursued"),
5987
6732
  taskId: z.string().optional().describe("Task identifier"),
5988
6733
  outcome: z.enum(["success", "failure", "reverted", "neutral"]).describe("Outcome of the task"),
@@ -5998,16 +6743,16 @@ function createBilmemMcpApp(options = {}) {
5998
6743
  }).optional().describe("Explicit user correction, if any occurred")
5999
6744
  },
6000
6745
  handler: async (args) => {
6001
- await ensureInitialized();
6746
+ const ctx = await getProjectContext(args.path);
6002
6747
  if (args.userCorrection) {
6003
6748
  const { observation, knowledge } = processUserCorrection(args.userCorrection);
6004
- await storage.saveObservation(observation);
6005
- await storage.saveEvidence(createEvidence(observation, "supports"));
6006
- const existing = await storage.getAllKnowledge();
6749
+ await ctx.storage.saveObservation(observation);
6750
+ await ctx.storage.saveEvidence(createEvidence(observation, "supports"));
6751
+ const existing = await ctx.storage.getAllKnowledge();
6007
6752
  const rec = reconcileKnowledge(knowledge, existing);
6008
- const { updatedKnowledge } = applyReconciliation(rec, knowledge, existing, storage);
6009
- await storage.saveKnowledge(updatedKnowledge);
6010
- await index.upsert(updatedKnowledge);
6753
+ const { updatedKnowledge } = applyReconciliation(rec, knowledge, existing, ctx.storage);
6754
+ await ctx.storage.saveKnowledge(updatedKnowledge);
6755
+ await ctx.index.upsert(updatedKnowledge);
6011
6756
  return {
6012
6757
  type: "user_correction",
6013
6758
  knowledgeId: updatedKnowledge.id,
@@ -6023,20 +6768,20 @@ function createBilmemMcpApp(options = {}) {
6023
6768
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6024
6769
  finalOutcome: args.outcome
6025
6770
  };
6026
- const existing = await storage.getAllKnowledge();
6771
+ const existing = await ctx.storage.getAllKnowledge();
6027
6772
  const learningResult = learnFromTrajectory(trajectory, existing);
6028
6773
  for (const obs of learningResult.observations) {
6029
- await storage.saveObservation(obs);
6774
+ await ctx.storage.saveObservation(obs);
6030
6775
  }
6031
6776
  for (const ev of learningResult.evidence) {
6032
- await storage.saveEvidence(ev);
6777
+ await ctx.storage.saveEvidence(ev);
6033
6778
  }
6034
6779
  let updatedKnowledge;
6035
6780
  let action = "NEW";
6036
6781
  for (const item of learningResult.reconciliations) {
6037
6782
  if (item.appliedKnowledge) {
6038
- await storage.saveKnowledge(item.appliedKnowledge);
6039
- await index.upsert(item.appliedKnowledge);
6783
+ await ctx.storage.saveKnowledge(item.appliedKnowledge);
6784
+ await ctx.index.upsert(item.appliedKnowledge);
6040
6785
  updatedKnowledge = item.appliedKnowledge;
6041
6786
  action = item.action;
6042
6787
  }
@@ -6048,9 +6793,9 @@ function createBilmemMcpApp(options = {}) {
6048
6793
  result: args.outcome,
6049
6794
  context: { positive: updatedKnowledge.positive, negative: updatedKnowledge.negative }
6050
6795
  });
6051
- await storage.appendOutcome(out);
6796
+ await ctx.storage.appendOutcome(out);
6052
6797
  applyOutcomeToKnowledge(updatedKnowledge, out);
6053
- await storage.saveKnowledge(updatedKnowledge);
6798
+ await ctx.storage.saveKnowledge(updatedKnowledge);
6054
6799
  return {
6055
6800
  type: "trajectory_lesson",
6056
6801
  action,
@@ -6072,12 +6817,13 @@ function createBilmemMcpApp(options = {}) {
6072
6817
  name: "bilmem_explain",
6073
6818
  description: "Explain why a knowledge ID exists: statement, evidence provenance, confidence, support/contradiction history, and applicable context.",
6074
6819
  inputSchema: {
6820
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
6075
6821
  id: z.string().describe("The knowledge ID to explain"),
6076
6822
  history: z.boolean().optional().describe("Whether to format lifecycle transition history (--history)")
6077
6823
  },
6078
6824
  handler: async (args) => {
6079
- await ensureInitialized();
6080
- const k = await storage.getKnowledge(args.id);
6825
+ const ctx = await getProjectContext(args.path);
6826
+ const k = await ctx.storage.getKnowledge(args.id);
6081
6827
  if (!k) {
6082
6828
  return { error: `Knowledge not found: "${args.id}"` };
6083
6829
  }
@@ -6111,6 +6857,7 @@ function createBilmemMcpApp(options = {}) {
6111
6857
  name: "bilmem_unknown",
6112
6858
  description: "Query or record first-class unknowns (NOT_OBSERVED, INSUFFICIENT_EVIDENCE, CONFLICTING_EVIDENCE, UNRESOLVABLE_LOCALLY) to prevent repeating useless investigations.",
6113
6859
  inputSchema: {
6860
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
6114
6861
  action: z.enum(["query", "check", "record", "resolve"]).describe("Action to perform"),
6115
6862
  id: z.string().optional().describe("Unknown ID or query subject"),
6116
6863
  reason: z.enum(["NOT_OBSERVED", "INSUFFICIENT_EVIDENCE", "CONFLICTING_EVIDENCE", "STALE", "UNRESOLVABLE_LOCALLY"]).optional(),
@@ -6119,16 +6866,16 @@ function createBilmemMcpApp(options = {}) {
6119
6866
  question: z.string().optional().describe("Clarifying question for the user or next agent")
6120
6867
  },
6121
6868
  handler: async (args) => {
6122
- await ensureInitialized();
6869
+ const ctx = await getProjectContext(args.path);
6123
6870
  if (args.action === "query") {
6124
- const list = await storage.getAllUnknowns();
6871
+ const list = await ctx.storage.getAllUnknowns();
6125
6872
  return {
6126
6873
  count: list.length,
6127
6874
  unknowns: list
6128
6875
  };
6129
6876
  }
6130
6877
  if (args.action === "check") {
6131
- const list = await storage.getAllUnknowns();
6878
+ const list = await ctx.storage.getAllUnknowns();
6132
6879
  const checkResult = hasBeenInvestigated(list, args.id || "");
6133
6880
  return checkResult;
6134
6881
  }
@@ -6142,12 +6889,12 @@ function createBilmemMcpApp(options = {}) {
6142
6889
  checked: args.checked ?? [],
6143
6890
  next: args.question ? { type: "ask", question: args.question } : void 0
6144
6891
  });
6145
- await storage.saveUnknown(record);
6892
+ await ctx.storage.saveUnknown(record);
6146
6893
  return { success: true, recorded: record };
6147
6894
  }
6148
6895
  if (args.action === "resolve") {
6149
6896
  if (!args.id) throw new Error("ID is required to resolve an unknown");
6150
- const deleted = await storage.deleteUnknown(args.id);
6897
+ const deleted = await ctx.storage.deleteUnknown(args.id);
6151
6898
  return { success: deleted, resolvedId: args.id };
6152
6899
  }
6153
6900
  return { error: "Unknown action" };
@@ -6157,16 +6904,16 @@ function createBilmemMcpApp(options = {}) {
6157
6904
  name: "bilmem_scan",
6158
6905
  description: "Trigger a language-independent scan of the repository filesystem, markdown documentation, and git history.",
6159
6906
  inputSchema: {
6160
- path: z.string().optional().describe("Root directory to scan (defaults to project root)"),
6907
+ path: z.string().optional().describe("Project root directory to scan (defaults to active workspace or current working directory)"),
6161
6908
  incremental: z.boolean().optional().describe("Whether to scan incrementally from last scanned git commit")
6162
6909
  },
6163
6910
  handler: async (args) => {
6164
- const targetDir = args.path ? path8.resolve(args.path) : rootDir;
6165
- const report = await scanRepository(targetDir, {
6911
+ const ctx = await getProjectContext(args.path);
6912
+ const report = await scanRepository(ctx.rootDir, {
6166
6913
  incremental: args.incremental ?? false
6167
6914
  });
6168
- const all = await storage.getAllKnowledge();
6169
- await index.buildIndex(all);
6915
+ const all = await ctx.storage.getAllKnowledge();
6916
+ await ctx.index.buildIndex(all);
6170
6917
  return report;
6171
6918
  }
6172
6919
  });
@@ -6174,13 +6921,14 @@ function createBilmemMcpApp(options = {}) {
6174
6921
  name: "bilmem_stats",
6175
6922
  description: "Inspect local-first statistics, knowledge health, and effectiveness metrics (measuring reuse, investigation avoidance, and JEV accuracy). Read-only.",
6176
6923
  inputSchema: {
6924
+ path: z.string().optional().describe("Project root directory (defaults to active workspace or current working directory)"),
6177
6925
  since: z.string().optional().describe("Time window (e.g. 24h, 7d, 30d) or Git revision (e.g. HEAD~20, commit-sha)"),
6178
6926
  knowledgeId: z.string().optional().describe("Specific knowledge ID to inspect single-item lifecycle and diagnostics")
6179
6927
  },
6180
6928
  handler: async (args) => {
6181
- await ensureInitialized();
6929
+ const ctx = await getProjectContext(args.path);
6182
6930
  const res = await runStats({
6183
- rootDir,
6931
+ rootDir: ctx.rootDir,
6184
6932
  since: args.since,
6185
6933
  knowledgeId: args.knowledgeId
6186
6934
  });
@@ -6194,20 +6942,20 @@ function createBilmemMcpApp(options = {}) {
6194
6942
  }
6195
6943
 
6196
6944
  // src/install/index.ts
6197
- import * as path17 from "path";
6945
+ import * as path19 from "path";
6198
6946
  import * as readline from "readline/promises";
6199
6947
 
6200
6948
  // src/install/adapters/cursor.ts
6201
- import * as path12 from "path";
6949
+ import * as path13 from "path";
6202
6950
  import * as os from "os";
6203
6951
 
6204
6952
  // src/install/adapters/base.ts
6205
- import * as path11 from "path";
6206
- import * as fs8 from "fs/promises";
6953
+ import * as path12 from "path";
6954
+ import * as fs9 from "fs/promises";
6207
6955
 
6208
6956
  // src/install/atomic.ts
6209
- import * as fs7 from "fs/promises";
6210
- import * as path9 from "path";
6957
+ import * as fs8 from "fs/promises";
6958
+ import * as path10 from "path";
6211
6959
 
6212
6960
  // src/install/instructions.ts
6213
6961
  var CURRENT_INTEGRATION_VERSION = 1;
@@ -6219,6 +6967,8 @@ function getAgentInstructionText(version = CURRENT_INTEGRATION_VERSION) {
6219
6967
  ## Bilmem Knowledge Runtime
6220
6968
  Bilmem is available through MCP.
6221
6969
 
6970
+ 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.
6971
+
6222
6972
  Use \`bilmem_resolve\` when:
6223
6973
  - uncertain about project-specific behavior,
6224
6974
  - multiple plausible approaches exist,
@@ -6242,18 +6992,171 @@ function extractInstructionVersion(content) {
6242
6992
  function hasBilmemInstruction(content) {
6243
6993
  return content.includes(BILMEM_START_MARKER) && content.includes(BILMEM_END_MARKER);
6244
6994
  }
6995
+ function getBilmemSkillContent(version = CURRENT_INTEGRATION_VERSION) {
6996
+ return `---
6997
+ name: bilmem
6998
+ description: >-
6999
+ 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).
7000
+ ---
7001
+ <!-- bilmem-version: ${version} -->
7002
+
7003
+ # Bilmem Epistemic Knowledge Runtime
7004
+
7005
+ Bilmem is an epistemic knowledge engine designed to answer:
7006
+ > **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?**
7007
+
7008
+ 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.
7009
+
7010
+ ---
7011
+
7012
+ ## \u26A1 Core Epistemic Commitments
7013
+
7014
+ 1. **Cheaper than not knowing:** Querying Bilmem or resolving uncertainty is significantly cheaper than re-investigating, guessing, or recovering from incorrect architectural assumptions.
7015
+ 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.
7016
+ 3. **No queries on trivial edits:** Do not query Bilmem for simple typo fixes, formatting, or obvious local syntax changes.
7017
+ 4. **Receipts & Honest Confidence:** Bilmem uses explainable confidence tiers (\`low\`, \`medium\`, \`high\`) and returns receipts explaining why knowledge was accepted or rejected.
7018
+
7019
+ ---
7020
+
7021
+ ## \u{1F680} Progressive Disclosure: Tool Runbook
7022
+
7023
+ > [!TIP]
7024
+ > **Project Directory Target (\`path\` parameter)**:
7025
+ > 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.
7026
+
7027
+ ### 1. Before Substantial Implementation: \`bilmem_preflight\`
7028
+ Run \`bilmem_preflight\` with your proposed goal before modifying architecture, core logic, or adding features:
7029
+ \`\`\`json
7030
+ {
7031
+ "path": "/path/to/project",
7032
+ "goal": "Add OAuth login with Google",
7033
+ "positive": ["auth", "oauth", "session"],
7034
+ "negative": ["stateless-jwt"]
7035
+ }
7036
+ \`\`\`
7037
+ **Returns:**
7038
+ - Applicable verified constraints & architectural rules.
7039
+ - Relevant lessons from past implementations or regressions.
7040
+ - Past failures in similar domains.
7041
+ - Blocking unknowns that must be investigated first.
7042
+ - Stale dependencies that need review.
7043
+ - Next recommended investigation actions.
7044
+
7045
+ ### 2. When Uncertain or Resolving Contradictions: \`bilmem_resolve\`
7046
+ Use \`bilmem_resolve\` when encountering ambiguous codebase behavior, multiple plausible paths, or conflicting assumptions:
7047
+ \`\`\`json
7048
+ {
7049
+ "path": "/path/to/project",
7050
+ "question": "How are user sessions renewed and validated?",
7051
+ "positive": ["server-session", "postgres", "redis"],
7052
+ "negative": ["client-cookie-only"]
7053
+ }
7054
+ \`\`\`
7055
+ **Returns:**
7056
+ - \`state\`: \`resolved\` (sufficient facts known), \`needs_investigation\`, or \`contradiction\`.
7057
+ - \`known\`: Verified statements applicable to the current context.
7058
+ - \`rejected\`: Inapplicable knowledge units filtered out by negative cues.
7059
+ - \`missing\`: Critical gaps that need observation before proceeding.
7060
+ - \`next\`: The cheapest concrete investigation step.
7061
+
7062
+ ### 3. Exact Knowledge Retrieval: \`bilmem_lookup\`
7063
+ Quickly look up specific conventions, lessons, or rules:
7064
+ \`\`\`json
7065
+ {
7066
+ "path": "/path/to/project",
7067
+ "query": "session timeout configuration",
7068
+ "limit": 5
7069
+ }
7070
+ \`\`\`
7071
+
7072
+ ### 4. Recording Fresh Discoveries: \`bilmem_observe\`
7073
+ Record verified discoveries, configuration invariants, or environment realities:
7074
+ \`\`\`json
7075
+ {
7076
+ "path": "/path/to/project",
7077
+ "claim": "Production session cookies require SameSite=Strict and Secure flags",
7078
+ "source": "file:src/auth/session.ts#L42",
7079
+ "positive": ["production", "cookie", "auth"],
7080
+ "negative": ["development"]
7081
+ }
7082
+ \`\`\`
7083
+
7084
+ ### 5. Learning from Corrections & Trajectories: \`bilmem_learn\`
7085
+ After resolving a difficult bug, fixing a regression, or receiving a critical user correction, distill the trajectory:
7086
+ \`\`\`json
7087
+ {
7088
+ "path": "/path/to/project",
7089
+ "goal": "Fix refresh token rotation loop",
7090
+ "outcome": "success",
7091
+ "userCorrection": {
7092
+ "topic": "session-auth",
7093
+ "incorrectAssumption": "Token refresh does not require a concurrent mutex",
7094
+ "correctFact": "Token refresh requires a concurrency mutex to avoid 401 race conditions"
7095
+ }
7096
+ }
7097
+ \`\`\`
7098
+ Bilmem runs staged reconciliation: extracts observations, creates candidate lessons, evaluates polar contradictions, and reconciles into durable knowledge.
7099
+
7100
+ ### 6. Recording Known Unknowns: \`bilmem_unknown\`
7101
+ When you realize critical context is missing, record an unknown instead of making a silent assumption:
7102
+ \`\`\`json
7103
+ {
7104
+ "path": "/path/to/project",
7105
+ "action": "record",
7106
+ "id": "staging.redis-cache",
7107
+ "question": "Does the staging database share the Redis cache cluster with production?"
7108
+ }
7109
+ \`\`\`
7110
+
7111
+ ### 7. Auditing Effectiveness & Reuse: \`bilmem_stats\`
7112
+ Verify how well Bilmem is reducing repeated investigation:
7113
+ \`\`\`json
7114
+ {
7115
+ "path": "/path/to/project",
7116
+ "since": "7d"
7117
+ }
7118
+ \`\`\`
7119
+
7120
+ ---
7121
+
7122
+ ## \u{1F4BB} CLI Quick Reference
7123
+
7124
+ All tools are also available directly on the command line:
7125
+
7126
+ | Command | Purpose |
7127
+ | :--- | :--- |
7128
+ | \`bilmem preflight "<goal>"\` | Pre-implementation readiness & constraint analysis |
7129
+ | \`bilmem resolve "<question>"\` | Epistemic resolution with context cues & receipts |
7130
+ | \`bilmem lookup "<query>"\` | Fast contextual lookup of verified knowledge |
7131
+ | \`bilmem observe "<claim>" --source <src>\` | Record verified evidence or invariant |
7132
+ | \`bilmem learn --trajectory "<text>"\` | Reconcile candidate lessons into knowledge |
7133
+ | \`bilmem scan [--dry-run]\` | Conservative filesystem & convention discovery |
7134
+ | \`bilmem stats [--since 7d]\` | Measure knowledge reuse and effectiveness |
7135
+ | \`bilmem doctor [--fix]\` | Verify and repair integrations and storage |
7136
+ `;
7137
+ }
7138
+ function hasBilmemSkill(content) {
7139
+ return content.includes("name: bilmem") && content.includes("---");
7140
+ }
7141
+ function extractSkillVersion(content) {
7142
+ const match = content.match(/<!--\s*bilmem-version:\s*(\d+)\s*-->/);
7143
+ if (match && match[1]) {
7144
+ return parseInt(match[1], 10);
7145
+ }
7146
+ return null;
7147
+ }
6245
7148
 
6246
7149
  // src/install/atomic.ts
6247
7150
  async function writeAtomic(filePath, content) {
6248
- const dir = path9.dirname(filePath);
6249
- await fs7.mkdir(dir, { recursive: true });
7151
+ const dir = path10.dirname(filePath);
7152
+ await fs8.mkdir(dir, { recursive: true });
6250
7153
  const tmpPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
6251
7154
  try {
6252
- await fs7.writeFile(tmpPath, content, "utf-8");
6253
- await fs7.rename(tmpPath, filePath);
7155
+ await fs8.writeFile(tmpPath, content, "utf-8");
7156
+ await fs8.rename(tmpPath, filePath);
6254
7157
  } catch (err) {
6255
7158
  try {
6256
- await fs7.unlink(tmpPath);
7159
+ await fs8.unlink(tmpPath);
6257
7160
  } catch {
6258
7161
  }
6259
7162
  throw err;
@@ -6261,7 +7164,7 @@ async function writeAtomic(filePath, content) {
6261
7164
  }
6262
7165
  async function safeReadJson(filePath) {
6263
7166
  try {
6264
- const raw = await fs7.readFile(filePath, "utf-8");
7167
+ const raw = await fs8.readFile(filePath, "utf-8");
6265
7168
  try {
6266
7169
  const data = JSON.parse(raw);
6267
7170
  return { exists: true, data, raw };
@@ -6341,7 +7244,7 @@ async function updateMarkdownInstructionFile(filePath, desiredBlock = getAgentIn
6341
7244
  let existingContent = "";
6342
7245
  let exists = false;
6343
7246
  try {
6344
- existingContent = await fs7.readFile(filePath, "utf-8");
7247
+ existingContent = await fs8.readFile(filePath, "utf-8");
6345
7248
  exists = true;
6346
7249
  } catch (err) {
6347
7250
  if (err.code !== "ENOENT") throw err;
@@ -6376,7 +7279,7 @@ async function updateMarkdownInstructionFile(filePath, desiredBlock = getAgentIn
6376
7279
  async function removeMarkdownInstructionFile(filePath, dryRun = false) {
6377
7280
  let content = "";
6378
7281
  try {
6379
- content = await fs7.readFile(filePath, "utf-8");
7282
+ content = await fs8.readFile(filePath, "utf-8");
6380
7283
  } catch (err) {
6381
7284
  if (err.code === "ENOENT") {
6382
7285
  return { removed: false, fileDeleted: false };
@@ -6391,7 +7294,7 @@ async function removeMarkdownInstructionFile(filePath, dryRun = false) {
6391
7294
  if (stripped.length === 0) {
6392
7295
  if (!dryRun) {
6393
7296
  try {
6394
- await fs7.unlink(filePath);
7297
+ await fs8.unlink(filePath);
6395
7298
  } catch {
6396
7299
  }
6397
7300
  }
@@ -6404,21 +7307,57 @@ async function removeMarkdownInstructionFile(filePath, dryRun = false) {
6404
7307
  }
6405
7308
  async function pathExists(p) {
6406
7309
  try {
6407
- await fs7.access(p);
7310
+ await fs8.access(p);
6408
7311
  return true;
6409
7312
  } catch {
6410
7313
  return false;
6411
7314
  }
6412
7315
  }
7316
+ async function updateSkillFile(filePath, content, dryRun = false) {
7317
+ let existingContent = "";
7318
+ let exists = false;
7319
+ try {
7320
+ existingContent = await fs8.readFile(filePath, "utf-8");
7321
+ exists = true;
7322
+ } catch (err) {
7323
+ if (err.code !== "ENOENT") throw err;
7324
+ }
7325
+ if (exists && existingContent.trim() === content.trim()) {
7326
+ return { changed: false, created: false, originalExisted: true };
7327
+ }
7328
+ if (!dryRun) {
7329
+ await writeAtomic(filePath, content.trimEnd() + "\n");
7330
+ }
7331
+ return { changed: true, created: !exists, originalExisted: exists };
7332
+ }
7333
+ async function removeSkillFile(filePath, dryRun = false) {
7334
+ try {
7335
+ await fs8.access(filePath);
7336
+ } catch {
7337
+ return { removed: false, fileDeleted: false };
7338
+ }
7339
+ if (!dryRun) {
7340
+ try {
7341
+ await fs8.unlink(filePath);
7342
+ const parentDir = path10.dirname(filePath);
7343
+ const remaining = await fs8.readdir(parentDir);
7344
+ if (remaining.length === 0) {
7345
+ await fs8.rmdir(parentDir);
7346
+ }
7347
+ } catch {
7348
+ }
7349
+ }
7350
+ return { removed: true, fileDeleted: true };
7351
+ }
6413
7352
  async function isExecutableInPath(command) {
6414
7353
  const envPath = process.env.PATH;
6415
7354
  if (!envPath) return false;
6416
- const dirs = envPath.split(path9.delimiter);
7355
+ const dirs = envPath.split(path10.delimiter);
6417
7356
  for (const dir of dirs) {
6418
7357
  if (!dir) continue;
6419
- const fullPath = path9.join(dir, command);
7358
+ const fullPath = path10.join(dir, command);
6420
7359
  try {
6421
- await fs7.access(fullPath, fs7.constants.X_OK);
7360
+ await fs8.access(fullPath, fs8.constants.X_OK);
6422
7361
  return true;
6423
7362
  } catch {
6424
7363
  }
@@ -6427,9 +7366,9 @@ async function isExecutableInPath(command) {
6427
7366
  }
6428
7367
 
6429
7368
  // src/install/ownership.ts
6430
- import * as path10 from "path";
7369
+ import * as path11 from "path";
6431
7370
  function getIntegrationsFilePath(rootDir) {
6432
- return path10.join(rootDir, ".bilmem", "integrations.json");
7371
+ return path11.join(rootDir, ".bilmem", "integrations.json");
6433
7372
  }
6434
7373
  async function loadIntegrationsMetadata(rootDir) {
6435
7374
  const metaPath = getIntegrationsFilePath(rootDir);
@@ -6475,6 +7414,7 @@ async function getIntegrationRecord(rootDir, agentId) {
6475
7414
 
6476
7415
  // src/install/adapters/base.ts
6477
7416
  var BaseAgentAdapter = class {
7417
+ aliases;
6478
7418
  supportedScopes = ["project", "global"];
6479
7419
  async inspect(context) {
6480
7420
  const configPath = this.resolveConfigPath(context);
@@ -6488,12 +7428,14 @@ var BaseAgentAdapter = class {
6488
7428
  if (read.exists && !read.error && read.data?.mcpServers?.bilmem) {
6489
7429
  mcpConfigured = true;
6490
7430
  const b = read.data.mcpServers.bilmem;
6491
- mcpCommandMatches = b.command === targetCommand && Array.isArray(b.args) && b.args.length === targetArgs.length && b.args.every((v, i) => v === targetArgs[i]);
7431
+ const targetMatch = b.command === targetCommand && Array.isArray(b.args) && b.args.length === targetArgs.length && b.args.every((v, i) => v === targetArgs[i]);
7432
+ const wrapperMatch = Array.isArray(b.args) && b.args.some((arg) => typeof arg === "string" && arg.includes("bilmem"));
7433
+ mcpCommandMatches = Boolean(targetMatch || wrapperMatch);
6492
7434
  }
6493
7435
  let instructionInstalled = false;
6494
7436
  let instructionVersion;
6495
7437
  try {
6496
- const content = await fs8.readFile(instructionPath, "utf-8");
7438
+ const content = await fs9.readFile(instructionPath, "utf-8");
6497
7439
  instructionInstalled = hasBilmemInstruction(content);
6498
7440
  const extracted = extractInstructionVersion(content);
6499
7441
  if (extracted !== null) {
@@ -6501,6 +7443,20 @@ var BaseAgentAdapter = class {
6501
7443
  }
6502
7444
  } catch {
6503
7445
  }
7446
+ let skillInstalled = false;
7447
+ let skillVersion;
7448
+ const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
7449
+ if (skillPath) {
7450
+ try {
7451
+ const skillContent = await fs9.readFile(skillPath, "utf-8");
7452
+ skillInstalled = hasBilmemSkill(skillContent);
7453
+ const extracted = extractSkillVersion(skillContent);
7454
+ if (extracted !== null) {
7455
+ skillVersion = extracted;
7456
+ }
7457
+ } catch {
7458
+ }
7459
+ }
6504
7460
  return {
6505
7461
  id: this.id,
6506
7462
  name: this.name,
@@ -6510,9 +7466,12 @@ var BaseAgentAdapter = class {
6510
7466
  mcpCommandMatches,
6511
7467
  instructionInstalled,
6512
7468
  instructionVersion,
7469
+ skillInstalled: skillPath ? skillInstalled : void 0,
7470
+ skillVersion: skillPath ? skillVersion : void 0,
6513
7471
  currentInstructionVersion: CURRENT_INTEGRATION_VERSION,
6514
7472
  configPath,
6515
7473
  instructionPath,
7474
+ skillPath,
6516
7475
  details: {
6517
7476
  rawConfigExisted: read.exists,
6518
7477
  configError: read.error
@@ -6532,7 +7491,7 @@ var BaseAgentAdapter = class {
6532
7491
  const storage = context.storage || new Storage(context.projectDir);
6533
7492
  if (!await storage.isInitialized()) {
6534
7493
  if (!options.dryRun) {
6535
- await storage.init({ projectName: path11.basename(context.projectDir) });
7494
+ await storage.init({ projectName: path12.basename(context.projectDir) });
6536
7495
  }
6537
7496
  projectInitialized = true;
6538
7497
  actionsTaken.push("Initialized .bilmem/ storage");
@@ -6557,6 +7516,19 @@ var BaseAgentAdapter = class {
6557
7516
  if (instRes.changed) {
6558
7517
  actionsTaken.push(`Installed/updated agent instruction block in ${instructionPath}`);
6559
7518
  }
7519
+ let skillChanged = false;
7520
+ const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
7521
+ if (skillPath) {
7522
+ const skillRes = await updateSkillFile(
7523
+ skillPath,
7524
+ getBilmemSkillContent(CURRENT_INTEGRATION_VERSION),
7525
+ options.dryRun ?? false
7526
+ );
7527
+ skillChanged = skillRes.changed;
7528
+ if (skillRes.changed) {
7529
+ actionsTaken.push(`Installed/updated Antigravity skill in ${skillPath}`);
7530
+ }
7531
+ }
6560
7532
  if (context.scope === "project") {
6561
7533
  await recordIntegration(
6562
7534
  context.projectDir,
@@ -6564,9 +7536,11 @@ var BaseAgentAdapter = class {
6564
7536
  {
6565
7537
  scope: options.scope,
6566
7538
  mcp: true,
6567
- mcpConfigPath: path11.relative(context.projectDir, configPath),
7539
+ mcpConfigPath: path12.relative(context.projectDir, configPath),
6568
7540
  instructions: true,
6569
- instructionPath: path11.relative(context.projectDir, instructionPath),
7541
+ instructionPath: path12.relative(context.projectDir, instructionPath),
7542
+ skill: skillPath ? true : void 0,
7543
+ skillPath: skillPath ? path12.relative(context.projectDir, skillPath) : void 0,
6570
7544
  version: CURRENT_INTEGRATION_VERSION,
6571
7545
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
6572
7546
  },
@@ -6577,7 +7551,7 @@ var BaseAgentAdapter = class {
6577
7551
  if (!resolvable && !options.dryRun) {
6578
7552
  warnings.push(`Executable "${targetCommand}" is not in system PATH. Ensure it can be run by ${this.name}.`);
6579
7553
  }
6580
- const changesMade = mcpRes.changed || instRes.changed || projectInitialized;
7554
+ const changesMade = mcpRes.changed || instRes.changed || skillChanged || projectInitialized;
6581
7555
  if (!changesMade) {
6582
7556
  actionsTaken.push("Already up to date");
6583
7557
  }
@@ -6588,6 +7562,8 @@ var BaseAgentAdapter = class {
6588
7562
  scope: options.scope,
6589
7563
  mcpChanged: mcpRes.changed,
6590
7564
  instructionChanged: instRes.changed,
7565
+ skillChanged,
7566
+ skillPath,
6591
7567
  projectInitialized,
6592
7568
  dryRun: options.dryRun,
6593
7569
  actionsTaken,
@@ -6607,10 +7583,19 @@ var BaseAgentAdapter = class {
6607
7583
  if (instRes.removed) {
6608
7584
  actionsTaken.push(`Removed Bilmem instruction block from ${instructionPath}`);
6609
7585
  }
7586
+ let skillRemoved = false;
7587
+ const skillPath = this.resolveSkillPath ? this.resolveSkillPath(context) : void 0;
7588
+ if (skillPath) {
7589
+ const skillRes = await removeSkillFile(skillPath, options.dryRun ?? false);
7590
+ skillRemoved = skillRes.removed;
7591
+ if (skillRes.removed) {
7592
+ actionsTaken.push(`Removed Antigravity skill from ${skillPath}`);
7593
+ }
7594
+ }
6610
7595
  if (context.scope === "project") {
6611
7596
  await removeIntegration(context.projectDir, this.id, options.dryRun ?? false);
6612
7597
  }
6613
- const anyRemoved = mcpRes.removed || instRes.removed;
7598
+ const anyRemoved = mcpRes.removed || instRes.removed || skillRemoved;
6614
7599
  if (!anyRemoved) {
6615
7600
  actionsTaken.push("No Bilmem configuration found to remove");
6616
7601
  }
@@ -6621,6 +7606,8 @@ var BaseAgentAdapter = class {
6621
7606
  scope: options.scope,
6622
7607
  mcpRemoved: mcpRes.removed,
6623
7608
  instructionRemoved: instRes.removed,
7609
+ skillRemoved,
7610
+ skillPath,
6624
7611
  dryRun: options.dryRun,
6625
7612
  actionsTaken,
6626
7613
  message: anyRemoved ? `Successfully uninstalled Bilmem from ${this.name}` : `No Bilmem configuration found for ${this.name}`
@@ -6632,6 +7619,7 @@ var BaseAgentAdapter = class {
6632
7619
  const recommendations = [];
6633
7620
  let mcpValid = false;
6634
7621
  let instructionValid = false;
7622
+ let skillValid = true;
6635
7623
  if (!state.mcpConfigured) {
6636
7624
  issues.push(`MCP server "bilmem" is not configured in ${state.configPath}`);
6637
7625
  recommendations.push(`Run "bilmem install ${this.id}" to configure MCP`);
@@ -6652,17 +7640,31 @@ var BaseAgentAdapter = class {
6652
7640
  } else {
6653
7641
  instructionValid = true;
6654
7642
  }
7643
+ if (state.skillPath) {
7644
+ if (!state.skillInstalled) {
7645
+ skillValid = false;
7646
+ issues.push(`Skill file missing from ${state.skillPath}`);
7647
+ recommendations.push(`Run "bilmem install ${this.id}" to install the skill`);
7648
+ } else if (state.skillVersion !== CURRENT_INTEGRATION_VERSION) {
7649
+ skillValid = false;
7650
+ issues.push(
7651
+ `Skill file version (${state.skillVersion || "legacy"}) is outdated (current: ${CURRENT_INTEGRATION_VERSION})`
7652
+ );
7653
+ recommendations.push(`Run "bilmem doctor --fix" to update the skill`);
7654
+ }
7655
+ }
6655
7656
  const targetCommand = context.bilmemCommand || "bilmem";
6656
7657
  const commandResolvable = await isExecutableInPath(targetCommand);
6657
7658
  if (!commandResolvable) {
6658
7659
  issues.push(`Command "${targetCommand}" is not found in PATH`);
6659
7660
  recommendations.push(`Ensure "${targetCommand}" is installed globally or in your PATH`);
6660
7661
  }
6661
- const valid = mcpValid && instructionValid && commandResolvable;
7662
+ const valid = mcpValid && instructionValid && skillValid && commandResolvable;
6662
7663
  return {
6663
7664
  valid,
6664
7665
  mcpValid,
6665
7666
  instructionValid,
7667
+ skillValid,
6666
7668
  commandResolvable,
6667
7669
  issues,
6668
7670
  recommendations
@@ -6676,23 +7678,23 @@ var CursorAdapter = class extends BaseAgentAdapter {
6676
7678
  name = "Cursor";
6677
7679
  resolveConfigPath(context) {
6678
7680
  if (context.scope === "project") {
6679
- return path12.join(context.projectDir, ".cursor", "mcp.json");
7681
+ return path13.join(context.projectDir, ".cursor", "mcp.json");
6680
7682
  }
6681
7683
  const home = context.globalDir || os.homedir();
6682
- return path12.join(home, ".cursor", "mcp.json");
7684
+ return path13.join(home, ".cursor", "mcp.json");
6683
7685
  }
6684
7686
  resolveInstructionPath(context) {
6685
7687
  if (context.scope === "project") {
6686
- return path12.join(context.projectDir, ".cursorrules");
7688
+ return path13.join(context.projectDir, ".cursorrules");
6687
7689
  }
6688
7690
  const home = context.globalDir || os.homedir();
6689
- return path12.join(home, ".cursorrules");
7691
+ return path13.join(home, ".cursorrules");
6690
7692
  }
6691
7693
  async detect(context) {
6692
7694
  const reasons = [];
6693
- const dotCursor = path12.join(context.projectDir, ".cursor");
6694
- const dotCursorrules = path12.join(context.projectDir, ".cursorrules");
6695
- const mcpConfig = path12.join(context.projectDir, ".cursor", "mcp.json");
7695
+ const dotCursor = path13.join(context.projectDir, ".cursor");
7696
+ const dotCursorrules = path13.join(context.projectDir, ".cursorrules");
7697
+ const mcpConfig = path13.join(context.projectDir, ".cursor", "mcp.json");
6696
7698
  const hasDotCursor = await pathExists(dotCursor);
6697
7699
  const hasCursorrules = await pathExists(dotCursorrules);
6698
7700
  const hasMcp = await pathExists(mcpConfig);
@@ -6700,7 +7702,7 @@ var CursorAdapter = class extends BaseAgentAdapter {
6700
7702
  if (hasCursorrules) reasons.push("Found .cursorrules file");
6701
7703
  if (hasMcp) reasons.push("Found .cursor/mcp.json file");
6702
7704
  const globalHome = context.globalDir || os.homedir();
6703
- const globalCursor = path12.join(globalHome, ".cursor");
7705
+ const globalCursor = path13.join(globalHome, ".cursor");
6704
7706
  const hasGlobalCursor = await pathExists(globalCursor);
6705
7707
  if (hasGlobalCursor) reasons.push(`Found global directory at ${globalCursor}`);
6706
7708
  const hasExecutable = await isExecutableInPath("cursor");
@@ -6717,31 +7719,31 @@ var CursorAdapter = class extends BaseAgentAdapter {
6717
7719
  };
6718
7720
 
6719
7721
  // src/install/adapters/claude.ts
6720
- import * as path13 from "path";
7722
+ import * as path14 from "path";
6721
7723
  import * as os2 from "os";
6722
7724
  var ClaudeAdapter = class extends BaseAgentAdapter {
6723
7725
  id = "claude";
6724
7726
  name = "Claude Code";
6725
7727
  resolveConfigPath(context) {
6726
7728
  if (context.scope === "project") {
6727
- return path13.join(context.projectDir, ".claude", "mcp.json");
7729
+ return path14.join(context.projectDir, ".claude", "mcp.json");
6728
7730
  }
6729
7731
  const home = context.globalDir || os2.homedir();
6730
- return path13.join(home, ".claude", "mcp.json");
7732
+ return path14.join(home, ".claude", "mcp.json");
6731
7733
  }
6732
7734
  resolveInstructionPath(context) {
6733
7735
  if (context.scope === "project") {
6734
- return path13.join(context.projectDir, "CLAUDE.md");
7736
+ return path14.join(context.projectDir, "CLAUDE.md");
6735
7737
  }
6736
7738
  const home = context.globalDir || os2.homedir();
6737
- return path13.join(home, ".claude", "CLAUDE.md");
7739
+ return path14.join(home, ".claude", "CLAUDE.md");
6738
7740
  }
6739
7741
  async detect(context) {
6740
7742
  const reasons = [];
6741
- const dotClaude = path13.join(context.projectDir, ".claude");
6742
- const claudeMd = path13.join(context.projectDir, "CLAUDE.md");
6743
- const claudeJson = path13.join(context.projectDir, "claude.json");
6744
- const mcpConfig = path13.join(context.projectDir, ".claude", "mcp.json");
7743
+ const dotClaude = path14.join(context.projectDir, ".claude");
7744
+ const claudeMd = path14.join(context.projectDir, "CLAUDE.md");
7745
+ const claudeJson = path14.join(context.projectDir, "claude.json");
7746
+ const mcpConfig = path14.join(context.projectDir, ".claude", "mcp.json");
6745
7747
  const hasDotClaude = await pathExists(dotClaude);
6746
7748
  const hasClaudeMd = await pathExists(claudeMd);
6747
7749
  const hasClaudeJson = await pathExists(claudeJson);
@@ -6751,7 +7753,7 @@ var ClaudeAdapter = class extends BaseAgentAdapter {
6751
7753
  if (hasClaudeJson) reasons.push("Found claude.json file");
6752
7754
  if (hasMcp) reasons.push("Found .claude/mcp.json file");
6753
7755
  const globalHome = context.globalDir || os2.homedir();
6754
- const globalClaude = path13.join(globalHome, ".claude");
7756
+ const globalClaude = path14.join(globalHome, ".claude");
6755
7757
  const hasGlobalClaude = await pathExists(globalClaude);
6756
7758
  if (hasGlobalClaude) reasons.push(`Found global directory at ${globalClaude}`);
6757
7759
  const hasExecutable = await isExecutableInPath("claude");
@@ -6768,31 +7770,31 @@ var ClaudeAdapter = class extends BaseAgentAdapter {
6768
7770
  };
6769
7771
 
6770
7772
  // src/install/adapters/codex.ts
6771
- import * as path14 from "path";
7773
+ import * as path15 from "path";
6772
7774
  import * as os3 from "os";
6773
7775
  var CodexAdapter = class extends BaseAgentAdapter {
6774
7776
  id = "codex";
6775
7777
  name = "Codex";
6776
7778
  resolveConfigPath(context) {
6777
7779
  if (context.scope === "project") {
6778
- return path14.join(context.projectDir, ".codex", "mcp.json");
7780
+ return path15.join(context.projectDir, ".codex", "mcp.json");
6779
7781
  }
6780
7782
  const home = context.globalDir || os3.homedir();
6781
- return path14.join(home, ".codex", "mcp.json");
7783
+ return path15.join(home, ".codex", "mcp.json");
6782
7784
  }
6783
7785
  resolveInstructionPath(context) {
6784
7786
  if (context.scope === "project") {
6785
- return path14.join(context.projectDir, "CODEX.md");
7787
+ return path15.join(context.projectDir, "CODEX.md");
6786
7788
  }
6787
7789
  const home = context.globalDir || os3.homedir();
6788
- return path14.join(home, ".codex", "instructions.md");
7790
+ return path15.join(home, ".codex", "instructions.md");
6789
7791
  }
6790
7792
  async detect(context) {
6791
7793
  const reasons = [];
6792
- const dotCodex = path14.join(context.projectDir, ".codex");
6793
- const codexMd = path14.join(context.projectDir, "CODEX.md");
6794
- const codexJson = path14.join(context.projectDir, "codex.json");
6795
- const mcpConfig = path14.join(context.projectDir, ".codex", "mcp.json");
7794
+ const dotCodex = path15.join(context.projectDir, ".codex");
7795
+ const codexMd = path15.join(context.projectDir, "CODEX.md");
7796
+ const codexJson = path15.join(context.projectDir, "codex.json");
7797
+ const mcpConfig = path15.join(context.projectDir, ".codex", "mcp.json");
6796
7798
  const hasDotCodex = await pathExists(dotCodex);
6797
7799
  const hasCodexMd = await pathExists(codexMd);
6798
7800
  const hasCodexJson = await pathExists(codexJson);
@@ -6802,7 +7804,7 @@ var CodexAdapter = class extends BaseAgentAdapter {
6802
7804
  if (hasCodexJson) reasons.push("Found codex.json file");
6803
7805
  if (hasMcp) reasons.push("Found .codex/mcp.json file");
6804
7806
  const globalHome = context.globalDir || os3.homedir();
6805
- const globalCodex = path14.join(globalHome, ".codex");
7807
+ const globalCodex = path15.join(globalHome, ".codex");
6806
7808
  const hasGlobalCodex = await pathExists(globalCodex);
6807
7809
  if (hasGlobalCodex) reasons.push(`Found global directory at ${globalCodex}`);
6808
7810
  const hasExecutable = await isExecutableInPath("codex");
@@ -6819,31 +7821,31 @@ var CodexAdapter = class extends BaseAgentAdapter {
6819
7821
  };
6820
7822
 
6821
7823
  // src/install/adapters/gemini.ts
6822
- import * as path15 from "path";
7824
+ import * as path16 from "path";
6823
7825
  import * as os4 from "os";
6824
7826
  var GeminiAdapter = class extends BaseAgentAdapter {
6825
7827
  id = "gemini";
6826
7828
  name = "Gemini CLI";
6827
7829
  resolveConfigPath(context) {
6828
7830
  if (context.scope === "project") {
6829
- return path15.join(context.projectDir, ".gemini", "settings.json");
7831
+ return path16.join(context.projectDir, ".gemini", "settings.json");
6830
7832
  }
6831
7833
  const home = context.globalDir || os4.homedir();
6832
- return path15.join(home, ".gemini", "settings.json");
7834
+ return path16.join(home, ".gemini", "settings.json");
6833
7835
  }
6834
7836
  resolveInstructionPath(context) {
6835
7837
  if (context.scope === "project") {
6836
- return path15.join(context.projectDir, "GEMINI.md");
7838
+ return path16.join(context.projectDir, "GEMINI.md");
6837
7839
  }
6838
7840
  const home = context.globalDir || os4.homedir();
6839
- return path15.join(home, ".gemini", "GEMINI.md");
7841
+ return path16.join(home, ".gemini", "GEMINI.md");
6840
7842
  }
6841
7843
  async detect(context) {
6842
7844
  const reasons = [];
6843
- const dotGemini = path15.join(context.projectDir, ".gemini");
6844
- const geminiMd = path15.join(context.projectDir, "GEMINI.md");
6845
- const geminiJson = path15.join(context.projectDir, "gemini.json");
6846
- const settingsJson = path15.join(context.projectDir, ".gemini", "settings.json");
7845
+ const dotGemini = path16.join(context.projectDir, ".gemini");
7846
+ const geminiMd = path16.join(context.projectDir, "GEMINI.md");
7847
+ const geminiJson = path16.join(context.projectDir, "gemini.json");
7848
+ const settingsJson = path16.join(context.projectDir, ".gemini", "settings.json");
6847
7849
  const hasDotGemini = await pathExists(dotGemini);
6848
7850
  const hasGeminiMd = await pathExists(geminiMd);
6849
7851
  const hasGeminiJson = await pathExists(geminiJson);
@@ -6853,7 +7855,7 @@ var GeminiAdapter = class extends BaseAgentAdapter {
6853
7855
  if (hasGeminiJson) reasons.push("Found gemini.json file");
6854
7856
  if (hasSettingsJson) reasons.push("Found .gemini/settings.json file");
6855
7857
  const globalHome = context.globalDir || os4.homedir();
6856
- const globalGemini = path15.join(globalHome, ".gemini");
7858
+ const globalGemini = path16.join(globalHome, ".gemini");
6857
7859
  const hasGlobalGemini = await pathExists(globalGemini);
6858
7860
  if (hasGlobalGemini) reasons.push(`Found global directory at ${globalGemini}`);
6859
7861
  const hasExecutable = await isExecutableInPath("gemini");
@@ -6869,12 +7871,110 @@ var GeminiAdapter = class extends BaseAgentAdapter {
6869
7871
  }
6870
7872
  };
6871
7873
 
7874
+ // src/install/adapters/antigravity.ts
7875
+ import * as path17 from "path";
7876
+ import * as os5 from "os";
7877
+ import * as fs10 from "fs";
7878
+ function resolveAntigravityGlobalDir(context) {
7879
+ const home = context.globalDir || os5.homedir();
7880
+ if (path17.basename(home) === "config" && path17.basename(path17.dirname(home)) === ".gemini") {
7881
+ return home;
7882
+ }
7883
+ return path17.join(home, ".gemini", "config");
7884
+ }
7885
+ var AntigravityAdapter = class extends BaseAgentAdapter {
7886
+ id = "antigravity";
7887
+ name = "Antigravity";
7888
+ resolveConfigPath(context) {
7889
+ if (context.scope === "project") {
7890
+ const rootMcp = path17.join(context.projectDir, "mcp_config.json");
7891
+ if (fs10.existsSync(rootMcp)) {
7892
+ return rootMcp;
7893
+ }
7894
+ return path17.join(context.projectDir, ".agents", "mcp_config.json");
7895
+ }
7896
+ const globalRoot = resolveAntigravityGlobalDir(context);
7897
+ return path17.join(globalRoot, "mcp_config.json");
7898
+ }
7899
+ resolveSkillPath(context) {
7900
+ if (context.scope === "project") {
7901
+ return path17.join(context.projectDir, ".agents", "skills", "bilmem", "SKILL.md");
7902
+ }
7903
+ const globalRoot = resolveAntigravityGlobalDir(context);
7904
+ return path17.join(globalRoot, "skills", "bilmem", "SKILL.md");
7905
+ }
7906
+ resolveInstructionPath(context) {
7907
+ if (context.scope === "project") {
7908
+ const geminiMd = path17.join(context.projectDir, "GEMINI.md");
7909
+ const agentsMd = path17.join(context.projectDir, "AGENTS.md");
7910
+ const rulesBilmem = path17.join(context.projectDir, ".agents", "rules", "bilmem.md");
7911
+ if (fs10.existsSync(rulesBilmem)) {
7912
+ return rulesBilmem;
7913
+ }
7914
+ if (fs10.existsSync(geminiMd)) {
7915
+ return geminiMd;
7916
+ }
7917
+ if (fs10.existsSync(agentsMd)) {
7918
+ return agentsMd;
7919
+ }
7920
+ return path17.join(context.projectDir, ".agents", "rules", "bilmem.md");
7921
+ }
7922
+ const globalRoot = resolveAntigravityGlobalDir(context);
7923
+ return path17.join(globalRoot, "rules", "bilmem.md");
7924
+ }
7925
+ async detect(context) {
7926
+ const reasons = [];
7927
+ const dotAgents = path17.join(context.projectDir, ".agents");
7928
+ const skillsDir = path17.join(context.projectDir, ".agents", "skills");
7929
+ const agentsMcp = path17.join(context.projectDir, ".agents", "mcp_config.json");
7930
+ const rootMcp = path17.join(context.projectDir, "mcp_config.json");
7931
+ const geminiMd = path17.join(context.projectDir, "GEMINI.md");
7932
+ const agentsMd = path17.join(context.projectDir, "AGENTS.md");
7933
+ const hasDotAgents = await pathExists(dotAgents);
7934
+ const hasSkillsDir = await pathExists(skillsDir);
7935
+ const hasAgentsMcp = await pathExists(agentsMcp);
7936
+ const hasRootMcp = await pathExists(rootMcp);
7937
+ const hasGeminiMd = await pathExists(geminiMd);
7938
+ const hasAgentsMd = await pathExists(agentsMd);
7939
+ if (hasDotAgents) reasons.push("Found .agents/ directory");
7940
+ if (hasSkillsDir) reasons.push("Found .agents/skills/ directory");
7941
+ if (hasAgentsMcp) reasons.push("Found .agents/mcp_config.json");
7942
+ if (hasRootMcp) reasons.push("Found mcp_config.json file");
7943
+ if (hasGeminiMd) reasons.push("Found GEMINI.md rule file");
7944
+ if (hasAgentsMd) reasons.push("Found AGENTS.md rule file");
7945
+ const home = context.globalDir || os5.homedir();
7946
+ const globalConfig = resolveAntigravityGlobalDir(context);
7947
+ const globalIde = path17.join(home, ".gemini", "antigravity-ide");
7948
+ const globalCli = path17.join(home, ".gemini", "antigravity-cli");
7949
+ const globalAgy = path17.join(home, ".gemini", "antigravity");
7950
+ const hasGlobalConfig = await pathExists(globalConfig);
7951
+ const hasGlobalIde = await pathExists(globalIde);
7952
+ const hasGlobalCli = await pathExists(globalCli);
7953
+ const hasGlobalAgy = await pathExists(globalAgy);
7954
+ if (hasGlobalConfig) reasons.push(`Found Antigravity global config at ${globalConfig}`);
7955
+ if (hasGlobalIde) reasons.push(`Found Antigravity IDE at ${globalIde}`);
7956
+ if (hasGlobalCli) reasons.push(`Found Antigravity CLI at ${globalCli}`);
7957
+ if (hasGlobalAgy) reasons.push(`Found Antigravity directory at ${globalAgy}`);
7958
+ const hasAgyExec = await isExecutableInPath("agy");
7959
+ if (hasAgyExec) reasons.push('Found "agy" command in PATH');
7960
+ const detected = reasons.length > 0;
7961
+ const confidence = hasDotAgents || hasSkillsDir || hasAgentsMcp || hasRootMcp || hasGeminiMd || hasAgentsMd ? "high" : hasGlobalConfig || hasGlobalIde || hasGlobalCli || hasGlobalAgy || hasAgyExec ? "medium" : "low";
7962
+ return {
7963
+ detected,
7964
+ confidence: detected ? confidence : "low",
7965
+ reasons,
7966
+ supportedScopes: this.supportedScopes
7967
+ };
7968
+ }
7969
+ };
7970
+
6872
7971
  // src/install/registry.ts
6873
7972
  var adapters = [
6874
7973
  new CursorAdapter(),
6875
7974
  new ClaudeAdapter(),
6876
7975
  new CodexAdapter(),
6877
- new GeminiAdapter()
7976
+ new GeminiAdapter(),
7977
+ new AntigravityAdapter()
6878
7978
  ];
6879
7979
  function getAllAdapters() {
6880
7980
  return [...adapters];
@@ -6894,7 +7994,7 @@ async function detectAgents(context) {
6894
7994
  }
6895
7995
 
6896
7996
  // src/install/doctor.ts
6897
- import * as path16 from "path";
7997
+ import * as path18 from "path";
6898
7998
  async function runDoctor(context) {
6899
7999
  const storage = context.storage || new Storage(context.projectDir);
6900
8000
  let isInit = false;
@@ -6986,6 +8086,26 @@ async function runDoctor(context) {
6986
8086
  canFix = true;
6987
8087
  }
6988
8088
  }
8089
+ let skillStatus = "none";
8090
+ let skillDetails;
8091
+ if (state.skillPath) {
8092
+ if (state.skillInstalled) {
8093
+ if (state.skillVersion === CURRENT_INTEGRATION_VERSION) {
8094
+ skillStatus = "ok";
8095
+ } else {
8096
+ skillStatus = "outdated";
8097
+ skillDetails = `Version ${state.skillVersion || "legacy"} (current: ${CURRENT_INTEGRATION_VERSION})`;
8098
+ issuesCount++;
8099
+ canFix = true;
8100
+ }
8101
+ } else {
8102
+ skillStatus = "missing";
8103
+ if (state.detected) {
8104
+ issuesCount++;
8105
+ canFix = true;
8106
+ }
8107
+ }
8108
+ }
6989
8109
  let cmdStatus = "ok";
6990
8110
  let cmdDetails;
6991
8111
  if (!commandResolvable) {
@@ -6999,9 +8119,11 @@ async function runDoctor(context) {
6999
8119
  scope: context.scope,
7000
8120
  mcp: { status: mcpStatus, details: mcpDetails },
7001
8121
  instructions: { status: instStatus, details: instDetails },
8122
+ skill: state.skillPath ? { status: skillStatus, details: skillDetails } : void 0,
7002
8123
  command: { status: cmdStatus, details: cmdDetails },
7003
8124
  configPath: state.configPath,
7004
- instructionPath: state.instructionPath
8125
+ instructionPath: state.instructionPath,
8126
+ skillPath: state.skillPath
7005
8127
  });
7006
8128
  }
7007
8129
  if (!core.storage.initialized) {
@@ -7021,18 +8143,19 @@ async function runDoctorFix(context, dryRun = false) {
7021
8143
  const agentsFixed = {};
7022
8144
  if (!await storage.isInitialized()) {
7023
8145
  if (!dryRun) {
7024
- await storage.init({ projectName: path16.basename(context.projectDir) });
8146
+ await storage.init({ projectName: path18.basename(context.projectDir) });
7025
8147
  }
7026
8148
  coreFixed.push("Initialized .bilmem/ storage repository");
7027
8149
  }
7028
8150
  const adapters2 = getAllAdapters();
7029
8151
  for (const adapter of adapters2) {
7030
8152
  const state = await adapter.inspect(context);
7031
- const shouldFix = state.detected || state.mcpConfigured || state.instructionInstalled;
8153
+ const shouldFix = state.detected || state.mcpConfigured || state.instructionInstalled || Boolean(state.skillInstalled);
7032
8154
  if (!shouldFix) continue;
7033
8155
  const needsMcpFix = !state.mcpConfigured || !state.mcpCommandMatches;
7034
8156
  const needsInstFix = !state.instructionInstalled || state.instructionVersion !== CURRENT_INTEGRATION_VERSION;
7035
- if (needsMcpFix || needsInstFix) {
8157
+ const needsSkillFix = Boolean(state.skillPath) && (!state.skillInstalled || state.skillVersion !== CURRENT_INTEGRATION_VERSION);
8158
+ if (needsMcpFix || needsInstFix || needsSkillFix) {
7036
8159
  const fixResult = await adapter.install(context, {
7037
8160
  scope: context.scope,
7038
8161
  dryRun
@@ -7139,6 +8262,10 @@ function formatDoctorReport(report) {
7139
8262
  lines.push(` MCP ${mcpMark}${agent.mcp.details ? ` (${agent.mcp.details})` : ""}`);
7140
8263
  const instMark = agent.instructions.status === "ok" ? "\u2713" : agent.instructions.status === "outdated" ? "!" : agent.instructions.status === "missing" ? "\u2717" : "\u25CB";
7141
8264
  lines.push(` Instructions ${instMark}${agent.instructions.details ? ` (${agent.instructions.details})` : ""}`);
8265
+ if (agent.skillPath) {
8266
+ const skillMark = agent.skill?.status === "ok" ? "\u2713" : agent.skill?.status === "outdated" ? "!" : agent.skill?.status === "missing" ? "\u2717" : "\u25CB";
8267
+ lines.push(` Skill ${skillMark}${agent.skill?.details ? ` (${agent.skill?.details})` : ""}`);
8268
+ }
7142
8269
  const cmdMark = agent.command.status === "ok" ? "\u2713" : "!";
7143
8270
  lines.push(` Connection ${cmdMark}${agent.command.details ? ` (${agent.command.details})` : ""}`);
7144
8271
  }
@@ -7183,7 +8310,7 @@ function formatDoctorFixResult(fixed) {
7183
8310
 
7184
8311
  // src/install/index.ts
7185
8312
  function createInstallContext(options) {
7186
- const projectDir = path17.resolve(options.projectDir || process.cwd());
8313
+ const projectDir = path19.resolve(options.projectDir || process.cwd());
7187
8314
  const scope = options.global || options.scope === "global" ? "global" : "project";
7188
8315
  return {
7189
8316
  projectDir,
@@ -7204,7 +8331,7 @@ async function handleInstallCommand(target, options = {}) {
7204
8331
  if (target) {
7205
8332
  const adapter = getAdapter(target);
7206
8333
  if (!adapter) {
7207
- const valid = getAllAdapters().map((a) => a.id).join(", ");
8334
+ const valid = [...getAllAdapters().map((a) => a.id), "gemini"].join(", ");
7208
8335
  throw new Error(`Unknown agent/editor target "${target}". Supported targets: ${valid}`);
7209
8336
  }
7210
8337
  const result = await adapter.install(context, installOptions);
@@ -7286,7 +8413,7 @@ async function handleUninstallCommand(target, options = {}) {
7286
8413
  const context = createInstallContext(options);
7287
8414
  const adapter = getAdapter(target);
7288
8415
  if (!adapter) {
7289
- const valid = getAllAdapters().map((a) => a.id).join(", ");
8416
+ const valid = [...getAllAdapters().map((a) => a.id), "gemini"].join(", ");
7290
8417
  throw new Error(`Unknown agent/editor target "${target}". Supported targets: ${valid}`);
7291
8418
  }
7292
8419
  const uninstallOptions = {
@@ -7308,7 +8435,7 @@ async function handleDoctorCommand(options = {}) {
7308
8435
  }
7309
8436
 
7310
8437
  // src/runtime.ts
7311
- import * as path18 from "path";
8438
+ import * as path20 from "path";
7312
8439
  var Bilmem = class {
7313
8440
  rootDir;
7314
8441
  storage;
@@ -7316,7 +8443,7 @@ var Bilmem = class {
7316
8443
  retriever;
7317
8444
  initialized = false;
7318
8445
  constructor(options = {}) {
7319
- this.rootDir = options.rootDir ? path18.resolve(options.rootDir) : process.cwd();
8446
+ this.rootDir = options.rootDir ? path20.resolve(options.rootDir) : process.cwd();
7320
8447
  this.storage = new Storage(this.rootDir);
7321
8448
  this.index = new SearchIndex();
7322
8449
  this.retriever = new Retriever(this.index, this.storage);
@@ -7491,6 +8618,15 @@ ${item.result.reason}`
7491
8618
  await this.ensureInitialized();
7492
8619
  return resolveEpistemicQuery(options, this.storage, this.retriever);
7493
8620
  }
8621
+ /**
8622
+ * Preflight analysis:
8623
+ * Answers "What must I know before I start this work?"
8624
+ * Analyzes existing constraints, relevant lessons, stale knowledge, and important unknowns.
8625
+ */
8626
+ async preflight(options) {
8627
+ await this.ensureInitialized();
8628
+ return executePreflight(options, this.storage, this.retriever, this.index);
8629
+ }
7494
8630
  /**
7495
8631
  * Check knowledge staleness based on changed source files.
7496
8632
  */
@@ -7629,6 +8765,7 @@ function createBilmem(options) {
7629
8765
  return new Bilmem(options);
7630
8766
  }
7631
8767
  export {
8768
+ AntigravityAdapter,
7632
8769
  BILMEM_END_MARKER,
7633
8770
  BILMEM_START_MARKER,
7634
8771
  BUILTIN_DETECTORS,
@@ -7663,6 +8800,7 @@ export {
7663
8800
  checkKnowledgeListStaleness,
7664
8801
  checkKnowledgeStaleness,
7665
8802
  classifyKnowledgeHealth,
8803
+ classifyUnknown,
7666
8804
  compact,
7667
8805
  computeKnowledgeStats,
7668
8806
  computeStats,
@@ -7688,8 +8826,10 @@ export {
7688
8826
  evaluatePromotion,
7689
8827
  evaluateRule,
7690
8828
  evaluateRuleSet,
8829
+ executePreflight,
7691
8830
  exportKnowledgeToYaml,
7692
8831
  extractInstructionVersion,
8832
+ extractSkillVersion,
7693
8833
  findRuleFiles,
7694
8834
  formatDoctorFixResult,
7695
8835
  formatDoctorReport,
@@ -7697,6 +8837,7 @@ export {
7697
8837
  formatJevExplanation,
7698
8838
  formatKnowledgeDiff,
7699
8839
  formatKnowledgeStatsReport,
8840
+ formatPreflightReport,
7700
8841
  formatStatsReport,
7701
8842
  formatUninstallResult,
7702
8843
  formatWhyExplanation,
@@ -7704,6 +8845,7 @@ export {
7704
8845
  getAdapter,
7705
8846
  getAgentInstructionText,
7706
8847
  getAllAdapters,
8848
+ getBilmemSkillContent,
7707
8849
  getEventsFilePath,
7708
8850
  getIntegrationRecord,
7709
8851
  getIntegrationsFilePath,
@@ -7712,8 +8854,10 @@ export {
7712
8854
  handleUninstallCommand,
7713
8855
  hasBeenInvestigated,
7714
8856
  hasBilmemInstruction,
8857
+ hasBilmemSkill,
7715
8858
  hashString,
7716
8859
  importKnowledgeFromYaml,
8860
+ inferLikelyDomains,
7717
8861
  isExecutableInPath,
7718
8862
  isGitRepository,
7719
8863
  isSourceFile,
@@ -7727,6 +8871,7 @@ export {
7727
8871
  parseMarkdownFile,
7728
8872
  parseRuleSetYaml,
7729
8873
  pathExists,
8874
+ prioritizeQuestions,
7730
8875
  processUserCorrection,
7731
8876
  readMetricEvents,
7732
8877
  reconcileKnowledge,
@@ -7737,6 +8882,7 @@ export {
7737
8882
  removeIntegration,
7738
8883
  removeMarkdownInstructionFile,
7739
8884
  removeMcpJsonConfig,
8885
+ removeSkillFile,
7740
8886
  rerankCandidates,
7741
8887
  resetMetricEvents,
7742
8888
  resolveEpistemicQuery,
@@ -7754,8 +8900,10 @@ export {
7754
8900
  scanRepository,
7755
8901
  stringifyRuleSetYaml,
7756
8902
  tierToConfidence,
8903
+ tokenizeGoal,
7757
8904
  updateMarkdownInstructionFile,
7758
8905
  updateMcpJsonConfig,
8906
+ updateSkillFile,
7759
8907
  updateUnknown,
7760
8908
  validateRuleDefinition,
7761
8909
  validateRuleSet,