@ridit/milo 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 (111) hide show
  1. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +122 -0
  4. package/dist/index.mjs +106603 -0
  5. package/package.json +64 -0
  6. package/src/commands/clear.ts +18 -0
  7. package/src/commands/crimes.ts +48 -0
  8. package/src/commands/feed.ts +20 -0
  9. package/src/commands/genz.ts +33 -0
  10. package/src/commands/help.ts +25 -0
  11. package/src/commands/init.ts +65 -0
  12. package/src/commands/mode.ts +22 -0
  13. package/src/commands/pet.ts +35 -0
  14. package/src/commands/provider.ts +46 -0
  15. package/src/commands/roast.ts +40 -0
  16. package/src/commands/vibe.ts +42 -0
  17. package/src/commands.ts +43 -0
  18. package/src/components/AsciiLogo.tsx +25 -0
  19. package/src/components/CommandSuggestions.tsx +78 -0
  20. package/src/components/Header.tsx +68 -0
  21. package/src/components/HighlightedCode.tsx +23 -0
  22. package/src/components/Message.tsx +43 -0
  23. package/src/components/ProviderWizard.tsx +278 -0
  24. package/src/components/Spinner.tsx +76 -0
  25. package/src/components/StatusBar.tsx +85 -0
  26. package/src/components/StructuredDiff.tsx +194 -0
  27. package/src/components/TextInput.tsx +144 -0
  28. package/src/components/messages/AssistantMessage.tsx +68 -0
  29. package/src/components/messages/ToolCallMessage.tsx +77 -0
  30. package/src/components/messages/ToolResultMessage.tsx +181 -0
  31. package/src/components/messages/UserMessage.tsx +32 -0
  32. package/src/components/permissions/PermissionCard.tsx +152 -0
  33. package/src/history.ts +27 -0
  34. package/src/hooks/useArrowKeyHistory.ts +0 -0
  35. package/src/hooks/useChat.ts +271 -0
  36. package/src/hooks/useDoublePress.ts +35 -0
  37. package/src/hooks/useTerminalSize.ts +24 -0
  38. package/src/hooks/useTextInput.ts +263 -0
  39. package/src/icons.ts +31 -0
  40. package/src/index.tsx +5 -0
  41. package/src/multi-agent/agent/agent.ts +33 -0
  42. package/src/multi-agent/orchestrator/orchestrator.ts +103 -0
  43. package/src/multi-agent/schemas.ts +12 -0
  44. package/src/multi-agent/types.ts +8 -0
  45. package/src/permissions.ts +54 -0
  46. package/src/pet.ts +239 -0
  47. package/src/screens/REPL.tsx +261 -0
  48. package/src/shortcuts.ts +37 -0
  49. package/src/skills/backend.ts +76 -0
  50. package/src/skills/cicd.ts +57 -0
  51. package/src/skills/colors.ts +72 -0
  52. package/src/skills/database.ts +55 -0
  53. package/src/skills/docker.ts +74 -0
  54. package/src/skills/frontend.ts +70 -0
  55. package/src/skills/git.ts +52 -0
  56. package/src/skills/testing.ts +73 -0
  57. package/src/skills/typography.ts +57 -0
  58. package/src/skills/uiux.ts +43 -0
  59. package/src/tools/AgentTool/prompt.ts +17 -0
  60. package/src/tools/AgentTool/tool.ts +22 -0
  61. package/src/tools/BashTool/prompt.ts +82 -0
  62. package/src/tools/BashTool/tool.ts +54 -0
  63. package/src/tools/FileEditTool/prompt.ts +13 -0
  64. package/src/tools/FileEditTool/tool.ts +39 -0
  65. package/src/tools/FileReadTool/prompt.ts +5 -0
  66. package/src/tools/FileReadTool/tool.ts +34 -0
  67. package/src/tools/FileWriteTool/prompt.ts +19 -0
  68. package/src/tools/FileWriteTool/tool.ts +34 -0
  69. package/src/tools/GlobTool/prompt.ts +11 -0
  70. package/src/tools/GlobTool/tool.ts +34 -0
  71. package/src/tools/GrepTool/prompt.ts +13 -0
  72. package/src/tools/GrepTool/tool.ts +41 -0
  73. package/src/tools/MemoryEditTool/prompt.ts +10 -0
  74. package/src/tools/MemoryEditTool/tool.ts +38 -0
  75. package/src/tools/MemoryReadTool/prompt.ts +9 -0
  76. package/src/tools/MemoryReadTool/tool.ts +47 -0
  77. package/src/tools/MemoryWriteTool/prompt.ts +10 -0
  78. package/src/tools/MemoryWriteTool/tool.ts +30 -0
  79. package/src/tools/OrchestratorTool/prompt.ts +26 -0
  80. package/src/tools/OrchestratorTool/tool.ts +20 -0
  81. package/src/tools/RecallTool/prompt.ts +13 -0
  82. package/src/tools/RecallTool/tool.ts +47 -0
  83. package/src/tools/ThinkTool/tool.ts +16 -0
  84. package/src/tools/WebFetchTool/prompt.ts +7 -0
  85. package/src/tools/WebFetchTool/tool.ts +33 -0
  86. package/src/tools/WebSearchTool/prompt.ts +8 -0
  87. package/src/tools/WebSearchTool/tool.ts +49 -0
  88. package/src/types.ts +124 -0
  89. package/src/utils/Cursor.ts +423 -0
  90. package/src/utils/PersistentShell.ts +306 -0
  91. package/src/utils/agent.ts +21 -0
  92. package/src/utils/chat.ts +21 -0
  93. package/src/utils/compaction.ts +71 -0
  94. package/src/utils/env.ts +11 -0
  95. package/src/utils/file.ts +42 -0
  96. package/src/utils/format.ts +46 -0
  97. package/src/utils/imagePaste.ts +78 -0
  98. package/src/utils/json.ts +10 -0
  99. package/src/utils/llm.ts +65 -0
  100. package/src/utils/markdown.ts +258 -0
  101. package/src/utils/messages.ts +81 -0
  102. package/src/utils/model.ts +16 -0
  103. package/src/utils/plan.ts +26 -0
  104. package/src/utils/providers.ts +100 -0
  105. package/src/utils/ripgrep.ts +175 -0
  106. package/src/utils/session.ts +100 -0
  107. package/src/utils/skills.ts +26 -0
  108. package/src/utils/systemPrompt.ts +218 -0
  109. package/src/utils/theme.ts +110 -0
  110. package/src/utils/tools.ts +58 -0
  111. package/tsconfig.json +29 -0
@@ -0,0 +1,72 @@
1
+ export const COLORS_SKILL = `
2
+ ## Skill: Colors & Dark Mode
3
+
4
+ ### Color System
5
+ - Use HSL for manipulation — easier to reason about:
6
+ \`\`\`css
7
+ --color-primary: hsl(220, 90%, 56%);
8
+ --color-primary-light: hsl(220, 90%, 70%);
9
+ --color-primary-dark: hsl(220, 90%, 40%);
10
+ \`\`\`
11
+ - Semantic naming — never name by value:
12
+ \`\`\`css
13
+ /* ❌ wrong */
14
+ --color-blue: #3b82f6;
15
+ --color-red: #ef4444;
16
+
17
+ /* ✅ correct */
18
+ --color-primary: #3b82f6;
19
+ --color-error: #ef4444;
20
+ --color-success: #22c55e;
21
+ --color-warning: #f59e0b;
22
+ --color-info: #06b6d4;
23
+ \`\`\`
24
+
25
+ ### Palette Structure
26
+ - 1 primary (brand color)
27
+ - 1 secondary (accent)
28
+ - Neutrals (gray scale, 9-11 steps)
29
+ - Semantic (success, error, warning, info)
30
+ - Never use pure colors — desaturate slightly:
31
+ \`\`\`
32
+ ❌ #ff0000 (pure red)
33
+ ✅ #ef4444 (slightly desaturated)
34
+ \`\`\`
35
+
36
+ ### Contrast
37
+ - WCAG AA minimum:
38
+ - Normal text: 4.5:1
39
+ - Large text (18px+ or 14px+ bold): 3:1
40
+ - UI components (borders, icons): 3:1
41
+ - Tools: use oklch() for perceptually uniform colors
42
+
43
+ ### Dark Mode
44
+ - Don't just invert — rethink:
45
+ \`\`\`css
46
+ :root {
47
+ --bg: #ffffff;
48
+ --bg-subtle: #f9fafb;
49
+ --text: #111827;
50
+ --text-muted: #6b7280;
51
+ --border: #e5e7eb;
52
+ }
53
+
54
+ [data-theme="dark"] {
55
+ --bg: #0f0f0f;
56
+ --bg-subtle: #1a1a1a;
57
+ --text: #f9fafb;
58
+ --text-muted: #9ca3af;
59
+ --border: #2a2a2a;
60
+ }
61
+ \`\`\`
62
+ - Dark mode: use #0f0f0f or #121212 not pure black
63
+ - Reduce saturation in dark mode — vivid colors look harsh
64
+ - Elevate surfaces with subtle lightness, not shadows:
65
+ \`\`\`
66
+ Layer 0 (base): #0f0f0f
67
+ Layer 1 (cards): #1a1a1a
68
+ Layer 2 (modals): #242424
69
+ Layer 3 (tooltip): #2e2e2e
70
+ \`\`\`
71
+ - Never use color as the only differentiator — always pair with icon/label
72
+ `;
@@ -0,0 +1,55 @@
1
+ export const DATABASE_SKILL = `
2
+ ## Skill: Database
3
+
4
+ ### Queries
5
+ - Always use parameterized queries — never concatenate SQL:
6
+ \`\`\`ts
7
+ // ❌ wrong
8
+ db.query(\`SELECT * FROM users WHERE id = \${userId}\`);
9
+
10
+ // ✅ correct
11
+ db.query('SELECT * FROM users WHERE id = $1', [userId]);
12
+
13
+ // ✅ with ORM (Drizzle)
14
+ db.select().from(users).where(eq(users.id, userId));
15
+ \`\`\`
16
+ - Never SELECT * in production — specify columns:
17
+ \`\`\`ts
18
+ db.select({ id: users.id, name: users.name, email: users.email }).from(users);
19
+ \`\`\`
20
+
21
+ ### Schema Design
22
+ - Always have a primary key
23
+ - Use \`created_at\` and \`updated_at\` timestamps on every table
24
+ - Soft delete with \`deleted_at\` when data matters:
25
+ \`\`\`ts
26
+ const users = pgTable('users', {
27
+ id: uuid('id').primaryKey().defaultRandom(),
28
+ name: text('name').notNull(),
29
+ email: text('email').notNull().unique(),
30
+ createdAt: timestamp('created_at').defaultNow().notNull(),
31
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
32
+ deletedAt: timestamp('deleted_at'),
33
+ });
34
+ \`\`\`
35
+ - Index foreign keys and search columns:
36
+ \`\`\`sql
37
+ CREATE INDEX idx_posts_user_id ON posts(user_id);
38
+ CREATE INDEX idx_users_email ON users(email);
39
+ \`\`\`
40
+
41
+ ### Transactions
42
+ - Use transactions for atomic operations:
43
+ \`\`\`ts
44
+ await db.transaction(async (tx) => {
45
+ const [order] = await tx.insert(orders).values(orderData).returning();
46
+ await tx.insert(orderItems).values(items.map(i => ({ ...i, orderId: order.id })));
47
+ await tx.update(inventory).set({ stock: sql\`stock - 1\` }).where(eq(inventory.id, item.id));
48
+ });
49
+ \`\`\`
50
+
51
+ ### Migrations
52
+ - Never alter production schema directly — always use migrations
53
+ - Make migrations reversible (up + down)
54
+ - Test migrations on staging before production
55
+ `;
@@ -0,0 +1,74 @@
1
+ export const DOCKER_SKILL = `
2
+ ## Skill: Docker
3
+
4
+ ### Dockerfile Best Practices
5
+ - Multi-stage builds to keep images small:
6
+ \`\`\`dockerfile
7
+ # Build stage
8
+ FROM node:20-alpine AS builder
9
+ WORKDIR /app
10
+ COPY package*.json ./
11
+ RUN npm ci
12
+ COPY . .
13
+ RUN npm run build
14
+
15
+ # Production stage
16
+ FROM node:20-alpine AS runner
17
+ WORKDIR /app
18
+ RUN addgroup -S app && adduser -S app -G app
19
+ COPY --from=builder /app/dist ./dist
20
+ COPY --from=builder /app/node_modules ./node_modules
21
+ USER app
22
+ EXPOSE 3000
23
+ CMD ["node", "dist/index.js"]
24
+ \`\`\`
25
+ - Never use \`latest\` tag — pin versions: \`node:20.11-alpine\`
26
+ - Never run as root — always create and use a non-root user
27
+ - Use .dockerignore:
28
+ \`\`\`
29
+ node_modules
30
+ .env
31
+ .git
32
+ dist
33
+ *.log
34
+ \`\`\`
35
+
36
+ ### Docker Compose
37
+ \`\`\`yaml
38
+ services:
39
+ app:
40
+ build: .
41
+ ports:
42
+ - "3000:3000"
43
+ environment:
44
+ - NODE_ENV=production
45
+ - DATABASE_URL=\${DATABASE_URL}
46
+ depends_on:
47
+ db:
48
+ condition: service_healthy
49
+
50
+ db:
51
+ image: postgres:16-alpine
52
+ environment:
53
+ POSTGRES_DB: myapp
54
+ POSTGRES_USER: \${DB_USER}
55
+ POSTGRES_PASSWORD: \${DB_PASSWORD}
56
+ volumes:
57
+ - postgres_data:/var/lib/postgresql/data
58
+ healthcheck:
59
+ test: ["CMD-SHELL", "pg_isready -U \${DB_USER}"]
60
+ interval: 5s
61
+ timeout: 5s
62
+ retries: 5
63
+
64
+ volumes:
65
+ postgres_data:
66
+ \`\`\`
67
+
68
+ ### Rules
69
+ - One process per container
70
+ - Use COPY over ADD unless extracting tarballs
71
+ - Set WORKDIR explicitly — never rely on default
72
+ - Use ARG for build-time, ENV for runtime variables
73
+ - Health checks on all services
74
+ `;
@@ -0,0 +1,70 @@
1
+ export const FRONTEND_SKILL = `
2
+ ## Skill: Frontend Development
3
+
4
+ ### Component Architecture
5
+ - Use functional components with TypeScript — never class components
6
+ - Always type props with explicit interfaces:
7
+ \`\`\`tsx
8
+ interface ButtonProps {
9
+ label: string;
10
+ onClick: () => void;
11
+ disabled?: boolean;
12
+ variant?: 'primary' | 'secondary';
13
+ }
14
+
15
+ const Button = ({ label, onClick, disabled = false, variant = 'primary' }: ButtonProps) => {
16
+ return <button onClick={onClick} disabled={disabled} className={variant}>{label}</button>;
17
+ };
18
+ \`\`\`
19
+ - Use named exports for components, default exports for pages/routes
20
+ - Keep components under 150 lines — split if larger
21
+ - One component per file
22
+
23
+ ### Hooks
24
+ - Extract reusable logic into custom hooks prefixed with \`use\`:
25
+ \`\`\`ts
26
+ function useDebounce<T>(value: T, delay: number): T {
27
+ const [debounced, setDebounced] = useState(value);
28
+ useEffect(() => {
29
+ const timer = setTimeout(() => setDebounced(value), delay);
30
+ return () => clearTimeout(timer);
31
+ }, [value, delay]);
32
+ return debounced;
33
+ }
34
+ \`\`\`
35
+ - Never put business logic directly in components — use hooks
36
+ - Keep useEffect dependencies complete — never suppress exhaustive-deps
37
+
38
+ ### State Management
39
+ - Local state: useState / useReducer
40
+ - Server state: React Query / SWR
41
+ - Global state: Zustand or Context (avoid Redux unless needed)
42
+ - Never mutate state directly:
43
+ \`\`\`ts
44
+ // ❌ wrong
45
+ state.items.push(newItem);
46
+
47
+ // ✅ correct
48
+ setItems(prev => [...prev, newItem]);
49
+ \`\`\`
50
+
51
+ ### Performance
52
+ - Memoize expensive computations with useMemo
53
+ - Memoize callbacks passed to children with useCallback
54
+ - Wrap pure components with React.memo
55
+ - Lazy load heavy components:
56
+ \`\`\`ts
57
+ const HeavyChart = React.lazy(() => import('./HeavyChart'));
58
+ \`\`\`
59
+
60
+ ### TypeScript
61
+ - No \`any\` — use \`unknown\` and narrow down
62
+ - Use discriminated unions for complex state:
63
+ \`\`\`ts
64
+ type State =
65
+ | { status: 'idle' }
66
+ | { status: 'loading' }
67
+ | { status: 'success'; data: User[] }
68
+ | { status: 'error'; error: string };
69
+ \`\`\`
70
+ `;
@@ -0,0 +1,52 @@
1
+ export const GIT_SKILL = `
2
+ ## Skill: Git
3
+
4
+ ### Commit Conventions
5
+ - Use conventional commits:
6
+ \`\`\`
7
+ feat: add user authentication
8
+ fix: resolve login redirect loop
9
+ chore: update dependencies
10
+ refactor: extract auth middleware
11
+ docs: add API documentation
12
+ test: add unit tests for UserService
13
+ style: format with prettier
14
+ \`\`\`
15
+ - Imperative mood: "add feature" not "added feature"
16
+ - Keep subject under 72 characters
17
+ - Reference issues: \`fix: resolve login bug (#123)\`
18
+
19
+ ### Branching
20
+ \`\`\`
21
+ main → production
22
+ feat/auth → new features
23
+ fix/login → bug fixes
24
+ chore/deps → maintenance
25
+ release/1.2.0 → release prep
26
+ \`\`\`
27
+
28
+ ### Best Practices
29
+ - Small, focused commits — one logical change per commit
30
+ - Never commit: .env files, secrets, node_modules, build artifacts
31
+ - Always pull before pushing on shared branches
32
+ - Squash WIP commits before merging:
33
+ \`\`\`bash
34
+ git rebase -i HEAD~3
35
+ \`\`\`
36
+ - Tag releases with semantic versioning:
37
+ \`\`\`bash
38
+ git tag -a v1.2.0 -m "Release v1.2.0"
39
+ git push origin v1.2.0
40
+ \`\`\`
41
+
42
+ ### .gitignore essentials
43
+ \`\`\`
44
+ node_modules/
45
+ .env
46
+ .env.local
47
+ dist/
48
+ build/
49
+ *.log
50
+ .DS_Store
51
+ \`\`\`
52
+ `;
@@ -0,0 +1,73 @@
1
+ export const TESTING_SKILL = `
2
+ ## Skill: Testing
3
+
4
+ ### Unit Tests
5
+ - Test behavior, not implementation:
6
+ \`\`\`ts
7
+ // ❌ wrong — testing implementation
8
+ expect(component.state.isLoading).toBe(true);
9
+
10
+ // ✅ correct — testing behavior
11
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
12
+ \`\`\`
13
+ - Descriptive test names:
14
+ \`\`\`ts
15
+ describe('UserService', () => {
16
+ it('should throw 404 when user not found', async () => {});
17
+ it('should hash password before saving', async () => {});
18
+ it('should return user without password field', async () => {});
19
+ });
20
+ \`\`\`
21
+ - Always test error paths:
22
+ \`\`\`ts
23
+ it('should return error when email already exists', async () => {
24
+ await userService.create({ email: 'test@test.com' });
25
+ await expect(userService.create({ email: 'test@test.com' })).rejects.toThrow('Email already exists');
26
+ });
27
+ \`\`\`
28
+
29
+ ### Mocking
30
+ - Mock external dependencies in unit tests:
31
+ \`\`\`ts
32
+ jest.mock('../db', () => ({
33
+ users: { findOne: jest.fn() }
34
+ }));
35
+
36
+ it('should return null when user not found', async () => {
37
+ mockDb.users.findOne.mockResolvedValue(null);
38
+ const result = await userService.findById('123');
39
+ expect(result).toBeNull();
40
+ });
41
+ \`\`\`
42
+
43
+ ### Test Data
44
+ - Use factories, not hardcoded values:
45
+ \`\`\`ts
46
+ const createUser = (overrides = {}) => ({
47
+ id: crypto.randomUUID(),
48
+ name: 'Test User',
49
+ email: 'test@example.com',
50
+ createdAt: new Date(),
51
+ ...overrides,
52
+ });
53
+
54
+ const user = createUser({ name: 'Alice' });
55
+ \`\`\`
56
+
57
+ ### Structure
58
+ - Keep tests independent — no shared mutable state
59
+ - One logical assertion per test
60
+ - Arrange → Act → Assert pattern:
61
+ \`\`\`ts
62
+ it('should update user name', async () => {
63
+ // Arrange
64
+ const user = await createUser();
65
+
66
+ // Act
67
+ const updated = await userService.update(user.id, { name: 'New Name' });
68
+
69
+ // Assert
70
+ expect(updated.name).toBe('New Name');
71
+ });
72
+ \`\`\`
73
+ `;
@@ -0,0 +1,57 @@
1
+ export const TYPOGRAPHY_SKILL = `
2
+ ## Skill: Typography
3
+
4
+ ### Type Scale
5
+ - Use a consistent ratio — 1.25x (Major Third) or 1.333x (Perfect Fourth):
6
+ \`\`\`
7
+ xs: 12px
8
+ sm: 14px
9
+ base: 16px
10
+ lg: 20px
11
+ xl: 24px
12
+ 2xl: 32px
13
+ 3xl: 40px
14
+ 4xl: 48px
15
+ \`\`\`
16
+
17
+ ### Readability
18
+ - Body text: 16px minimum, 1.5-1.6 line height
19
+ - Limit line length: 60-75 characters (45-75ch)
20
+ - Heading line height: 1.1-1.3 (tighter than body)
21
+ \`\`\`css
22
+ body {
23
+ font-size: 16px;
24
+ line-height: 1.6;
25
+ max-width: 65ch;
26
+ }
27
+
28
+ h1, h2, h3 {
29
+ line-height: 1.2;
30
+ }
31
+ \`\`\`
32
+
33
+ ### Font Pairing
34
+ - Max 2-3 typefaces per design
35
+ - Classic combos:
36
+ - Inter + Fraunces (sans + serif)
37
+ - Geist + Geist Mono (code-friendly)
38
+ - DM Sans + DM Serif Display
39
+ - Use system font stack when performance matters:
40
+ \`\`\`css
41
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
42
+ \`\`\`
43
+
44
+ ### Weight & Style
45
+ - Body: 400
46
+ - UI labels, captions: 500
47
+ - Headings: 600-700
48
+ - Never use 500 for headings — jump from 400 to 600+
49
+ - Letter spacing:
50
+ - Headings: -0.02em (tighten)
51
+ - All caps / labels: 0.05-0.1em (loosen)
52
+ - Body: 0 (default)
53
+
54
+ ### Colors
55
+ - Never pure black for body text — use #111 or #1a1a1a
56
+ - Hierarchy through weight and size, not just color
57
+ `;
@@ -0,0 +1,43 @@
1
+ export const UIUX_SKILL = `
2
+ ## Skill: UI/UX Design
3
+
4
+ ### Layout & Spacing
5
+ - Use a spacing scale — multiples of 4:
6
+ - 4, 8, 12, 16, 24, 32, 48, 64, 96, 128px
7
+ - Mobile first — design small then enhance:
8
+ \`\`\`css
9
+ .container {
10
+ padding: 16px;
11
+ }
12
+ @media (min-width: 768px) {
13
+ .container {
14
+ padding: 32px;
15
+ }
16
+ }
17
+ \`\`\`
18
+ - Minimum touch target: 44x44px
19
+ - Consistent grid: 12-column or 4/8-column for mobile
20
+
21
+ ### Interaction Design
22
+ - Always show loading states for async operations:
23
+ - Use skeleton screens for content, spinners for actions
24
+ - Show errors inline, close to the source — not just toasts
25
+ - Never disable buttons — show why the action is unavailable
26
+ - Keyboard navigation must work for all interactive elements
27
+ - Manage focus after modals/dialogs open and close
28
+ - Animations under 300ms for UI feedback, 500ms for transitions
29
+
30
+ ### Component Patterns
31
+ - Cards: consistent padding (16-24px), subtle shadow or border
32
+ - Forms: label above input, error below, helper text below label
33
+ - Tables: zebra striping or row hover for readability
34
+ - Empty states: illustration + message + action
35
+ - 404/Error pages: clear message + way back home
36
+
37
+ ### Accessibility
38
+ - Color is never the only differentiator
39
+ - Alt text on all meaningful images
40
+ - ARIA labels on icon-only buttons
41
+ - Focus indicators must be visible
42
+ - Contrast: 4.5:1 for text, 3:1 for large text and UI components
43
+ `;
@@ -0,0 +1,17 @@
1
+ export const DESCRIPTION =
2
+ "Launch a focused sub-agent to handle a specific task.";
3
+
4
+ export const PROMPT = `Spawns a sub-agent with full tool access to complete a focused task.
5
+
6
+ Use this when:
7
+ - A subtask is too complex or long to handle inline
8
+ - You need to delegate a well-defined unit of work
9
+ - The task requires multiple tool calls but is a single concern
10
+
11
+ Do NOT use this for:
12
+ - Simple tool calls you can do directly
13
+ - Tasks requiring multiple parallel agents (use OrchestratorTool instead)
14
+ - Recursive delegation
15
+
16
+ The sub-agent has access to: FileReadTool, FileWriteTool, FileEditTool, BashTool, GrepTool.
17
+ It returns a text summary of what it did.`;
@@ -0,0 +1,22 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { DESCRIPTION, PROMPT } from "./prompt";
4
+
5
+ export const AgentTool = tool({
6
+ title: "Agent",
7
+ description: DESCRIPTION + "\n\n" + PROMPT,
8
+ inputSchema: z.object({
9
+ prompt: z.string().describe("The task for the sub-agent to perform"),
10
+ }),
11
+ execute: async ({
12
+ prompt,
13
+ }): Promise<{ success: boolean; result?: string; error?: string }> => {
14
+ try {
15
+ const { createAgent } = await import("../../utils/agent.js");
16
+ const result = await createAgent(prompt);
17
+ return { success: true, result };
18
+ } catch (err) {
19
+ return { success: false, error: String(err) };
20
+ }
21
+ },
22
+ });
@@ -0,0 +1,82 @@
1
+ import { FileReadTool } from "../FileReadTool/tool.js";
2
+ import { platform } from "os";
3
+
4
+ export const MAX_OUTPUT_LENGTH = 30000;
5
+ export const MAX_RENDERED_LINES = 50;
6
+
7
+ export const BANNED_COMMANDS = [
8
+ "alias",
9
+ "curl",
10
+ "curlie",
11
+ "wget",
12
+ "axel",
13
+ "aria2c",
14
+ "nc",
15
+ "telnet",
16
+ "lynx",
17
+ "w3m",
18
+ "links",
19
+ "httpie",
20
+ "xh",
21
+ "http-prompt",
22
+ "chrome",
23
+ "firefox",
24
+ "safari",
25
+ ];
26
+
27
+ const isWindows = platform() === "win32";
28
+
29
+ const PLATFORM_NOTES = isWindows
30
+ ? `- This is Windows — use dir instead of ls, findstr instead of grep, use backslashes in paths
31
+ - NEVER use find or grep — use findstr or dir /s instead`
32
+ : `- This is ${platform()} — use standard unix commands (ls, grep, find, cat etc.)`;
33
+
34
+ export const DESCRIPTION =
35
+ "Execute a bash command in a persistent shell session.";
36
+
37
+ export const PROMPT = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
38
+
39
+ Before executing the command, please follow these steps:
40
+
41
+ 1. Directory Verification:
42
+ - If the command will create new directories or files, first verify the parent directory exists
43
+
44
+ 2. Security Check:
45
+ - Some commands are banned. If you use one, you will receive an error. Explain it to the user.
46
+ - Banned commands: ${BANNED_COMMANDS.join(", ")}
47
+
48
+ 3. Command Execution:
49
+ - After ensuring proper quoting, execute the command.
50
+
51
+ 4. Output Processing:
52
+ - Output exceeding ${MAX_OUTPUT_LENGTH} characters will be truncated.
53
+
54
+ 5. Return Result:
55
+ - Provide the processed output.
56
+ - Include any errors that occurred.
57
+
58
+ Usage notes:
59
+ ${PLATFORM_NOTES}
60
+ - Use ; or && to chain multiple commands, never newlines
61
+ - Avoid cat, head, tail — use ${FileReadTool.title} to read files instead
62
+ - Only use tools available: FileReadTool, FileWriteTool, FileEditTool, BashTool
63
+ - Timeout defaults to 30 minutes, max 10 minutes per command
64
+ - All commands share the same shell session — env vars and cwd persist between commands
65
+ - Prefer absolute paths, avoid cd
66
+ - When listing directory contents recursively, NEVER recurse into node_modules, .git, dist, or build folders
67
+ - When using dir /s, always exclude node_modules: dir /s /b /a-d "path" | findstr /v "\\node_modules\\"
68
+
69
+ # Directory listing
70
+ Before listing files recursively:
71
+ 1. Read .gitignore if it exists and exclude those directories
72
+ 2. If .gitignore is not found, always exclude these folders by default: node_modules, .git, dist, build, .next, out, coverage
73
+ 3. Never dump raw recursive listings — always filter to relevant files only
74
+
75
+ # Git commits
76
+ When asked to commit:
77
+ 1. Run git status, git diff, and git log in a single step
78
+ 2. Stage only relevant files
79
+ 3. Write a concise commit message focused on "why" not "what"
80
+ 4. Never use git commands with -i flag
81
+ 5. Never push to remote
82
+ 6. Never update git config`;
@@ -0,0 +1,54 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import {
4
+ DESCRIPTION,
5
+ PROMPT,
6
+ MAX_OUTPUT_LENGTH,
7
+ BANNED_COMMANDS,
8
+ } from "./prompt.js";
9
+ import { PersistentShell } from "../../utils/PersistentShell.js";
10
+ import { requestPermission } from "../../permissions.js";
11
+
12
+ const inputSchema = z.object({
13
+ command: z.string().describe("The bash command to execute"),
14
+ timeout: z
15
+ .number()
16
+ .optional()
17
+ .describe("Timeout in milliseconds, max 600000"),
18
+ });
19
+
20
+ export const BashTool = {
21
+ description: DESCRIPTION + "\n\n" + PROMPT,
22
+ title: "Bash",
23
+ inputSchema,
24
+ execute: async ({ command, timeout }: z.infer<typeof inputSchema>) => {
25
+ const decision = await requestPermission("BashTool", { command });
26
+ if (decision === "deny")
27
+ return { success: false, output: "User denied permission" };
28
+
29
+ try {
30
+ const banned = BANNED_COMMANDS.find(
31
+ (cmd) => command.split(/\s+/)[0] === cmd,
32
+ );
33
+ if (banned)
34
+ return {
35
+ success: false,
36
+ error: `Command "${banned}" is not allowed`,
37
+ };
38
+
39
+ const shell = PersistentShell.getInstance();
40
+ const output = await shell.execute(command, timeout);
41
+
42
+ const truncated = output.length > MAX_OUTPUT_LENGTH;
43
+ return {
44
+ success: true,
45
+ output: truncated
46
+ ? output.slice(0, MAX_OUTPUT_LENGTH) + "\n... (truncated)"
47
+ : output,
48
+ truncated,
49
+ };
50
+ } catch (err) {
51
+ return { success: false, error: String(err) };
52
+ }
53
+ },
54
+ };
@@ -0,0 +1,13 @@
1
+ export const DESCRIPTION =
2
+ "Edit an existing file by replacing a specific string with new content.";
3
+ export const PROMPT = `Edits a file by replacing an exact string match with new content. The path parameter must be an absolute path, not a relative path.
4
+
5
+ The old_string must match exactly once in the file — if it matches zero or multiple times, the edit will fail. Make old_string specific enough to be unique, including surrounding context if needed.
6
+
7
+ Guidelines:
8
+ - Always read the file before editing so you know the exact content
9
+ - Never use this tool on a file you haven't read first
10
+ - Prefer small, targeted edits over rewriting large sections
11
+ - Preserve the original indentation and formatting
12
+ - If you need to make multiple edits, make them one at a time
13
+ - When using old_string, copy the exact content WITHOUT the line number prefix (e.g. use "app.listen" not " 9\tapp.listen")`;