@rigstate/mcp 0.4.2

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 (40) hide show
  1. package/.env.example +8 -0
  2. package/README.md +352 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +3445 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +43 -0
  7. package/roadmap.json +531 -0
  8. package/src/agents/the-scribe.ts +122 -0
  9. package/src/index.ts +1792 -0
  10. package/src/lib/supabase.ts +120 -0
  11. package/src/lib/tool-registry.ts +134 -0
  12. package/src/lib/types.ts +415 -0
  13. package/src/lib/utils.ts +10 -0
  14. package/src/resources/project-morals.ts +92 -0
  15. package/src/tools/arch-tools.ts +166 -0
  16. package/src/tools/archaeological-scan.ts +335 -0
  17. package/src/tools/check-agent-bridge.ts +169 -0
  18. package/src/tools/check-rules-sync.ts +85 -0
  19. package/src/tools/complete-roadmap-task.ts +96 -0
  20. package/src/tools/generate-professional-pdf.ts +232 -0
  21. package/src/tools/get-latest-decisions.ts +130 -0
  22. package/src/tools/get-next-roadmap-step.ts +76 -0
  23. package/src/tools/get-project-context.ts +163 -0
  24. package/src/tools/index.ts +17 -0
  25. package/src/tools/list-features.ts +67 -0
  26. package/src/tools/list-roadmap-tasks.ts +61 -0
  27. package/src/tools/pending-tasks.ts +228 -0
  28. package/src/tools/planning-tools.ts +123 -0
  29. package/src/tools/query-brain.ts +125 -0
  30. package/src/tools/research-tools.ts +149 -0
  31. package/src/tools/run-architecture-audit.ts +203 -0
  32. package/src/tools/save-decision.ts +77 -0
  33. package/src/tools/security-tools.ts +82 -0
  34. package/src/tools/submit-idea.ts +66 -0
  35. package/src/tools/sync-ide-rules.ts +76 -0
  36. package/src/tools/teacher-mode.ts +171 -0
  37. package/src/tools/ui-tools.ts +191 -0
  38. package/src/tools/update-roadmap.ts +105 -0
  39. package/tsconfig.json +29 -0
  40. package/tsup.config.ts +16 -0
@@ -0,0 +1,191 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { AnalyzeUiComponentInput, ApplyDesignSystemInput, FetchUiLibraryDocsInput } from '../lib/types.js';
4
+
5
+ /**
6
+ * Linus's Tool: Analyze UI Component
7
+ * Checks for design deviations, hardcoded values, and accessibility gaps.
8
+ */
9
+ export async function analyzeUiComponent(input: AnalyzeUiComponentInput) {
10
+ const { filePath } = input;
11
+
12
+ if (!await fileExists(filePath)) {
13
+ throw new Error(`File not found: ${filePath}`);
14
+ }
15
+
16
+ const content = await fs.readFile(filePath, 'utf-8');
17
+ const issues: string[] = [];
18
+ const score = 100;
19
+
20
+ // 1. Check for Hardcoded Hex Colors
21
+ // Regex matches text-[#...], bg-[#...], or just #... inside classNames often
22
+ const hexRegex = /#([0-9A-Fa-f]{3}){1,2}\b/g;
23
+ const hexMatches = content.match(hexRegex);
24
+ if (hexMatches) {
25
+ // Filter out ID selectors in CSS if file is .css
26
+ const isCss = filePath.endsWith('.css');
27
+ const uniqueHex = [...new Set(hexMatches)];
28
+
29
+ for (const hex of uniqueHex) {
30
+ // In CSS, #id is valid. In TSX className, usually implies hardcoded color.
31
+ if (!isCss || content.includes(`color: ${hex}`) || content.includes(`background: ${hex}`)) {
32
+ issues.push(`Hardcoded color found: ${hex}. Use design tokens (e.g., bg-background, text-primary).`);
33
+ }
34
+ }
35
+ }
36
+
37
+ // 2. Check for missing Alt text on images (TSX only)
38
+ if (filePath.endsWith('.tsx')) {
39
+ const imgRegex = /<img(?![^>]*\balt=)[^>]*>/g;
40
+ if (imgRegex.test(content)) {
41
+ issues.push('Accessibility: <img> tag detected without "alt" attribute.');
42
+ }
43
+
44
+ // Check for Image component (Next.js)
45
+ const nextImgRegex = /<Image(?![^>]*\balt=)[^>]*>/g;
46
+ if (nextImgRegex.test(content)) {
47
+ issues.push('Accessibility: <Image> component missing "alt" prop.');
48
+ }
49
+ }
50
+
51
+ // 3. Check for arbitrary values (Tailwind)
52
+ // Matches w-[123px] or p-[10px] etc.
53
+ const arbitraryRegex = /\w-\[\d+px\]/g;
54
+ if (arbitraryRegex.test(content)) {
55
+ issues.push('maintainability: Arbitrary pixel values detected (e.g., w-[50px]). Use standard spacing scale (w-12).');
56
+ }
57
+
58
+ return {
59
+ filePath: path.basename(filePath),
60
+ issueCount: issues.length,
61
+ issues: issues.length > 0 ? issues : ['✅ No obvious design violations found. Great job!']
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Linus's Tool: Apply Design System
67
+ * Auto-fixes common design system violations (e.g. replacing hardcoded logic).
68
+ */
69
+ export async function applyDesignSystem(input: ApplyDesignSystemInput) {
70
+ const { filePath } = input;
71
+
72
+ if (!await fileExists(filePath)) {
73
+ throw new Error(`File not found: ${filePath}`);
74
+ }
75
+
76
+ let content = await fs.readFile(filePath, 'utf-8');
77
+ let replacements = 0;
78
+
79
+ // Mapping of hardcoded values to System Tokens
80
+ // Determine context (light/dark agnostic logic)
81
+ const adjustments = [
82
+ // Text Colors
83
+ { pattern: /text-\[#000000\]/g, replacement: 'text-foreground' },
84
+ { pattern: /text-\[#000\]/g, replacement: 'text-foreground' },
85
+ { pattern: /text-\[#ffffff\]/g, replacement: 'text-primary-foreground' },
86
+ { pattern: /text-\[#fff\]/g, replacement: 'text-primary-foreground' },
87
+
88
+ // Backgrounds
89
+ { pattern: /bg-\[#ffffff\]/g, replacement: 'bg-background' },
90
+ { pattern: /bg-\[#fff\]/g, replacement: 'bg-background' },
91
+ { pattern: /bg-\[#000000\]/g, replacement: 'bg-black' }, // Or bg-foreground? keeping bg-black safe.
92
+
93
+ // Borders
94
+ { pattern: /border-\[#e5e7eb\]/g, replacement: 'border-border' }, // Common gray
95
+ { pattern: /border-\[#d1d5db\]/g, replacement: 'border-border' },
96
+ ];
97
+
98
+ for (const adj of adjustments) {
99
+ if (adj.pattern.test(content)) {
100
+ const matches = content.match(adj.pattern);
101
+ replacements += matches ? matches.length : 0;
102
+ content = content.replace(adj.pattern, adj.replacement);
103
+ }
104
+ }
105
+
106
+ if (replacements > 0) {
107
+ await fs.writeFile(filePath, content, 'utf-8');
108
+ return {
109
+ success: true,
110
+ filesFixed: 1,
111
+ replacementsCount: replacements,
112
+ message: `Applied design system: Replaced ${replacements} hardcoded values with tokens in ${path.basename(filePath)}.`
113
+ };
114
+ }
115
+
116
+ return {
117
+ success: true,
118
+ filesFixed: 0,
119
+ replacementsCount: 0,
120
+ message: 'No automatic replacements could be applied. Manual review may be needed.'
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Astrid's Tool: Fetch UI Library Docs
126
+ * Retrieves reference code for components (Shadcn/UI).
127
+ */
128
+ export async function fetchUiLibraryDocs(input: FetchUiLibraryDocsInput) {
129
+ const { componentName, library } = input;
130
+
131
+ if (library === 'shadcn') {
132
+ // Try to find the component in apps/web/src/components/ui
133
+ // We scan for the file.
134
+ // Assuming typical monorepo path from root.
135
+ const basePaths = [
136
+ '/Users/steinhofve/Documents/Nowhere/CURSOR/Rigstate/apps/web/src/components/ui',
137
+ path.resolve(process.cwd(), 'apps/web/src/components/ui')
138
+ ];
139
+
140
+ for (const basePath of basePaths) {
141
+ if (await dirExists(basePath)) {
142
+ // Try .tsx and .ts
143
+ const variations = [`${componentName}.tsx`, `${componentName}.ts`, `${componentName}/index.tsx`];
144
+
145
+ for (const variant of variations) {
146
+ const fullPath = path.join(basePath, variant);
147
+ if (await fileExists(fullPath)) {
148
+ const content = await fs.readFile(fullPath, 'utf-8');
149
+ // Return a truncated/summarized version or full?
150
+ // Full is better for "Copy syntax".
151
+ return {
152
+ found: true,
153
+ source: 'Local Component Definition',
154
+ path: fullPath,
155
+ content: content
156
+ };
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ return {
163
+ found: false,
164
+ message: `Component "${componentName}" not found in local Shadcn UI directory.`
165
+ };
166
+ }
167
+
168
+ return {
169
+ found: false,
170
+ message: `Library "${library}" docs access not implemented yet.`
171
+ };
172
+ }
173
+
174
+ // Helpers
175
+ async function fileExists(path: string) {
176
+ try {
177
+ await fs.access(path);
178
+ return true;
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+
184
+ async function dirExists(path: string) {
185
+ try {
186
+ const stat = await fs.stat(path);
187
+ return stat.isDirectory();
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Tool: update_roadmap
3
+ *
4
+ * Updates the status of a roadmap chunk (step).
5
+ * Can search by chunk ID or by title.
6
+ */
7
+
8
+ import { SupabaseClient } from '@supabase/supabase-js';
9
+ import type { UpdateRoadmapResponse } from '../lib/types.js';
10
+
11
+ export async function updateRoadmap(
12
+ supabase: SupabaseClient,
13
+ userId: string,
14
+ projectId: string,
15
+ status: 'LOCKED' | 'ACTIVE' | 'COMPLETED',
16
+ chunkId?: string,
17
+ title?: string
18
+ ): Promise<UpdateRoadmapResponse> {
19
+ // First, verify project ownership
20
+ const { data: project, error: projectError } = await supabase
21
+ .from('projects')
22
+ .select('id, name')
23
+ .eq('id', projectId)
24
+ .eq('owner_id', userId)
25
+ .single();
26
+
27
+ if (projectError || !project) {
28
+ throw new Error('Project not found or access denied');
29
+ }
30
+
31
+ // Find the roadmap chunk
32
+ let targetChunk: { id: string; title: string; status: string } | null = null;
33
+
34
+ if (chunkId) {
35
+ // Search by ID
36
+ const { data, error } = await supabase
37
+ .from('roadmap_chunks')
38
+ .select('id, title, status')
39
+ .eq('id', chunkId)
40
+ .eq('project_id', projectId)
41
+ .single();
42
+
43
+ if (error || !data) {
44
+ throw new Error(`Roadmap step with ID "${chunkId}" not found`);
45
+ }
46
+ targetChunk = data;
47
+ } else if (title) {
48
+ // Search by title (fuzzy match using ilike)
49
+ const { data, error } = await supabase
50
+ .from('roadmap_chunks')
51
+ .select('id, title, status')
52
+ .eq('project_id', projectId)
53
+ .ilike('title', `%${title}%`)
54
+ .limit(1)
55
+ .single();
56
+
57
+ if (error || !data) {
58
+ throw new Error(`Roadmap step matching "${title}" not found`);
59
+ }
60
+ targetChunk = data;
61
+ } else {
62
+ throw new Error('Either chunkId or title must be provided');
63
+ }
64
+
65
+ const previousStatus = targetChunk.status;
66
+
67
+ // Don't update if status is the same
68
+ if (previousStatus === status) {
69
+ return {
70
+ success: true,
71
+ chunkId: targetChunk.id,
72
+ previousStatus,
73
+ newStatus: status,
74
+ message: `â„šī¸ Roadmap step "${targetChunk.title}" is already ${status}`
75
+ };
76
+ }
77
+
78
+ // Update the status
79
+ const { error: updateError } = await supabase
80
+ .from('roadmap_chunks')
81
+ .update({ status })
82
+ .eq('id', targetChunk.id);
83
+
84
+ if (updateError) {
85
+ if (updateError.code === '42501') {
86
+ throw new Error('Permission denied: Cannot update this roadmap');
87
+ }
88
+ throw new Error(`Failed to update roadmap: ${updateError.message}`);
89
+ }
90
+
91
+ // Build status transition message
92
+ const statusEmoji: Record<string, string> = {
93
+ 'LOCKED': '🔒',
94
+ 'ACTIVE': '🚧',
95
+ 'COMPLETED': '✅'
96
+ };
97
+
98
+ return {
99
+ success: true,
100
+ chunkId: targetChunk.id,
101
+ previousStatus,
102
+ newStatus: status,
103
+ message: `${statusEmoji[status]} Roadmap step "${targetChunk.title}" updated: ${previousStatus} → ${status}`
104
+ };
105
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "lib": [
6
+ "ES2022"
7
+ ],
8
+ "moduleResolution": "bundler",
9
+ "resolveJsonModule": true,
10
+ "allowJs": true,
11
+ "outDir": "dist",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "esModuleInterop": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "strict": true,
17
+ "skipLibCheck": true,
18
+ "types": [
19
+ "node"
20
+ ]
21
+ },
22
+ "include": [
23
+ "src/**/*"
24
+ ],
25
+ "exclude": [
26
+ "node_modules",
27
+ "dist"
28
+ ]
29
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ dts: true,
7
+ splitting: false,
8
+ sourcemap: true,
9
+ clean: true,
10
+ shims: true,
11
+ banner: {
12
+ js: '#!/usr/bin/env node'
13
+ },
14
+ minify: false,
15
+ target: 'node18'
16
+ });