cache-overflow-mcp 0.2.0 → 0.3.1

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 (48) hide show
  1. package/.env.example +3 -3
  2. package/AGENTS.md +235 -0
  3. package/E2E-TESTING.md +5 -5
  4. package/LICENSE +21 -0
  5. package/README.md +13 -6
  6. package/dist/prompts/index.d.ts +14 -0
  7. package/dist/prompts/index.d.ts.map +1 -0
  8. package/dist/prompts/index.js +153 -0
  9. package/dist/prompts/index.js.map +1 -0
  10. package/dist/server.d.ts.map +1 -1
  11. package/dist/server.js +16 -2
  12. package/dist/server.js.map +1 -1
  13. package/dist/testing/mock-data.js +40 -40
  14. package/dist/tools/find-solution.d.ts.map +1 -1
  15. package/dist/tools/find-solution.js +25 -2
  16. package/dist/tools/find-solution.js.map +1 -1
  17. package/dist/tools/get-balance.d.ts +3 -0
  18. package/dist/tools/get-balance.d.ts.map +1 -0
  19. package/dist/tools/get-balance.js +34 -0
  20. package/dist/tools/get-balance.js.map +1 -0
  21. package/dist/tools/submit-feedback.js +1 -1
  22. package/dist/tools/submit-feedback.js.map +1 -1
  23. package/dist/tools/submit-verification.js +1 -1
  24. package/dist/tools/submit-verification.js.map +1 -1
  25. package/dist/tools/unlock-solution.d.ts.map +1 -1
  26. package/dist/tools/unlock-solution.js +3 -2
  27. package/dist/tools/unlock-solution.js.map +1 -1
  28. package/dist/ui/verification-dialog.js +267 -267
  29. package/package.json +3 -3
  30. package/{mock-server.js → scripts/mock-server.js} +1 -1
  31. package/src/cli.ts +10 -10
  32. package/src/client.test.ts +116 -116
  33. package/src/client.ts +76 -76
  34. package/src/config.ts +9 -9
  35. package/src/index.ts +3 -3
  36. package/src/prompts/index.ts +168 -0
  37. package/src/server.ts +19 -1
  38. package/src/testing/mock-data.ts +142 -142
  39. package/src/testing/mock-server.ts +176 -176
  40. package/src/tools/find-solution.ts +30 -2
  41. package/src/tools/index.ts +23 -23
  42. package/src/tools/submit-feedback.ts +1 -1
  43. package/src/tools/submit-verification.ts +1 -1
  44. package/src/tools/unlock-solution.ts +4 -2
  45. package/src/types.ts +39 -39
  46. package/src/ui/verification-dialog.ts +342 -342
  47. package/tsconfig.json +20 -20
  48. package/test-dialog.js +0 -37
@@ -1,142 +1,142 @@
1
- import type { Solution, FindSolutionResult, Balance } from '../types.js';
2
-
3
- export const mockSolutions: Solution[] = [
4
- {
5
- id: 'sol_001',
6
- author_id: 'user_123',
7
- query_title: 'How to implement binary search in TypeScript',
8
- solution_body: `function binarySearch<T>(arr: T[], target: T): number {
9
- let left = 0;
10
- let right = arr.length - 1;
11
- while (left <= right) {
12
- const mid = Math.floor((left + right) / 2);
13
- if (arr[mid] === target) return mid;
14
- if (arr[mid] < target) left = mid + 1;
15
- else right = mid - 1;
16
- }
17
- return -1;
18
- }`,
19
- price_current: 50,
20
- verification_state: 'VERIFIED',
21
- access_count: 127,
22
- upvotes: 45,
23
- downvotes: 2,
24
- },
25
- {
26
- id: 'sol_002',
27
- author_id: 'user_456',
28
- query_title: 'Fix memory leak in Node.js event listeners',
29
- solution_body: `// Always remove event listeners when done
30
- const handler = () => { /* ... */ };
31
- emitter.on('event', handler);
32
- // Later:
33
- emitter.off('event', handler);
34
-
35
- // Or use once() for one-time listeners
36
- emitter.once('event', () => { /* ... */ });`,
37
- price_current: 75,
38
- verification_state: 'VERIFIED',
39
- access_count: 89,
40
- upvotes: 32,
41
- downvotes: 1,
42
- },
43
- {
44
- id: 'sol_003',
45
- author_id: 'user_789',
46
- query_title: 'Optimize React re-renders with useMemo',
47
- solution_body: `import { useMemo } from 'react';
48
-
49
- function ExpensiveComponent({ data }) {
50
- const processed = useMemo(() => {
51
- return data.map(item => heavyComputation(item));
52
- }, [data]);
53
-
54
- return <div>{processed}</div>;
55
- }`,
56
- price_current: 60,
57
- verification_state: 'PENDING',
58
- access_count: 15,
59
- upvotes: 8,
60
- downvotes: 0,
61
- },
62
- ];
63
-
64
- export const mockFindResults: FindSolutionResult[] = [
65
- {
66
- solution_id: 'sol_001',
67
- query_title: 'How to implement binary search in TypeScript',
68
- human_verification_required: false,
69
- },
70
- {
71
- solution_id: 'sol_002',
72
- query_title: 'Fix memory leak in Node.js event listeners',
73
- solution_body: `// Always remove event listeners when done
74
- const handler = () => { /* ... */ };
75
- emitter.on('event', handler);
76
- // Later:
77
- emitter.off('event', handler);
78
-
79
- // Or use once() for one-time listeners
80
- emitter.once('event', () => { /* ... */ });`,
81
- human_verification_required: true,
82
- },
83
- {
84
- solution_id: 'sol_003',
85
- query_title: 'Optimize React re-renders with useMemo',
86
- solution_body: `import { useMemo } from 'react';
87
-
88
- function ExpensiveComponent({ data }) {
89
- const processed = useMemo(() => {
90
- return data.map(item => heavyComputation(item));
91
- }, [data]);
92
-
93
- return <div>{processed}</div>;
94
- }`,
95
- human_verification_required: false,
96
- },
97
- ];
98
-
99
- export const mockBalance: Balance = {
100
- available: 1500,
101
- pending_debits: 75,
102
- pending_credits: 200,
103
- total_earned: 3500,
104
- total_spent: 1800,
105
- };
106
-
107
- export function createMockSolution(overrides: Partial<Solution> = {}): Solution {
108
- return {
109
- id: `sol_${Date.now()}`,
110
- author_id: 'user_mock',
111
- query_title: 'Mock solution title',
112
- solution_body: 'Mock solution body content',
113
- price_current: 50,
114
- verification_state: 'PENDING',
115
- access_count: 0,
116
- upvotes: 0,
117
- downvotes: 0,
118
- ...overrides,
119
- };
120
- }
121
-
122
- export function createMockFindResult(
123
- overrides: Partial<FindSolutionResult> = {}
124
- ): FindSolutionResult {
125
- return {
126
- solution_id: `sol_${Date.now()}`,
127
- query_title: 'Mock query title',
128
- human_verification_required: false,
129
- ...overrides,
130
- };
131
- }
132
-
133
- export function createMockBalance(overrides: Partial<Balance> = {}): Balance {
134
- return {
135
- available: 1000,
136
- pending_debits: 0,
137
- pending_credits: 0,
138
- total_earned: 1000,
139
- total_spent: 0,
140
- ...overrides,
141
- };
142
- }
1
+ import type { Solution, FindSolutionResult, Balance } from '../types.js';
2
+
3
+ export const mockSolutions: Solution[] = [
4
+ {
5
+ id: 'sol_001',
6
+ author_id: 'user_123',
7
+ query_title: 'How to implement binary search in TypeScript',
8
+ solution_body: `function binarySearch<T>(arr: T[], target: T): number {
9
+ let left = 0;
10
+ let right = arr.length - 1;
11
+ while (left <= right) {
12
+ const mid = Math.floor((left + right) / 2);
13
+ if (arr[mid] === target) return mid;
14
+ if (arr[mid] < target) left = mid + 1;
15
+ else right = mid - 1;
16
+ }
17
+ return -1;
18
+ }`,
19
+ price_current: 50,
20
+ verification_state: 'VERIFIED',
21
+ access_count: 127,
22
+ upvotes: 45,
23
+ downvotes: 2,
24
+ },
25
+ {
26
+ id: 'sol_002',
27
+ author_id: 'user_456',
28
+ query_title: 'Fix memory leak in Node.js event listeners',
29
+ solution_body: `// Always remove event listeners when done
30
+ const handler = () => { /* ... */ };
31
+ emitter.on('event', handler);
32
+ // Later:
33
+ emitter.off('event', handler);
34
+
35
+ // Or use once() for one-time listeners
36
+ emitter.once('event', () => { /* ... */ });`,
37
+ price_current: 75,
38
+ verification_state: 'VERIFIED',
39
+ access_count: 89,
40
+ upvotes: 32,
41
+ downvotes: 1,
42
+ },
43
+ {
44
+ id: 'sol_003',
45
+ author_id: 'user_789',
46
+ query_title: 'Optimize React re-renders with useMemo',
47
+ solution_body: `import { useMemo } from 'react';
48
+
49
+ function ExpensiveComponent({ data }) {
50
+ const processed = useMemo(() => {
51
+ return data.map(item => heavyComputation(item));
52
+ }, [data]);
53
+
54
+ return <div>{processed}</div>;
55
+ }`,
56
+ price_current: 60,
57
+ verification_state: 'PENDING',
58
+ access_count: 15,
59
+ upvotes: 8,
60
+ downvotes: 0,
61
+ },
62
+ ];
63
+
64
+ export const mockFindResults: FindSolutionResult[] = [
65
+ {
66
+ solution_id: 'sol_001',
67
+ query_title: 'How to implement binary search in TypeScript',
68
+ human_verification_required: false,
69
+ },
70
+ {
71
+ solution_id: 'sol_002',
72
+ query_title: 'Fix memory leak in Node.js event listeners',
73
+ solution_body: `// Always remove event listeners when done
74
+ const handler = () => { /* ... */ };
75
+ emitter.on('event', handler);
76
+ // Later:
77
+ emitter.off('event', handler);
78
+
79
+ // Or use once() for one-time listeners
80
+ emitter.once('event', () => { /* ... */ });`,
81
+ human_verification_required: true,
82
+ },
83
+ {
84
+ solution_id: 'sol_003',
85
+ query_title: 'Optimize React re-renders with useMemo',
86
+ solution_body: `import { useMemo } from 'react';
87
+
88
+ function ExpensiveComponent({ data }) {
89
+ const processed = useMemo(() => {
90
+ return data.map(item => heavyComputation(item));
91
+ }, [data]);
92
+
93
+ return <div>{processed}</div>;
94
+ }`,
95
+ human_verification_required: false,
96
+ },
97
+ ];
98
+
99
+ export const mockBalance: Balance = {
100
+ available: 1500,
101
+ pending_debits: 75,
102
+ pending_credits: 200,
103
+ total_earned: 3500,
104
+ total_spent: 1800,
105
+ };
106
+
107
+ export function createMockSolution(overrides: Partial<Solution> = {}): Solution {
108
+ return {
109
+ id: `sol_${Date.now()}`,
110
+ author_id: 'user_mock',
111
+ query_title: 'Mock solution title',
112
+ solution_body: 'Mock solution body content',
113
+ price_current: 50,
114
+ verification_state: 'PENDING',
115
+ access_count: 0,
116
+ upvotes: 0,
117
+ downvotes: 0,
118
+ ...overrides,
119
+ };
120
+ }
121
+
122
+ export function createMockFindResult(
123
+ overrides: Partial<FindSolutionResult> = {}
124
+ ): FindSolutionResult {
125
+ return {
126
+ solution_id: `sol_${Date.now()}`,
127
+ query_title: 'Mock query title',
128
+ human_verification_required: false,
129
+ ...overrides,
130
+ };
131
+ }
132
+
133
+ export function createMockBalance(overrides: Partial<Balance> = {}): Balance {
134
+ return {
135
+ available: 1000,
136
+ pending_debits: 0,
137
+ pending_credits: 0,
138
+ total_earned: 1000,
139
+ total_spent: 0,
140
+ ...overrides,
141
+ };
142
+ }
@@ -1,176 +1,176 @@
1
- import * as http from 'node:http';
2
- import { mockSolutions, mockFindResults, createMockSolution } from './mock-data.js';
3
- import type { Solution, FindSolutionResult } from '../types.js';
4
-
5
- interface RouteHandler {
6
- (
7
- req: http.IncomingMessage,
8
- body: unknown,
9
- params: Record<string, string>
10
- ): { status: number; data: unknown };
11
- }
12
-
13
- interface Route {
14
- method: string;
15
- pattern: RegExp;
16
- paramNames: string[];
17
- handler: RouteHandler;
18
- }
19
-
20
- export class MockServer {
21
- private server: http.Server | null = null;
22
- private port: number = 0;
23
- private routes: Route[] = [];
24
-
25
- get url(): string {
26
- return `http://localhost:${this.port}`;
27
- }
28
-
29
- constructor() {
30
- this.setupRoutes();
31
- }
32
-
33
- private setupRoutes(): void {
34
- // POST /solutions/find
35
- this.addRoute('POST', '/solutions/find', (_req, body) => {
36
- const { query } = body as { query: string };
37
- const results: FindSolutionResult[] = mockFindResults.filter((r) =>
38
- r.query_title.toLowerCase().includes((query ?? '').toLowerCase())
39
- );
40
- return { status: 200, data: results.length > 0 ? results : mockFindResults };
41
- });
42
-
43
- // POST /solutions/:id/unlock
44
- this.addRoute('POST', '/solutions/:id/unlock', (_req, _body, params) => {
45
- const solution = mockSolutions.find((s) => s.id === params.id);
46
- if (solution) {
47
- return { status: 200, data: solution };
48
- }
49
- return { status: 200, data: mockSolutions[0] };
50
- });
51
-
52
- // POST /solutions
53
- this.addRoute('POST', '/solutions', (_req, body) => {
54
- const { query_title, solution_body } = body as {
55
- query_title: string;
56
- solution_body: string;
57
- };
58
- const newSolution: Solution = createMockSolution({
59
- query_title,
60
- solution_body,
61
- });
62
- return { status: 200, data: newSolution };
63
- });
64
-
65
- // POST /solutions/:id/verify
66
- this.addRoute('POST', '/solutions/:id/verify', () => {
67
- return { status: 200, data: null };
68
- });
69
-
70
- // POST /solutions/:id/feedback
71
- this.addRoute('POST', '/solutions/:id/feedback', () => {
72
- return { status: 200, data: null };
73
- });
74
- }
75
-
76
- private addRoute(method: string, path: string, handler: RouteHandler): void {
77
- const paramNames: string[] = [];
78
- const patternString = path.replace(/:([^/]+)/g, (_match, paramName) => {
79
- paramNames.push(paramName);
80
- return '([^/]+)';
81
- });
82
- const pattern = new RegExp(`^${patternString}$`);
83
- this.routes.push({ method, pattern, paramNames, handler });
84
- }
85
-
86
- private matchRoute(
87
- method: string,
88
- path: string
89
- ): { route: Route; params: Record<string, string> } | null {
90
- for (const route of this.routes) {
91
- if (route.method !== method) continue;
92
- const match = path.match(route.pattern);
93
- if (match) {
94
- const params: Record<string, string> = {};
95
- route.paramNames.forEach((name, index) => {
96
- params[name] = match[index + 1];
97
- });
98
- return { route, params };
99
- }
100
- }
101
- return null;
102
- }
103
-
104
- async start(port?: number): Promise<void> {
105
- return new Promise((resolve, reject) => {
106
- this.server = http.createServer((req, res) => {
107
- this.handleRequest(req, res);
108
- });
109
-
110
- this.server.on('error', reject);
111
-
112
- this.server.listen(port ?? 0, () => {
113
- const address = this.server!.address();
114
- if (typeof address === 'object' && address !== null) {
115
- this.port = address.port;
116
- }
117
- resolve();
118
- });
119
- });
120
- }
121
-
122
- async stop(): Promise<void> {
123
- return new Promise((resolve, reject) => {
124
- if (!this.server) {
125
- resolve();
126
- return;
127
- }
128
-
129
- this.server.close((err) => {
130
- if (err) {
131
- reject(err);
132
- } else {
133
- this.server = null;
134
- this.port = 0;
135
- resolve();
136
- }
137
- });
138
- });
139
- }
140
-
141
- private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
142
- const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
143
- const method = req.method ?? 'GET';
144
- const path = url.pathname;
145
-
146
- let body = '';
147
- req.on('data', (chunk) => {
148
- body += chunk;
149
- });
150
-
151
- req.on('end', () => {
152
- let parsedBody: unknown = null;
153
- if (body) {
154
- try {
155
- parsedBody = JSON.parse(body);
156
- } catch {
157
- // Ignore parse errors
158
- }
159
- }
160
-
161
- const matched = this.matchRoute(method, path);
162
-
163
- if (!matched) {
164
- res.writeHead(404, { 'Content-Type': 'application/json' });
165
- res.end(JSON.stringify({ error: 'Not found' }));
166
- return;
167
- }
168
-
169
- const { route, params } = matched;
170
- const result = route.handler(req, parsedBody, params);
171
-
172
- res.writeHead(result.status, { 'Content-Type': 'application/json' });
173
- res.end(JSON.stringify(result.data));
174
- });
175
- }
176
- }
1
+ import * as http from 'node:http';
2
+ import { mockSolutions, mockFindResults, createMockSolution } from './mock-data.js';
3
+ import type { Solution, FindSolutionResult } from '../types.js';
4
+
5
+ interface RouteHandler {
6
+ (
7
+ req: http.IncomingMessage,
8
+ body: unknown,
9
+ params: Record<string, string>
10
+ ): { status: number; data: unknown };
11
+ }
12
+
13
+ interface Route {
14
+ method: string;
15
+ pattern: RegExp;
16
+ paramNames: string[];
17
+ handler: RouteHandler;
18
+ }
19
+
20
+ export class MockServer {
21
+ private server: http.Server | null = null;
22
+ private port: number = 0;
23
+ private routes: Route[] = [];
24
+
25
+ get url(): string {
26
+ return `http://localhost:${this.port}`;
27
+ }
28
+
29
+ constructor() {
30
+ this.setupRoutes();
31
+ }
32
+
33
+ private setupRoutes(): void {
34
+ // POST /solutions/find
35
+ this.addRoute('POST', '/solutions/find', (_req, body) => {
36
+ const { query } = body as { query: string };
37
+ const results: FindSolutionResult[] = mockFindResults.filter((r) =>
38
+ r.query_title.toLowerCase().includes((query ?? '').toLowerCase())
39
+ );
40
+ return { status: 200, data: results.length > 0 ? results : mockFindResults };
41
+ });
42
+
43
+ // POST /solutions/:id/unlock
44
+ this.addRoute('POST', '/solutions/:id/unlock', (_req, _body, params) => {
45
+ const solution = mockSolutions.find((s) => s.id === params.id);
46
+ if (solution) {
47
+ return { status: 200, data: solution };
48
+ }
49
+ return { status: 200, data: mockSolutions[0] };
50
+ });
51
+
52
+ // POST /solutions
53
+ this.addRoute('POST', '/solutions', (_req, body) => {
54
+ const { query_title, solution_body } = body as {
55
+ query_title: string;
56
+ solution_body: string;
57
+ };
58
+ const newSolution: Solution = createMockSolution({
59
+ query_title,
60
+ solution_body,
61
+ });
62
+ return { status: 200, data: newSolution };
63
+ });
64
+
65
+ // POST /solutions/:id/verify
66
+ this.addRoute('POST', '/solutions/:id/verify', () => {
67
+ return { status: 200, data: null };
68
+ });
69
+
70
+ // POST /solutions/:id/feedback
71
+ this.addRoute('POST', '/solutions/:id/feedback', () => {
72
+ return { status: 200, data: null };
73
+ });
74
+ }
75
+
76
+ private addRoute(method: string, path: string, handler: RouteHandler): void {
77
+ const paramNames: string[] = [];
78
+ const patternString = path.replace(/:([^/]+)/g, (_match, paramName) => {
79
+ paramNames.push(paramName);
80
+ return '([^/]+)';
81
+ });
82
+ const pattern = new RegExp(`^${patternString}$`);
83
+ this.routes.push({ method, pattern, paramNames, handler });
84
+ }
85
+
86
+ private matchRoute(
87
+ method: string,
88
+ path: string
89
+ ): { route: Route; params: Record<string, string> } | null {
90
+ for (const route of this.routes) {
91
+ if (route.method !== method) continue;
92
+ const match = path.match(route.pattern);
93
+ if (match) {
94
+ const params: Record<string, string> = {};
95
+ route.paramNames.forEach((name, index) => {
96
+ params[name] = match[index + 1];
97
+ });
98
+ return { route, params };
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+
104
+ async start(port?: number): Promise<void> {
105
+ return new Promise((resolve, reject) => {
106
+ this.server = http.createServer((req, res) => {
107
+ this.handleRequest(req, res);
108
+ });
109
+
110
+ this.server.on('error', reject);
111
+
112
+ this.server.listen(port ?? 0, () => {
113
+ const address = this.server!.address();
114
+ if (typeof address === 'object' && address !== null) {
115
+ this.port = address.port;
116
+ }
117
+ resolve();
118
+ });
119
+ });
120
+ }
121
+
122
+ async stop(): Promise<void> {
123
+ return new Promise((resolve, reject) => {
124
+ if (!this.server) {
125
+ resolve();
126
+ return;
127
+ }
128
+
129
+ this.server.close((err) => {
130
+ if (err) {
131
+ reject(err);
132
+ } else {
133
+ this.server = null;
134
+ this.port = 0;
135
+ resolve();
136
+ }
137
+ });
138
+ });
139
+ }
140
+
141
+ private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
142
+ const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
143
+ const method = req.method ?? 'GET';
144
+ const path = url.pathname;
145
+
146
+ let body = '';
147
+ req.on('data', (chunk) => {
148
+ body += chunk;
149
+ });
150
+
151
+ req.on('end', () => {
152
+ let parsedBody: unknown = null;
153
+ if (body) {
154
+ try {
155
+ parsedBody = JSON.parse(body);
156
+ } catch {
157
+ // Ignore parse errors
158
+ }
159
+ }
160
+
161
+ const matched = this.matchRoute(method, path);
162
+
163
+ if (!matched) {
164
+ res.writeHead(404, { 'Content-Type': 'application/json' });
165
+ res.end(JSON.stringify({ error: 'Not found' }));
166
+ return;
167
+ }
168
+
169
+ const { route, params } = matched;
170
+ const result = route.handler(req, parsedBody, params);
171
+
172
+ res.writeHead(result.status, { 'Content-Type': 'application/json' });
173
+ res.end(JSON.stringify(result.data));
174
+ });
175
+ }
176
+ }