@yurtsever/capsa 0.1.0-alpha.7 → 0.1.0-alpha.8

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.
Files changed (3) hide show
  1. package/README.md +3 -2
  2. package/dist/index.js +457 -190
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2807,8 +2807,8 @@ var require_validate = __commonJS({
2807
2807
  return data;
2808
2808
  }
2809
2809
  let expr = data;
2810
- const segments3 = jsonPointer.split("/");
2811
- for (const segment of segments3) {
2810
+ const segments4 = jsonPointer.split("/");
2811
+ for (const segment of segments4) {
2812
2812
  if (segment) {
2813
2813
  data = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)((0, util_1.unescapeJsonPointer)(segment))}`;
2814
2814
  expr = (0, codegen_1._)`${expr} && ${data}`;
@@ -3870,49 +3870,49 @@ var require_fast_uri = __commonJS({
3870
3870
  schemelessOptions.skipEscape = true;
3871
3871
  return serialize(resolved, schemelessOptions);
3872
3872
  }
3873
- function resolveComponent(base, relative6, options, skipNormalization) {
3873
+ function resolveComponent(base, relative7, options, skipNormalization) {
3874
3874
  const target = {};
3875
3875
  if (!skipNormalization) {
3876
3876
  base = parse3(serialize(base, options), options);
3877
- relative6 = parse3(serialize(relative6, options), options);
3877
+ relative7 = parse3(serialize(relative7, options), options);
3878
3878
  }
3879
3879
  options = options || {};
3880
- if (!options.tolerant && relative6.scheme) {
3881
- target.scheme = relative6.scheme;
3882
- target.userinfo = relative6.userinfo;
3883
- target.host = relative6.host;
3884
- target.port = relative6.port;
3885
- target.path = removeDotSegments(relative6.path || "");
3886
- target.query = relative6.query;
3880
+ if (!options.tolerant && relative7.scheme) {
3881
+ target.scheme = relative7.scheme;
3882
+ target.userinfo = relative7.userinfo;
3883
+ target.host = relative7.host;
3884
+ target.port = relative7.port;
3885
+ target.path = removeDotSegments(relative7.path || "");
3886
+ target.query = relative7.query;
3887
3887
  } else {
3888
- if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
3889
- target.userinfo = relative6.userinfo;
3890
- target.host = relative6.host;
3891
- target.port = relative6.port;
3892
- target.path = removeDotSegments(relative6.path || "");
3893
- target.query = relative6.query;
3888
+ if (relative7.userinfo !== void 0 || relative7.host !== void 0 || relative7.port !== void 0) {
3889
+ target.userinfo = relative7.userinfo;
3890
+ target.host = relative7.host;
3891
+ target.port = relative7.port;
3892
+ target.path = removeDotSegments(relative7.path || "");
3893
+ target.query = relative7.query;
3894
3894
  } else {
3895
- if (!relative6.path) {
3895
+ if (!relative7.path) {
3896
3896
  target.path = base.path;
3897
- if (relative6.query !== void 0) {
3898
- target.query = relative6.query;
3897
+ if (relative7.query !== void 0) {
3898
+ target.query = relative7.query;
3899
3899
  } else {
3900
3900
  target.query = base.query;
3901
3901
  }
3902
3902
  } else {
3903
- if (relative6.path[0] === "/") {
3904
- target.path = removeDotSegments(relative6.path);
3903
+ if (relative7.path[0] === "/") {
3904
+ target.path = removeDotSegments(relative7.path);
3905
3905
  } else {
3906
3906
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3907
- target.path = "/" + relative6.path;
3907
+ target.path = "/" + relative7.path;
3908
3908
  } else if (!base.path) {
3909
- target.path = relative6.path;
3909
+ target.path = relative7.path;
3910
3910
  } else {
3911
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
3911
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative7.path;
3912
3912
  }
3913
3913
  target.path = removeDotSegments(target.path);
3914
3914
  }
3915
- target.query = relative6.query;
3915
+ target.query = relative7.query;
3916
3916
  }
3917
3917
  target.userinfo = base.userinfo;
3918
3918
  target.host = base.host;
@@ -3920,7 +3920,7 @@ var require_fast_uri = __commonJS({
3920
3920
  }
3921
3921
  target.scheme = base.scheme;
3922
3922
  }
3923
- target.fragment = relative6.fragment;
3923
+ target.fragment = relative7.fragment;
3924
3924
  return target;
3925
3925
  }
3926
3926
  function equal(uriA, uriB, options) {
@@ -4621,15 +4621,15 @@ var require_core = __commonJS({
4621
4621
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4622
4622
  if (!errors || errors.length === 0)
4623
4623
  return "No errors";
4624
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
4624
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
4625
4625
  }
4626
4626
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
4627
4627
  const rules = this.RULES.all;
4628
4628
  metaSchema = JSON.parse(JSON.stringify(metaSchema));
4629
4629
  for (const jsonPointer of keywordsJsonPointers) {
4630
- const segments3 = jsonPointer.split("/").slice(1);
4630
+ const segments4 = jsonPointer.split("/").slice(1);
4631
4631
  let keywords = metaSchema;
4632
- for (const seg of segments3)
4632
+ for (const seg of segments4)
4633
4633
  keywords = keywords[seg];
4634
4634
  for (const key in rules) {
4635
4635
  const rule = rules[key];
@@ -7196,8 +7196,8 @@ var require_dist = __commonJS({
7196
7196
  });
7197
7197
 
7198
7198
  // src/index.ts
7199
- import { readFile as readFile7 } from "fs/promises";
7200
- import { dirname as dirname9, resolve as resolve4 } from "path";
7199
+ import { readFile as readFile8 } from "fs/promises";
7200
+ import { dirname as dirname10, resolve as resolve4 } from "path";
7201
7201
  import { fileURLToPath as fileURLToPath2 } from "url";
7202
7202
 
7203
7203
  // ../core/dist/scanner.js
@@ -7247,7 +7247,7 @@ async function scan(adapters, options = {}) {
7247
7247
  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
7248
7248
  const issues = [];
7249
7249
  const items = [];
7250
- const seenItemPaths = /* @__PURE__ */ new Set();
7250
+ const seenItemPaths = /* @__PURE__ */ new Map();
7251
7251
  const scannedRoots = [];
7252
7252
  const formatAdapters = adapters.filter((a) => !isSourceAdapter(a));
7253
7253
  const sourceAdapters = adapters.filter(isSourceAdapter);
@@ -7259,10 +7259,19 @@ async function scan(adapters, options = {}) {
7259
7259
  rootSet.add(resolve(r));
7260
7260
  }
7261
7261
  const roots = [...rootSet].filter((r) => ![...rootSet].some((other) => other !== r && r.startsWith(other + "/")));
7262
- const addItem = (item) => {
7263
- if (seenItemPaths.has(item.path))
7262
+ const addItem = (item, adapterId) => {
7263
+ const owner = seenItemPaths.get(item.path);
7264
+ if (owner !== void 0) {
7265
+ if (owner !== adapterId) {
7266
+ issues.push({
7267
+ path: item.path,
7268
+ adapter: adapterId,
7269
+ message: `claimed by both "${owner}" and "${adapterId}"; kept "${owner}" \u2014 two adapters' conventions overlap here`
7270
+ });
7271
+ }
7264
7272
  return;
7265
- seenItemPaths.add(item.path);
7273
+ }
7274
+ seenItemPaths.set(item.path, adapterId);
7266
7275
  items.push(item);
7267
7276
  };
7268
7277
  const handleEntry = async (entry) => {
@@ -7281,8 +7290,13 @@ async function scan(adapters, options = {}) {
7281
7290
  if (!matched)
7282
7291
  continue;
7283
7292
  try {
7284
- for (const item of await adapter.parse(entry, env))
7285
- addItem(item);
7293
+ const parsed = await adapter.parse(entry, env);
7294
+ const result = Array.isArray(parsed) ? { items: parsed } : parsed;
7295
+ for (const item of result.items)
7296
+ addItem(item, adapter.id);
7297
+ for (const issue2 of result.issues ?? []) {
7298
+ issues.push({ adapter: adapter.id, ...issue2 });
7299
+ }
7286
7300
  } catch (err) {
7287
7301
  issues.push({
7288
7302
  path: entry.absPath,
@@ -7356,7 +7370,7 @@ async function scan(adapters, options = {}) {
7356
7370
  for (const adapter of sourceAdapters) {
7357
7371
  try {
7358
7372
  for (const item of await adapter.collect(root, env))
7359
- addItem(item);
7373
+ addItem(item, adapter.id);
7360
7374
  } catch (err) {
7361
7375
  issues.push({
7362
7376
  path: root,
@@ -7414,20 +7428,24 @@ function findProjectRoot(startDir, homeDir) {
7414
7428
 
7415
7429
  // ../core/dist/chunk.js
7416
7430
  var DEFAULT_MAX_CHARS = 2e3;
7417
- function chunkMarkdown(markdown, maxChars = DEFAULT_MAX_CHARS) {
7431
+ var DEFAULT_MAX_HEADING_LEVEL = 6;
7432
+ function chunkMarkdown(markdown, options = {}) {
7433
+ const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
7434
+ const maxHeadingLevel = options.maxHeadingLevel ?? DEFAULT_MAX_HEADING_LEVEL;
7418
7435
  const body = parseFrontmatter(markdown).body;
7419
7436
  const chunks = [];
7420
7437
  const trail = [];
7421
7438
  let buffer = [];
7422
7439
  let inFence = false;
7440
+ let openPath = [];
7441
+ const path = () => trail.filter((s) => Boolean(s));
7423
7442
  const flush = () => {
7424
- const text = buffer.join("\n").trim();
7443
+ const text2 = buffer.join("\n").trim();
7425
7444
  buffer = [];
7426
- if (!text)
7445
+ if (!text2)
7427
7446
  return;
7428
- const headingPath = trail.filter((s) => Boolean(s));
7429
- for (const part of splitLong(text, maxChars)) {
7430
- chunks.push({ index: chunks.length, headingPath, text: part });
7447
+ for (const part of splitLong(text2, maxChars)) {
7448
+ chunks.push({ index: chunks.length, headingPath: openPath, text: part });
7431
7449
  }
7432
7450
  };
7433
7451
  for (const line of body.split(/\r?\n/)) {
@@ -7435,10 +7453,13 @@ function chunkMarkdown(markdown, maxChars = DEFAULT_MAX_CHARS) {
7435
7453
  inFence = !inFence;
7436
7454
  const heading = !inFence && /^(#{1,6})\s+(.*\S)\s*$/.exec(line);
7437
7455
  if (heading) {
7438
- flush();
7439
7456
  const level = heading[1].length;
7457
+ if (level <= maxHeadingLevel)
7458
+ flush();
7440
7459
  trail.splice(level - 1);
7441
7460
  trail[level - 1] = heading[2];
7461
+ if (buffer.length === 0)
7462
+ openPath = path();
7442
7463
  buffer.push(line);
7443
7464
  continue;
7444
7465
  }
@@ -7447,25 +7468,58 @@ function chunkMarkdown(markdown, maxChars = DEFAULT_MAX_CHARS) {
7447
7468
  flush();
7448
7469
  return chunks;
7449
7470
  }
7450
- function splitLong(text, maxChars) {
7451
- if (text.length <= maxChars)
7452
- return [text];
7471
+ function splitLong(text2, maxChars) {
7472
+ if (text2.length <= maxChars)
7473
+ return [text2];
7453
7474
  const out = [];
7454
7475
  let current = "";
7455
- for (const para of text.split(/\n{2,}/)) {
7456
- if (current && current.length + para.length + 2 > maxChars) {
7476
+ for (const { sep: sep7, piece } of pieces(text2, maxChars)) {
7477
+ if (current && current.length + sep7.length + piece.length > maxChars) {
7457
7478
  out.push(current);
7458
- current = para;
7479
+ current = piece;
7459
7480
  } else {
7460
- current = current ? `${current}
7461
-
7462
- ${para}` : para;
7481
+ current = current ? current + sep7 + piece : piece;
7463
7482
  }
7464
7483
  }
7465
7484
  if (current)
7466
7485
  out.push(current);
7467
7486
  return out;
7468
7487
  }
7488
+ var SENTENCE_BREAK = /(?<=[.!?…]["'’”»)\]]*)(\s+)/;
7489
+ function* pieces(text2, maxChars) {
7490
+ let sep7 = "";
7491
+ for (const para of text2.split(/\n{2,}/)) {
7492
+ if (para.length <= maxChars) {
7493
+ yield { sep: sep7, piece: para };
7494
+ } else {
7495
+ const parts = para.split(SENTENCE_BREAK);
7496
+ for (let i = 0; i < parts.length; i += 2) {
7497
+ yield* cut(parts[i], sep7, maxChars);
7498
+ sep7 = parts[i + 1] ?? "";
7499
+ }
7500
+ }
7501
+ sep7 = "\n\n";
7502
+ }
7503
+ }
7504
+ function* cut(sentence, sep7, maxChars) {
7505
+ let rest = sentence;
7506
+ while (rest.length > maxChars) {
7507
+ const window = rest.slice(0, maxChars + 1);
7508
+ let at = window.lastIndexOf("\n");
7509
+ if (at < maxChars / 2)
7510
+ at = Math.max(at, window.lastIndexOf(" "));
7511
+ if (at <= 0)
7512
+ at = Math.max(maxChars, 1);
7513
+ const piece = rest.slice(0, at).trimEnd();
7514
+ const gap = /^\s*/.exec(rest.slice(piece.length))[0];
7515
+ if (piece)
7516
+ yield { sep: sep7, piece };
7517
+ sep7 = piece ? gap : sep7 + gap;
7518
+ rest = rest.slice(piece.length + gap.length);
7519
+ }
7520
+ if (rest)
7521
+ yield { sep: sep7, piece: rest };
7522
+ }
7469
7523
 
7470
7524
  // ../adapter-claude-skills/dist/index.js
7471
7525
  import { readFile } from "fs/promises";
@@ -7516,10 +7570,10 @@ import { readFile as readFile2 } from "fs/promises";
7516
7570
  import { basename as basename2, dirname as dirname3, join as join4, relative as relative2 } from "path";
7517
7571
  var INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md", "AGENTS.md"]);
7518
7572
  var MAX_DESCRIPTION_LENGTH = 120;
7519
- function truncate(text) {
7520
- if (text.length <= MAX_DESCRIPTION_LENGTH)
7521
- return text;
7522
- return text.slice(0, MAX_DESCRIPTION_LENGTH - 1).trimEnd() + "\u2026";
7573
+ function truncate(text2) {
7574
+ if (text2.length <= MAX_DESCRIPTION_LENGTH)
7575
+ return text2;
7576
+ return text2.slice(0, MAX_DESCRIPTION_LENGTH - 1).trimEnd() + "\u2026";
7523
7577
  }
7524
7578
  function deriveDescription(raw2) {
7525
7579
  let firstNonEmpty;
@@ -7583,8 +7637,8 @@ var LEGACY_FILE = ".cursorrules";
7583
7637
  var MDC_EXTENSION = ".mdc";
7584
7638
  var RULES_MARKER = sep2 + join5(".cursor", "rules") + sep2;
7585
7639
  var MAX_DESCRIPTION_LENGTH2 = 120;
7586
- function firstLine(text) {
7587
- for (const line of text.split(/\r?\n/)) {
7640
+ function firstLine(text2) {
7641
+ for (const line of text2.split(/\r?\n/)) {
7588
7642
  const trimmed = line.trim();
7589
7643
  if (!trimmed)
7590
7644
  continue;
@@ -7958,10 +8012,10 @@ var checkOptionalParameter = (path) => {
7958
8012
  if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) {
7959
8013
  return null;
7960
8014
  }
7961
- const segments3 = path.split("/");
8015
+ const segments4 = path.split("/");
7962
8016
  const results = [];
7963
8017
  let basePath = "";
7964
- segments3.forEach((segment) => {
8018
+ segments4.forEach((segment) => {
7965
8019
  if (segment !== "" && !/\:/.test(segment)) {
7966
8020
  basePath += "/" + segment;
7967
8021
  } else if (/\:/.test(segment)) {
@@ -8178,7 +8232,7 @@ var HonoRequest = class {
8178
8232
  * ```
8179
8233
  */
8180
8234
  json() {
8181
- return this.#cachedBody("text").then((text) => JSON.parse(text));
8235
+ return this.#cachedBody("text").then((text2) => JSON.parse(text2));
8182
8236
  }
8183
8237
  /**
8184
8238
  * `.text()` can parse Request body of type `text/plain`
@@ -8744,9 +8798,9 @@ var Context = class {
8744
8798
  * })
8745
8799
  * ```
8746
8800
  */
8747
- text = (text, arg, headers) => {
8748
- return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(
8749
- text,
8801
+ text = (text2, arg, headers) => {
8802
+ return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text2) : this.#newResponse(
8803
+ text2,
8750
8804
  arg,
8751
8805
  setDefaultContentType(TEXT_PLAIN, headers)
8752
8806
  );
@@ -10050,8 +10104,8 @@ function ollamaEmbedder(options = {}) {
10050
10104
  }
10051
10105
  };
10052
10106
  }
10053
- function estimateTokens(text) {
10054
- return Math.ceil(text.length / 4);
10107
+ function estimateTokens(text2) {
10108
+ return Math.ceil(text2.length / 4);
10055
10109
  }
10056
10110
 
10057
10111
  // ../index/dist/store.js
@@ -10074,7 +10128,7 @@ function loadSqlite() {
10074
10128
  }
10075
10129
  var DB_DIR = ".capsa";
10076
10130
  var DB_FILE = "index.db";
10077
- var SCHEMA_VERSION = 7;
10131
+ var SCHEMA_VERSION = 11;
10078
10132
  var META_INDEXED_AT = "indexed_at";
10079
10133
  var META_INDEX_FAILED_AT = "index_failed_at";
10080
10134
  var META_INDEX_ERROR = "index_error";
@@ -10173,6 +10227,7 @@ var Store = class {
10173
10227
  status TEXT,
10174
10228
  updated_at TEXT,
10175
10229
  tags TEXT,
10230
+ metadata TEXT,
10176
10231
  origin TEXT NOT NULL DEFAULT 'local',
10177
10232
  indexed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
10178
10233
  );
@@ -10260,21 +10315,22 @@ var Store = class {
10260
10315
  this.db.prepare("DELETE FROM items WHERE id = ?").run(id);
10261
10316
  }
10262
10317
  upsertItem(row) {
10263
- this.db.prepare(`INSERT INTO items (id, format, kind, name, title, path, rel_path, content_hash, status, updated_at, tags)
10264
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10318
+ this.db.prepare(`INSERT INTO items (id, format, kind, name, title, path, rel_path, content_hash, status, updated_at, tags, metadata)
10319
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10265
10320
  ON CONFLICT(id) DO UPDATE SET
10266
10321
  format = excluded.format, kind = excluded.kind, name = excluded.name,
10267
10322
  title = excluded.title,
10268
10323
  rel_path = excluded.rel_path, content_hash = excluded.content_hash,
10269
10324
  status = excluded.status, updated_at = excluded.updated_at, tags = excluded.tags,
10270
- indexed_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')`).run(row.id, row.format, row.kind, row.name, row.title, row.path, row.rel_path, row.content_hash, row.status, row.updated_at, row.tags);
10325
+ metadata = excluded.metadata,
10326
+ indexed_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')`).run(row.id, row.format, row.kind, row.name, row.title, row.path, row.rel_path, row.content_hash, row.status, row.updated_at, row.tags, row.metadata);
10271
10327
  }
10272
10328
  /**
10273
10329
  * `text` is what the user sees; `searchText` (chunk + provenance header)
10274
10330
  * is what FTS and the embedder see. Falls back to `text` when omitted.
10275
10331
  */
10276
- insertChunk(itemId, idx, headingPath, text, embedding, searchText = text) {
10277
- const res = this.db.prepare("INSERT INTO chunks (item_id, idx, heading_path, text) VALUES (?, ?, ?, ?)").run(itemId, idx, JSON.stringify(headingPath), text);
10332
+ insertChunk(itemId, idx, headingPath, text2, embedding, searchText = text2) {
10333
+ const res = this.db.prepare("INSERT INTO chunks (item_id, idx, heading_path, text) VALUES (?, ?, ?, ?)").run(itemId, idx, JSON.stringify(headingPath), text2);
10278
10334
  const id = Number(res.lastInsertRowid);
10279
10335
  this.db.prepare("INSERT INTO chunks_fts (rowid, text) VALUES (?, ?)").run(id, searchText);
10280
10336
  this.db.prepare("INSERT INTO chunks_vec (chunk_id, embedding) VALUES (?, ?)").run(BigInt(id), new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength));
@@ -10298,7 +10354,7 @@ var Store = class {
10298
10354
  return [];
10299
10355
  const placeholders = ids.map(() => "?").join(",");
10300
10356
  const rows = this.db.prepare(`SELECT c.id, c.item_id, c.idx, c.heading_path, c.text,
10301
- i.id AS i_id, i.format, i.kind, i.name, i.title, i.path, i.rel_path, i.content_hash, i.status, i.updated_at, i.tags
10357
+ i.id AS i_id, i.format, i.kind, i.name, i.title, i.path, i.rel_path, i.content_hash, i.status, i.updated_at, i.tags, i.metadata
10302
10358
  FROM chunks c JOIN items i ON i.id = c.item_id WHERE c.id IN (${placeholders})`).all(...ids);
10303
10359
  return rows.map((r) => ({
10304
10360
  id: r["id"],
@@ -10317,7 +10373,8 @@ var Store = class {
10317
10373
  content_hash: r["content_hash"],
10318
10374
  status: r["status"],
10319
10375
  updated_at: r["updated_at"],
10320
- tags: r["tags"]
10376
+ tags: r["tags"],
10377
+ metadata: r["metadata"]
10321
10378
  }
10322
10379
  }));
10323
10380
  }
@@ -10353,12 +10410,116 @@ var Store = class {
10353
10410
 
10354
10411
  // ../index/dist/indexer.js
10355
10412
  import { createHash as createHash2 } from "crypto";
10356
- import { relative as relative5 } from "path";
10413
+ import { relative as relative6 } from "path";
10414
+
10415
+ // ../adapter-documents/dist/index.js
10416
+ import { readFile as readFile5 } from "fs/promises";
10417
+ import { basename as basename4, dirname as dirname7, relative as relative3, sep as sep4 } from "path";
10418
+ var BOOK_DIR = "books";
10419
+ var OUTLINE_FILE = "index.md";
10420
+ var CHAPTER_HEADING_LEVEL = 3;
10421
+ var CHAPTER_MAX_CHARS = 1e3;
10422
+ function segments(relPath) {
10423
+ return relPath.split(/[\\/]/).filter(Boolean);
10424
+ }
10425
+ function documentsAdapter() {
10426
+ return {
10427
+ id: "documents",
10428
+ displayName: "Document",
10429
+ matches(entry) {
10430
+ if (!entry.isFile || !entry.name.toLowerCase().endsWith(".md"))
10431
+ return false;
10432
+ return segments(entry.relPath).slice(0, -1).some((p) => p.toLowerCase() === BOOK_DIR);
10433
+ },
10434
+ async parse(entry, env) {
10435
+ let raw2;
10436
+ try {
10437
+ raw2 = await readFile5(entry.absPath, "utf8");
10438
+ } catch {
10439
+ return { items: [] };
10440
+ }
10441
+ const fm = parseFrontmatter(raw2);
10442
+ const book = fm.data["book"];
10443
+ if (!book) {
10444
+ return {
10445
+ items: [],
10446
+ issues: [
10447
+ {
10448
+ path: entry.absPath,
10449
+ message: `no \`book:\` in frontmatter \u2014 a markdown file under ${BOOK_DIR}/ is a chapter of a document or it is nothing`
10450
+ }
10451
+ ]
10452
+ };
10453
+ }
10454
+ const projectRoot = findProjectRoot(dirname7(entry.absPath), env.homeDir);
10455
+ const relPathInProject = relative3(projectRoot, entry.absPath);
10456
+ const outline = entry.name.toLowerCase() === OUTLINE_FILE;
10457
+ const title = fm.data["title"] || firstHeading(fm.body) || basename4(entry.name, ".md");
10458
+ const chapter = fm.data["chapter"];
10459
+ const metadata = {
10460
+ relPathInProject,
10461
+ book,
10462
+ title,
10463
+ ...fm.data["author"] ? { author: fm.data["author"] } : {},
10464
+ ...chapter ? { chapter: number(chapter) } : {},
10465
+ ...pages("printed_pages", fm.data["printed_pages"]),
10466
+ ...pages("pdf_pages", fm.data["pdf_pages"])
10467
+ };
10468
+ return {
10469
+ items: [
10470
+ {
10471
+ id: stableId(entry.absPath),
10472
+ format: "documents",
10473
+ formatLabel: outline ? "Outline" : "Document",
10474
+ name: `${basename4(projectRoot)}${sep4}${relPathInProject}`,
10475
+ // What the item calls itself, and what a pinned item's
10476
+ // neighbourhood is searched with: a chapter title alone
10477
+ // ("Conclusion") belongs to nine books at once.
10478
+ description: outline ? `${book} \u2014 Contents` : `${book} \u2014 ${chapterLabel(chapter, title)}`,
10479
+ path: entry.absPath,
10480
+ scope: "project",
10481
+ projectRoot,
10482
+ content: raw2,
10483
+ metadata,
10484
+ knowledge: { kind: outline ? "outline" : "document" },
10485
+ // An outline is delivered whole or not at all — a table of
10486
+ // contents retrieved three lines at a time is noise. Empty, so
10487
+ // the indexer stores it and embeds nothing of it.
10488
+ chunks: outline ? [] : chapterChunks(raw2)
10489
+ }
10490
+ ]
10491
+ };
10492
+ }
10493
+ };
10494
+ }
10495
+ function chapterLabel(chapter, title) {
10496
+ return chapter === void 0 || chapter === "" ? title : `Ch. ${chapter} ${title}`;
10497
+ }
10498
+ function chapterChunks(raw2) {
10499
+ return chunkMarkdown(raw2, { maxHeadingLevel: CHAPTER_HEADING_LEVEL, maxChars: CHAPTER_MAX_CHARS });
10500
+ }
10501
+ function pages(key, value) {
10502
+ if (!value)
10503
+ return {};
10504
+ const parts = value.replace(/^\[|\]$/g, "").split(",").map((p) => p.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
10505
+ return parts.length ? { [key]: parts.map(number) } : {};
10506
+ }
10507
+ function number(value) {
10508
+ return /^\d+$/.test(value) ? Number(value) : value;
10509
+ }
10510
+ function firstHeading(body) {
10511
+ for (const line of body.split(/\r?\n/)) {
10512
+ const m = /^#{1,6}\s+(.*\S)\s*$/.exec(line.trim());
10513
+ if (m)
10514
+ return m[1];
10515
+ }
10516
+ return void 0;
10517
+ }
10357
10518
 
10358
10519
  // ../adapter-git-log/dist/index.js
10359
10520
  import { execFile } from "child_process";
10360
10521
  import { existsSync as existsSync2 } from "fs";
10361
- import { basename as basename4, join as join7 } from "path";
10522
+ import { basename as basename5, join as join7 } from "path";
10362
10523
  import { promisify } from "util";
10363
10524
  var execFileAsync = promisify(execFile);
10364
10525
  var DEFAULTS = {
@@ -10461,7 +10622,7 @@ function toItem(root, c, opts) {
10461
10622
  author: c.author,
10462
10623
  subject,
10463
10624
  files: c.files,
10464
- repo: basename4(root)
10625
+ repo: basename5(root)
10465
10626
  },
10466
10627
  knowledge: {
10467
10628
  kind: "commit",
@@ -10506,18 +10667,27 @@ function conventionalTags(subject) {
10506
10667
  const tags = [m[1], ...m[2] ? [m[2]] : []];
10507
10668
  return tags;
10508
10669
  }
10509
- function firstLine2(text) {
10510
- return (text ?? "").split("\n").find((l) => l.trim()) ?? "";
10670
+ function firstLine2(text2) {
10671
+ return (text2 ?? "").split("\n").find((l) => l.trim()) ?? "";
10511
10672
  }
10512
10673
 
10513
10674
  // ../adapter-runbooks/dist/index.js
10514
- import { readFile as readFile5, stat as stat3 } from "fs/promises";
10515
- import { basename as basename5, dirname as dirname7, relative as relative3, sep as sep4 } from "path";
10675
+ import { readFile as readFile6, stat as stat3 } from "fs/promises";
10676
+ import { basename as basename6, dirname as dirname8, relative as relative4, sep as sep5 } from "path";
10516
10677
  var DOC_DIRS = /* @__PURE__ */ new Set(["docs", "doc", "runbooks", "runbook", "wiki", "adr", "decisions"]);
10517
- var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["tickets", "issues", "todo", "backlog", "plans", "plan", "roadmap", "milestones", "milestone", "node_modules"]);
10518
- function segments(relPath) {
10678
+ var CLAIMED_ELSEWHERE = /* @__PURE__ */ new Set(["skill.md"]);
10679
+ var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["books", "tickets", "issues", "todo", "backlog", "plans", "plan", "roadmap", "milestones", "milestone", "node_modules"]);
10680
+ function segments2(relPath) {
10519
10681
  return relPath.split(/[\\/]/).filter(Boolean);
10520
10682
  }
10683
+ function kindOf(relPathInProject) {
10684
+ const parts = segments2(relPathInProject).map((p) => p.toLowerCase());
10685
+ if (parts.some((p) => p === "adr" || p === "decisions"))
10686
+ return "decision";
10687
+ if (parts.some((p) => p === "runbooks" || p === "runbook"))
10688
+ return "runbook";
10689
+ return "doc";
10690
+ }
10521
10691
  function runbooksAdapter() {
10522
10692
  return {
10523
10693
  id: "runbooks",
@@ -10525,7 +10695,9 @@ function runbooksAdapter() {
10525
10695
  matches(entry) {
10526
10696
  if (!entry.isFile || !entry.name.toLowerCase().endsWith(".md"))
10527
10697
  return false;
10528
- const parts = segments(entry.relPath).slice(0, -1);
10698
+ if (CLAIMED_ELSEWHERE.has(entry.name.toLowerCase()))
10699
+ return false;
10700
+ const parts = segments2(entry.relPath).slice(0, -1);
10529
10701
  if (!parts.some((p) => DOC_DIRS.has(p.toLowerCase())))
10530
10702
  return false;
10531
10703
  return !parts.some((p) => EXCLUDED_DIRS.has(p.toLowerCase()));
@@ -10534,24 +10706,23 @@ function runbooksAdapter() {
10534
10706
  let raw2;
10535
10707
  let mtime;
10536
10708
  try {
10537
- raw2 = await readFile5(entry.absPath, "utf8");
10709
+ raw2 = await readFile6(entry.absPath, "utf8");
10538
10710
  mtime = (await stat3(entry.absPath)).mtime;
10539
10711
  } catch {
10540
10712
  return [];
10541
10713
  }
10542
- const projectRoot = findProjectRoot(dirname7(entry.absPath), env.homeDir);
10543
- const relPathInProject = relative3(projectRoot, entry.absPath);
10714
+ const projectRoot = findProjectRoot(dirname8(entry.absPath), env.homeDir);
10715
+ const relPathInProject = relative4(projectRoot, entry.absPath);
10544
10716
  const fm = parseFrontmatter(raw2);
10545
10717
  const data = fm.data;
10546
- const title = data["title"] || firstHeading(raw2) || basename5(entry.name, ".md");
10547
- const kindFromPath = segments(relPathInProject).some((p) => ["adr", "decisions"].includes(p.toLowerCase())) ? "decision" : "runbook";
10718
+ const title = data["title"] || firstHeading2(raw2) || basename6(entry.name, ".md");
10548
10719
  const tags = parseTags(data["tags"]);
10549
10720
  return [
10550
10721
  {
10551
10722
  id: stableId(entry.absPath),
10552
10723
  format: "runbooks",
10553
10724
  formatLabel: "Runbook",
10554
- name: `${basename5(projectRoot)}${sep4}${relPathInProject}`,
10725
+ name: `${basename6(projectRoot)}${sep5}${relPathInProject}`,
10555
10726
  description: title,
10556
10727
  path: entry.absPath,
10557
10728
  scope: "project",
@@ -10559,7 +10730,7 @@ function runbooksAdapter() {
10559
10730
  content: raw2,
10560
10731
  metadata: { relPathInProject, title },
10561
10732
  knowledge: {
10562
- kind: kindFromPath,
10733
+ kind: kindOf(relPathInProject),
10563
10734
  updatedAt: mtime?.toISOString(),
10564
10735
  tags
10565
10736
  }
@@ -10574,7 +10745,7 @@ function parseTags(value) {
10574
10745
  const tags = value.replace(/^\[|\]$/g, "").split(",").map((t) => t.trim()).filter(Boolean);
10575
10746
  return tags.length ? tags : void 0;
10576
10747
  }
10577
- function firstHeading(raw2) {
10748
+ function firstHeading2(raw2) {
10578
10749
  for (const line of raw2.split(/\r?\n/)) {
10579
10750
  const m = /^#{1,6}\s+(.*\S)\s*$/.exec(line.trim());
10580
10751
  if (m)
@@ -10584,12 +10755,12 @@ function firstHeading(raw2) {
10584
10755
  }
10585
10756
 
10586
10757
  // ../adapter-tickets/dist/index.js
10587
- import { readFile as readFile6, stat as stat4 } from "fs/promises";
10588
- import { basename as basename6, dirname as dirname8, relative as relative4, sep as sep5 } from "path";
10758
+ import { readFile as readFile7, stat as stat4 } from "fs/promises";
10759
+ import { basename as basename7, dirname as dirname9, relative as relative5, sep as sep6 } from "path";
10589
10760
  var TICKET_DIRS = /* @__PURE__ */ new Set(["tickets", "issues", "todo", "backlog"]);
10590
10761
  var PLAN_DIRS = /* @__PURE__ */ new Set(["plans", "plan", "roadmap", "milestones", "milestone"]);
10591
10762
  var CHECKBOX = /^\s*[-*+]\s+\[( |x|X)\]\s+/gm;
10592
- function segments2(relPath) {
10763
+ function segments3(relPath) {
10593
10764
  return relPath.split(/[\\/]/).filter(Boolean);
10594
10765
  }
10595
10766
  function deriveStatus(raw2) {
@@ -10627,33 +10798,33 @@ function ticketsAdapter() {
10627
10798
  matches(entry) {
10628
10799
  if (!entry.isFile || !entry.name.toLowerCase().endsWith(".md"))
10629
10800
  return false;
10630
- const parts = segments2(entry.relPath).slice(0, -1).map((p) => p.toLowerCase());
10801
+ const parts = segments3(entry.relPath).slice(0, -1).map((p) => p.toLowerCase());
10631
10802
  return parts.some((p) => TICKET_DIRS.has(p) || PLAN_DIRS.has(p));
10632
10803
  },
10633
10804
  async parse(entry, env) {
10634
10805
  let raw2;
10635
10806
  let mtime;
10636
10807
  try {
10637
- raw2 = await readFile6(entry.absPath, "utf8");
10808
+ raw2 = await readFile7(entry.absPath, "utf8");
10638
10809
  mtime = (await stat4(entry.absPath)).mtime;
10639
10810
  } catch {
10640
10811
  return [];
10641
10812
  }
10642
- const projectRoot = findProjectRoot(dirname8(entry.absPath), env.homeDir);
10643
- const relPathInProject = relative4(projectRoot, entry.absPath);
10644
- const isPlan = segments2(relPathInProject).slice(0, -1).some((p) => PLAN_DIRS.has(p.toLowerCase()));
10813
+ const projectRoot = findProjectRoot(dirname9(entry.absPath), env.homeDir);
10814
+ const relPathInProject = relative5(projectRoot, entry.absPath);
10815
+ const isPlan = segments3(relPathInProject).slice(0, -1).some((p) => PLAN_DIRS.has(p.toLowerCase()));
10645
10816
  const fm = parseFrontmatter(raw2);
10646
10817
  const data = fm.data;
10647
10818
  const derived2 = deriveStatus(raw2);
10648
10819
  const status = normaliseStatus(data["status"]) ?? derived2.status;
10649
- const title = data["title"] || firstHeading2(raw2) || basename6(entry.name, ".md");
10820
+ const title = data["title"] || firstHeading3(raw2) || basename7(entry.name, ".md");
10650
10821
  const tags = parseTags2(data["tags"]);
10651
10822
  return [
10652
10823
  {
10653
10824
  id: stableId(entry.absPath),
10654
10825
  format: "tickets",
10655
10826
  formatLabel: isPlan ? "Plan" : "Ticket",
10656
- name: `${basename6(projectRoot)}${sep5}${relPathInProject}`,
10827
+ name: `${basename7(projectRoot)}${sep6}${relPathInProject}`,
10657
10828
  description: title,
10658
10829
  path: entry.absPath,
10659
10830
  scope: "project",
@@ -10681,7 +10852,7 @@ function parseTags2(value) {
10681
10852
  const tags = value.replace(/^\[|\]$/g, "").split(",").map((t) => t.trim()).filter(Boolean);
10682
10853
  return tags.length ? tags : void 0;
10683
10854
  }
10684
- function firstHeading2(raw2) {
10855
+ function firstHeading3(raw2) {
10685
10856
  for (const line of raw2.split(/\r?\n/)) {
10686
10857
  const m = /^#{1,6}\s+(.*\S)\s*$/.exec(line.trim());
10687
10858
  if (m)
@@ -10758,6 +10929,7 @@ function knowledgeAdapters() {
10758
10929
  cursorRulesAdapter(),
10759
10930
  runbooksAdapter(),
10760
10931
  ticketsAdapter(),
10932
+ documentsAdapter(),
10761
10933
  gitLogAdapter()
10762
10934
  ];
10763
10935
  }
@@ -10887,7 +11059,7 @@ async function recordDecision(store, embedder, projectRoot, input2) {
10887
11059
  }
10888
11060
  }
10889
11061
  async function indexItem(store, embedder, item, projectRoot, hash2, previousId) {
10890
- const chunks = chunkMarkdown(item.content);
11062
+ const chunks = item.chunks ?? chunkMarkdown(item.content);
10891
11063
  const searchTexts = chunks.map((c) => withContext(item, c.headingPath, c.text));
10892
11064
  const vectors = searchTexts.length ? await embedder.embed(searchTexts) : [];
10893
11065
  if (previousId)
@@ -10903,26 +11075,27 @@ async function indexItem(store, embedder, item, projectRoot, hash2, previousId)
10903
11075
  // format has none to give.
10904
11076
  title: item.description ?? item.name,
10905
11077
  path: item.path,
10906
- rel_path: relative5(projectRoot, item.path),
11078
+ rel_path: relative6(projectRoot, item.path),
10907
11079
  content_hash: hash2,
10908
11080
  status: k?.status ?? null,
10909
11081
  updated_at: k?.updatedAt ?? null,
10910
- tags: k?.tags ? JSON.stringify(k.tags) : null
11082
+ tags: k?.tags ? JSON.stringify(k.tags) : null,
11083
+ metadata: item.metadata ? JSON.stringify(item.metadata) : null
10911
11084
  });
10912
11085
  chunks.forEach((c, i) => store.insertChunk(item.id, c.index, c.headingPath, c.text, vectors[i], searchTexts[i]));
10913
11086
  }
10914
- function withContext(item, headingPath, text) {
11087
+ function withContext(item, headingPath, text2) {
10915
11088
  const kind = item.knowledge?.kind ?? "instruction";
10916
11089
  const head = [kind, item.description ?? item.name, ...headingPath].filter(Boolean).join(" \u203A ");
10917
11090
  return `${head}
10918
11091
 
10919
- ${text}`;
11092
+ ${text2}`;
10920
11093
  }
10921
11094
  function sha1(s) {
10922
11095
  return createHash2("sha1").update(s).digest("hex");
10923
11096
  }
10924
11097
  function isInside(root, path) {
10925
- const rel = relative5(root, path);
11098
+ const rel = relative6(root, path);
10926
11099
  return !!rel && !rel.startsWith("..") && !rel.includes(":");
10927
11100
  }
10928
11101
 
@@ -10943,6 +11116,13 @@ var DEFAULT_WEIGHTS = {
10943
11116
  plan: 1.05,
10944
11117
  ticket: 1,
10945
11118
  runbook: 1,
11119
+ // A book is reference, not project knowledge: neutral until the context
11120
+ // log says otherwise. It carries no recency and no status either — see
11121
+ // the documents adapter on what it refuses to claim.
11122
+ document: 1,
11123
+ // Never ranked: an outline has no chunks to rank. The map is total, so
11124
+ // it needs a number; 1.0 is the one that says nothing.
11125
+ outline: 1,
10946
11126
  doc: 0.95,
10947
11127
  wiki: 0.95,
10948
11128
  commit: 0.85
@@ -10975,15 +11155,19 @@ function rrf(lists, k = DEFAULT_WEIGHTS.rrfK) {
10975
11155
 
10976
11156
  // ../index/dist/pin.js
10977
11157
  var LEADING_ID = /^([A-Za-z]{1,8}-?\d{1,5}(?:-\d{1,5})?|\d{2,6})(?=[-_.\s]|$)/;
11158
+ var POSITIONAL_KINDS = /* @__PURE__ */ new Set(["document", "outline"]);
10978
11159
  function leadingId(relPath) {
10979
11160
  const base = relPath.split(/[\\/]/).pop() ?? "";
10980
11161
  return LEADING_ID.exec(base)?.[1];
10981
11162
  }
11163
+ function pinnableId(item) {
11164
+ return POSITIONAL_KINDS.has(item.kind) ? void 0 : leadingId(item.rel_path);
11165
+ }
10982
11166
  function pinnedItems(query, items) {
10983
11167
  const q = query.toLowerCase();
10984
11168
  const idCount = /* @__PURE__ */ new Map();
10985
11169
  for (const item of items) {
10986
- const id = leadingId(item.rel_path)?.toLowerCase();
11170
+ const id = pinnableId(item)?.toLowerCase();
10987
11171
  if (id)
10988
11172
  idCount.set(id, (idCount.get(id) ?? 0) + 1);
10989
11173
  }
@@ -10994,7 +11178,7 @@ function pinnedItems(query, items) {
10994
11178
  out.push(item);
10995
11179
  continue;
10996
11180
  }
10997
- const id = leadingId(item.rel_path);
11181
+ const id = pinnableId(item);
10998
11182
  if (!id || (idCount.get(id.toLowerCase()) ?? 0) > 1)
10999
11183
  continue;
11000
11184
  const re = new RegExp(`(^|[^\\w-])${escape2(id)}(?=$|[^\\w-])`, "i");
@@ -11014,6 +11198,7 @@ async function retrieve(store, embedder, query, options = {}) {
11014
11198
  const candidates = options.candidates ?? 30;
11015
11199
  const weights = options.weights ?? DEFAULT_WEIGHTS;
11016
11200
  const minRelative = options.minRelativeScore ?? 0.5;
11201
+ const perItemCap = { ...DEFAULT_PER_ITEM_CAP, ...options.perItemCap };
11017
11202
  const perItem = /* @__PURE__ */ new Map();
11018
11203
  const chunks = [];
11019
11204
  const debug = [];
@@ -11023,11 +11208,13 @@ async function retrieve(store, embedder, query, options = {}) {
11023
11208
  for (const c of store.chunksForItem(item.id)) {
11024
11209
  const t = estimateTokens(c.text);
11025
11210
  const fits = tokens + t <= maxTokens || chunks.length === 0;
11211
+ const metadata = parseMetadata(item.metadata);
11026
11212
  if (options.debug) {
11027
11213
  debug.push({
11028
11214
  chunkId: c.id,
11029
11215
  relPath: item.rel_path,
11030
11216
  headingPath: JSON.parse(c.heading_path),
11217
+ metadata,
11031
11218
  kind: item.kind,
11032
11219
  status: item.status,
11033
11220
  vecRank: null,
@@ -11052,7 +11239,8 @@ async function retrieve(store, embedder, query, options = {}) {
11052
11239
  relPath: item.rel_path,
11053
11240
  headingPath: JSON.parse(c.heading_path),
11054
11241
  text: c.text,
11055
- tokens: t
11242
+ tokens: t,
11243
+ ...metadata ? { metadata } : {}
11056
11244
  });
11057
11245
  }
11058
11246
  }
@@ -11086,6 +11274,7 @@ async function retrieve(store, embedder, query, options = {}) {
11086
11274
  chunkId: c.row.id,
11087
11275
  relPath: c.row.item.rel_path,
11088
11276
  headingPath: JSON.parse(c.row.heading_path),
11277
+ metadata: parseMetadata(c.row.item.metadata),
11089
11278
  kind: c.row.item.kind,
11090
11279
  status: c.row.item.status,
11091
11280
  vecRank: rankIn(vec, c.row.id),
@@ -11101,24 +11290,21 @@ async function retrieve(store, embedder, query, options = {}) {
11101
11290
  }
11102
11291
  neighbourhood.push(c);
11103
11292
  }
11104
- const best = neighbourhood[0]?.score ?? 0;
11105
- let cut = tokens >= maxTokens;
11106
- let kept = 0;
11107
- for (const { row, fusedScore, factor, score } of neighbourhood) {
11108
- let why = "kept";
11109
- if (cut || kept > 0 && score < best * neighbourCutoff) {
11110
- cut = true;
11111
- why = "cutoff";
11112
- } else if ((perItem.get(row.item_id) ?? 0) >= 2) {
11113
- why = "per-item cap";
11114
- } else if (tokens + estimateTokens(row.text) > neighbourBudget && chunks.length > 0) {
11115
- why = "budget";
11116
- }
11293
+ const verdicts = keep(neighbourhood.map((c) => ({
11294
+ itemId: c.row.item_id,
11295
+ kind: c.row.item.kind,
11296
+ score: c.score,
11297
+ tokens: estimateTokens(c.row.text)
11298
+ })), { budget: neighbourBudget, minRelativeScore: neighbourCutoff, perItemCap }, { tokens, chunks: chunks.length, perItem });
11299
+ for (const [i, { row, fusedScore, factor, score }] of neighbourhood.entries()) {
11300
+ const why = verdicts[i];
11301
+ const metadata = parseMetadata(row.item.metadata);
11117
11302
  if (options.debug) {
11118
11303
  debug.push({
11119
11304
  chunkId: row.id,
11120
11305
  relPath: row.item.rel_path,
11121
11306
  headingPath: JSON.parse(row.heading_path),
11307
+ metadata,
11122
11308
  kind: row.item.kind,
11123
11309
  status: row.item.status,
11124
11310
  vecRank: rankIn(vec, row.id),
@@ -11133,9 +11319,7 @@ async function retrieve(store, embedder, query, options = {}) {
11133
11319
  if (why !== "kept")
11134
11320
  continue;
11135
11321
  const t = estimateTokens(row.text);
11136
- perItem.set(row.item_id, (perItem.get(row.item_id) ?? 0) + 1);
11137
11322
  tokens += t;
11138
- kept++;
11139
11323
  chunks.push({
11140
11324
  chunkId: row.id,
11141
11325
  score,
@@ -11145,10 +11329,9 @@ async function retrieve(store, embedder, query, options = {}) {
11145
11329
  relPath: row.item.rel_path,
11146
11330
  headingPath: JSON.parse(row.heading_path),
11147
11331
  text: row.text,
11148
- tokens: t
11332
+ tokens: t,
11333
+ ...metadata ? { metadata } : {}
11149
11334
  });
11150
- if (tokens >= neighbourBudget)
11151
- cut = true;
11152
11335
  }
11153
11336
  const durationMs = Date.now() - started;
11154
11337
  store.logContext({
@@ -11175,6 +11358,47 @@ async function retrieve(store, embedder, query, options = {}) {
11175
11358
  ...options.debug ? { debug } : {}
11176
11359
  };
11177
11360
  }
11361
+ var PER_ITEM_CAP = 2;
11362
+ var DOCUMENT_PER_ITEM_CAP = 6;
11363
+ var DEFAULT_PER_ITEM_CAP = {
11364
+ instruction: PER_ITEM_CAP,
11365
+ doc: PER_ITEM_CAP,
11366
+ runbook: PER_ITEM_CAP,
11367
+ ticket: PER_ITEM_CAP,
11368
+ plan: PER_ITEM_CAP,
11369
+ decision: PER_ITEM_CAP,
11370
+ wiki: PER_ITEM_CAP,
11371
+ commit: PER_ITEM_CAP,
11372
+ document: DOCUMENT_PER_ITEM_CAP,
11373
+ // An outline has no chunks; the number is here because the map is total.
11374
+ outline: PER_ITEM_CAP
11375
+ };
11376
+ function keep(candidates, rules, start = { tokens: 0, chunks: 0, perItem: /* @__PURE__ */ new Map() }) {
11377
+ const best = candidates[0]?.score ?? 0;
11378
+ const perItem = new Map(start.perItem);
11379
+ let tokens = start.tokens;
11380
+ let chunks = start.chunks;
11381
+ let kept = 0;
11382
+ let end = tokens >= rules.budget ? "budget" : void 0;
11383
+ return candidates.map((c) => {
11384
+ if (end)
11385
+ return end;
11386
+ if (kept > 0 && c.score < best * rules.minRelativeScore)
11387
+ return end = "cutoff";
11388
+ const taken = perItem.get(c.itemId) ?? 0;
11389
+ if (taken >= (rules.perItemCap[c.kind] ?? PER_ITEM_CAP))
11390
+ return "per-item cap";
11391
+ if (chunks > 0 && tokens + c.tokens > rules.budget)
11392
+ return end = "budget";
11393
+ perItem.set(c.itemId, taken + 1);
11394
+ tokens += c.tokens;
11395
+ chunks++;
11396
+ kept++;
11397
+ if (tokens >= rules.budget)
11398
+ end = "budget";
11399
+ return "kept";
11400
+ });
11401
+ }
11178
11402
  function neighbourQuery(query, pinned) {
11179
11403
  if (pinned.length === 0)
11180
11404
  return query;
@@ -11188,6 +11412,30 @@ function rankIn(list, id) {
11188
11412
  const i = list.indexOf(id);
11189
11413
  return i === -1 ? null : i + 1;
11190
11414
  }
11415
+ function parseMetadata(json2) {
11416
+ if (!json2)
11417
+ return void 0;
11418
+ try {
11419
+ const parsed = JSON.parse(json2);
11420
+ return parsed && typeof parsed === "object" ? parsed : void 0;
11421
+ } catch {
11422
+ return void 0;
11423
+ }
11424
+ }
11425
+ function whereOf(c) {
11426
+ const book = text(c.metadata?.["book"]);
11427
+ if (c.kind !== "document" || !book)
11428
+ return [c.relPath, ...c.headingPath].join(" \u203A ");
11429
+ const title = text(c.metadata?.["title"]);
11430
+ const chapter = text(c.metadata?.["chapter"]);
11431
+ const head = [chapter ? `Ch. ${chapter}` : "", title].filter(Boolean).join(" ");
11432
+ const first = c.headingPath[0];
11433
+ const rest = title && first && first.toLowerCase().includes(title.toLowerCase()) ? c.headingPath.slice(1) : c.headingPath;
11434
+ return [book, head, ...rest].filter(Boolean).join(" \u203A ");
11435
+ }
11436
+ function text(value) {
11437
+ return value === void 0 || value === null ? "" : String(value);
11438
+ }
11191
11439
  function formatDebug(result) {
11192
11440
  if (!result.debug)
11193
11441
  return "";
@@ -11197,7 +11445,7 @@ function formatDebug(result) {
11197
11445
  " vec fts factor score keep where"
11198
11446
  ];
11199
11447
  for (const d of result.debug) {
11200
- const where = [d.relPath, ...d.headingPath].join(" \u203A ").slice(0, 70);
11448
+ const where = whereOf(d).slice(0, 100);
11201
11449
  lines.push([
11202
11450
  String(d.vecRank ?? "-").padStart(5),
11203
11451
  String(d.ftsRank ?? "-").padStart(4),
@@ -11213,14 +11461,32 @@ function formatContext(result) {
11213
11461
  if (result.chunks.length === 0)
11214
11462
  return "(no relevant project knowledge found)";
11215
11463
  return result.chunks.map((c) => {
11216
- const where = [c.relPath, ...c.headingPath].join(" \u203A ");
11217
- const meta3 = [c.kind, c.status, c.updatedAt?.slice(0, 10)].filter(Boolean).join(", ");
11218
- return `### ${where}
11464
+ const meta3 = citation(c) ?? [c.kind, c.status, c.updatedAt?.slice(0, 10)].filter(Boolean).join(", ");
11465
+ return `### ${whereOf(c)}
11219
11466
  _${meta3}_
11220
11467
 
11221
11468
  ${c.text.trim()}`;
11222
11469
  }).join("\n\n");
11223
11470
  }
11471
+ function citation(c) {
11472
+ const book = text(c.metadata?.["book"]);
11473
+ if (c.kind !== "document" || !book)
11474
+ return void 0;
11475
+ const title = text(c.metadata?.["title"]);
11476
+ const chapter = text(c.metadata?.["chapter"]);
11477
+ const head = [chapter ? `Ch. ${chapter}` : "", title ? `"${title}"` : ""].filter(Boolean).join(" ");
11478
+ const pp = pageRange(c.metadata?.["printed_pages"]);
11479
+ return [book, text(c.metadata?.["author"]), head, pp ? `pp. ${pp}` : ""].filter(Boolean).join(", ");
11480
+ }
11481
+ function pageRange(value) {
11482
+ if (!Array.isArray(value) || value.length === 0)
11483
+ return void 0;
11484
+ const from = text(value[0]);
11485
+ const to = text(value[value.length - 1]);
11486
+ if (!from)
11487
+ return void 0;
11488
+ return !to || to === from ? from : `${from}\u2013${to}`;
11489
+ }
11224
11490
 
11225
11491
  // ../index/dist/state.js
11226
11492
  function projectState(store) {
@@ -11231,7 +11497,8 @@ function projectState(store) {
11231
11497
  const live = (i) => (i.kind === "ticket" || i.kind === "plan") && (i.status === "open" || i.status === "in-progress");
11232
11498
  const rank = (i) => i.status === "in-progress" ? 0 : 1;
11233
11499
  const open2 = items.filter(live).sort((a, b) => rank(a) - rank(b) || a.rel_path.localeCompare(b.rel_path)).map((i) => ({ relPath: i.rel_path, name: i.name, status: i.status ?? "unknown", updatedAt: i.updated_at }));
11234
- const recentlyChanged = items.filter((i) => i.kind !== "commit").sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? "")).slice(0, 10).map((i) => ({ relPath: i.rel_path, kind: i.kind, updatedAt: i.updated_at }));
11500
+ const STILL = /* @__PURE__ */ new Set(["commit", "document", "outline"]);
11501
+ const recentlyChanged = items.filter((i) => !STILL.has(i.kind)).sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? "")).slice(0, 10).map((i) => ({ relPath: i.rel_path, kind: i.kind, updatedAt: i.updated_at }));
11235
11502
  const decisions = store.db.prepare("SELECT id, created_at, title FROM decisions ORDER BY id DESC LIMIT 10").all().map((d) => ({ id: d.id, createdAt: d.created_at, title: d.title }));
11236
11503
  return { counts, open: open2, recentlyChanged, decisions };
11237
11504
  }
@@ -16823,7 +17090,7 @@ __export(regexes_exports, {
16823
17090
  nanoid: () => nanoid,
16824
17091
  nanoidOfLength: () => nanoidOfLength,
16825
17092
  null: () => _null,
16826
- number: () => number,
17093
+ number: () => number2,
16827
17094
  rfc5322Email: () => rfc5322Email,
16828
17095
  sha1_base64: () => sha1_base64,
16829
17096
  sha1_base64url: () => sha1_base64url,
@@ -16924,7 +17191,7 @@ var string = (params) => {
16924
17191
  };
16925
17192
  var bigint = /^-?\d+n?$/;
16926
17193
  var integer = /^-?\d+$/;
16927
- var number = /^-?\d+(?:\.\d+)?$/;
17194
+ var number2 = /^-?\d+(?:\.\d+)?$/;
16928
17195
  var boolean = /^(?:true|false)$/i;
16929
17196
  var _null = /^null$/i;
16930
17197
  var _undefined = /^undefined$/i;
@@ -18051,7 +18318,7 @@ var $ZodCustomStringFormat = /* @__PURE__ */ $constructor("$ZodCustomStringForma
18051
18318
  });
18052
18319
  var $ZodNumber = /* @__PURE__ */ $constructor("$ZodNumber", (inst, def) => {
18053
18320
  $ZodType.init(inst, def);
18054
- inst._zod.pattern = number;
18321
+ inst._zod.pattern = number2;
18055
18322
  inst._zod.parse = (payload, _ctx) => {
18056
18323
  if (def.coerce)
18057
18324
  try {
@@ -19128,7 +19395,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
19128
19395
  if (keyResult instanceof Promise) {
19129
19396
  throw new Error("Async schemas not supported in object keys currently");
19130
19397
  }
19131
- const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length;
19398
+ const checkNumericKey = typeof key === "string" && number2.test(key) && keyResult.issues.length;
19132
19399
  if (checkNumericKey) {
19133
19400
  const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
19134
19401
  if (retryResult instanceof Promise) {
@@ -19695,7 +19962,7 @@ function leafPattern(schema) {
19695
19962
  if (minimum !== void 0 || maximum !== void 0)
19696
19963
  return string({ minimum, maximum }).source;
19697
19964
  const own2 = schema._zod.pattern;
19698
- return (isInt && own2 === number ? integer : own2)?.source;
19965
+ return (isInt && own2 === number2 ? integer : own2)?.source;
19699
19966
  }
19700
19967
  function partPattern(schema) {
19701
19968
  const def = schema._zod.def;
@@ -24193,11 +24460,11 @@ function ko_default() {
24193
24460
  }
24194
24461
 
24195
24462
  // ../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/locales/lt.js
24196
- var capitalizeFirstCharacter = (text) => {
24197
- return text.charAt(0).toUpperCase() + text.slice(1);
24463
+ var capitalizeFirstCharacter = (text2) => {
24464
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
24198
24465
  };
24199
- function getUnitTypeFromNumber(number4) {
24200
- const abs = Math.abs(number4);
24466
+ function getUnitTypeFromNumber(number5) {
24467
+ const abs = Math.abs(number5);
24201
24468
  const last = abs % 10;
24202
24469
  const last2 = abs % 100;
24203
24470
  if (last2 >= 11 && last2 <= 19 || last === 0)
@@ -29169,7 +29436,7 @@ function generateRecordCheck(doc, ctx, schema, accessor) {
29169
29436
  if (keyFn.definite === false)
29170
29437
  ctx.definite = false;
29171
29438
  const keyFast = addConstant(ctx, keyFn);
29172
- const numericConst = addConstant(ctx, number);
29439
+ const numericConst = addConstant(ctx, number2);
29173
29440
  const outKeyVar = newVar(ctx);
29174
29441
  emitOwnKeys(doc, ctx, accessor, kVar, (d) => {
29175
29442
  d.write(`let ${outKeyVar} = ${keyFast}(${kVar});`);
@@ -31477,7 +31744,7 @@ function stringifyKeyNames(bySchema, json2, visited) {
31477
31744
  return rest;
31478
31745
  rest.type = "string";
31479
31746
  if (!values)
31480
- rest.pattern = (types.includes("number") ? number : integer).source;
31747
+ rest.pattern = (types.includes("number") ? number2 : integer).source;
31481
31748
  return rest;
31482
31749
  }
31483
31750
  var pendingRecords = /* @__PURE__ */ new WeakMap();
@@ -32379,7 +32646,7 @@ __export(external_exports, {
32379
32646
  null: () => _null3,
32380
32647
  nullable: () => nullable,
32381
32648
  nullish: () => nullish2,
32382
- number: () => number2,
32649
+ number: () => number3,
32383
32650
  object: () => object2,
32384
32651
  optional: () => optional,
32385
32652
  output: () => output,
@@ -32592,7 +32859,7 @@ __export(schemas_exports2, {
32592
32859
  null: () => _null3,
32593
32860
  nullable: () => nullable,
32594
32861
  nullish: () => nullish2,
32595
- number: () => number2,
32862
+ number: () => number3,
32596
32863
  object: () => object2,
32597
32864
  optional: () => optional,
32598
32865
  partialRecord: () => partialRecord,
@@ -33338,7 +33605,7 @@ var ZodNumber2 = /* @__PURE__ */ $constructor(
33338
33605
  }
33339
33606
  })
33340
33607
  );
33341
- function number2(params) {
33608
+ function number3(params) {
33342
33609
  return _number(ZodNumber2, params);
33343
33610
  }
33344
33611
  var ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, def) => {
@@ -34178,7 +34445,7 @@ var stringbool = (...args) => _stringbool({
34178
34445
  }, ...args);
34179
34446
  function json(params) {
34180
34447
  const jsonSchema = lazy(() => {
34181
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
34448
+ return union([string2(params), number3(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
34182
34449
  });
34183
34450
  return jsonSchema;
34184
34451
  }
@@ -35022,13 +35289,13 @@ __export(coerce_exports2, {
35022
35289
  bigint: () => bigint3,
35023
35290
  boolean: () => boolean3,
35024
35291
  date: () => date4,
35025
- number: () => number3,
35292
+ number: () => number4,
35026
35293
  string: () => string3
35027
35294
  });
35028
35295
  function string3(params) {
35029
35296
  return _coercedString(ZodString2, params);
35030
35297
  }
35031
- function number3(params) {
35298
+ function number4(params) {
35032
35299
  return _coercedNumber(ZodNumber2, params);
35033
35300
  }
35034
35301
  function boolean3(params) {
@@ -35047,20 +35314,20 @@ var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-
35047
35314
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
35048
35315
  var JSONRPC_VERSION = "2.0";
35049
35316
  var AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" || typeof v === "function"));
35050
- var ProgressTokenSchema = union([string2(), number2().int()]);
35317
+ var ProgressTokenSchema = union([string2(), number3().int()]);
35051
35318
  var CursorSchema = string2();
35052
35319
  var TaskCreationParamsSchema = looseObject({
35053
35320
  /**
35054
35321
  * Requested duration in milliseconds to retain task from creation.
35055
35322
  */
35056
- ttl: number2().optional(),
35323
+ ttl: number3().optional(),
35057
35324
  /**
35058
35325
  * Time in milliseconds to wait between task status requests.
35059
35326
  */
35060
- pollInterval: number2().optional()
35327
+ pollInterval: number3().optional()
35061
35328
  });
35062
35329
  var TaskMetadataSchema = object2({
35063
- ttl: number2().optional()
35330
+ ttl: number3().optional()
35064
35331
  });
35065
35332
  var RelatedTaskMetadataSchema = object2({
35066
35333
  taskId: string2()
@@ -35115,7 +35382,7 @@ var ResultSchema = looseObject({
35115
35382
  */
35116
35383
  _meta: RequestMetaSchema.optional()
35117
35384
  });
35118
- var RequestIdSchema = union([string2(), number2().int()]);
35385
+ var RequestIdSchema = union([string2(), number3().int()]);
35119
35386
  var JSONRPCRequestSchema = object2({
35120
35387
  jsonrpc: literal(JSONRPC_VERSION),
35121
35388
  id: RequestIdSchema,
@@ -35151,7 +35418,7 @@ var JSONRPCErrorResponseSchema = object2({
35151
35418
  /**
35152
35419
  * The error type that occurred.
35153
35420
  */
35154
- code: number2().int(),
35421
+ code: number3().int(),
35155
35422
  /**
35156
35423
  * A short description of the error. The message SHOULD be limited to a concise single sentence.
35157
35424
  */
@@ -35450,11 +35717,11 @@ var ProgressSchema = object2({
35450
35717
  /**
35451
35718
  * The progress thus far. This should increase every time progress is made, even if the total is unknown.
35452
35719
  */
35453
- progress: number2(),
35720
+ progress: number3(),
35454
35721
  /**
35455
35722
  * Total number of items to process (or total progress required), if known.
35456
35723
  */
35457
- total: optional(number2()),
35724
+ total: optional(number3()),
35458
35725
  /**
35459
35726
  * An optional message describing the current progress.
35460
35727
  */
@@ -35497,7 +35764,7 @@ var TaskSchema = object2({
35497
35764
  * Time in milliseconds to keep task results available after completion.
35498
35765
  * If null, the task has unlimited lifetime until manually cleaned up.
35499
35766
  */
35500
- ttl: union([number2(), _null3()]),
35767
+ ttl: union([number3(), _null3()]),
35501
35768
  /**
35502
35769
  * ISO 8601 timestamp when the task was created.
35503
35770
  */
@@ -35506,7 +35773,7 @@ var TaskSchema = object2({
35506
35773
  * ISO 8601 timestamp when the task was last updated.
35507
35774
  */
35508
35775
  lastUpdatedAt: string2(),
35509
- pollInterval: optional(number2()),
35776
+ pollInterval: optional(number3()),
35510
35777
  /**
35511
35778
  * Optional diagnostic message for failed tasks or other status information.
35512
35779
  */
@@ -35591,7 +35858,7 @@ var AnnotationsSchema = object2({
35591
35858
  /**
35592
35859
  * Importance hint for the resource, from 0 (least) to 1 (most).
35593
35860
  */
35594
- priority: number2().min(0).max(1).optional(),
35861
+ priority: number3().min(0).max(1).optional(),
35595
35862
  /**
35596
35863
  * ISO 8601 timestamp for the most recent modification.
35597
35864
  */
@@ -35619,7 +35886,7 @@ var ResourceSchema = object2({
35619
35886
  *
35620
35887
  * This can be used by Hosts to display file sizes and estimate context window usage.
35621
35888
  */
35622
- size: optional(number2()),
35889
+ size: optional(number3()),
35623
35890
  /**
35624
35891
  * Optional annotations for the client.
35625
35892
  */
@@ -36042,7 +36309,7 @@ var ListChangedOptionsBaseSchema = object2({
36042
36309
  *
36043
36310
  * @default 300
36044
36311
  */
36045
- debounceMs: number2().int().nonnegative().default(300)
36312
+ debounceMs: number3().int().nonnegative().default(300)
36046
36313
  });
36047
36314
  var LoggingLevelSchema = _enum2(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]);
36048
36315
  var SetLevelRequestParamsSchema = BaseRequestParamsSchema.extend({
@@ -36087,15 +36354,15 @@ var ModelPreferencesSchema = object2({
36087
36354
  /**
36088
36355
  * How much to prioritize cost when selecting a model.
36089
36356
  */
36090
- costPriority: number2().min(0).max(1).optional(),
36357
+ costPriority: number3().min(0).max(1).optional(),
36091
36358
  /**
36092
36359
  * How much to prioritize sampling speed (latency) when selecting a model.
36093
36360
  */
36094
- speedPriority: number2().min(0).max(1).optional(),
36361
+ speedPriority: number3().min(0).max(1).optional(),
36095
36362
  /**
36096
36363
  * How much to prioritize intelligence and capabilities when selecting a model.
36097
36364
  */
36098
- intelligencePriority: number2().min(0).max(1).optional()
36365
+ intelligencePriority: number3().min(0).max(1).optional()
36099
36366
  });
36100
36367
  var ToolChoiceSchema = object2({
36101
36368
  /**
@@ -36153,13 +36420,13 @@ var CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
36153
36420
  * declares ClientCapabilities.sampling.context. These values may be removed in future spec releases.
36154
36421
  */
36155
36422
  includeContext: _enum2(["none", "thisServer", "allServers"]).optional(),
36156
- temperature: number2().optional(),
36423
+ temperature: number3().optional(),
36157
36424
  /**
36158
36425
  * The requested maximum number of tokens to sample (to prevent runaway completions).
36159
36426
  *
36160
36427
  * The client MAY choose to sample fewer tokens than the requested maximum.
36161
36428
  */
36162
- maxTokens: number2().int(),
36429
+ maxTokens: number3().int(),
36163
36430
  stopSequences: array(string2()).optional(),
36164
36431
  /**
36165
36432
  * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
@@ -36236,8 +36503,8 @@ var StringSchemaSchema = object2({
36236
36503
  type: literal("string"),
36237
36504
  title: string2().optional(),
36238
36505
  description: string2().optional(),
36239
- minLength: number2().optional(),
36240
- maxLength: number2().optional(),
36506
+ minLength: number3().optional(),
36507
+ maxLength: number3().optional(),
36241
36508
  format: _enum2(["email", "uri", "date", "date-time"]).optional(),
36242
36509
  default: string2().optional()
36243
36510
  });
@@ -36245,9 +36512,9 @@ var NumberSchemaSchema = object2({
36245
36512
  type: _enum2(["number", "integer"]),
36246
36513
  title: string2().optional(),
36247
36514
  description: string2().optional(),
36248
- minimum: number2().optional(),
36249
- maximum: number2().optional(),
36250
- default: number2().optional()
36515
+ minimum: number3().optional(),
36516
+ maximum: number3().optional(),
36517
+ default: number3().optional()
36251
36518
  });
36252
36519
  var UntitledSingleSelectEnumSchemaSchema = object2({
36253
36520
  type: literal("string"),
@@ -36279,8 +36546,8 @@ var UntitledMultiSelectEnumSchemaSchema = object2({
36279
36546
  type: literal("array"),
36280
36547
  title: string2().optional(),
36281
36548
  description: string2().optional(),
36282
- minItems: number2().optional(),
36283
- maxItems: number2().optional(),
36549
+ minItems: number3().optional(),
36550
+ maxItems: number3().optional(),
36284
36551
  items: object2({
36285
36552
  type: literal("string"),
36286
36553
  enum: array(string2())
@@ -36291,8 +36558,8 @@ var TitledMultiSelectEnumSchemaSchema = object2({
36291
36558
  type: literal("array"),
36292
36559
  title: string2().optional(),
36293
36560
  description: string2().optional(),
36294
- minItems: number2().optional(),
36295
- maxItems: number2().optional(),
36561
+ minItems: number3().optional(),
36562
+ maxItems: number3().optional(),
36296
36563
  items: object2({
36297
36564
  anyOf: array(object2({
36298
36565
  const: string2(),
@@ -36373,7 +36640,7 @@ var ElicitResultSchema = ResultSchema.extend({
36373
36640
  * Per MCP spec, content is "typically omitted" for decline/cancel actions.
36374
36641
  * We normalize null to undefined for leniency while maintaining type compatibility.
36375
36642
  */
36376
- content: preprocess((val) => val === null ? void 0 : val, record(string2(), union([string2(), number2(), boolean2(), array(string2())])).optional())
36643
+ content: preprocess((val) => val === null ? void 0 : val, record(string2(), union([string2(), number3(), boolean2(), array(string2())])).optional())
36377
36644
  });
36378
36645
  var ResourceTemplateReferenceSchema = object2({
36379
36646
  type: literal("ref/resource"),
@@ -36436,7 +36703,7 @@ var CompleteResultSchema = ResultSchema.extend({
36436
36703
  /**
36437
36704
  * The total number of completion options available. This can exceed the number of values actually sent in the response.
36438
36705
  */
36439
- total: optional(number2().int()),
36706
+ total: optional(number3().int()),
36440
36707
  /**
36441
36708
  * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
36442
36709
  */
@@ -40537,13 +40804,13 @@ function createCapsaServer(options) {
40537
40804
  const ensureFresh = freshener(options, embedder);
40538
40805
  server.registerTool("get_context", {
40539
40806
  title: "Get project context",
40540
- description: "Return the smallest set of project knowledge (runbooks, tickets, plans, decisions, instruction files) relevant to a task. Call this before starting work on a task; pass the task as you understand it.",
40807
+ description: 'Return the smallest set of project knowledge (runbooks, tickets, plans, decisions, instruction files) relevant to a task. Call this before starting work on a task; pass the task as you understand it. Answers are markdown: each passage has a `###` heading saying where it is from and an italic line under it. For a chapter of an indexed book that line is its citation \u2014 `_<book>, <author>, Ch. <n> "<title>", pp. <from>\u2013<to>_` \u2014 so quote the book, the chapter and the printed pages when you use the passage, rather than the file it was read from.',
40541
40808
  inputSchema: {
40542
40809
  task: external_exports.string().describe("The task or question, in one or two sentences"),
40543
40810
  // 3000 is a ceiling on a ceiling: retrieval stops at the relevance
40544
40811
  // cliff long before it fills a budget, so a larger number only ever
40545
40812
  // buys a longer tail of near-misses.
40546
- maxTokens: external_exports.number().int().min(200).max(3e3).optional().describe("Token budget, default 1500")
40813
+ maxTokens: external_exports.number().int().min(200).max(3e3).optional().describe("Token budget, default 1500. Raise it when this project is a library of reference documents rather than a codebase: a book section runs several hundred tokens, so 1500 is a handful of them.")
40547
40814
  }
40548
40815
  }, async ({ task, maxTokens }) => {
40549
40816
  const freshness = await ensureFresh();
@@ -40573,10 +40840,10 @@ _capsa: ${result.chunks.length} chunks, ~${result.tokensEst} tokens, ${result.du
40573
40840
  const store = openStore(options.projectRoot, embedder);
40574
40841
  try {
40575
40842
  const caveat = caveats(freshness);
40576
- const text = formatState(projectState(store)) + (caveat ? `
40843
+ const text2 = formatState(projectState(store)) + (caveat ? `
40577
40844
 
40578
40845
  _${caveat}_` : "");
40579
- return { content: [{ type: "text", text }] };
40846
+ return { content: [{ type: "text", text: text2 }] };
40580
40847
  } finally {
40581
40848
  store.close();
40582
40849
  }
@@ -40771,8 +41038,8 @@ agent explicitly records.
40771
41038
  `;
40772
41039
  async function readVersion() {
40773
41040
  try {
40774
- const pkgPath = resolve4(dirname9(fileURLToPath2(import.meta.url)), "../package.json");
40775
- const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
41041
+ const pkgPath = resolve4(dirname10(fileURLToPath2(import.meta.url)), "../package.json");
41042
+ const pkg = JSON.parse(await readFile8(pkgPath, "utf8"));
40776
41043
  const version2 = pkg.version;
40777
41044
  return typeof version2 === "string" ? version2 : "unknown";
40778
41045
  } catch {