ampcode-connector 0.1.5 → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ampcode-connector",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Proxy AmpCode through local OAuth subscriptions (Claude Code, Codex, Gemini CLI, Antigravity)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,8 +34,9 @@
34
34
  "dev": "bun run --watch src/index.ts",
35
35
  "setup": "bun run src/index.ts setup",
36
36
  "login": "bun run src/index.ts login",
37
- "test": "bun test",
38
- "check": "biome check src/ tests/ && tsc --noEmit && bun test",
37
+ "test": "bun test tests/router.test.ts tests/middleware.test.ts tests/rewriter.test.ts",
38
+ "test:e2e": "bun test tests/code-assist.test.ts",
39
+ "check": "biome check src/ tests/ && tsc --noEmit && bun run test",
39
40
  "format": "biome check --write src/ tests/"
40
41
  },
41
42
  "devDependencies": {
@@ -48,6 +49,7 @@
48
49
  "dependencies": {
49
50
  "@google/genai": "^1.41.0",
50
51
  "@kreuzberg/html-to-markdown": "^2.25.0",
52
+ "ampcode-connector": "^0.1.5",
51
53
  "exa-js": "^2.4.0"
52
54
  }
53
55
  }
package/src/auth/store.ts CHANGED
@@ -54,6 +54,7 @@ function init() {
54
54
  mkdirSync(DIR, { recursive: true, mode: 0o700 });
55
55
  _db = new Database(DB_PATH, { strict: true });
56
56
  _db.exec("PRAGMA journal_mode=WAL");
57
+ _db.exec("PRAGMA busy_timeout=5000");
57
58
  _db.exec(`
58
59
  CREATE TABLE IF NOT EXISTS credentials (
59
60
  provider TEXT NOT NULL,
package/src/index.ts CHANGED
File without changes
@@ -44,6 +44,9 @@ const RANKING = {
44
44
  MIN_KEYWORD_LEN: 3,
45
45
  HEADING_BOOST: 2,
46
46
  BIGRAM_BOOST: 1.5,
47
+ POSITION_DECAY: 0.1,
48
+ BM25_K1: 1.5,
49
+ BM25_B: 0.75,
47
50
  } as const;
48
51
 
49
52
  const CLIPPING = {
@@ -155,20 +158,20 @@ function convertToMarkdown(raw: string, contentType: string): string {
155
158
  function rankExcerpts(markdown: string, objective: string): string[] {
156
159
  const sections = splitSections(markdown);
157
160
  if (!sections.length) return [clipText(markdown)];
158
-
159
161
  const { unigrams, bigrams } = parseTerms(objective);
160
162
  if (!unigrams.length) return [clipText(markdown)];
161
-
162
- const idfWeights = computeIdf(sections, unigrams);
163
- const scored = sections.map((section) => scoreSection(section, unigrams, bigrams, idfWeights));
164
-
163
+ const unigramPatterns = unigrams.map((w) => new RegExp(`\\b${RegExp.escape(w)}\\b`, "g"));
164
+ const idfWeights = computeIdf(sections, unigramPatterns);
165
+ const avgDocLen = sections.reduce((sum, s) => sum + (s.text.split(/\s+/).length || 1), 0) / sections.length;
166
+ const totalSections = sections.length;
167
+ const scored = sections.map((section) =>
168
+ scoreSection(section, unigramPatterns, bigrams, idfWeights, avgDocLen, totalSections),
169
+ );
165
170
  const hits = scored.filter((s) => s.score > 0);
166
171
  if (!hits.length) return [clipText(markdown)];
167
-
168
172
  hits.sort((a, b) => b.score - a.score || a.index - b.index);
169
173
  const top = hits.slice(0, RANKING.MAX_SECTIONS);
170
174
  top.sort((a, b) => a.index - b.index);
171
-
172
175
  return clipMany(top.map((s) => s.text));
173
176
  }
174
177
 
@@ -185,45 +188,61 @@ function parseTerms(objective: string): { unigrams: string[]; bigrams: RegExp[]
185
188
  return { unigrams: words, bigrams };
186
189
  }
187
190
 
188
- function computeIdf(sections: Section[], unigrams: string[]): number[] {
191
+ function computeIdf(sections: Section[], patterns: RegExp[]): number[] {
189
192
  const lowerTexts = sections.map((section) => section.text.toLowerCase());
190
193
  const totalSections = sections.length;
191
-
192
- return unigrams.map((word) => {
193
- const pattern = new RegExp(`\\b${RegExp.escape(word)}\\b`);
194
- const docFreq = lowerTexts.filter((text) => pattern.test(text)).length;
195
- return docFreq > 0 ? Math.log(totalSections / docFreq) + 1 : 0;
194
+ return patterns.map((pattern) => {
195
+ const docFreq = lowerTexts.filter((text) => {
196
+ pattern.lastIndex = 0;
197
+ return pattern.test(text);
198
+ }).length;
199
+ return docFreq > 0 ? Math.log((totalSections - docFreq + 0.5) / (docFreq + 0.5) + 1) : 0;
196
200
  });
197
201
  }
198
202
 
199
- function scoreSection(section: Section, unigrams: string[], bigrams: RegExp[], idfWeights: number[]): ScoredSection {
203
+ function scoreSection(
204
+ section: Section,
205
+ unigramPatterns: RegExp[],
206
+ bigrams: RegExp[],
207
+ idfWeights: number[],
208
+ avgDocLen: number,
209
+ totalSections: number,
210
+ ): ScoredSection {
200
211
  const lowerText = section.text.toLowerCase();
201
212
  const lowerHeading = section.heading.toLowerCase();
202
- const wordCount = lowerText.split(/\s+/).length || 1;
213
+ const docLen = lowerText.split(/\s+/).length || 1;
203
214
 
204
- // Unigram TF-IDF
215
+ // BM25 scoring
216
+ const { BM25_K1: k1, BM25_B: b } = RANKING;
205
217
  let score = 0;
206
- for (let i = 0; i < unigrams.length; i++) {
207
- const pattern = new RegExp(`\\b${RegExp.escape(unigrams[i]!)}\\b`, "g");
218
+ for (let i = 0; i < unigramPatterns.length; i++) {
219
+ const pattern = unigramPatterns[i]!;
220
+ pattern.lastIndex = 0;
208
221
  const matches = lowerText.match(pattern);
209
222
  if (matches) {
210
- score += (matches.length / wordCount) * idfWeights[i]!;
223
+ const tf = matches.length;
224
+ score += idfWeights[i]! * ((tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (docLen / avgDocLen))));
211
225
  }
212
226
  }
213
-
214
227
  // Bigram bonus
215
228
  for (const pattern of bigrams) {
216
229
  if (pattern.test(lowerText)) score *= RANKING.BIGRAM_BOOST;
217
230
  }
218
231
 
219
- // Heading match boost
232
+ // Heading match boost (reuse pre-compiled patterns)
220
233
  if (section.heading) {
221
- const headingPattern = unigrams.map((word) => new RegExp(`\\b${RegExp.escape(word)}\\b`));
222
- if (headingPattern.some((pattern) => pattern.test(lowerHeading))) {
234
+ if (
235
+ unigramPatterns.some((pattern) => {
236
+ pattern.lastIndex = 0;
237
+ return pattern.test(lowerHeading);
238
+ })
239
+ ) {
223
240
  score *= RANKING.HEADING_BOOST;
224
241
  }
225
242
  }
226
243
 
244
+ // Position decay — earlier sections get mild boost
245
+ score *= 1 + RANKING.POSITION_DECAY * (1 - section.index / totalSections);
227
246
  return { text: section.text, score, index: section.index };
228
247
  }
229
248