sweet-search 2.5.2 → 2.5.3

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 (155) hide show
  1. package/core/cli.js +24 -3
  2. package/core/graph/graph-expansion.js +215 -36
  3. package/core/graph/graph-extractor.js +196 -11
  4. package/core/graph/graph-search.js +395 -92
  5. package/core/graph/hcgs-generator.js +2 -1
  6. package/core/graph/index.js +2 -0
  7. package/core/graph/repo-map.js +28 -6
  8. package/core/graph/structural-answer-cues.js +168 -0
  9. package/core/graph/structural-callsite-hints.js +40 -0
  10. package/core/graph/structural-context-format.js +40 -0
  11. package/core/graph/structural-context.js +450 -0
  12. package/core/graph/structural-forward-push.js +156 -0
  13. package/core/graph/structural-header-context.js +19 -0
  14. package/core/graph/structural-importance.js +148 -0
  15. package/core/graph/structural-pagerank.js +197 -0
  16. package/core/graph/summary-manager.js +13 -9
  17. package/core/incremental-indexing/application/dirty-scan.mjs +236 -0
  18. package/core/incremental-indexing/application/file-watcher.mjs +197 -0
  19. package/core/incremental-indexing/application/maintenance-handlers.mjs +519 -0
  20. package/core/incremental-indexing/application/maintenance-worker.mjs +380 -0
  21. package/core/incremental-indexing/application/operator-cli.mjs +554 -0
  22. package/core/incremental-indexing/application/production-li-delta.mjs +192 -0
  23. package/core/incremental-indexing/application/production-reconciler-helpers.mjs +107 -0
  24. package/core/incremental-indexing/application/production-reconciler.mjs +583 -0
  25. package/core/incremental-indexing/application/reconciler.mjs +477 -0
  26. package/core/incremental-indexing/application/tombstone-injector.mjs +148 -0
  27. package/core/incremental-indexing/domain/chunk-identity.mjs +260 -0
  28. package/core/incremental-indexing/domain/encoder-deps.mjs +193 -0
  29. package/core/incremental-indexing/domain/encoder-input.mjs +225 -0
  30. package/core/incremental-indexing/domain/interval-autotune.mjs +255 -0
  31. package/core/incremental-indexing/domain/reconcile-counters.mjs +149 -0
  32. package/core/incremental-indexing/domain/watermark-scheduler.mjs +239 -0
  33. package/core/incremental-indexing/infrastructure/artifact-temp-sweep.mjs +163 -0
  34. package/core/incremental-indexing/infrastructure/baseline-readiness.mjs +121 -0
  35. package/core/incremental-indexing/infrastructure/dirty-set.mjs +233 -0
  36. package/core/incremental-indexing/infrastructure/graph-gc.mjs +314 -0
  37. package/core/incremental-indexing/infrastructure/hashing.mjs +298 -0
  38. package/core/incremental-indexing/infrastructure/hcgs-invalidation.mjs +182 -0
  39. package/core/incremental-indexing/infrastructure/li-segment-merge.mjs +278 -0
  40. package/core/incremental-indexing/infrastructure/li-segment-state.mjs +173 -0
  41. package/core/incremental-indexing/infrastructure/lockfile.mjs +119 -0
  42. package/core/incremental-indexing/infrastructure/maintenance-state-reader.mjs +283 -0
  43. package/core/incremental-indexing/infrastructure/manifest.mjs +194 -0
  44. package/core/incremental-indexing/infrastructure/path-filter.mjs +190 -0
  45. package/core/incremental-indexing/infrastructure/reader-heartbeat.mjs +201 -0
  46. package/core/incremental-indexing/infrastructure/schema-migrations.mjs +257 -0
  47. package/core/incremental-indexing/infrastructure/sparse-gram-delta.mjs +335 -0
  48. package/core/incremental-indexing/infrastructure/sqlite-fts5.mjs +176 -0
  49. package/core/incremental-indexing/infrastructure/staleness-display.mjs +105 -0
  50. package/core/incremental-indexing/infrastructure/tombstone-bitmap.mjs +234 -0
  51. package/core/incremental-indexing/infrastructure/vector-delta-writer.mjs +359 -0
  52. package/core/incremental-indexing/infrastructure/vector-gc.mjs +133 -0
  53. package/core/incremental-indexing/infrastructure/worktree-stamp.mjs +155 -0
  54. package/core/incremental-indexing/infrastructure/wsl2-detect.mjs +115 -0
  55. package/core/indexing/admission-policy.js +139 -0
  56. package/core/indexing/artifact-builder.js +29 -12
  57. package/core/indexing/ast-chunker.js +107 -30
  58. package/core/indexing/dedup/exemplar-selector.js +19 -1
  59. package/core/indexing/gitignore-filter.js +223 -0
  60. package/core/indexing/incremental-tracker.js +99 -30
  61. package/core/indexing/index-codebase-v21.js +6 -5
  62. package/core/indexing/index-maintainer.mjs +698 -6
  63. package/core/indexing/indexer-ann.js +99 -15
  64. package/core/indexing/indexer-build.js +158 -45
  65. package/core/indexing/indexer-empty-baseline.js +80 -0
  66. package/core/indexing/indexer-manifest.js +66 -0
  67. package/core/indexing/indexer-phases.js +56 -23
  68. package/core/indexing/indexer-sparse-gram.js +54 -13
  69. package/core/indexing/indexer-utils.js +26 -208
  70. package/core/indexing/indexing-file-policy.js +32 -7
  71. package/core/indexing/maintainer-launcher.mjs +137 -0
  72. package/core/indexing/merkle-tracker.js +251 -244
  73. package/core/indexing/model-pool.js +46 -5
  74. package/core/infrastructure/code-graph-repository.js +758 -6
  75. package/core/infrastructure/code-graph-visibility.js +157 -0
  76. package/core/infrastructure/codebase-repository.js +100 -13
  77. package/core/infrastructure/config/search.js +1 -1
  78. package/core/infrastructure/db-utils.js +118 -0
  79. package/core/infrastructure/dedup-hashing.js +10 -13
  80. package/core/infrastructure/hardware-capability.js +17 -7
  81. package/core/infrastructure/index.js +8 -2
  82. package/core/infrastructure/language-patterns/maps.js +4 -1
  83. package/core/infrastructure/language-patterns/registry-core.js +56 -17
  84. package/core/infrastructure/language-patterns/registry-object-oriented.js +12 -5
  85. package/core/infrastructure/language-patterns.js +69 -0
  86. package/core/infrastructure/model-registry.js +20 -0
  87. package/core/infrastructure/native-inference.js +7 -12
  88. package/core/infrastructure/native-resolver.js +52 -37
  89. package/core/infrastructure/native-sparse-gram.js +261 -20
  90. package/core/infrastructure/native-tokenizer.js +6 -15
  91. package/core/infrastructure/simd-distance.js +10 -16
  92. package/core/infrastructure/sparse-gram-delta-reader.js +76 -0
  93. package/core/infrastructure/structural-alias-resolver.js +122 -0
  94. package/core/infrastructure/structural-candidate-ranker.js +34 -0
  95. package/core/infrastructure/structural-context-repository.js +472 -0
  96. package/core/infrastructure/structural-context-utils.js +51 -0
  97. package/core/infrastructure/structural-graph-signals.js +121 -0
  98. package/core/infrastructure/structural-qualified-resolution.js +15 -0
  99. package/core/infrastructure/structural-source-definitions.js +100 -0
  100. package/core/infrastructure/tombstone-bitmap-reader.js +139 -0
  101. package/core/infrastructure/tree-sitter-provider.js +811 -37
  102. package/core/prompt-optimization/data/p7-final/sweet-search-system-prompt.md +50 -0
  103. package/core/query/query-router.js +55 -5
  104. package/core/ranking/file-kind-ranking.js +2192 -15
  105. package/core/ranking/late-interaction-index.js +87 -12
  106. package/core/search/cli-decoration.js +290 -0
  107. package/core/search/context-expander.js +988 -78
  108. package/core/search/index.js +1 -0
  109. package/core/search/output-policy.js +275 -0
  110. package/core/search/search-anchor.js +499 -0
  111. package/core/search/search-boost.js +93 -1
  112. package/core/search/search-cli.js +61 -204
  113. package/core/search/search-hybrid.js +250 -10
  114. package/core/search/search-pattern-chunks.js +57 -8
  115. package/core/search/search-pattern-planner.js +68 -9
  116. package/core/search/search-pattern-prefilter.js +30 -10
  117. package/core/search/search-pattern-ripgrep.js +40 -4
  118. package/core/search/search-pattern-sparse-overlay.js +256 -0
  119. package/core/search/search-pattern.js +117 -29
  120. package/core/search/search-postprocess.js +479 -5
  121. package/core/search/search-read-semantic.js +260 -23
  122. package/core/search/search-read.js +82 -64
  123. package/core/search/search-reader-pin.js +71 -0
  124. package/core/search/search-rrf.js +279 -0
  125. package/core/search/search-semantic.js +110 -5
  126. package/core/search/search-server.js +130 -57
  127. package/core/search/search-trace.js +107 -0
  128. package/core/search/server-identity.js +93 -0
  129. package/core/search/session-daemon-prewarm.mjs +33 -10
  130. package/core/search/sweet-search.js +399 -7
  131. package/core/skills/sweet-index/SKILL.md +8 -6
  132. package/core/vector-store/binary-hnsw-index.js +194 -30
  133. package/core/vector-store/float-vector-store.js +96 -6
  134. package/core/vector-store/hnsw-index.js +220 -49
  135. package/eval/agent-read-workflows/bin/_ss-helpers.mjs +471 -0
  136. package/eval/agent-read-workflows/bin/ss-find +15 -0
  137. package/eval/agent-read-workflows/bin/ss-grep +12 -0
  138. package/eval/agent-read-workflows/bin/ss-read +14 -0
  139. package/eval/agent-read-workflows/bin/ss-search +18 -0
  140. package/eval/agent-read-workflows/bin/ss-semantic +12 -0
  141. package/eval/agent-read-workflows/bin/ss-trace +11 -0
  142. package/mcp/read-tool.js +109 -0
  143. package/mcp/server.js +55 -15
  144. package/mcp/tool-handlers.js +14 -124
  145. package/mcp/trace-tool.js +81 -0
  146. package/package.json +25 -10
  147. package/scripts/hooks/intercept-read.mjs +55 -0
  148. package/scripts/hooks/remind-tools.mjs +40 -0
  149. package/scripts/init.js +698 -54
  150. package/scripts/inject-agent-instructions.js +431 -0
  151. package/scripts/install-prompt-reminders.js +188 -0
  152. package/scripts/install-tool-enforcement.js +220 -0
  153. package/scripts/smoke-test.js +12 -9
  154. package/scripts/uninstall.js +276 -18
  155. package/scripts/write-claude-rules.js +110 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Project-scoped warm-server identity (release-gate C3).
3
+ *
4
+ * The warm search server was discovered via a single GLOBAL Unix socket
5
+ * (`/tmp/sweet-search.sock`) + a global legacy symlink (`/tmp/search.sock`) and
6
+ * an unconditional TCP port (9876). With more than one project on a machine
7
+ * this silently routed project B's queries to project A's server, and a second
8
+ * project's server crashed on `EADDRINUSE`.
9
+ *
10
+ * The fix derives a per-project socket/pidfile from the canonical project root,
11
+ * so each project gets its own server and can never answer another project's
12
+ * queries. The native Rust CLI re-implements the SAME derivation (FNV-1a/64 of
13
+ * the canonical root → `/tmp/sweet-search-<hash16>.sock`) so both sides agree.
14
+ *
15
+ * Why a hashed `/tmp` path and not `<root>/.sweet-search/search.sock`:
16
+ * `sockaddr_un.sun_path` is capped (~104 bytes on macOS); a deep project path
17
+ * would overflow it. The hashed `/tmp` path is always short and collision-safe.
18
+ *
19
+ * Overrides:
20
+ * - `SWEET_SEARCH_SOCKET_PATH` — explicit socket; when set, NO legacy fallback.
21
+ * - `SWEET_SEARCH_PID_FILE` — explicit pidfile.
22
+ * - `SWEET_SEARCH_TCP_PORT` — opt-in TCP (off by default; see search-server).
23
+ */
24
+
25
+ import { realpathSync, existsSync } from 'node:fs';
26
+ import path from 'node:path';
27
+
28
+ const FNV_OFFSET = 14695981039346656037n; // 0xcbf29ce484222325
29
+ const FNV_PRIME = 1099511628211n; // 0x100000001b3
30
+ const U64_MASK = (1n << 64n) - 1n;
31
+
32
+ /** FNV-1a 64-bit of a string's UTF-8 bytes → 16 lowercase hex chars. */
33
+ export function fnv1a64Hex(str) {
34
+ let hash = FNV_OFFSET;
35
+ const bytes = Buffer.from(String(str), 'utf8');
36
+ for (let i = 0; i < bytes.length; i++) {
37
+ hash ^= BigInt(bytes[i]);
38
+ hash = (hash * FNV_PRIME) & U64_MASK;
39
+ }
40
+ return hash.toString(16).padStart(16, '0');
41
+ }
42
+
43
+ /** Canonicalise a path (resolve symlinks, e.g. macOS /tmp → /private/tmp). */
44
+ function canonicalize(p) {
45
+ const abs = path.resolve(p);
46
+ try {
47
+ return realpathSync.native(abs);
48
+ } catch {
49
+ return abs; // path may not exist yet (pre-index cold start) — use lexical.
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Resolve the canonical project root for socket derivation. Honors an explicit
55
+ * `SWEET_SEARCH_PROJECT_ROOT`; otherwise walks up from `cwd` to the nearest
56
+ * ancestor containing a `.sweet-search/` state dir (so queries from a
57
+ * subdirectory map to the same server), falling back to the canonical `cwd`.
58
+ */
59
+ export function resolveProjectRoot(env = process.env, cwd = process.cwd()) {
60
+ const base = canonicalize(env.SWEET_SEARCH_PROJECT_ROOT || cwd);
61
+ let dir = base;
62
+ while (true) {
63
+ if (existsSync(path.join(dir, '.sweet-search'))) return dir;
64
+ const parent = path.dirname(dir);
65
+ if (parent === dir) break;
66
+ dir = parent;
67
+ }
68
+ return base;
69
+ }
70
+
71
+ /** Project-scoped socket path (or the explicit override). */
72
+ export function projectSocketPath(env = process.env, cwd = process.cwd()) {
73
+ if (env.SWEET_SEARCH_SOCKET_PATH) return env.SWEET_SEARCH_SOCKET_PATH;
74
+ return `/tmp/sweet-search-${fnv1a64Hex(resolveProjectRoot(env, cwd))}.sock`;
75
+ }
76
+
77
+ /** Project-scoped server pidfile (or the explicit override). */
78
+ export function projectPidFile(env = process.env, cwd = process.cwd()) {
79
+ if (env.SWEET_SEARCH_PID_FILE) return env.SWEET_SEARCH_PID_FILE;
80
+ return `/tmp/sweet-search-${fnv1a64Hex(resolveProjectRoot(env, cwd))}.pid`;
81
+ }
82
+
83
+ /**
84
+ * Opt-in TCP port. Off by default — TCP on a single global port is the
85
+ * multi-project collision source (EADDRINUSE) and a cross-project leak vector.
86
+ * Returns a finite port number, or null when TCP should not be bound.
87
+ */
88
+ export function tcpPort(env = process.env) {
89
+ const raw = env.SWEET_SEARCH_TCP_PORT;
90
+ if (raw == null || raw === '') return null;
91
+ const n = Number.parseInt(String(raw), 10);
92
+ return Number.isInteger(n) && n > 0 && n < 65536 ? n : null;
93
+ }
@@ -26,12 +26,16 @@ import { existsSync, readFileSync, openSync, writeSync, closeSync, unlinkSync }
26
26
  import { connect } from 'node:net';
27
27
  import { dirname, join } from 'node:path';
28
28
  import { fileURLToPath } from 'node:url';
29
+ import { launchMaintainer } from '../indexing/maintainer-launcher.mjs';
30
+ import { projectSocketPath, projectPidFile } from './server-identity.js';
29
31
 
30
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
31
33
 
32
34
  const SERVER_ENTRY = process.env.SWEET_SEARCH_SERVER_ENTRY || join(__dirname, '..', 'start-server.js');
33
- const SOCKET_PATH = process.env.SWEET_SEARCH_SOCKET_PATH || '/tmp/sweet-search.sock';
34
- const PID_FILE = process.env.SWEET_SEARCH_PID_FILE || '/tmp/sweet-search-server.pid';
35
+ // Per-project socket/pidfile (C3) — honors SWEET_SEARCH_SOCKET_PATH /
36
+ // SWEET_SEARCH_PID_FILE overrides, otherwise derives from the canonical root.
37
+ const SOCKET_PATH = projectSocketPath();
38
+ const PID_FILE = projectPidFile();
35
39
  const LOCK_PATH = process.env.SWEET_SEARCH_PREWARM_LOCK || '/tmp/sweet-search-prewarm.lock';
36
40
  const SOCKET_PROBE_TIMEOUT_MS = Number(process.env.SWEET_SEARCH_PREWARM_PROBE_MS ?? 300);
37
41
 
@@ -127,23 +131,25 @@ function releaseLock(fd) {
127
131
  try { unlinkSync(LOCK_PATH); } catch { /* ignore */ }
128
132
  }
129
133
 
130
- try {
134
+ /**
135
+ * Spawn the detached search server unless it is already responsive or another
136
+ * concurrent prewarm hook is mid-spawn. Returns (does not exit) so the
137
+ * maintainer launch below always runs.
138
+ */
139
+ async function prewarmServer() {
131
140
  if (await daemonHealthy()) {
132
141
  log('daemon already responsive on socket');
133
- process.exit(0);
142
+ return;
134
143
  }
135
-
136
144
  if (!existsSync(SERVER_ENTRY)) {
137
145
  log(`server entry missing: ${SERVER_ENTRY}`);
138
- process.exit(0);
146
+ return;
139
147
  }
140
-
141
148
  const lockFd = acquireLock();
142
149
  if (lockFd === null) {
143
150
  log('another prewarm hook already holds the lock; skipping');
144
- process.exit(0);
151
+ return;
145
152
  }
146
-
147
153
  try {
148
154
  // Fully detach: new session, no stdio, parent can exit while daemon loads.
149
155
  const child = spawn(process.execPath, [SERVER_ENTRY, '--serve'], {
@@ -157,8 +163,25 @@ try {
157
163
  } finally {
158
164
  releaseLock(lockFd);
159
165
  }
166
+ }
167
+
168
+ // The search server and the index maintainer are independent: a stuck/already
169
+ // running server must not stop the maintainer from starting, and vice versa.
170
+ // Each is isolated in its own try so one failing never blocks the other.
171
+ try {
172
+ await prewarmServer();
173
+ } catch (err) {
174
+ log(`server prewarm non-fatal: ${err?.message || err}`);
175
+ }
176
+
177
+ try {
178
+ // Delegate to the shared launcher (core/indexing/maintainer-launcher.mjs).
179
+ // The hook is a best-effort convenience layer; the durable guarantee lives in
180
+ // the warm search-server startup, which calls this same launcher. Pass our
181
+ // verbose-gated logger so output keeps the prewarm prefix + stays stderr-only.
182
+ launchMaintainer({ log });
160
183
  } catch (err) {
161
- log(`non-fatal: ${err?.message || err}`);
184
+ log(`maintainer prewarm non-fatal: ${err?.message || err}`);
162
185
  }
163
186
 
164
187
  process.exit(0);