@bendyline/gezel-service 1.0.4 → 1.0.6

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 (86) hide show
  1. package/dist/bin/gezeld.js +16929 -6105
  2. package/dist/handboek-content/conceptual/connected-apps.md +64 -2
  3. package/dist/handboek-content/conceptual/night-shift.md +22 -2
  4. package/dist/handboek-content/conceptual/welcome.md +2 -0
  5. package/dist/handboek-content/technical/where-files-live.md +3 -1
  6. package/dist/handboek-content/whats-new/1.26226.md +4 -7
  7. package/dist/handboek-content/whats-new/1.26230.md +100 -0
  8. package/dist/handboek.js +19 -9
  9. package/dist/index-store/static-index-worker.js +116 -27
  10. package/dist/index.d.ts +1165 -253
  11. package/dist/index.js +16922 -6098
  12. package/dist/providers/mlx/python/gezel_mlx_server.py +126 -11
  13. package/dist/ui/assets/{TerminalCodeEditor-BuMcfTS4.js → TerminalCodeEditor-BUHpk3pJ.js} +2 -2
  14. package/dist/ui/assets/{_.contribution-CZO2XAKt.js → _.contribution-B34uMeM2.js} +1 -1
  15. package/dist/ui/assets/{chunk-2JJ5RFDZ-Ckbzd10a.js → chunk-2JJ5RFDZ-B2-BFNXu.js} +1 -1
  16. package/dist/ui/assets/{chunk-BAOV476U-yPHu-ECL.js → chunk-BAOV476U-CEOFYt8X.js} +1 -1
  17. package/dist/ui/assets/{chunk-GAZKTT4R-BUf32614.js → chunk-GAZKTT4R-BkP6-w3z.js} +1 -1
  18. package/dist/ui/assets/{chunk-USU6HTKB-B-ElCUQS.js → chunk-USU6HTKB-htGRNErW.js} +1 -1
  19. package/dist/ui/assets/{cpp.contribution-BJzhk0Zj.js → cpp.contribution-CvG4GFxL.js} +1 -1
  20. package/dist/ui/assets/{csharp.contribution-BR9K2qKj.js → csharp.contribution-DekWAPj3.js} +1 -1
  21. package/dist/ui/assets/{css.contribution-CfkVmdah.js → css.contribution-CDuF4Xjo.js} +1 -1
  22. package/dist/ui/assets/{cssMode-MMeGUmV2.js → cssMode-D848zTHU.js} +1 -1
  23. package/dist/ui/assets/{csv-RAgECoUg.js → csv-PublMwG8.js} +1 -1
  24. package/dist/ui/assets/dist-CJRsJTdk.js +222 -0
  25. package/dist/ui/assets/dist-Cg3Ab49q.js +2 -0
  26. package/dist/ui/assets/{doc-B7NXSLxH.js → doc-Bq50ED-X.js} +41 -41
  27. package/dist/ui/assets/{dockerfile.contribution-IZMl57B5.js → dockerfile.contribution-ChT95Kbh.js} +1 -1
  28. package/dist/ui/assets/{docx-BEJpkI6I.js → docx-BSDeP5Eu.js} +1 -1
  29. package/dist/ui/assets/{epub-00L2M424.js → epub-D8zTSe9-.js} +1 -1
  30. package/dist/ui/assets/{go.contribution-BxCBwhZs.js → go.contribution-D_1myt_B.js} +1 -1
  31. package/dist/ui/assets/{handlebars-CreIkams.js → handlebars-Mxcy9PWv.js} +1 -1
  32. package/dist/ui/assets/{handlebars.contribution-BGlyL-dm.js → handlebars.contribution-DYF5z0XX.js} +2 -2
  33. package/dist/ui/assets/{html-BSrz1IKi.js → html-HXzM-WGZ.js} +1 -1
  34. package/dist/ui/assets/{html-Cgejr9YM.js → html-X72kRvXT.js} +1 -1
  35. package/dist/ui/assets/{html.contribution-CucOMFQ_.js → html.contribution-C0xFTPDL.js} +2 -2
  36. package/dist/ui/assets/{htmlMode-DRG7XY5Z.js → htmlMode-BVtBWJQM.js} +1 -1
  37. package/dist/ui/assets/{index-Bqu-32S3.js → index-DUf4wIj5.js} +207 -200
  38. package/dist/ui/assets/index-udlcniNf.css +1 -0
  39. package/dist/ui/assets/{infer-D6e75m-s.js → infer-Be4Xn3O1.js} +1 -1
  40. package/dist/ui/assets/{ini.contribution-CGX5OdXM.js → ini.contribution-BqUQ8Oa0.js} +1 -1
  41. package/dist/ui/assets/{java.contribution-CVhfJ_us.js → java.contribution-u4QvI2UZ.js} +1 -1
  42. package/dist/ui/assets/{javascript-QelFp9t5.js → javascript-BVm8fvMk.js} +1 -1
  43. package/dist/ui/assets/javascript.contribution-BtlENuoy.js +2 -0
  44. package/dist/ui/assets/{jsonMode-T4Ss3lDu.js → jsonMode-DcoSB4Jg.js} +1 -1
  45. package/dist/ui/assets/{kotlin.contribution-DxFbGDYA.js → kotlin.contribution-D1ZfW5lW.js} +1 -1
  46. package/dist/ui/assets/{less.contribution-g9R8XRZf.js → less.contribution-BBWek5Gc.js} +1 -1
  47. package/dist/ui/assets/{lua.contribution-YqDsEy8Z.js → lua.contribution-DJEpMduq.js} +1 -1
  48. package/dist/ui/assets/mapTheme-4TWH25FT-C2uLgUhs.js +1 -0
  49. package/dist/ui/assets/{markdown.contribution-DboXnPnx.js → markdown.contribution-CsVCTE0K.js} +1 -1
  50. package/dist/ui/assets/{monaco-BdFw56DI.js → monaco-DcZAEsIZ.js} +3 -3
  51. package/dist/ui/assets/{monaco-base-DrP3D13F.js → monaco-base-C-UMfHa9.js} +1 -1
  52. package/dist/ui/assets/{monaco-setup-QyF22y9h.js → monaco-setup-Dy_kv6Gv.js} +1 -1
  53. package/dist/ui/assets/{monaco.contribution-FA_ESP-W.js → monaco.contribution-BpJqoa65.js} +2 -2
  54. package/dist/ui/assets/{monaco.contribution-XEpc63ty.js → monaco.contribution-BujOhotZ.js} +2 -2
  55. package/dist/ui/assets/{monaco.contribution-BkqIxESv.js → monaco.contribution-DW4-UIJp.js} +2 -2
  56. package/dist/ui/assets/{monaco.contribution-CMzPFHtO.js → monaco.contribution-ypbCAEx4.js} +2 -2
  57. package/dist/ui/assets/{pdf-66KslaXf.js → pdf-BZeBurJI.js} +1 -1
  58. package/dist/ui/assets/{php.contribution-rnpik0Kk.js → php.contribution-B6Atm_xI.js} +1 -1
  59. package/dist/ui/assets/{pptx-DZdEoDP9.js → pptx-BbAcnlML.js} +4 -4
  60. package/dist/ui/assets/{python-CF7X5t9Q.js → python-CCiWacbd.js} +1 -1
  61. package/dist/ui/assets/{python.contribution-CyJGCfSq.js → python.contribution-3CThbaoI.js} +2 -2
  62. package/dist/ui/assets/{ruby.contribution-4ZyPGu2E.js → ruby.contribution-D3g6EADN.js} +1 -1
  63. package/dist/ui/assets/{rust.contribution-Cl_B3X2G.js → rust.contribution-CSdjIX0R.js} +1 -1
  64. package/dist/ui/assets/{schemas-Drh23Boe.js → schemas-JY4jmLbC.js} +1 -1
  65. package/dist/ui/assets/{scss.contribution-BO02gVLZ.js → scss.contribution-mQ_vuYxE.js} +1 -1
  66. package/dist/ui/assets/{shell.contribution-Dn9Y0uHs.js → shell.contribution-BLQSLx3H.js} +1 -1
  67. package/dist/ui/assets/{sql.contribution-Bog_5Rc8.js → sql.contribution-GScL7Mrt.js} +1 -1
  68. package/dist/ui/assets/{swift.contribution-CfiAr7AX.js → swift.contribution-DG__yqNr.js} +1 -1
  69. package/dist/ui/assets/{terminal-monaco-setup-B-EnkGiy.js → terminal-monaco-setup-x6piH1Un.js} +1 -1
  70. package/dist/ui/assets/{transform-MBfVXq54.js → transform-ywncQECB.js} +1 -1
  71. package/dist/ui/assets/{tsMode-B2WuLuPL.js → tsMode-B5nM5MR7.js} +1 -1
  72. package/dist/ui/assets/{typescript-Bv_J1tKT.js → typescript-xiOVha1M.js} +1 -1
  73. package/dist/ui/assets/{typescript.contribution-CM0XQCYi.js → typescript.contribution-BU2V4Vz8.js} +2 -2
  74. package/dist/ui/assets/worker-BzdDEeh7.js +1 -0
  75. package/dist/ui/assets/{xlsx-DDBRN-IU.js → xlsx-f9AYHB2W.js} +1 -1
  76. package/dist/ui/assets/{xml-DMV-f4tm.js → xml-m3MSnvuD.js} +1 -1
  77. package/dist/ui/assets/{xml.contribution-QqmOB9gT.js → xml.contribution-bOoaS55N.js} +2 -2
  78. package/dist/ui/assets/{yaml-Bb6mZXY5.js → yaml-DOtMVgbb.js} +1 -1
  79. package/dist/ui/assets/{yaml.contribution-DCDnWhHR.js → yaml.contribution-CNCYEtwk.js} +2 -2
  80. package/dist/ui/index.html +14 -14
  81. package/package.json +13 -12
  82. package/dist/ui/assets/dist-8uH4DcHx.js +0 -2
  83. package/dist/ui/assets/dist-CFOmf7nD.js +0 -222
  84. package/dist/ui/assets/index-DTicLc4o.css +0 -1
  85. package/dist/ui/assets/javascript.contribution--no0ijCu.js +0 -2
  86. package/dist/ui/assets/mapTheme-4TWH25FT-B5nAKRmb.js +0 -1
@@ -9,6 +9,36 @@ import { extname as extname3, join as join10 } from "path";
9
9
  import { nowIso as nowIso2 } from "@bendyline/gezel";
10
10
  import { PROJECT_SHADOW_DIR_NAME as PROJECT_SHADOW_DIR_NAME2, projectLocalIndexDbFile } from "@bendyline/gezel/paths";
11
11
 
12
+ // src/fs/sync-junk.ts
13
+ import { isOutsideInInternalPath } from "@bendyline/gezel";
14
+ var EXACT_NAMES = /* @__PURE__ */ new Set([
15
+ ".DS_Store",
16
+ "Thumbs.db",
17
+ "desktop.ini",
18
+ "Icon\r",
19
+ ".dropbox",
20
+ ".dropbox.attr"
21
+ ]);
22
+ var SUFFIXES = [".tmp", ".temp", ".partial", ".crdownload", ".download", ".lnk"];
23
+ function isOfficeLock(name) {
24
+ return name.startsWith("~$") || name.startsWith(".~lock.") && name.endsWith("#");
25
+ }
26
+ var SYNC_STAGING_DIRS = /* @__PURE__ */ new Set([".tmp.drivedownload", ".tmp.driveupload", ".dropbox.cache"]);
27
+ function isSyncJunkName(name) {
28
+ if (EXACT_NAMES.has(name) || SYNC_STAGING_DIRS.has(name)) return true;
29
+ if (isOfficeLock(name)) return true;
30
+ const lower = name.toLowerCase();
31
+ return SUFFIXES.some((suffix) => lower.endsWith(suffix));
32
+ }
33
+ function isSyncJunkPath(relPath) {
34
+ return relPath.split("/").some((segment) => segment.length > 0 && isSyncJunkName(segment));
35
+ }
36
+ function isLibraryInternalPath(relPath) {
37
+ if (isOutsideInInternalPath(relPath)) return true;
38
+ if (isSyncJunkPath(relPath)) return true;
39
+ return relPath.split("/").includes(".versions");
40
+ }
41
+
12
42
  // src/security/code-patterns.ts
13
43
  var EVIDENCE_CAP = 160;
14
44
  var CODE_PATTERNS = [
@@ -309,7 +339,7 @@ async function runGit(args, opts = {}) {
309
339
  "credential.interactive=false",
310
340
  ...cleanedArgs
311
341
  ];
312
- return new Promise((resolve2, reject) => {
342
+ return new Promise((resolve3, reject) => {
313
343
  const hasStdinPayload = opts.stdin !== void 0;
314
344
  const child = hasStdinPayload ? spawn("git", gitArgs, {
315
345
  cwd: opts.cwd,
@@ -364,11 +394,11 @@ async function runGit(args, opts = {}) {
364
394
  return;
365
395
  }
366
396
  if (code === 0) {
367
- resolve2({ stdout, stderr });
397
+ resolve3({ stdout, stderr });
368
398
  return;
369
399
  }
370
400
  if (code !== null && opts.acceptExitCodes?.includes(code)) {
371
- resolve2({ stdout, stderr, code });
401
+ resolve3({ stdout, stderr, code });
372
402
  return;
373
403
  }
374
404
  reject(
@@ -427,13 +457,18 @@ var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
427
457
  ".DS_Store"
428
458
  ]);
429
459
  async function discoverWorkspaceFiles(workspaceDir, opts) {
430
- const gitPaths = await listGitVisiblePaths(workspaceDir, opts.maxFiles);
460
+ const gitPaths = await listGitVisiblePaths(
461
+ workspaceDir,
462
+ opts.maxFiles,
463
+ opts.gitTimeoutMs,
464
+ opts.ignorePath
465
+ );
431
466
  if (gitPaths) return statListedFiles(workspaceDir, gitPaths, opts.maxFiles);
432
467
  const out = [];
433
- await walkFilesystem(workspaceDir, workspaceDir, out, opts.maxFiles);
468
+ await walkFilesystem(workspaceDir, workspaceDir, out, opts.maxFiles, opts.ignorePath);
434
469
  return out;
435
470
  }
436
- async function listGitVisiblePaths(workspaceDir, maxFiles) {
471
+ async function listGitVisiblePaths(workspaceDir, maxFiles, timeoutMs = 3e4, ignorePath) {
437
472
  try {
438
473
  const { stdout } = await runGit(
439
474
  [
@@ -447,7 +482,7 @@ async function listGitVisiblePaths(workspaceDir, maxFiles) {
447
482
  "--",
448
483
  "."
449
484
  ],
450
- { cwd: workspaceDir, timeoutMs: 3e4 }
485
+ { cwd: workspaceDir, timeoutMs }
451
486
  );
452
487
  const seen = /* @__PURE__ */ new Set();
453
488
  const paths = [];
@@ -455,6 +490,7 @@ async function listGitVisiblePaths(workspaceDir, maxFiles) {
455
490
  if (!raw) continue;
456
491
  const path = raw.replaceAll("\\", "/").replace(/^\.\//, "");
457
492
  if (!path || seen.has(path) || containsAlwaysSkippedDir(path)) continue;
493
+ if (ignorePath?.(path)) continue;
458
494
  seen.add(path);
459
495
  paths.push(path);
460
496
  if (paths.length >= maxFiles) break;
@@ -488,7 +524,7 @@ async function statListedFiles(workspaceDir, paths, maxFiles) {
488
524
  }
489
525
  return out;
490
526
  }
491
- async function walkFilesystem(root, dir, out, maxFiles) {
527
+ async function walkFilesystem(root, dir, out, maxFiles, ignorePath) {
492
528
  if (out.length >= maxFiles) return;
493
529
  let entries;
494
530
  try {
@@ -500,13 +536,15 @@ async function walkFilesystem(root, dir, out, maxFiles) {
500
536
  if (out.length >= maxFiles) return;
501
537
  if (entry.isDirectory() && ALWAYS_SKIP_DIRS.has(entry.name)) continue;
502
538
  const abs = join(dir, entry.name);
539
+ const rel = relative(root, abs).replaceAll("\\", "/");
540
+ if (ignorePath?.(rel)) continue;
503
541
  if (entry.isDirectory()) {
504
- await walkFilesystem(root, abs, out, maxFiles);
542
+ await walkFilesystem(root, abs, out, maxFiles, ignorePath);
505
543
  } else if (entry.isFile()) {
506
544
  try {
507
545
  const st = await lstat(abs);
508
546
  out.push({
509
- path: relative(root, abs).replaceAll("\\", "/"),
547
+ path: rel,
510
548
  abs,
511
549
  size: st.size,
512
550
  mtimeMs: st.mtimeMs
@@ -750,7 +788,7 @@ async function renameWithWindowsRetry(from, to) {
750
788
 
751
789
  // src/fs/safe-paths.ts
752
790
  import { realpath as realpath2 } from "fs/promises";
753
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, normalize, sep } from "path";
791
+ import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, normalize, resolve, sep } from "path";
754
792
  function pathEq(a, b) {
755
793
  if (process.platform === "win32" || process.platform === "darwin") {
756
794
  return a.toLowerCase() === b.toLowerCase();
@@ -1160,7 +1198,7 @@ async function resolveNodePermissionFlag(nodeBin) {
1160
1198
  }
1161
1199
  }
1162
1200
  async function readNodeHelp(nodeBin) {
1163
- return await new Promise((resolve2, reject) => {
1201
+ return await new Promise((resolve3, reject) => {
1164
1202
  const child = spawn2(nodeBin, ["--help"], {
1165
1203
  stdio: ["ignore", "pipe", "pipe"],
1166
1204
  env: sandboxEnv(process.env),
@@ -1178,13 +1216,13 @@ async function readNodeHelp(nodeBin) {
1178
1216
  });
1179
1217
  child.once("error", reject);
1180
1218
  child.once("close", (code) => {
1181
- if (code === 0) resolve2(stdout);
1219
+ if (code === 0) resolve3(stdout);
1182
1220
  else reject(new Error(`failed to inspect Node permission support (${code}): ${stderr}`));
1183
1221
  });
1184
1222
  });
1185
1223
  }
1186
1224
  async function runSandboxChild(command, args, opts) {
1187
- return await new Promise((resolve2) => {
1225
+ return await new Promise((resolve3) => {
1188
1226
  const child = spawn2(command, args, {
1189
1227
  cwd: opts.cwd,
1190
1228
  stdio: opts.rpcChannel ? ["pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
@@ -1262,7 +1300,7 @@ async function runSandboxChild(command, args, opts) {
1262
1300
  if (timer) clearTimeout(timer);
1263
1301
  const signalSuffix = code === null && signal ? `[process closed by signal ${signal}]
1264
1302
  ` : "";
1265
- resolve2({
1303
+ resolve3({
1266
1304
  exitCode: code ?? -1,
1267
1305
  signal,
1268
1306
  stdout,
@@ -1393,12 +1431,12 @@ async function wrapForPlatform(command, args, ctx) {
1393
1431
  }
1394
1432
  var macSandboxProbe;
1395
1433
  function canApplyMacSandbox() {
1396
- macSandboxProbe ??= new Promise((resolve2) => {
1434
+ macSandboxProbe ??= new Promise((resolve3) => {
1397
1435
  let settled = false;
1398
1436
  const finish = (ok) => {
1399
1437
  if (settled) return;
1400
1438
  settled = true;
1401
- resolve2(ok);
1439
+ resolve3(ok);
1402
1440
  };
1403
1441
  const child = spawn2("sandbox-exec", ["-p", "(version 1)\n(allow default)", "/usr/bin/true"], {
1404
1442
  stdio: ["ignore", "ignore", "ignore"],
@@ -1420,12 +1458,12 @@ function canApplyMacSandbox() {
1420
1458
  var linuxSystemdSandboxProbe;
1421
1459
  function canApplyLinuxSystemdSandbox() {
1422
1460
  if (process.platform !== "linux") return Promise.resolve(false);
1423
- linuxSystemdSandboxProbe ??= new Promise((resolve2) => {
1461
+ linuxSystemdSandboxProbe ??= new Promise((resolve3) => {
1424
1462
  let settled = false;
1425
1463
  const finish = (ok) => {
1426
1464
  if (settled) return;
1427
1465
  settled = true;
1428
- resolve2(ok);
1466
+ resolve3(ok);
1429
1467
  };
1430
1468
  const probe = "const net=require('node:net');const s=net.connect({host:'127.0.0.1',port:9});s.once('connect',()=>process.exit(9));s.once('error',e=>process.exit(['EAFNOSUPPORT','EPERM','EACCES'].includes(e.code)?0:2));setTimeout(()=>process.exit(3),1000);";
1431
1469
  const child = spawn2(
@@ -1541,7 +1579,7 @@ syncBuiltinESMExports();
1541
1579
 
1542
1580
  // src/utils/service-worker-entry.ts
1543
1581
  import { existsSync } from "fs";
1544
- import { dirname as dirname3, join as join4, resolve } from "path";
1582
+ import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
1545
1583
  import { fileURLToPath } from "url";
1546
1584
  var SERVICE_WORKER_ENTRIES = {
1547
1585
  embeddings: { built: "memory/embed-worker.js" },
@@ -1559,7 +1597,7 @@ function findServiceBundleResource(moduleUrl, relativePaths) {
1559
1597
  for (let depth = 0; depth < MAX_ANCESTOR_DEPTH; depth++) {
1560
1598
  for (const root of [dir, join4(dir, "dist")]) {
1561
1599
  for (const relativePath of relativePaths) {
1562
- const candidate = resolve(root, relativePath);
1600
+ const candidate = resolve2(root, relativePath);
1563
1601
  if (visited.has(candidate)) continue;
1564
1602
  visited.add(candidate);
1565
1603
  if (existsSync(candidate)) return candidate;
@@ -2851,7 +2889,8 @@ var IndexStore = class _IndexStore {
2851
2889
  if (!this.caps.fts) return [];
2852
2890
  return this.db.prepare(
2853
2891
  `SELECT file_path, name, kind, line_start, line_end, signature
2854
- FROM fts_symbols WHERE fts_symbols MATCH ? AND collection_id = ? LIMIT ?`
2892
+ FROM fts_symbols WHERE fts_symbols MATCH ? AND collection_id = ?
2893
+ ORDER BY bm25(fts_symbols) LIMIT ?`
2855
2894
  ).all(ftsPhrase(query), this.collectionId, limit).map((r) => ({
2856
2895
  id: symbolId(r.file_path, r.name),
2857
2896
  filePath: r.file_path,
@@ -3040,7 +3079,8 @@ var IndexStore = class _IndexStore {
3040
3079
  return this.db.prepare(
3041
3080
  `SELECT f.path AS file_path, s.snip FROM (
3042
3081
  SELECT content_hash, collection_id, snippet(fts_summaries, 0, '', '', '\u2026', 12) AS snip
3043
- FROM fts_summaries WHERE fts_summaries MATCH ? AND collection_id = ? LIMIT ?
3082
+ FROM fts_summaries WHERE fts_summaries MATCH ? AND collection_id = ?
3083
+ ORDER BY bm25(fts_summaries) LIMIT ?
3044
3084
  ) s
3045
3085
  JOIN files f ON f.hash = s.content_hash AND f.collection_id = s.collection_id
3046
3086
  LIMIT ?`
@@ -3163,8 +3203,12 @@ var IndexStore = class _IndexStore {
3163
3203
  searchDocs(query, limit = 20) {
3164
3204
  if (!this.caps.fts) return [];
3165
3205
  return this.db.prepare(
3206
+ // FTS5 bm25() is lower-is-better, so plain ascending order is the
3207
+ // best-first order. Without it FTS5 yields rowid (insertion) order and
3208
+ // a LIMIT returns the oldest-indexed matches, not the closest ones.
3166
3209
  `SELECT file_path, line_start, chunk_id, snippet(fts_docs, 0, '', '', '\u2026', 12) AS snip
3167
- FROM fts_docs WHERE fts_docs MATCH ? AND collection_id = ? LIMIT ?`
3210
+ FROM fts_docs WHERE fts_docs MATCH ? AND collection_id = ?
3211
+ ORDER BY bm25(fts_docs) LIMIT ?`
3168
3212
  ).all(
3169
3213
  ftsPhrase(query),
3170
3214
  this.collectionId,
@@ -3406,6 +3450,42 @@ var IndexStore = class _IndexStore {
3406
3450
  ).get(this.collectionId, kind, rubricHash, rubricHash, maxAttempts);
3407
3451
  return row?.n ?? 0;
3408
3452
  }
3453
+ /**
3454
+ * How much indexing work landed in `[since, until)`, read off the tiers'
3455
+ * own success stamps rather than any separate bookkeeping — which is what
3456
+ * makes a Night Shift tally survive a daemon restart mid-window. Bounds
3457
+ * are ISO-UTC strings, compared lexicographically (`nowIso()` is the only
3458
+ * writer of these columns, so the format is uniform).
3459
+ *
3460
+ * Counted per content hash, so a file re-summarized after an edit counts
3461
+ * again — that is real work done in the period, not double-counting.
3462
+ */
3463
+ workCountsSince(since, until) {
3464
+ const count = (sql, ...params) => Number(this.db.prepare(sql).get(...params)?.n ?? 0);
3465
+ return {
3466
+ summarized: count(
3467
+ `SELECT COUNT(*) AS n FROM summaries
3468
+ WHERE collection_id = ? AND created_at >= ? AND created_at < ?`,
3469
+ this.collectionId,
3470
+ since,
3471
+ until
3472
+ ),
3473
+ reviewed: count(
3474
+ `SELECT COUNT(*) AS n FROM file_reviews
3475
+ WHERE collection_id = ? AND reviewed_at >= ? AND reviewed_at < ?`,
3476
+ this.collectionId,
3477
+ since,
3478
+ until
3479
+ ),
3480
+ described: count(
3481
+ `SELECT COUNT(*) AS n FROM shadow_state
3482
+ WHERE collection_id = ? AND state = 'ok' AND updated_at >= ? AND updated_at < ?`,
3483
+ this.collectionId,
3484
+ since,
3485
+ until
3486
+ )
3487
+ };
3488
+ }
3409
3489
  /**
3410
3490
  * Review coverage across the given rubric set. `reviewed` counts ANY
3411
3491
  * successful review on a current file hash regardless of rubric — coverage
@@ -4737,7 +4817,7 @@ function embeddablePngText(text) {
4737
4817
  }
4738
4818
  return parts.length > 0 ? ` ${parts.join(" ")}` : "";
4739
4819
  }
4740
- async function indexWorkspaceContent(store, workspaceDir, artifactsDir) {
4820
+ async function indexWorkspaceContent(store, workspaceDir, artifactsDir, opts = {}) {
4741
4821
  const stats = {
4742
4822
  scanned: 0,
4743
4823
  changed: 0,
@@ -4749,7 +4829,15 @@ async function indexWorkspaceContent(store, workspaceDir, artifactsDir) {
4749
4829
  const seen = /* @__PURE__ */ new Set();
4750
4830
  const indexedAt = nowIso2();
4751
4831
  const forceCode = store.getMeta("extractor_version") !== String(EXTRACTOR_VERSION);
4752
- const walkedFiles = await discoverWorkspaceFiles(workspaceDir, { maxFiles: MAX_FILES });
4832
+ const walkedFiles = await discoverWorkspaceFiles(workspaceDir, {
4833
+ maxFiles: MAX_FILES,
4834
+ // The shared document library is a user folder, not a source tree: it
4835
+ // holds outside-in companion twins (a derived markdown view of a
4836
+ // document already in the listing) and whatever a cloud-sync client
4837
+ // leaves behind. Indexing either produces a second, worse hit for the
4838
+ // same document.
4839
+ ...opts.scope === "library" ? { ignorePath: isLibraryInternalPath } : {}
4840
+ });
4753
4841
  for (const file of walkedFiles) {
4754
4842
  stats.scanned++;
4755
4843
  seen.add(file.path);
@@ -5046,7 +5134,8 @@ port.on("message", (request) => {
5046
5134
  const stats = await indexWorkspaceContent(
5047
5135
  index,
5048
5136
  request.workspaceDir,
5049
- request.artifactsDir
5137
+ request.artifactsDir,
5138
+ { ...request.scope ? { scope: request.scope } : {} }
5050
5139
  );
5051
5140
  port.postMessage({ id: request.id, stats });
5052
5141
  } finally {