Skip to content

Commit 405b7cf

Browse files
authored
Merge pull request #175 from GizzZmo/copilot/onboard-copilot-cloud-agent
docs: add .github/copilot-instructions.md for cloud agent onboarding
2 parents 4968d8f + 1cf8a4e commit 405b7cf

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Copilot Instructions for Omni-Grid 2.0
2+
3+
## Project Overview
4+
5+
Omni-Grid is a privacy-centric, local-first "Super App" built with **React 19 + TypeScript (strict) + Vite 8**. It replaces browser tabs with a unified, widget-based grid dashboard. State is managed with **Zustand 5** (persisted via `zustand/middleware`); the UI follows a **Cyberpunk/High-Density** aesthetic using Tailwind utility classes on a `slate-950` dark background with vivid accents (`cyan`, `emerald`, `fuchsia`, `amber`). Icons come exclusively from `lucide-react`.
6+
7+
---
8+
9+
## Repository Layout
10+
11+
```
12+
/
13+
├── App.tsx # App root (theme application, layout shell)
14+
├── index.tsx # Entry point
15+
├── store.ts # Global Zustand store (all state slices)
16+
├── types.ts # Shared TypeScript types (WidgetType enum, interfaces)
17+
├── utils.ts # Shared utility functions
18+
├── components/ # Core UI shells
19+
│ ├── GridContainer.tsx # Renders all widgets; MUST be updated for new widgets
20+
│ ├── WidgetLauncher.tsx # WIDGET_REGISTRY; MUST be updated for new widgets
21+
│ ├── WidgetShell.tsx # Per-widget resize/title chrome
22+
│ ├── CommandPalette.tsx
23+
│ ├── MatrixRain.tsx
24+
│ └── SettingsPanel/
25+
├── widgets/ # Individual widget implementations (PascalCase.tsx)
26+
│ ├── marketplaceCatalog.ts # Static catalog; MUST be updated for new widgets
27+
│ └── ...
28+
├── services/ # AI providers, sandbox, plugin API, PWA
29+
│ ├── aiProviders.ts
30+
│ ├── geminiService.ts
31+
│ ├── e2bSandbox.ts
32+
│ ├── gridIntelligence.ts
33+
│ ├── multiAgentOrchestrator.ts
34+
│ ├── pluginApiV2.ts
35+
│ ├── promptEngine.ts
36+
│ └── pwaService.ts
37+
├── test/ # Vitest + React Testing Library (one file per widget)
38+
│ └── setup.ts
39+
├── types/ # Additional TypeScript declaration files
40+
├── themes/ # Theme definitions
41+
├── docs/ # Extended documentation
42+
└── server/ # Optional C++ server component (built via Makefile)
43+
```
44+
45+
---
46+
47+
## Setup
48+
49+
```bash
50+
# Node.js >= 20.0.0 is required (see .nvmrc)
51+
npm install
52+
```
53+
54+
For AI and sandboxed-execution features, create a `.env` file (see `.env.example`):
55+
56+
```env
57+
GEMINI_API_KEY=your_google_gemini_api_key_here
58+
E2B_API_KEY=your_e2b_api_key_here
59+
```
60+
61+
`GEMINI_API_KEY` is the canonical name used in `.env`. The store's key resolver also accepts `VITE_API_KEY` via `import.meta.env` (useful for Vite-prefixed secrets in some deployment environments), but `GEMINI_API_KEY` is preferred.
62+
63+
---
64+
65+
## Key Commands — Run Before Every PR
66+
67+
```bash
68+
npm run typecheck # tsc --noEmit, strict mode — MUST pass
69+
npm run lint # eslint . --ext .ts,.tsx — MUST pass
70+
npm run test:run # vitest run (all tests once) — MUST pass
71+
npm run build # production Vite build — MUST pass
72+
```
73+
74+
Other useful commands:
75+
76+
```bash
77+
npm run dev # Vite dev server (port 3000)
78+
npm run lint:fix # eslint --fix
79+
npm run format # prettier --write .
80+
npm run format:check # prettier --check .
81+
npm run test:coverage # vitest run --coverage
82+
npm run build:server # optional C++ server (make server)
83+
```
84+
85+
---
86+
87+
## Naming Conventions
88+
89+
| Item | Convention | Example |
90+
| ----------------- | ------------------------ | ------------------------ |
91+
| Widget files | PascalCase | `MyWidget.tsx` |
92+
| WidgetType values | SCREAMING_SNAKE_CASE | `MY_WIDGET` |
93+
| Store slices | camelCase | `myWidgetData` |
94+
| Test files | camelCase matching widget| `test/myWidget.test.tsx` |
95+
| Catalog ID | Must match WidgetType | `'MY_WIDGET'` |
96+
97+
---
98+
99+
## Adding a New Widget — 7 Required File Changes
100+
101+
Every new widget requires coordinated edits across these files (in order):
102+
103+
1. **`widgets/MyWidget.tsx`** — The component. Export a named React function component; use `useAppStore` only for cross-widget or persisted state.
104+
105+
2. **`types.ts`** — Add the new `WidgetType` literal to the union type and any new interfaces or enums.
106+
107+
3. **`components/GridContainer.tsx`** — Import the component and add a `case 'MY_WIDGET':` branch in the widget renderer switch.
108+
109+
4. **`components/WidgetLauncher.tsx`** — Add an entry to `WIDGET_REGISTRY` with `id`, `name`, `icon` (lucide-react), `color`, `bg`, `border`.
110+
111+
5. **`widgets/marketplaceCatalog.ts`** — Append a `MarketplaceEntry` to `MARKETPLACE_CATALOG`. Use `isCore: false` for community widgets, `isCore: true` for platform widgets. The `id` field **must** match the `WidgetType` string exactly.
112+
113+
6. **`store.ts`** — If the widget needs persisted state, add a state slice (interface, initial values, and actions). All store state is persisted via `zustand/middleware` `persist`.
114+
115+
7. **`test/myWidget.test.tsx`** — Required for all widgets. Include at minimum: a render test (`render(<MyWidget />)` does not throw) and one key interaction test.
116+
117+
---
118+
119+
## Widget Guidelines
120+
121+
- **Self-contained:** Widgets communicate with others only via the global Zustand store or Cross-Talk, never by importing each other's internals.
122+
- **Responsive:** Use `flex` and `min-h-0` patterns so scroll areas work correctly within grid items.
123+
- **Error handling:** All external/API calls must gracefully handle missing API keys and network failures without crashing.
124+
- **Performance:** Use `useMemo`/`useCallback` for expensive computations; move CPU-intensive work to Web Workers.
125+
- **Styling:** Tailwind utility classes only. No inline styles or CSS modules. Vivid accent colors for data; `slate-950` backgrounds.
126+
127+
---
128+
129+
## Testing Patterns
130+
131+
Test stack: **Vitest + `@testing-library/react` + `@testing-library/jest-dom` + `@testing-library/user-event`**
132+
133+
Every test file must mock the Zustand store using only the slices the widget under test actually uses:
134+
135+
```tsx
136+
import { describe, it, expect, vi } from 'vitest';
137+
import { render, screen, fireEvent } from '@testing-library/react';
138+
import { MyWidget } from '../widgets/MyWidget';
139+
140+
vi.mock('../store', () => ({
141+
useAppStore: () => ({
142+
// include only the slices MyWidget uses
143+
mySlice: 'value',
144+
setMySlice: vi.fn(),
145+
}),
146+
}));
147+
148+
describe('MyWidget', () => {
149+
it('renders without crashing', () => {
150+
render(<MyWidget />);
151+
expect(screen.getByText(/expected text/i)).toBeTruthy();
152+
});
153+
154+
it('handles a key interaction', () => {
155+
render(<MyWidget />);
156+
fireEvent.click(screen.getByRole('button', { name: /action/i }));
157+
expect(screen.getByText(/result/i)).toBeTruthy();
158+
});
159+
});
160+
```
161+
162+
Reference tests: `test/gitPulse.test.tsx`, `test/projectTracker.test.tsx`, `test/promptEngine.test.ts`.
163+
164+
---
165+
166+
## State Management
167+
168+
`store.ts` exports a single `useAppStore` Zustand hook with persisted state. Slices include:
169+
170+
- **Layout:** `layouts`, `visibleWidgets`, `updateLayout`, `toggleWidget`
171+
- **Settings:** `settings` (geminiApiKey, e2bApiKey, scanlines, sound, startupBehavior)
172+
- **UI State:** `ghostWidget`, `isLayoutLocked`, `isCompact`, `isCmdPaletteOpen`, `isSettingsPanelOpen`
173+
- **Per-widget slices:** one slice per widget that needs persisted state (e.g., `gitToken`, `promptTemplates`, `calendarEvents`)
174+
175+
`store.ts` automatically syncs `GEMINI_API_KEY`/`E2B_API_KEY` to `process.env` and `window.process.env` at runtime for libraries that read them.
176+
177+
---
178+
179+
## AI Features
180+
181+
The project uses `@google/genai` for AI features. All AI-backed features must:
182+
183+
- Check for the API key before making calls; show a helpful prompt when missing.
184+
- Show visual feedback (spinners/loaders) during API calls.
185+
- Display error messages on failure without crashing.
186+
- Be tested both with and without API keys configured.
187+
188+
The Gemini key is read from `VITE_API_KEY` or `GEMINI_API_KEY` environment variables and stored in the Zustand `settings` slice. Services access it via `useAppStore.getState().settings.geminiApiKey`.
189+
190+
---
191+
192+
## Commit and PR Conventions
193+
194+
- Use **Conventional Commits**: `feat: ...`, `fix: ...`, `docs: ...`, `test: ...`, `refactor: ...`, `chore: ...`.
195+
- PRs must: pass all tests, pass TypeScript strict typecheck, pass lint, and (for new widgets) include tests and a marketplace catalog entry.
196+
- Use the PR template at `.github/PULL_REQUEST_TEMPLATE.md`.
197+
198+
---
199+
200+
## Common Errors and Workarounds
201+
202+
### TypeScript strict errors with implicit `any`
203+
204+
All function parameters and return types must be explicitly typed. If upgrading a JS file, add types rather than using `// @ts-ignore`.
205+
206+
### `process.env` not defined in browser
207+
208+
The Vite config defines `process.env.API_KEY` and `process.env.GEMINI_API_KEY` at build time. For runtime key updates (user sets key in Settings), `store.ts` syncs to `window.process.env`. Services should read keys via `useAppStore.getState().settings.geminiApiKey` instead of accessing `process.env` directly at module load time.
209+
210+
### `react-grid-layout` CSS not loading in tests
211+
212+
CSS imports are ignored in the Vitest jsdom environment by default — this is expected. The `vite.config.ts` `test.css: true` option handles this in the test runner without any workaround needed.
213+
214+
### Widget not rendering in the grid
215+
216+
Check that all 7 required files have been updated (see "Adding a New Widget" above). The most commonly missed file is `components/GridContainer.tsx` — the `case 'MY_WIDGET':` branch in the widget switch statement.
217+
218+
### `lucide-react` icon not found
219+
220+
Check the exact icon name at https://lucide.dev/icons/ — names are CamelCase in import but may differ from the icon page slug. Always import from `lucide-react`.

0 commit comments

Comments
 (0)