@claudecam/server 0.1.0

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 (52) hide show
  1. package/dist/db/index.js +68 -0
  2. package/dist/db/index.js.map +1 -0
  3. package/dist/db/queries.js +658 -0
  4. package/dist/db/queries.js.map +1 -0
  5. package/dist/db/schema.sql +259 -0
  6. package/dist/index.js +128 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/routes/agents.js +68 -0
  9. package/dist/routes/agents.js.map +1 -0
  10. package/dist/routes/correlation-audit.js +31 -0
  11. package/dist/routes/correlation-audit.js.map +1 -0
  12. package/dist/routes/events.js +81 -0
  13. package/dist/routes/events.js.map +1 -0
  14. package/dist/routes/files.js +24 -0
  15. package/dist/routes/files.js.map +1 -0
  16. package/dist/routes/parse-prd.js +38 -0
  17. package/dist/routes/parse-prd.js.map +1 -0
  18. package/dist/routes/projects.js +96 -0
  19. package/dist/routes/projects.js.map +1 -0
  20. package/dist/routes/registry.js +88 -0
  21. package/dist/routes/registry.js.map +1 -0
  22. package/dist/routes/session-groups.js +182 -0
  23. package/dist/routes/session-groups.js.map +1 -0
  24. package/dist/routes/sessions.js +109 -0
  25. package/dist/routes/sessions.js.map +1 -0
  26. package/dist/routes/sprints.js +58 -0
  27. package/dist/routes/sprints.js.map +1 -0
  28. package/dist/routes/stats.js +63 -0
  29. package/dist/routes/stats.js.map +1 -0
  30. package/dist/routes/stream.js +21 -0
  31. package/dist/routes/stream.js.map +1 -0
  32. package/dist/routes/tasks.js +198 -0
  33. package/dist/routes/tasks.js.map +1 -0
  34. package/dist/services/correlation-engine.js +577 -0
  35. package/dist/services/correlation-engine.js.map +1 -0
  36. package/dist/services/event-processor.js +857 -0
  37. package/dist/services/event-processor.js.map +1 -0
  38. package/dist/services/prd-parser.js +142 -0
  39. package/dist/services/prd-parser.js.map +1 -0
  40. package/dist/services/project-manager.js +351 -0
  41. package/dist/services/project-manager.js.map +1 -0
  42. package/dist/services/project-router.js +56 -0
  43. package/dist/services/project-router.js.map +1 -0
  44. package/dist/services/session-manager.js +76 -0
  45. package/dist/services/session-manager.js.map +1 -0
  46. package/dist/services/sse-manager.js +115 -0
  47. package/dist/services/sse-manager.js.map +1 -0
  48. package/dist/services/string-similarity.js +256 -0
  49. package/dist/services/string-similarity.js.map +1 -0
  50. package/dist/services/task-completion.js +251 -0
  51. package/dist/services/task-completion.js.map +1 -0
  52. package/package.json +59 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-router.js","sourceRoot":"","sources":["../../src/services/project-router.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AA4B1B;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,MAAM,KAAK,GAAG,sBAAsB;SACjC,eAAe,EAAE;SACjB,GAAG,CAAC,UAAU,CAA4B,CAAC;IAC9C,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IAEnC,MAAM,MAAM,GAAG,sBAAsB;SAClC,qBAAqB,EAAE;SACvB,GAAG,CAAC,UAAU,CAA4B,CAAC;IAC9C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC;IAErC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAiB,EACjB,gBAAwB;IAExB,MAAM,SAAS,GAAG,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,QAAQ,GAAG,4BAA4B;SAC1C,YAAY,EAAE;SACd,GAAG,CAAC,SAAS,CAA2B,CAAC;IAC5C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,UAAU,CAAC;IAEzC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,4BAA4B,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACnE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,MAAM,OAAO,GAAG,4BAA4B;SACzC,YAAY,EAAE;SACd,GAAG,CAAC,SAAS,CAA2B,CAAC;IAC5C,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,MAAM,QAAQ,GAAG,4BAA4B;SAC1C,YAAY,EAAE;SACd,GAAG,CAAC,SAAS,CAAiB,CAAC;IAClC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { sessionQueries, agentQueries } from '../db/queries.js';
3
+ function rowToSession(row) {
4
+ return {
5
+ id: row.id,
6
+ startedAt: row.started_at,
7
+ endedAt: row.ended_at ?? undefined,
8
+ workingDirectory: row.working_directory,
9
+ status: row.status,
10
+ agentCount: row.agent_count,
11
+ eventCount: row.event_count,
12
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
13
+ };
14
+ }
15
+ export function createSession(workingDirectory, id) {
16
+ const sessionId = id || randomUUID();
17
+ const now = new Date().toISOString();
18
+ sessionQueries.insert().run(sessionId, now, workingDirectory, 'active', 0, 0, null);
19
+ return {
20
+ id: sessionId,
21
+ startedAt: now,
22
+ workingDirectory,
23
+ status: 'active',
24
+ agentCount: 0,
25
+ eventCount: 0,
26
+ };
27
+ }
28
+ export function getSession(id) {
29
+ const row = sessionQueries.getById().get(id);
30
+ return row ? rowToSession(row) : null;
31
+ }
32
+ export function listSessions(options = {}) {
33
+ const limit = options.limit ?? 10;
34
+ const offset = options.offset ?? 0;
35
+ let rows;
36
+ if (options.status) {
37
+ rows = sessionQueries.getByStatus().all(options.status, limit, offset);
38
+ }
39
+ else {
40
+ rows = sessionQueries.getAll().all(limit, offset);
41
+ }
42
+ return rows.map(rowToSession);
43
+ }
44
+ export function listSessionsByProject(projectId, options = {}) {
45
+ const limit = options.limit ?? 10;
46
+ const offset = options.offset ?? 0;
47
+ const rows = sessionQueries.getByProject().all(projectId, limit, offset);
48
+ return rows.map(rowToSession);
49
+ }
50
+ export function deleteSession(id) {
51
+ const result = sessionQueries.deleteById().run(id);
52
+ return result.changes > 0;
53
+ }
54
+ export function getSessionWithDetails(id) {
55
+ const session = getSession(id);
56
+ if (!session)
57
+ return null;
58
+ const agents = agentQueries.getBySession().all(id);
59
+ const mappedAgents = agents.map(a => ({
60
+ id: a['id'],
61
+ sessionId: a['session_id'],
62
+ name: a['name'],
63
+ type: a['type'],
64
+ status: a['status'],
65
+ firstSeenAt: a['first_seen_at'],
66
+ lastActivityAt: a['last_activity_at'],
67
+ currentTask: a['current_task'] ?? undefined,
68
+ toolCallCount: a['tool_call_count'],
69
+ errorCount: a['error_count'],
70
+ }));
71
+ return {
72
+ ...session,
73
+ agents: mappedAgents,
74
+ };
75
+ }
76
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/services/session-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ import { SSE_HEARTBEAT_INTERVAL_MS } from '@claudecam/shared';
2
+ class SSEManager {
3
+ clients = new Map();
4
+ heartbeatTimer = null;
5
+ projectSessionResolver = null;
6
+ /**
7
+ * Set the resolver that maps project IDs to session IDs.
8
+ * This avoids circular imports between sse-manager and queries.
9
+ * Placeholder for Sprint 8 Project Router implementation.
10
+ */
11
+ setProjectSessionResolver(resolver) {
12
+ this.projectSessionResolver = resolver;
13
+ }
14
+ addClient(id, res, sessionFilter, projectFilter) {
15
+ res.writeHead(200, {
16
+ 'Content-Type': 'text/event-stream',
17
+ 'Cache-Control': 'no-cache',
18
+ 'Connection': 'keep-alive',
19
+ 'X-Accel-Buffering': 'no',
20
+ });
21
+ res.write(`event: connected\ndata: ${JSON.stringify({ clientId: id, timestamp: new Date().toISOString() })}\n\n`);
22
+ this.clients.set(id, { id, res, sessionFilter, projectFilter });
23
+ res.on('close', () => {
24
+ this.clients.delete(id);
25
+ });
26
+ if (!this.heartbeatTimer) {
27
+ this.startHeartbeat();
28
+ }
29
+ }
30
+ broadcast(eventType, data, sessionId) {
31
+ const payload = `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
32
+ for (const client of this.clients.values()) {
33
+ if (this.shouldDeliverToClient(client, sessionId)) {
34
+ try {
35
+ client.res.write(payload);
36
+ }
37
+ catch {
38
+ this.clients.delete(client.id);
39
+ }
40
+ }
41
+ }
42
+ }
43
+ getConnectionCount() {
44
+ return this.clients.size;
45
+ }
46
+ /**
47
+ * Determine if a message should be delivered to a specific client.
48
+ * Supports session-level and project-level filtering.
49
+ */
50
+ shouldDeliverToClient(client, sessionId) {
51
+ // No filter on client = receives everything
52
+ if (!client.sessionFilter && !client.projectFilter) {
53
+ return true;
54
+ }
55
+ // Session filter: direct match
56
+ if (client.sessionFilter && sessionId && client.sessionFilter === sessionId) {
57
+ return true;
58
+ }
59
+ // Project filter: check if sessionId belongs to the project
60
+ if (client.projectFilter && sessionId && this.projectSessionResolver) {
61
+ try {
62
+ const projectSessionIds = this.projectSessionResolver(client.projectFilter);
63
+ if (projectSessionIds.includes(sessionId)) {
64
+ return true;
65
+ }
66
+ }
67
+ catch {
68
+ // If resolver fails, skip
69
+ }
70
+ }
71
+ // If client has a filter but sessionId doesn't match, skip
72
+ // (unless no sessionId was provided, in which case deliver to all)
73
+ if (!sessionId) {
74
+ return true;
75
+ }
76
+ return false;
77
+ }
78
+ startHeartbeat() {
79
+ this.heartbeatTimer = setInterval(() => {
80
+ const payload = `event: heartbeat\ndata: ${JSON.stringify({
81
+ timestamp: new Date().toISOString(),
82
+ connections: this.clients.size,
83
+ })}\n\n`;
84
+ for (const client of this.clients.values()) {
85
+ try {
86
+ client.res.write(payload);
87
+ }
88
+ catch {
89
+ this.clients.delete(client.id);
90
+ }
91
+ }
92
+ if (this.clients.size === 0 && this.heartbeatTimer) {
93
+ clearInterval(this.heartbeatTimer);
94
+ this.heartbeatTimer = null;
95
+ }
96
+ }, SSE_HEARTBEAT_INTERVAL_MS);
97
+ }
98
+ shutdown() {
99
+ if (this.heartbeatTimer) {
100
+ clearInterval(this.heartbeatTimer);
101
+ this.heartbeatTimer = null;
102
+ }
103
+ for (const client of this.clients.values()) {
104
+ try {
105
+ client.res.end();
106
+ }
107
+ catch {
108
+ // ignore
109
+ }
110
+ }
111
+ this.clients.clear();
112
+ }
113
+ }
114
+ export const sseManager = new SSEManager();
115
+ //# sourceMappingURL=sse-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-manager.js","sourceRoot":"","sources":["../../src/services/sse-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * String similarity algorithms for the Correlation Engine.
3
+ *
4
+ * Pure TypeScript implementations with ZERO external dependencies.
5
+ * All functions are exported for testing.
6
+ *
7
+ * Algorithms:
8
+ * - Jaro-Winkler distance (string-level similarity)
9
+ * - Token-based similarity (best token-to-token Jaro-Winkler)
10
+ * - Combined similarity (weighted blend of full-string + token)
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // Normalization helpers
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Decomposition map for common accented characters.
17
+ *
18
+ * Covers Portuguese (BR) diacritics and other Latin accents.
19
+ * We avoid `String.prototype.normalize('NFD')` + regex because some
20
+ * older runtimes handle combining marks inconsistently. An explicit map
21
+ * is deterministic and fast for the characters we care about.
22
+ */
23
+ const ACCENT_MAP = {
24
+ '\u00E0': 'a', '\u00E1': 'a', '\u00E2': 'a', '\u00E3': 'a', '\u00E4': 'a', '\u00E5': 'a', // a-graves/accents
25
+ '\u00E8': 'e', '\u00E9': 'e', '\u00EA': 'e', '\u00EB': 'e', // e-accents
26
+ '\u00EC': 'i', '\u00ED': 'i', '\u00EE': 'i', '\u00EF': 'i', // i-accents
27
+ '\u00F2': 'o', '\u00F3': 'o', '\u00F4': 'o', '\u00F5': 'o', '\u00F6': 'o', // o-accents
28
+ '\u00F9': 'u', '\u00FA': 'u', '\u00FB': 'u', '\u00FC': 'u', // u-accents
29
+ '\u00E7': 'c', // c-cedilla
30
+ '\u00F1': 'n', // n-tilde
31
+ '\u00FD': 'y', '\u00FF': 'y', // y-accents
32
+ // Uppercase equivalents (after toLowerCase these shouldn't appear, but just in case)
33
+ '\u00C0': 'a', '\u00C1': 'a', '\u00C2': 'a', '\u00C3': 'a', '\u00C4': 'a', '\u00C5': 'a',
34
+ '\u00C8': 'e', '\u00C9': 'e', '\u00CA': 'e', '\u00CB': 'e',
35
+ '\u00CC': 'i', '\u00CD': 'i', '\u00CE': 'i', '\u00CF': 'i',
36
+ '\u00D2': 'o', '\u00D3': 'o', '\u00D4': 'o', '\u00D5': 'o', '\u00D6': 'o',
37
+ '\u00D9': 'u', '\u00DA': 'u', '\u00DB': 'u', '\u00DC': 'u',
38
+ '\u00C7': 'c',
39
+ '\u00D1': 'n',
40
+ '\u00DD': 'y',
41
+ };
42
+ /**
43
+ * Normalise a string for comparison:
44
+ * - lowercase
45
+ * - replace accented characters with ASCII equivalents
46
+ * - trim leading/trailing whitespace
47
+ * - collapse multiple whitespace into a single space
48
+ */
49
+ export function normalizeString(s) {
50
+ let result = s.toLowerCase();
51
+ // Replace accented chars via map (fast path for common Latin chars)
52
+ let normalized = '';
53
+ for (let i = 0; i < result.length; i++) {
54
+ const ch = result[i];
55
+ const replacement = ACCENT_MAP[ch];
56
+ normalized += replacement !== undefined ? replacement : ch;
57
+ }
58
+ result = normalized;
59
+ // Collapse whitespace and trim
60
+ result = result.replace(/\s+/g, ' ').trim();
61
+ return result;
62
+ }
63
+ /**
64
+ * Tokenize a string into words.
65
+ *
66
+ * Splits on whitespace, underscores, hyphens, dots, slashes, colons, and
67
+ * other common separators.
68
+ *
69
+ * IMPORTANT: NO minimum length filter. Short tokens like "UI", "API", "DB",
70
+ * "SSE", "CI" are critical for matching technical task titles.
71
+ */
72
+ export function tokenize(s) {
73
+ const normalized = normalizeString(s);
74
+ if (normalized.length === 0)
75
+ return [];
76
+ return normalized
77
+ .split(/[\s_\-/\\.:,;|()[\]{}]+/)
78
+ .filter(token => token.length > 0);
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Jaro-Winkler similarity
82
+ // ---------------------------------------------------------------------------
83
+ /**
84
+ * Compute the Jaro similarity between two strings.
85
+ *
86
+ * The Jaro similarity is defined as:
87
+ * jaro = (1/3) * (m/|s1| + m/|s2| + (m - t)/m)
88
+ *
89
+ * where:
90
+ * m = number of matching characters
91
+ * t = number of transpositions / 2
92
+ * Characters are considered matching if they are the same and within
93
+ * floor(max(|s1|, |s2|) / 2) - 1 positions of each other.
94
+ *
95
+ * Returns a value between 0 (no similarity) and 1 (identical).
96
+ */
97
+ function jaroSimilarity(s1, s2) {
98
+ if (s1.length === 0 && s2.length === 0)
99
+ return 1.0;
100
+ if (s1.length === 0 || s2.length === 0)
101
+ return 0.0;
102
+ if (s1 === s2)
103
+ return 1.0;
104
+ const maxLen = Math.max(s1.length, s2.length);
105
+ // The match window: characters must be within this distance
106
+ const matchWindow = Math.max(Math.floor(maxLen / 2) - 1, 0);
107
+ const s1Matches = new Array(s1.length).fill(false);
108
+ const s2Matches = new Array(s2.length).fill(false);
109
+ let matches = 0;
110
+ let transpositions = 0;
111
+ // Find matching characters
112
+ for (let i = 0; i < s1.length; i++) {
113
+ const start = Math.max(0, i - matchWindow);
114
+ const end = Math.min(i + matchWindow + 1, s2.length);
115
+ for (let j = start; j < end; j++) {
116
+ if (s2Matches[j] || s1[i] !== s2[j])
117
+ continue;
118
+ s1Matches[i] = true;
119
+ s2Matches[j] = true;
120
+ matches++;
121
+ break;
122
+ }
123
+ }
124
+ if (matches === 0)
125
+ return 0.0;
126
+ // Count transpositions
127
+ let k = 0;
128
+ for (let i = 0; i < s1.length; i++) {
129
+ if (!s1Matches[i])
130
+ continue;
131
+ while (!s2Matches[k])
132
+ k++;
133
+ if (s1[i] !== s2[k])
134
+ transpositions++;
135
+ k++;
136
+ }
137
+ const jaro = (matches / s1.length +
138
+ matches / s2.length +
139
+ (matches - transpositions / 2) / matches) /
140
+ 3;
141
+ return jaro;
142
+ }
143
+ /**
144
+ * Compute the Jaro-Winkler similarity between two strings.
145
+ *
146
+ * Jaro-Winkler adds a prefix bonus to the base Jaro score. Strings that
147
+ * share a common prefix of up to 4 characters receive a boost, which is
148
+ * especially useful for matching task titles that start with the same verb
149
+ * (e.g. "Implement auth" vs "Implement authentication").
150
+ *
151
+ * The formula is:
152
+ * jaroWinkler = jaro + (prefixLen * scalingFactor * (1 - jaro))
153
+ *
154
+ * where scalingFactor = 0.1 (standard Winkler constant) and
155
+ * prefixLen is capped at 4.
156
+ *
157
+ * @returns A value between 0 (no similarity) and 1 (identical).
158
+ */
159
+ export function jaroWinkler(a, b) {
160
+ const s1 = normalizeString(a);
161
+ const s2 = normalizeString(b);
162
+ const jaro = jaroSimilarity(s1, s2);
163
+ // Calculate common prefix length (up to 4 characters)
164
+ const maxPrefix = Math.min(4, s1.length, s2.length);
165
+ let prefixLen = 0;
166
+ for (let i = 0; i < maxPrefix; i++) {
167
+ if (s1[i] === s2[i]) {
168
+ prefixLen++;
169
+ }
170
+ else {
171
+ break;
172
+ }
173
+ }
174
+ // Winkler modification: boost score for common prefix
175
+ const scalingFactor = 0.1;
176
+ const jaroWinklerScore = jaro + prefixLen * scalingFactor * (1 - jaro);
177
+ return Math.min(jaroWinklerScore, 1.0);
178
+ }
179
+ // ---------------------------------------------------------------------------
180
+ // Token-based similarity
181
+ // ---------------------------------------------------------------------------
182
+ /**
183
+ * Compute the best token-to-token similarity between two strings.
184
+ *
185
+ * Strategy:
186
+ * 1. Tokenize both strings.
187
+ * 2. For each token in `a`, find the best Jaro-Winkler match in `b`.
188
+ * 3. Compute coverage-weighted score: tokens from the SHORTER set drive
189
+ * the score (so "auth module" vs "Implement auth module for the app"
190
+ * scores high because both "auth" and "module" match well).
191
+ *
192
+ * This handles:
193
+ * - Word reordering: "auth module" vs "module auth" scores ~1.0
194
+ * - Partial overlap: "auth module" vs "auth module implementation" scores high
195
+ * - Morphological variants: "implement" vs "implementation" via Jaro-Winkler
196
+ *
197
+ * @returns A value between 0 (no token overlap) and 1 (perfect match).
198
+ */
199
+ export function tokenSimilarity(a, b) {
200
+ const tokensA = tokenize(a);
201
+ const tokensB = tokenize(b);
202
+ if (tokensA.length === 0 && tokensB.length === 0)
203
+ return 1.0;
204
+ if (tokensA.length === 0 || tokensB.length === 0)
205
+ return 0.0;
206
+ // Use the shorter token set as the "query" to maximize coverage
207
+ const [query, corpus] = tokensA.length <= tokensB.length
208
+ ? [tokensA, tokensB]
209
+ : [tokensB, tokensA];
210
+ let totalScore = 0;
211
+ for (const qToken of query) {
212
+ let bestTokenScore = 0;
213
+ for (const cToken of corpus) {
214
+ // Use raw jaroSimilarity on already-normalized tokens (tokenize calls normalizeString)
215
+ const score = jaroSimilarity(qToken, cToken);
216
+ if (score > bestTokenScore) {
217
+ bestTokenScore = score;
218
+ }
219
+ // Early exit if we found a perfect match
220
+ if (bestTokenScore >= 1.0)
221
+ break;
222
+ }
223
+ totalScore += bestTokenScore;
224
+ }
225
+ // Coverage factor: penalize if the corpus is much larger than the query.
226
+ // A query of 2 tokens matching 2 out of 10 corpus tokens should score
227
+ // lower than matching 2 out of 3.
228
+ const rawScore = totalScore / query.length;
229
+ const coverageRatio = query.length / corpus.length;
230
+ // Blend: 80% raw match quality + 20% coverage
231
+ // This ensures "auth" vs "implement auth module for the app" (1/5 coverage)
232
+ // scores lower than "auth module" vs "auth module impl" (2/3 coverage)
233
+ return rawScore * (0.8 + 0.2 * coverageRatio);
234
+ }
235
+ // ---------------------------------------------------------------------------
236
+ // Combined similarity
237
+ // ---------------------------------------------------------------------------
238
+ /**
239
+ * Compute the combined similarity score between two strings.
240
+ *
241
+ * Blends full-string Jaro-Winkler with token-based similarity:
242
+ * combined = 0.6 * jaroWinkler(a, b) + 0.4 * tokenSimilarity(a, b)
243
+ *
244
+ * The full-string component captures overall character-level similarity
245
+ * (good for typos, accents, minor variations). The token component
246
+ * captures semantic overlap at the word level (good for reordering,
247
+ * extra words, abbreviations).
248
+ *
249
+ * @returns A value between 0 and 1.
250
+ */
251
+ export function combinedSimilarity(a, b) {
252
+ const fullStringScore = jaroWinkler(a, b);
253
+ const tokenScore = tokenSimilarity(a, b);
254
+ return 0.6 * fullStringScore + 0.4 * tokenScore;
255
+ }
256
+ //# sourceMappingURL=string-similarity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-similarity.js","sourceRoot":"","sources":["../../src/services/string-similarity.ts"],"names":[],"mappings":"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"}