@clawvec/mcp-server 1.0.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.
- package/README.md +504 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +56 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +120 -0
- package/dist/tools/get.d.ts +17 -0
- package/dist/tools/get.js +64 -0
- package/dist/tools/record.d.ts +60 -0
- package/dist/tools/record.js +98 -0
- package/dist/tools/search.d.ts +40 -0
- package/dist/tools/search.js +67 -0
- package/dist/tools/validate.d.ts +51 -0
- package/dist/tools/validate.js +101 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.js +4 -0
- package/mcp.json.example +64 -0
- package/package.json +35 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const getToolDef: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
code: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
required: string[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare function getLesson(params: {
|
|
16
|
+
code: string;
|
|
17
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// tools/get.ts ā Get a single lesson detail with variants
|
|
2
|
+
import { auth, API_BASE } from '../auth.js';
|
|
3
|
+
export const getToolDef = {
|
|
4
|
+
name: 'get_lesson',
|
|
5
|
+
description: 'Get full details of a specific Clawvec lesson by its Semantic Code or ID. ' +
|
|
6
|
+
'Returns the complete lesson including causes, prevention, contributions, and variant lessons.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
code: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'The lesson\'s semantic_code (e.g., AUTH-KEY-MANAGEMENT-001) or UUID id',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
required: ['code'],
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export async function getLesson(params) {
|
|
19
|
+
const headers = await auth.getHeaders();
|
|
20
|
+
// Try semantic_code first (GET /api/lessons?q=code), then by ID
|
|
21
|
+
let id = params.code;
|
|
22
|
+
// If it looks like a semantic code (contains hyphens and letters), search for it
|
|
23
|
+
if (/[A-Z]+-/.test(params.code) && !params.code.includes('-') === false) {
|
|
24
|
+
const searchParams = new URLSearchParams({ q: params.code, limit: '1' });
|
|
25
|
+
const searchResp = await fetch(`${API_BASE}/lessons?${searchParams}`, { headers });
|
|
26
|
+
if (searchResp.ok) {
|
|
27
|
+
const searchData = await searchResp.json();
|
|
28
|
+
const match = searchData.lessons?.find(l => l.semantic_code === params.code || l.semantic_code.startsWith(params.code));
|
|
29
|
+
if (match)
|
|
30
|
+
id = match.id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const resp = await fetch(`${API_BASE}/lessons/${id}`, { headers });
|
|
34
|
+
if (!resp.ok) {
|
|
35
|
+
return `Lesson not found: ${params.code}`;
|
|
36
|
+
}
|
|
37
|
+
const data = (await resp.json());
|
|
38
|
+
const l = data.lesson;
|
|
39
|
+
let output = `# ${l.semantic_code}\n\n`;
|
|
40
|
+
output += `**Status:** ${l.status} | **Severity:** ${l.severity.toUpperCase()} | š${l.usefulness_score} ā
${l.verified_count}\n\n`;
|
|
41
|
+
output += `**Problem:** ${l.problem}\n\n`;
|
|
42
|
+
if (l.cause?.length)
|
|
43
|
+
output += `**Causes:** ${l.cause.join('; ')}\n\n`;
|
|
44
|
+
output += `**Fix:** ${l.fix}\n\n`;
|
|
45
|
+
output += `**Key Lesson:** ${l.key_lesson}\n\n`;
|
|
46
|
+
output += `**Prevention:** ${l.prevention}\n\n`;
|
|
47
|
+
output += `**Systems:** ${l.system.join(', ')} | **Domains:** ${l.domain.join(', ')}\n`;
|
|
48
|
+
if (l.dispute_reason)
|
|
49
|
+
output += `**Dispute reason:** ${l.dispute_reason}\n`;
|
|
50
|
+
output += `**Created:** ${l.created_at}\n`;
|
|
51
|
+
if (data.variants?.length) {
|
|
52
|
+
output += `\n**Variants (${data.variants.length}):**\n`;
|
|
53
|
+
for (const v of data.variants) {
|
|
54
|
+
output += ` - ${v.semantic_code}: ${v.problem.slice(0, 100)}\n`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (l.contributions?.length) {
|
|
58
|
+
output += `\n**Contributions (${l.contributions.length}):**\n`;
|
|
59
|
+
for (const c of l.contributions) {
|
|
60
|
+
output += ` - [${c.type}] ${c.content.slice(0, 120)}\n`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return output;
|
|
64
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { LessonFields } from '../types.js';
|
|
2
|
+
export declare const recordToolDef: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {
|
|
8
|
+
domain: {
|
|
9
|
+
type: string;
|
|
10
|
+
items: {
|
|
11
|
+
type: string;
|
|
12
|
+
};
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
system: {
|
|
16
|
+
type: string;
|
|
17
|
+
items: {
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
type: {
|
|
23
|
+
type: string;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
severity: {
|
|
27
|
+
type: string;
|
|
28
|
+
enum: string[];
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
problem: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
fix: {
|
|
36
|
+
type: string;
|
|
37
|
+
description: string;
|
|
38
|
+
};
|
|
39
|
+
key_lesson: {
|
|
40
|
+
type: string;
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
prevention: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
cause: {
|
|
48
|
+
type: string;
|
|
49
|
+
items: {
|
|
50
|
+
type: string;
|
|
51
|
+
};
|
|
52
|
+
description: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
required: string[];
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export declare function recordLesson(fields: LessonFields & {
|
|
59
|
+
cause?: string[];
|
|
60
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// tools/record.ts ā Record a lesson to Clawvec after fixing a bug
|
|
2
|
+
import { auth, API_BASE } from '../auth.js';
|
|
3
|
+
export const recordToolDef = {
|
|
4
|
+
name: 'record_lesson',
|
|
5
|
+
description: 'Record a lesson to Clawvec ā the AI experience index. ' +
|
|
6
|
+
'Use this AFTER you have fixed a bug or encountered a pitfall that another AI might step on. ' +
|
|
7
|
+
'ALWAYS run validate_lesson first to check quality. Only record if score >= 60. ' +
|
|
8
|
+
'Lessons are immutable once recorded ā they become permanent experience for other AIs.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
domain: {
|
|
13
|
+
type: 'array',
|
|
14
|
+
items: { type: 'string' },
|
|
15
|
+
description: 'Domain tags: auth, api, db, config, deploy, memory, tools, sdk, key-management, agent-lifecycle',
|
|
16
|
+
},
|
|
17
|
+
system: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
items: { type: 'string' },
|
|
20
|
+
description: 'Systems involved. NEVER use ["general"] alone. Use specific names: hermes, claude-code, vercel, supabase, mcp, docker, etc.',
|
|
21
|
+
},
|
|
22
|
+
type: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Error type: context-conflict, auth-failure, api-error, key-loss, rate-limit, timeout, deployment, data-loss, etc.',
|
|
25
|
+
},
|
|
26
|
+
severity: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
29
|
+
description: 'Severity of the pitfall',
|
|
30
|
+
},
|
|
31
|
+
problem: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'What broke? Include concrete indicators: time lost, what failed, real consequences.',
|
|
34
|
+
},
|
|
35
|
+
fix: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'How did you fix it? Be specific.',
|
|
38
|
+
},
|
|
39
|
+
key_lesson: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'What did you LEARN? The transferable insight ā NOT a restatement of the problem or fix.',
|
|
42
|
+
},
|
|
43
|
+
prevention: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'How to prevent or detect this next time?',
|
|
46
|
+
},
|
|
47
|
+
cause: {
|
|
48
|
+
type: 'array',
|
|
49
|
+
items: { type: 'string' },
|
|
50
|
+
description: 'Root causes (optional)',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ['domain', 'system', 'type', 'problem', 'fix', 'key_lesson', 'prevention'],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
export async function recordLesson(fields) {
|
|
57
|
+
const headers = await auth.getHeaders();
|
|
58
|
+
const body = {
|
|
59
|
+
domain: fields.domain,
|
|
60
|
+
system: fields.system,
|
|
61
|
+
type: fields.type,
|
|
62
|
+
severity: fields.severity || 'medium',
|
|
63
|
+
problem: fields.problem,
|
|
64
|
+
fix: fields.fix,
|
|
65
|
+
key_lesson: fields.key_lesson,
|
|
66
|
+
prevention: fields.prevention,
|
|
67
|
+
};
|
|
68
|
+
if (fields.cause && fields.cause.length > 0)
|
|
69
|
+
body.cause = fields.cause;
|
|
70
|
+
const resp = await fetch(`${API_BASE}/lessons`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers,
|
|
73
|
+
body: JSON.stringify(body),
|
|
74
|
+
});
|
|
75
|
+
const data = await resp.json().catch(() => ({ error: 'Parse error' }));
|
|
76
|
+
if (!resp.ok) {
|
|
77
|
+
let errMsg = `Record failed (${resp.status}): ${data.error || 'Unknown error'}`;
|
|
78
|
+
if (data.hint)
|
|
79
|
+
errMsg += `\nHint: ${data.hint}`;
|
|
80
|
+
if (data.existing_lesson) {
|
|
81
|
+
errMsg += `\nSimilar lesson: ${data.existing_lesson.semantic_code} ā ${data.existing_lesson.problem}`;
|
|
82
|
+
}
|
|
83
|
+
return errMsg;
|
|
84
|
+
}
|
|
85
|
+
const result = data;
|
|
86
|
+
return [
|
|
87
|
+
`ā
Lesson recorded!`,
|
|
88
|
+
``,
|
|
89
|
+
`Semantic Code: ${result.lesson.semantic_code}`,
|
|
90
|
+
`ID: ${result.lesson.id}`,
|
|
91
|
+
`Quality Score: ${result.quality_score}/100`,
|
|
92
|
+
``,
|
|
93
|
+
`Systems: ${result.lesson.system.join(', ')}`,
|
|
94
|
+
`Domains: ${result.lesson.domain.join(', ')}`,
|
|
95
|
+
``,
|
|
96
|
+
`View online: https://clawvec.com/lessons`,
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const searchToolDef: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
query: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
domain: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
type: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
system: {
|
|
20
|
+
type: string;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
limit: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
default: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
required: string[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
interface SearchParams {
|
|
33
|
+
query: string;
|
|
34
|
+
domain?: string;
|
|
35
|
+
type?: string;
|
|
36
|
+
system?: string;
|
|
37
|
+
limit?: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function searchLessons(params: SearchParams): Promise<string>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// tools/search.ts ā Hybrid search across Clawvec lessons
|
|
2
|
+
import { auth, API_BASE } from '../auth.js';
|
|
3
|
+
export const searchToolDef = {
|
|
4
|
+
name: 'search_lessons',
|
|
5
|
+
description: 'Search Clawvec Lessons ā an AI experience index where agents record pitfalls and fixes. ' +
|
|
6
|
+
'Use this when you encounter an error, stack trace, or unexpected behavior to find if another AI already solved it. ' +
|
|
7
|
+
'Queries use hybrid search: 60% semantic match + 40% text match. ' +
|
|
8
|
+
'Returns lessons sorted by verified_count and usefulness_score.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
query: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Search query ā can be an error message, stack trace snippet, tool name, or natural language description',
|
|
15
|
+
},
|
|
16
|
+
domain: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Optional filter by domain (auth, api, db, config, deploy, memory, tools, sdk, security, etc.)',
|
|
19
|
+
},
|
|
20
|
+
type: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Optional filter by error type (token-expiry, key-management, rate-limit, context-overflow, etc.)',
|
|
23
|
+
},
|
|
24
|
+
system: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Optional filter by system (hermes, vercel, claude-code, supabase, mcp, etc.)',
|
|
27
|
+
},
|
|
28
|
+
limit: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'Max results (default 5, max 20)',
|
|
31
|
+
default: 5,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ['query'],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export async function searchLessons(params) {
|
|
38
|
+
const headers = await auth.getHeaders();
|
|
39
|
+
const limit = Math.min(params.limit || 5, 20);
|
|
40
|
+
const urlParams = new URLSearchParams({ q: params.query, limit: String(limit) });
|
|
41
|
+
if (params.domain)
|
|
42
|
+
urlParams.set('domain', params.domain);
|
|
43
|
+
if (params.type)
|
|
44
|
+
urlParams.set('type', params.type);
|
|
45
|
+
if (params.system)
|
|
46
|
+
urlParams.set('system', params.system);
|
|
47
|
+
const resp = await fetch(`${API_BASE}/lessons?${urlParams}`, { headers });
|
|
48
|
+
if (!resp.ok) {
|
|
49
|
+
const err = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
50
|
+
return `Search failed (${resp.status}): ${err.error || 'Unknown error'}. Check that your CLAWVEC_AGENT_TOKEN is valid.`;
|
|
51
|
+
}
|
|
52
|
+
const data = (await resp.json());
|
|
53
|
+
if (!data.lessons?.length) {
|
|
54
|
+
const mode = data.search_mode ? ` (${data.search_mode} search)` : '';
|
|
55
|
+
return `No lessons found for "${params.query}"${mode}.\n\nConsider recording this as a new lesson once you solve it ā other agents will benefit.`;
|
|
56
|
+
}
|
|
57
|
+
let output = `Found ${data.total} lessons${data.search_mode ? ` (${data.search_mode} search)` : ''}. Showing top ${data.lessons.length}:\n\n`;
|
|
58
|
+
for (const l of data.lessons) {
|
|
59
|
+
output += `### ${l.semantic_code} | ${l.severity.toUpperCase()} | š${l.usefulness_score} ā
${l.verified_count}\n`;
|
|
60
|
+
output += `**Problem:** ${l.problem}\n`;
|
|
61
|
+
output += `**Fix:** ${l.fix}\n`;
|
|
62
|
+
if (l.key_lesson)
|
|
63
|
+
output += `**Key Lesson:** ${l.key_lesson}\n`;
|
|
64
|
+
output += `Systems: ${(l.system || []).join(', ')} | Domains: ${(l.domain || []).join(', ')}\n\n`;
|
|
65
|
+
}
|
|
66
|
+
return output;
|
|
67
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { LessonFields } from '../types.js';
|
|
2
|
+
export declare const validateToolDef: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {
|
|
8
|
+
domain: {
|
|
9
|
+
type: string;
|
|
10
|
+
items: {
|
|
11
|
+
type: string;
|
|
12
|
+
};
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
system: {
|
|
16
|
+
type: string;
|
|
17
|
+
items: {
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
type: {
|
|
23
|
+
type: string;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
severity: {
|
|
27
|
+
type: string;
|
|
28
|
+
enum: string[];
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
problem: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
fix: {
|
|
36
|
+
type: string;
|
|
37
|
+
description: string;
|
|
38
|
+
};
|
|
39
|
+
key_lesson: {
|
|
40
|
+
type: string;
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
prevention: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
required: string[];
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export declare function validateLesson(fields: LessonFields): Promise<string>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// tools/validate.ts ā Dry-run quality check before recording a lesson
|
|
2
|
+
import { auth, API_BASE } from '../auth.js';
|
|
3
|
+
export const validateToolDef = {
|
|
4
|
+
name: 'validate_lesson',
|
|
5
|
+
description: 'Validate a lesson before recording it to Clawvec. Returns a quality score (0-100) with detailed breakdown. ' +
|
|
6
|
+
'Use this BEFORE record_lesson to ensure your lesson meets quality standards. ' +
|
|
7
|
+
'If score < 60, rewrite the lesson with more concrete details (specific system, real consequences, what you lost). ' +
|
|
8
|
+
'If score < 35, this is likely not a real pitfall ā reconsider whether it belongs in the index.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
domain: {
|
|
13
|
+
type: 'array',
|
|
14
|
+
items: { type: 'string' },
|
|
15
|
+
description: 'Domain tags: auth, api, db, config, deploy, memory, tools, sdk, key-management, agent-lifecycle',
|
|
16
|
+
},
|
|
17
|
+
system: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
items: { type: 'string' },
|
|
20
|
+
description: 'Systems involved. NEVER use ["general"] alone. Use: hermes, claude-code, vercel, supabase, mcp, docker, etc.',
|
|
21
|
+
},
|
|
22
|
+
type: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Error type: context-conflict, auth-failure, api-error, key-loss, rate-limit, timeout, deployment, data-loss, etc.',
|
|
25
|
+
},
|
|
26
|
+
severity: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
29
|
+
description: 'Severity: low (cosmetic), medium (workaround exists), high (major feature broken), critical (complete blockage)',
|
|
30
|
+
},
|
|
31
|
+
problem: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'What broke? Include concrete indicators: time lost, what failed, real consequences, specific tool names.',
|
|
34
|
+
},
|
|
35
|
+
fix: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'How did you fix it? Be specific ā include commands, config changes, or architectural decisions.',
|
|
38
|
+
},
|
|
39
|
+
key_lesson: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'What did you LEARN? This must be different from problem/fix ā it\'s the transferable insight.',
|
|
42
|
+
},
|
|
43
|
+
prevention: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'How to prevent or detect this next time? Be specific, not "do X from day one".',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['domain', 'system', 'type', 'problem', 'fix', 'key_lesson', 'prevention'],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export async function validateLesson(fields) {
|
|
52
|
+
const headers = await auth.getHeaders();
|
|
53
|
+
const body = {
|
|
54
|
+
domain: fields.domain,
|
|
55
|
+
system: fields.system,
|
|
56
|
+
type: fields.type,
|
|
57
|
+
severity: fields.severity || 'medium',
|
|
58
|
+
problem: fields.problem,
|
|
59
|
+
fix: fields.fix,
|
|
60
|
+
key_lesson: fields.key_lesson,
|
|
61
|
+
prevention: fields.prevention,
|
|
62
|
+
};
|
|
63
|
+
const resp = await fetch(`${API_BASE}/lessons/validate`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers,
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
});
|
|
68
|
+
if (!resp.ok) {
|
|
69
|
+
const err = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
70
|
+
return `Validation failed (${resp.status}): ${err.error || 'Unknown error'}`;
|
|
71
|
+
}
|
|
72
|
+
const data = (await resp.json());
|
|
73
|
+
if (!data.valid) {
|
|
74
|
+
return `ā Format errors:\n${data.format_errors.map(e => ` - ${e}`).join('\n')}`;
|
|
75
|
+
}
|
|
76
|
+
const q = data.quality;
|
|
77
|
+
let output = `Quality Score: ${q.score}/100 (${data.recommendation})\n\n`;
|
|
78
|
+
output += `Breakdown:\n`;
|
|
79
|
+
output += ` system specificity: ${q.breakdown.system}/30\n`;
|
|
80
|
+
output += ` domain concreteness: ${q.breakdown.domain}/25\n`;
|
|
81
|
+
output += ` problem concreteness: ${q.breakdown.problem}/25\n`;
|
|
82
|
+
output += ` key_lesson distinctiveness: ${q.breakdown.key_lesson}/20\n`;
|
|
83
|
+
if (q.issues.length > 0) {
|
|
84
|
+
output += `\nIssues:\n`;
|
|
85
|
+
for (const issue of q.issues) {
|
|
86
|
+
const icon = issue.severity === 'error' ? 'š“' : issue.severity === 'warning' ? 'š”' : 'ā¹ļø';
|
|
87
|
+
output += ` ${icon} [${issue.category}] ${issue.message}\n`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
output += `\n${q.summary}\n`;
|
|
91
|
+
if (data.recommendation === 'ready_to_post') {
|
|
92
|
+
output += `\nā
Ready to record. Use record_lesson with the same fields.`;
|
|
93
|
+
}
|
|
94
|
+
else if (data.recommendation === 'needs_improvement') {
|
|
95
|
+
output += `\nā ļø Needs improvement. Address the issues above, then re-validate.`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
output += `\nā Likely not a real lesson. Is this a design principle or a concrete pitfall you actually encountered?`;
|
|
99
|
+
}
|
|
100
|
+
return output;
|
|
101
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export interface LessonFields {
|
|
2
|
+
domain: string[];
|
|
3
|
+
system: string[];
|
|
4
|
+
type: string;
|
|
5
|
+
severity: string;
|
|
6
|
+
problem: string;
|
|
7
|
+
fix: string;
|
|
8
|
+
key_lesson: string;
|
|
9
|
+
prevention: string;
|
|
10
|
+
cause?: string[];
|
|
11
|
+
variant_of?: string | null;
|
|
12
|
+
valid_as_of_version?: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface QualityResult {
|
|
15
|
+
score: number;
|
|
16
|
+
max_score: number;
|
|
17
|
+
breakdown: {
|
|
18
|
+
system: number;
|
|
19
|
+
domain: number;
|
|
20
|
+
problem: number;
|
|
21
|
+
key_lesson: number;
|
|
22
|
+
};
|
|
23
|
+
issues: {
|
|
24
|
+
category: string;
|
|
25
|
+
severity: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}[];
|
|
28
|
+
summary: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ValidateResponse {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
format_errors: string[];
|
|
33
|
+
quality: QualityResult | null;
|
|
34
|
+
recommendation: 'ready_to_post' | 'needs_improvement' | 'likely_not_a_lesson';
|
|
35
|
+
}
|
|
36
|
+
export interface RecordResponse {
|
|
37
|
+
lesson: {
|
|
38
|
+
id: string;
|
|
39
|
+
semantic_code: string;
|
|
40
|
+
domain: string[];
|
|
41
|
+
system: string[];
|
|
42
|
+
type: string;
|
|
43
|
+
severity: string;
|
|
44
|
+
problem: string;
|
|
45
|
+
fix: string;
|
|
46
|
+
key_lesson: string;
|
|
47
|
+
prevention: string;
|
|
48
|
+
usefulness_score: number;
|
|
49
|
+
verified_count: number;
|
|
50
|
+
status: string;
|
|
51
|
+
created_at: string;
|
|
52
|
+
};
|
|
53
|
+
quality_score: number;
|
|
54
|
+
quality: {
|
|
55
|
+
breakdown: Record<string, number>;
|
|
56
|
+
issues: {
|
|
57
|
+
category: string;
|
|
58
|
+
severity: string;
|
|
59
|
+
message: string;
|
|
60
|
+
}[];
|
|
61
|
+
summary: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface SearchResult {
|
|
65
|
+
id: string;
|
|
66
|
+
semantic_code: string;
|
|
67
|
+
domain: string[];
|
|
68
|
+
system: string[];
|
|
69
|
+
type: string;
|
|
70
|
+
severity: string;
|
|
71
|
+
problem: string;
|
|
72
|
+
fix: string;
|
|
73
|
+
key_lesson: string;
|
|
74
|
+
usefulness_score: number;
|
|
75
|
+
verified_count: number;
|
|
76
|
+
status: string;
|
|
77
|
+
created_at: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SearchResponse {
|
|
80
|
+
lessons: SearchResult[];
|
|
81
|
+
total: number;
|
|
82
|
+
search_mode?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface LessonDetail {
|
|
85
|
+
id: string;
|
|
86
|
+
semantic_code: string;
|
|
87
|
+
domain: string[];
|
|
88
|
+
system: string[];
|
|
89
|
+
type: string;
|
|
90
|
+
severity: string;
|
|
91
|
+
problem: string;
|
|
92
|
+
cause: string[];
|
|
93
|
+
fix: string;
|
|
94
|
+
key_lesson: string;
|
|
95
|
+
prevention: string;
|
|
96
|
+
usefulness_score: number;
|
|
97
|
+
verified_count: number;
|
|
98
|
+
status: string;
|
|
99
|
+
dispute_reason?: string;
|
|
100
|
+
contributions?: {
|
|
101
|
+
type: string;
|
|
102
|
+
agent_id: string;
|
|
103
|
+
content: string;
|
|
104
|
+
created_at: string;
|
|
105
|
+
}[];
|
|
106
|
+
variant_of?: string | null;
|
|
107
|
+
valid_as_of_version?: string | null;
|
|
108
|
+
created_at: string;
|
|
109
|
+
}
|
|
110
|
+
export interface GetDetailResponse {
|
|
111
|
+
lesson: LessonDetail;
|
|
112
|
+
variants: LessonDetail[];
|
|
113
|
+
}
|
|
114
|
+
export type ApiError = {
|
|
115
|
+
error: string;
|
|
116
|
+
detail?: string;
|
|
117
|
+
hint?: string;
|
|
118
|
+
};
|
|
119
|
+
export declare function isApiError(data: unknown): data is ApiError;
|
package/dist/types.js
ADDED