sweet-search 2.5.2 → 2.5.4

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
@@ -18,3 +18,4 @@ export { hybridSearch, hybridSearchV2 } from './search-hybrid.js';
18
18
  // Read tools — filesystem-grounded exact reads + hybrid semantic span selection
19
19
  export { readFile, readFiles, formatReadResults, handleReadCli } from './search-read.js';
20
20
  export { readSemantic, formatReadSemanticResult, handleReadSemanticCli } from './search-read-semantic.js';
21
+ export { traceSymbol, handleTraceCli, formatStructuralContext } from './search-trace.js';
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Output-decoration policy for sweet-search CLI tools.
3
+ *
4
+ * Decides WHERE decoration (banner/colors/animation) may be emitted so it is
5
+ * shown only on channels that are free to the user/model, and suppressed
6
+ * wherever captured output would spend tokens. This module makes decisions; it
7
+ * does not render. Renderers (search-cli.js) consult the returned policy.
8
+ *
9
+ * Channel facts that drive the policy (see investigation notes):
10
+ * - MCP reads only the JSON-RPC response content — decoration must never be
11
+ * placed there. (Enforced structurally in the MCP layer, not here.)
12
+ * - Claude Code Bash captures stdout+stderr, but the controlling terminal is
13
+ * usually still reachable via /dev/tty, which the pipe does not intercept —
14
+ * so decoration written there is free.
15
+ * - Codex-style shells pipe stdio and sandbox /dev/tty — never rely on it.
16
+ * - Cursor/Cline PTY integrations capture the PTY stream itself, so /dev/tty
17
+ * writes are captured. They present as isTTY=true; we keep them out of the
18
+ * decorate-on-stdout tier only when a harness env marker is detectable.
19
+ *
20
+ * Decoration is NEVER routed to stderr — that channel is captured too.
21
+ */
22
+
23
+ import { openSync, closeSync, writeSync } from 'node:fs';
24
+
25
+ // Best-effort env markers for captured harnesses where decoration on stdout
26
+ // (or on a PTY-backed /dev/tty) would cost tokens. Detection here means
27
+ // "suppress decoration". Extend conservatively — a false positive only costs a
28
+ // banner, a false negative costs the user tokens.
29
+ const OTHER_AGENT_ENV_KEYS = ['CURSOR_TRACE_ID', 'CLINE_ACTIVE', 'GEMINI_CLI', 'OPENCODE'];
30
+
31
+ function _truthy(value) {
32
+ if (value == null) return false;
33
+ const v = String(value).trim().toLowerCase();
34
+ return v !== '' && v !== '0' && v !== 'false' && v !== 'off' && v !== 'no';
35
+ }
36
+
37
+ function _hasNoColor(env) {
38
+ // NO_COLOR convention: any non-empty value disables color.
39
+ const v = env.NO_COLOR;
40
+ return v != null && String(v) !== '';
41
+ }
42
+
43
+ // Global decoration override: SWEET_SEARCH_DECORATION=auto|always|never.
44
+ // Unknown/empty values fall back to 'auto'.
45
+ function _decorationMode(env) {
46
+ const v = String(env.SWEET_SEARCH_DECORATION || '').trim().toLowerCase();
47
+ if (v === 'always' || v === 'never') return v;
48
+ return 'auto';
49
+ }
50
+
51
+ /**
52
+ * Classify the runtime harness from environment variables.
53
+ * @param {Record<string,string|undefined>} env
54
+ * @returns {{ codex: boolean, claudeCode: boolean, otherAgent: boolean }}
55
+ */
56
+ export function detectAgentEnv(env = process.env) {
57
+ const e = env || {};
58
+ const keys = Object.keys(e);
59
+ const codex = keys.some((k) => k.startsWith('CODEX_'));
60
+ const claudeCode = _truthy(e.CLAUDECODE) || _truthy(e.CLAUDE_CODE);
61
+ const otherAgent =
62
+ OTHER_AGENT_ENV_KEYS.some((k) => _truthy(e[k])) ||
63
+ keys.some((k) => k.startsWith('CURSOR_'));
64
+ return { codex, claudeCode, otherAgent };
65
+ }
66
+
67
+ /**
68
+ * Best-effort probe for a separately-writable controlling terminal.
69
+ *
70
+ * Opens /dev/tty for writing without emitting any bytes, then closes it.
71
+ * Success means a controlling terminal exists that the captured stdout pipe
72
+ * does not intercept (the Claude Code Bash case). Any error → unavailable.
73
+ * Never throws; never affects the caller's results.
74
+ *
75
+ * @returns {string|null} the tty path if usable, else null
76
+ */
77
+ export function defaultProbeTty() {
78
+ try {
79
+ const fd = openSync('/dev/tty', 'w');
80
+ closeSync(fd);
81
+ return '/dev/tty';
82
+ } catch {
83
+ return null;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @typedef {Object} OutputPolicy
89
+ * @property {('human-terminal'|'claude-tty-sidechannel'|'captured-plain'|'machine-readable')} mode
90
+ * @property {'stdout'} resultStream where search results go (always stdout)
91
+ * @property {('stdout'|'tty'|null)} decorationStream where banner/animation go
92
+ * @property {boolean} colorEnabled
93
+ * @property {boolean} bannerEnabled
94
+ * @property {boolean} animationEnabled
95
+ * @property {boolean} machineReadable
96
+ * @property {string|null} ttyPath
97
+ * @property {string} reason short tag for tests/debugging
98
+ */
99
+
100
+ /**
101
+ * Decide the output policy for a CLI invocation.
102
+ *
103
+ * @param {Object} [opts]
104
+ * @param {boolean} [opts.json=false] caller requested JSON output
105
+ * @param {string|null} [opts.format=null] explicit format (e.g. 'plain')
106
+ * @param {boolean} [opts.noBanner=false] --no-banner flag
107
+ * @param {Record<string,string|undefined>} [opts.env=process.env]
108
+ * @param {boolean} [opts.isTTY] override; defaults to stream.isTTY
109
+ * @param {{isTTY?: boolean}} [opts.stream=process.stdout]
110
+ * @param {() => (string|null)} [opts.probeTty=defaultProbeTty] injectable for tests
111
+ * @returns {OutputPolicy}
112
+ */
113
+ export function detectOutputPolicy(opts = {}) {
114
+ const env = opts.env || process.env;
115
+ const stream = opts.stream || process.stdout;
116
+ const isTTY = opts.isTTY != null ? !!opts.isTTY : !!(stream && stream.isTTY);
117
+ const json = !!opts.json;
118
+ const plain = opts.format === 'plain';
119
+ const noBanner = !!opts.noBanner;
120
+ const noColor = _hasNoColor(env);
121
+ const agent = detectAgentEnv(env);
122
+ const decorationMode = _decorationMode(env); // 'auto' | 'always' | 'never'
123
+ const probe = typeof opts.probeTty === 'function' ? opts.probeTty : defaultProbeTty;
124
+
125
+ /** @type {OutputPolicy} */
126
+ const policy = {
127
+ mode: 'captured-plain',
128
+ resultStream: 'stdout',
129
+ decorationStream: null,
130
+ colorEnabled: false,
131
+ bannerEnabled: false,
132
+ animationEnabled: false,
133
+ machineReadable: false,
134
+ ttyPath: null,
135
+ reason: 'captured-plain',
136
+ };
137
+
138
+ // Tier 4: an explicit machine-readable request wins outright — even over
139
+ // SWEET_SEARCH_DECORATION=always.
140
+ if (json) {
141
+ policy.mode = 'machine-readable';
142
+ policy.machineReadable = true;
143
+ policy.reason = 'json';
144
+ return policy;
145
+ }
146
+
147
+ if (decorationMode === 'never') {
148
+ // Explicit global opt-out.
149
+ policy.reason = 'decoration-never';
150
+ } else if (decorationMode === 'always') {
151
+ // Explicit global opt-in: decorate on stdout regardless of capture. The
152
+ // user has accepted any token cost. Still routes only to stdout, never
153
+ // stderr.
154
+ policy.mode = 'human-terminal';
155
+ policy.decorationStream = 'stdout';
156
+ policy.bannerEnabled = true;
157
+ policy.colorEnabled = true;
158
+ policy.animationEnabled = true;
159
+ policy.reason = 'decoration-always';
160
+ } else {
161
+ // Harness markers force suppression regardless of TTY state (defensive: a
162
+ // sandboxed codex shell may still report isTTY in some configs; PTY-based
163
+ // capture harnesses look like a TTY but capture the stream).
164
+ const suppress = agent.codex || agent.otherAgent;
165
+
166
+ if (isTTY && !suppress) {
167
+ // Tier 1 — real human terminal.
168
+ policy.mode = 'human-terminal';
169
+ policy.decorationStream = 'stdout';
170
+ policy.bannerEnabled = true;
171
+ policy.colorEnabled = true;
172
+ policy.animationEnabled = true;
173
+ policy.reason = 'human-terminal';
174
+ } else if (!isTTY && !suppress && agent.claudeCode) {
175
+ // Tier 2 — captured pipe, but Claude Code is positively detected, so the
176
+ // controlling terminal is reachable via /dev/tty (the pipe does not
177
+ // intercept it). Only Claude Code unlocks this side-channel; a merely
178
+ // writable /dev/tty with no agent marker is NOT trusted, because a
179
+ // PTY-capturing harness would route those bytes straight back into the
180
+ // captured stream.
181
+ const ttyPath = probe();
182
+ if (ttyPath) {
183
+ policy.mode = 'claude-tty-sidechannel';
184
+ policy.decorationStream = 'tty';
185
+ policy.ttyPath = ttyPath;
186
+ policy.bannerEnabled = true;
187
+ policy.colorEnabled = true;
188
+ policy.animationEnabled = true;
189
+ policy.reason = 'claude-code-tty';
190
+ } else {
191
+ policy.reason = 'claude-no-tty';
192
+ }
193
+ } else {
194
+ // Tier 3 — captured / harness-detected / no trusted side-channel.
195
+ policy.reason = agent.codex
196
+ ? 'codex-detected'
197
+ : agent.otherAgent
198
+ ? 'agent-detected'
199
+ : isTTY
200
+ ? 'captured-plain'
201
+ : 'captured-no-claude';
202
+ }
203
+ }
204
+
205
+ // Tier 4: explicit opt-outs always reduce decoration, never increase it.
206
+ if (noColor) policy.colorEnabled = false;
207
+ if (noBanner) policy.bannerEnabled = false;
208
+ if (plain) {
209
+ policy.bannerEnabled = false;
210
+ policy.colorEnabled = false;
211
+ policy.animationEnabled = false;
212
+ }
213
+
214
+ return policy;
215
+ }
216
+
217
+ /**
218
+ * Build a line writer for the decoration channel chosen by the policy.
219
+ *
220
+ * - 'stdout' → writes through the provided stdout stream.
221
+ * - 'tty' → opens the tty for writing; on any error falls back to a no-op
222
+ * (decoration is best-effort and must never break results).
223
+ * - null → no-op writer.
224
+ *
225
+ * The caller is responsible for only writing when policy.bannerEnabled (or
226
+ * animationEnabled) is true. Always call close() when done.
227
+ *
228
+ * @param {OutputPolicy} policy
229
+ * @param {Object} [opts] injectable I/O for tests
230
+ * @returns {{ write: (line?: string) => void, close: () => void }}
231
+ */
232
+ export function createDecorationWriter(policy, opts = {}) {
233
+ const target = policy ? policy.decorationStream : null;
234
+
235
+ if (target === 'stdout') {
236
+ const out = opts.stdout || process.stdout;
237
+ return {
238
+ write: (line = '') => out.write(`${line}\n`),
239
+ close: () => {},
240
+ };
241
+ }
242
+
243
+ if (target === 'tty') {
244
+ const open = opts.openTty || (() => openSync(policy.ttyPath || '/dev/tty', 'w'));
245
+ const _writeSync = opts.writeSync || writeSync;
246
+ const _closeSync = opts.closeSync || closeSync;
247
+ let fd = null;
248
+ try {
249
+ fd = open();
250
+ } catch {
251
+ fd = null;
252
+ }
253
+ if (fd == null) {
254
+ return { write: () => {}, close: () => {} };
255
+ }
256
+ return {
257
+ write: (line = '') => {
258
+ try {
259
+ _writeSync(fd, `${line}\n`);
260
+ } catch {
261
+ /* decoration is best-effort; ignore write failures */
262
+ }
263
+ },
264
+ close: () => {
265
+ try {
266
+ _closeSync(fd);
267
+ } catch {
268
+ /* ignore */
269
+ }
270
+ },
271
+ };
272
+ }
273
+
274
+ return { write: () => {}, close: () => {} };
275
+ }