@rigstate/mcp 0.4.2 → 0.4.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 (57) hide show
  1. package/.agent/skills/client-side-notification-logger/SKILL.md +139 -0
  2. package/.agent/skills/react-state-counter/SKILL.md +73 -0
  3. package/.agent/skills/rigstate-evolutionary-refactor/SKILL.md +40 -0
  4. package/.agent/skills/rigstate-integrity-gate/SKILL.md +55 -0
  5. package/.agent/skills/rigstate-legacy-renovator/SKILL.md +12 -0
  6. package/.agent/skills/sec-auth-04/SKILL.md +22 -0
  7. package/.agent/skills/sec-key-01/SKILL.md +21 -0
  8. package/.agent/skills/sec-rls-01/SKILL.md +22 -0
  9. package/.agent/skills/sec-sql-01/SKILL.md +23 -0
  10. package/.agent/skills/sec-ui-01/SKILL.md +21 -0
  11. package/.cursor/rules/rigstate-database.mdc +89 -0
  12. package/.cursor/rules/rigstate-guardian.mdc +43 -0
  13. package/.cursor/rules/rigstate-identity.mdc +45 -0
  14. package/.cursor/rules/rigstate-roadmap.mdc +9 -0
  15. package/.cursor/rules/rigstate-workflow.mdc +323 -0
  16. package/.cursorrules +402 -0
  17. package/AGENTS.md +34 -0
  18. package/dist/index.js +2604 -3067
  19. package/dist/index.js.map +1 -1
  20. package/package.json +3 -3
  21. package/roadmap.json +815 -21
  22. package/src/index.ts +16 -1765
  23. package/src/lib/context-engine.ts +85 -0
  24. package/src/lib/curator/actions/fortress.ts +77 -0
  25. package/src/lib/curator/actions/query.ts +73 -0
  26. package/src/lib/curator/actions/stats.ts +70 -0
  27. package/src/lib/curator/actions/submit.ts +190 -0
  28. package/src/lib/curator/index.ts +10 -0
  29. package/src/lib/curator/schemas.ts +37 -0
  30. package/src/lib/schemas.ts +191 -0
  31. package/src/lib/types.ts +102 -261
  32. package/src/server/core.ts +40 -0
  33. package/src/server/factory.ts +78 -0
  34. package/src/server/telemetry.ts +122 -0
  35. package/src/server/types.ts +21 -0
  36. package/src/tools/analyze-database-performance.ts +157 -0
  37. package/src/tools/arch-tools.ts +16 -0
  38. package/src/tools/audit-integrity-gate.ts +166 -0
  39. package/src/tools/check-rules-sync.ts +20 -0
  40. package/src/tools/complete-roadmap-task.ts +88 -31
  41. package/src/tools/curator-tools.ts +74 -0
  42. package/src/tools/get-latest-decisions.ts +23 -1
  43. package/src/tools/get-next-roadmap-step.ts +21 -0
  44. package/src/tools/get-project-context.ts +35 -1
  45. package/src/tools/index.ts +7 -0
  46. package/src/tools/list-features.ts +4 -1
  47. package/src/tools/list-roadmap-tasks.ts +21 -0
  48. package/src/tools/planning-tools.ts +40 -0
  49. package/src/tools/query-brain.ts +25 -1
  50. package/src/tools/run-architecture-audit.ts +23 -0
  51. package/src/tools/save-decision.ts +26 -0
  52. package/src/tools/security-checks.ts +241 -0
  53. package/src/tools/security-tools.ts +88 -18
  54. package/src/tools/submit-idea.ts +25 -0
  55. package/src/tools/sync-ide-rules.ts +35 -3
  56. package/src/tools/teacher-mode.ts +92 -13
  57. package/src/tools/update-roadmap.ts +24 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Phase 8.5.5: Context Engine
3
+ *
4
+ * Bridges the gap between the Curator Database and the Agent's working memory.
5
+ * Performs semantic/keyword lookup to inject relevant rules into the context.
6
+ */
7
+
8
+ import { SupabaseClient } from '@supabase/supabase-js';
9
+ import { queryGlobalAntidotes } from './curator/index.js';
10
+
11
+ export interface ContextInjectionInput {
12
+ frameworks: string[];
13
+ libraries: string[];
14
+ taskDescription?: string; // Optional: for future task-specific lookup
15
+ }
16
+
17
+ export async function injectGlobalContext(
18
+ supabase: SupabaseClient,
19
+ userId: string,
20
+ input: ContextInjectionInput
21
+ ): Promise<string> {
22
+ // 1. Identify relevant tags from stack
23
+ const searchTags = [
24
+ ...input.frameworks.map(f => f.split(' ')[0].toLowerCase()),
25
+ ...input.libraries.map(l => l.split(' ')[0].toLowerCase())
26
+ ].filter(Boolean);
27
+
28
+ // 2. Query Curator for Critical/High severity antidotes matching tags
29
+ // We want HIGH trust items (trust_score >= 80) or IMMUTABLE items.
30
+ // Since queryGlobalAntidotes is a bit generic, we might want a more specialized query here
31
+ // for "Active Constraints", but reusing it is a good MVP.
32
+
33
+ // We fetch ALL Fortress Rules (Immutable) regardless of score/tags?
34
+ // Usually Fortress Rules are universal or highly critical.
35
+
36
+ const { data: fortressRules } = await supabase
37
+ .from('global_antidotes')
38
+ .select('title, instruction, slug')
39
+ .eq('is_immutable', true)
40
+ .eq('is_active', true);
41
+
42
+ // Fetch High Trust Contextual Rules
43
+ let contextQuery = supabase
44
+ .from('global_antidotes')
45
+ .select('title, instruction, framework_tags, severity')
46
+ .eq('is_active', true)
47
+ .gte('trust_score', 80) // High trust only
48
+ .eq('is_immutable', false); // Exclude fortress (already fetched)
49
+
50
+ if (searchTags.length > 0) {
51
+ // PG operator for array overlap: framework_tags && searchTags
52
+ contextQuery = contextQuery.overlaps('framework_tags', searchTags);
53
+ } else {
54
+ // If no tags, maybe just generic ones? Or skip?
55
+ // Let's limit to generic if no tags
56
+ contextQuery = contextQuery.is('framework_tags', null);
57
+ }
58
+
59
+ const { data: contextualRules } = await contextQuery.limit(5);
60
+
61
+ // 3. Format Output
62
+ let output = '';
63
+
64
+ if (fortressRules && fortressRules.length > 0) {
65
+ output += `\n\n## 🏰 FORTRESS RULES (NON-NEGOTIABLE)\n`;
66
+ output += `These rules are immutable and must be followed without exception:\n`;
67
+ fortressRules.forEach(rule => {
68
+ output += `- **${rule.title}**: ${rule.instruction}\n`;
69
+ });
70
+ }
71
+
72
+ if (contextualRules && contextualRules.length > 0) {
73
+ output += `\n## 🛡️ INTELLIGENT CONTEXT (High Trust Antidotes)\n`;
74
+ output += `Learned best practices for your stack (${searchTags.join(', ')}):\n`;
75
+ contextualRules.forEach(rule => {
76
+ output += `- [${rule.severity}] **${rule.title}**: ${rule.instruction}\n`;
77
+ });
78
+ }
79
+
80
+ if (!output) {
81
+ output = `\n(No specific curator context found for stack: ${searchTags.join(', ')})`;
82
+ }
83
+
84
+ return output;
85
+ }
@@ -0,0 +1,77 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { z } from 'zod';
3
+ import { CheckFortressSchema } from '../schemas.js';
4
+
5
+ /**
6
+ * Check instruction against fortress rules
7
+ */
8
+ export async function checkFortress(
9
+ supabase: SupabaseClient,
10
+ userId: string,
11
+ input: z.infer<typeof CheckFortressSchema>
12
+ ) {
13
+ // Load fortress rules
14
+ const { data: fortressRules } = await supabase
15
+ .from('global_antidotes')
16
+ .select('slug, title, instruction')
17
+ .eq('is_immutable', true)
18
+ .eq('is_active', true);
19
+
20
+ if (!fortressRules || fortressRules.length === 0) {
21
+ return {
22
+ passed: true,
23
+ conflicts: [],
24
+ message: '✅ No fortress rules configured. Instruction passes by default.'
25
+ };
26
+ }
27
+
28
+ const conflicts: Array<{ rule: string; title: string; explanation: string }> = [];
29
+ const instructionLower = input.instruction.toLowerCase();
30
+
31
+ // Conflict patterns (simplified)
32
+ const conflictPatterns: Record<string, RegExp[]> = {
33
+ rls_required: [/disable\s+r(ow\s+level\s+)?s(ecurity)?/i, /skip\s+rls/i, /bypass\s+rls/i],
34
+ no_client_secrets: [/api\s+keys?\s+in\s+(frontend|client)/i, /hardcode\s+secret/i],
35
+ input_validation: [/skip\s+validation/i, /trust\s+all\s+input/i],
36
+ auth_required: [/skip\s+auth/i, /bypass\s+auth/i, /disable\s+auth/i]
37
+ };
38
+
39
+ for (const rule of fortressRules) {
40
+ const patterns = conflictPatterns[rule.slug];
41
+ if (patterns) {
42
+ for (const pattern of patterns) {
43
+ if (pattern.test(instructionLower)) {
44
+ conflicts.push({
45
+ rule: rule.slug,
46
+ title: rule.title,
47
+ explanation: `Instruction conflicts with fortress rule: ${rule.instruction.substring(0, 100)}...`
48
+ });
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ if (conflicts.length > 0) {
56
+ return {
57
+ passed: false,
58
+ conflicts,
59
+ message: `❌ FORTRESS CONFLICT DETECTED
60
+
61
+ ${conflicts.length} conflict(s) found:
62
+ ${conflicts.map((c, i) => `${i + 1}. [${c.rule}] ${c.title}
63
+ ${c.explanation}`).join('\n\n')}
64
+
65
+ ⚠️ This instruction cannot be added to the registry as it violates immutable security rules.`
66
+ };
67
+ }
68
+
69
+ return {
70
+ passed: true,
71
+ conflicts: [],
72
+ message: `✅ FORTRESS CHECK PASSED
73
+
74
+ Instruction does not conflict with any fortress rules.
75
+ Checked against ${fortressRules.length} immutable rules.`
76
+ };
77
+ }
@@ -0,0 +1,73 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { z } from 'zod';
3
+ import { QueryGlobalAntidotesSchema } from '../schemas.js';
4
+
5
+ /**
6
+ * Sigrid's Tool: Query Global Antidotes
7
+ * Search and retrieve antidotes from the global registry.
8
+ */
9
+ export async function queryGlobalAntidotes(
10
+ supabase: SupabaseClient,
11
+ userId: string,
12
+ input: z.infer<typeof QueryGlobalAntidotesSchema>
13
+ ) {
14
+ let query = supabase
15
+ .from('global_antidotes')
16
+ .select('id, slug, title, instruction, example, anti_example, category, severity, framework_tags, trust_score, occurrence_count, is_immutable')
17
+ .eq('is_active', true);
18
+
19
+ if (input.categories && input.categories.length > 0) {
20
+ query = query.in('category', input.categories);
21
+ }
22
+
23
+ if (input.severities && input.severities.length > 0) {
24
+ query = query.in('severity', input.severities);
25
+ }
26
+
27
+ if (input.framework_tags && input.framework_tags.length > 0) {
28
+ query = query.overlaps('framework_tags', input.framework_tags);
29
+ }
30
+
31
+ if (input.min_trust_score !== undefined) {
32
+ query = query.gte('trust_score', input.min_trust_score);
33
+ }
34
+
35
+ if (input.search_text) {
36
+ query = query.or(`title.ilike.%${input.search_text}%,instruction.ilike.%${input.search_text}%`);
37
+ }
38
+
39
+ const { data, error } = await query
40
+ .order('severity', { ascending: true }) // CRITICAL first
41
+ .order('trust_score', { ascending: false })
42
+ .limit(input.limit || 20);
43
+
44
+ if (error) throw new Error(`Query failed: ${error.message}`);
45
+
46
+ if (!data || data.length === 0) {
47
+ return {
48
+ antidotes: [],
49
+ formatted: `=== GLOBAL ANTIDOTES REGISTRY ===
50
+ No antidotes found matching your criteria.
51
+ ================================`
52
+ };
53
+ }
54
+
55
+ const formatted = data.map((a, i) => {
56
+ const immutableBadge = a.is_immutable ? ' 🏰 FORTRESS' : '';
57
+ const tags = a.framework_tags ? ` [${a.framework_tags.join(', ')}]` : '';
58
+ return `${i + 1}. [${a.severity}] ${a.title}${immutableBadge}
59
+ Category: ${a.category}${tags}
60
+ Trust: ${a.trust_score}/100 | Occurrences: ${a.occurrence_count}
61
+ ${a.instruction.substring(0, 200)}${a.instruction.length > 200 ? '...' : ''}`;
62
+ }).join('\n\n');
63
+
64
+ return {
65
+ antidotes: data,
66
+ formatted: `=== GLOBAL ANTIDOTES REGISTRY ===
67
+ Found ${data.length} antidotes:
68
+
69
+ ${formatted}
70
+
71
+ ================================`
72
+ };
73
+ }
@@ -0,0 +1,70 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { z } from 'zod';
3
+ import { GetCuratorStatsSchema } from '../schemas.js';
4
+
5
+ /**
6
+ * Get Curator Statistics
7
+ */
8
+ export async function getCuratorStats(
9
+ supabase: SupabaseClient,
10
+ userId: string,
11
+ input: z.infer<typeof GetCuratorStatsSchema>
12
+ ) {
13
+ // Get antidote counts
14
+ const { data: antidotes, count: totalCount } = await supabase
15
+ .from('global_antidotes')
16
+ .select('id, category, severity, is_immutable', { count: 'exact' })
17
+ .eq('is_active', true);
18
+
19
+ const fortressCount = antidotes?.filter(a => a.is_immutable).length || 0;
20
+
21
+ // Get quarantine count
22
+ const { count: quarantineCount } = await supabase
23
+ .from('curation_quarantine')
24
+ .select('id', { count: 'exact', head: true })
25
+ .is('decision', null);
26
+
27
+ // Get today's activity
28
+ const today = new Date();
29
+ today.setHours(0, 0, 0, 0);
30
+ const { data: todayActivity } = await supabase
31
+ .from('curation_audit_log')
32
+ .select('action')
33
+ .gte('created_at', today.toISOString());
34
+
35
+ const activityCounts = todayActivity?.reduce((acc: Record<string, number>, log: any) => {
36
+ acc[log.action] = (acc[log.action] || 0) + 1;
37
+ return acc;
38
+ }, {}) || {};
39
+
40
+ // Category distribution
41
+ const categoryDistribution = antidotes?.reduce((acc: Record<string, number>, a) => {
42
+ acc[a.category] = (acc[a.category] || 0) + 1;
43
+ return acc;
44
+ }, {}) || {};
45
+
46
+ return {
47
+ stats: {
48
+ total_antidotes: totalCount || 0,
49
+ fortress_rules: fortressCount,
50
+ pending_quarantine: quarantineCount || 0,
51
+ today_approved: activityCounts['AUTO_APPROVED'] || 0,
52
+ today_rejected: activityCounts['AUTO_REJECTED'] || 0,
53
+ today_quarantined: activityCounts['QUARANTINE_ADDED'] || 0,
54
+ categories: categoryDistribution
55
+ },
56
+ formatted: `=== CURATOR REGISTRY STATS ===
57
+ 📊 Global Antidotes: ${totalCount || 0}
58
+ 🏰 Fortress Rules: ${fortressCount}
59
+ ⏳ Pending Review: ${quarantineCount || 0}
60
+
61
+ 📅 Today's Activity:
62
+ ✅ Approved: ${activityCounts['AUTO_APPROVED'] || 0}
63
+ ❌ Rejected: ${activityCounts['AUTO_REJECTED'] || 0}
64
+ ⏸️ Quarantined: ${activityCounts['QUARANTINE_ADDED'] || 0}
65
+
66
+ 📁 By Category:
67
+ ${Object.entries(categoryDistribution).map(([cat, count]) => ` • ${cat}: ${count}`).join('\n')}
68
+ ==============================`
69
+ };
70
+ }
@@ -0,0 +1,190 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { z } from 'zod';
3
+ import { SubmitSignalSchema } from '../schemas.js';
4
+
5
+ /**
6
+ * Sigrid's Tool: Submit Signal for Curation
7
+ * Submit a new intelligence signal for processing through the curation pipeline.
8
+ */
9
+ export async function submitSignal(
10
+ supabase: SupabaseClient,
11
+ userId: string,
12
+ input: z.infer<typeof SubmitSignalSchema>
13
+ ) {
14
+ // 1. Verify project ownership
15
+ const { data: project, error: projectError } = await supabase
16
+ .from('projects')
17
+ .select('id')
18
+ .eq('id', input.projectId)
19
+ .eq('owner_id', userId)
20
+ .single();
21
+
22
+ if (projectError || !project) {
23
+ throw new Error('Project not found or access denied');
24
+ }
25
+
26
+ // 2. Simple fingerprint for deduplication check (basic version for MCP)
27
+ const fingerprintBase = `${input.instruction}::${input.category}`.toLowerCase();
28
+ const simpleHash = fingerprintBase.split('').reduce((a, b) => {
29
+ a = ((a << 5) - a) + b.charCodeAt(0);
30
+ return a & a;
31
+ }, 0).toString(16);
32
+
33
+ // 3. Check for existing similar antidotes
34
+ const { data: existing } = await supabase
35
+ .from('global_antidotes')
36
+ .select('id, slug, title')
37
+ .ilike('instruction', `%${input.instruction.substring(0, 100)}%`)
38
+ .eq('is_active', true)
39
+ .limit(1);
40
+
41
+ if (existing && existing.length > 0) {
42
+ return handleReinforcement(supabase, existing[0]);
43
+ }
44
+
45
+ // 4. Check against fortress rules
46
+ const fortressResult = await checkFortressConflicts(supabase, userId, input, simpleHash);
47
+ if (!fortressResult.success) {
48
+ return fortressResult;
49
+ }
50
+
51
+ // 5. Submit to Quarantine
52
+ return submitToQuarantine(supabase, userId, input, simpleHash);
53
+ }
54
+
55
+ // Helpers
56
+ async function handleReinforcement(supabase: SupabaseClient, existing: any) {
57
+ // Signal reinforces existing antidote - update last reinforced timestamp
58
+ await supabase
59
+ .from('global_antidotes')
60
+ .update({ last_reinforced_at: new Date().toISOString() })
61
+ .eq('id', existing.id);
62
+
63
+ return {
64
+ success: true,
65
+ action: 'reinforced',
66
+ message: `✅ Signal reinforces existing antidote: "${existing.title}" (${existing.slug}).
67
+ The occurrence count has been increased, boosting the antidote's authority.`,
68
+ antidote_id: existing.id
69
+ };
70
+ }
71
+
72
+ async function checkFortressConflicts(
73
+ supabase: SupabaseClient,
74
+ userId: string,
75
+ input: z.infer<typeof SubmitSignalSchema>,
76
+ hash: string
77
+ ) {
78
+ const fortressKeywords = {
79
+ rls_required: ['disable rls', 'skip rls', 'bypass rls', 'rls off'],
80
+ no_client_secrets: ['api key in client', 'hardcode secret', 'embed api key'],
81
+ input_validation: ['skip validation', 'trust input', 'no validation'],
82
+ auth_required: ['skip auth', 'bypass auth', 'disable auth']
83
+ };
84
+
85
+ const instructionLower = input.instruction.toLowerCase();
86
+ for (const [rule, keywords] of Object.entries(fortressKeywords)) {
87
+ for (const keyword of keywords) {
88
+ if (instructionLower.includes(keyword)) {
89
+ // Log violation
90
+ await supabase.from('fortress_violations').insert({
91
+ fortress_rule_slug: rule,
92
+ violated_by_signal_hash: hash,
93
+ conflict_type: 'DIRECT_CONTRADICTION',
94
+ severity: 'CRITICAL',
95
+ conflicting_instruction: input.instruction,
96
+ source_user_id: userId,
97
+ source_project_id: input.projectId
98
+ });
99
+
100
+ return {
101
+ success: false,
102
+ action: 'blocked',
103
+ message: `❌ FORTRESS VIOLATION: Signal conflicts with immutable rule "${rule}".
104
+ This instruction attempts to weaken a core security principle and has been rejected.
105
+ The violation has been logged for security audit.`
106
+ };
107
+ }
108
+ }
109
+ }
110
+ return { success: true };
111
+ }
112
+
113
+ async function submitToQuarantine(
114
+ supabase: SupabaseClient,
115
+ userId: string,
116
+ input: z.infer<typeof SubmitSignalSchema>,
117
+ hash: string
118
+ ) {
119
+ // Calculate basic trust score (simplified)
120
+ let trustScore = 50; // Base score
121
+ if (input.example) trustScore += 10;
122
+ if (input.anti_example) trustScore += 10;
123
+ if (input.instruction.length > 100) trustScore += 5;
124
+ if (input.reasoning) trustScore += 5;
125
+ if (input.category === 'SECURITY' && input.severity === 'CRITICAL') trustScore += 10;
126
+
127
+ // Add to quarantine for human review (signals from MCP always go to quarantine)
128
+ const { data: quarantine, error: quarantineError } = await supabase
129
+ .from('curation_quarantine')
130
+ .insert({
131
+ signal_hash: hash,
132
+ signal_content: {
133
+ title: input.title,
134
+ instruction: input.instruction,
135
+ category: input.category,
136
+ severity: input.severity,
137
+ example: input.example,
138
+ anti_example: input.anti_example,
139
+ framework_tags: input.framework_tags,
140
+ source_type: input.source_type || 'mcp_submission',
141
+ source_context: {
142
+ user_id: userId,
143
+ project_id: input.projectId,
144
+ reasoning: input.reasoning
145
+ }
146
+ },
147
+ trust_score: trustScore,
148
+ trust_breakdown: {
149
+ final_score: trustScore,
150
+ components: {
151
+ completeness: input.example && input.anti_example ? 25 : 15,
152
+ source_trust: 20,
153
+ semantic_quality: 15,
154
+ reinforcement_bonus: 0
155
+ }
156
+ },
157
+ reason: 'MCP_SUBMISSION',
158
+ suggested_action: trustScore >= 70 ? 'APPROVE' : trustScore >= 50 ? 'MERGE' : 'REJECT',
159
+ source_user_id: userId,
160
+ source_project_id: input.projectId
161
+ })
162
+ .select('id')
163
+ .single();
164
+
165
+ if (quarantineError) throw new Error(`Failed to submit signal: ${quarantineError.message}`);
166
+
167
+ // Log audit entry
168
+ await supabase.from('curation_audit_log').insert({
169
+ action: 'SIGNAL_SUBMITTED',
170
+ reason: `MCP signal submitted: "${input.title}" (trust: ${trustScore})`,
171
+ actor_type: 'SYSTEM',
172
+ actor_id: userId,
173
+ signal_hash: hash,
174
+ quarantine_id: quarantine?.id,
175
+ metadata: { projectId: input.projectId, category: input.category }
176
+ });
177
+
178
+ return {
179
+ success: true,
180
+ action: 'quarantined',
181
+ trust_score: trustScore,
182
+ quarantine_id: quarantine?.id,
183
+ message: `✅ Signal submitted for curation.
184
+ Trust Score: ${trustScore}/100
185
+ Status: QUARANTINED (pending human review)
186
+ Suggested Action: ${trustScore >= 70 ? 'APPROVE' : trustScore >= 50 ? 'MERGE' : 'NEEDS_REVIEW'}
187
+
188
+ Sigrid will process this signal and notify you of the result.`
189
+ };
190
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Phase 8.5: The Curator Protocol
3
+ * Logic and Actions
4
+ */
5
+
6
+ export * from './schemas.js';
7
+ export * from './actions/query.js';
8
+ export * from './actions/submit.js';
9
+ export * from './actions/stats.js';
10
+ export * from './actions/fortress.js';
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+
3
+ // ============================================
4
+ // Input Schemas
5
+ // ============================================
6
+
7
+ export const QueryGlobalAntidotesSchema = z.object({
8
+ categories: z.array(z.string()).optional().describe('Filter by categories (SECURITY, ARCHITECTURE, UX, PERFORMANCE, ACCESSIBILITY, MAINTAINABILITY)'),
9
+ severities: z.array(z.string()).optional().describe('Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)'),
10
+ framework_tags: z.array(z.string()).optional().describe('Filter by framework tags (e.g., ["nextjs", "react", "supabase"])'),
11
+ min_trust_score: z.number().optional().describe('Minimum trust score (0-100)'),
12
+ search_text: z.string().optional().describe('Search in title and instruction'),
13
+ limit: z.number().optional().describe('Max results (default: 20)')
14
+ });
15
+
16
+ export const SubmitSignalSchema = z.object({
17
+ projectId: z.string().describe('The UUID of the Rigstate project'),
18
+ title: z.string().min(10).max(100).describe('Short, descriptive title (10-100 chars)'),
19
+ instruction: z.string().min(50).max(2000).describe('The canonical instruction (50-2000 chars)'),
20
+ category: z.enum(['SECURITY', 'ARCHITECTURE', 'UX', 'PERFORMANCE', 'ACCESSIBILITY', 'MAINTAINABILITY']).describe('The category of this antidote'),
21
+ severity: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).describe('Severity level'),
22
+ example: z.string().optional().describe('Good example demonstrating the instruction'),
23
+ anti_example: z.string().optional().describe('Bad example showing what NOT to do'),
24
+ framework_tags: z.array(z.string()).optional().describe('Relevant framework tags'),
25
+ reasoning: z.string().optional().describe('Why this signal should be added'),
26
+ source_type: z.string().optional().describe('Internal source type override (e.g. TEACHER_MODE)')
27
+ });
28
+
29
+ export const GetCuratorStatsSchema = z.object({
30
+ projectId: z.string().optional().describe('Optional project ID for context')
31
+ });
32
+
33
+ export const CheckFortressSchema = z.object({
34
+ projectId: z.string().describe('The UUID of the Rigstate project'),
35
+ instruction: z.string().describe('The instruction to check against fortress rules'),
36
+ category: z.enum(['SECURITY', 'ARCHITECTURE', 'UX', 'PERFORMANCE', 'ACCESSIBILITY', 'MAINTAINABILITY']).describe('Category of instruction')
37
+ });