Skip to content

Commit 1928b4e

Browse files
Add eggs-ai with bundled myclaw agentic engine and myclaw provider
Adds eggs-ai with sworddut/myclaw bundled as a subproject, including the myclaw provider registered in src/providers/. - myclaw/ — full source: agent loop, tools, providers, event bus, session store, check-gate, user profile, oclif commands - src/engine/ — integration layer (agent loop, 9 tools, event bus, session persistence, provider bridge, check-gate safety) - src/providers/myclaw.ts — myclaw provider with native tool-call support, registered alongside the 7 existing providers - New CLI: 'eggs-ai agent <task>', 'chat --agentic', 'chat --resume' 112 tests pass. All files under eggs-ai/. Co-authored-by: Ona <no-reply@ona.com>
1 parent 13af92d commit 1928b4e

File tree

115 files changed

+19075
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+19075
-0
lines changed

eggs-ai/.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
dist/
3+
.git/
4+
test/
5+
examples/
6+
integrations/
7+
packaging/
8+
*.md
9+
.env*
10+
.npmignore
11+
vitest.config.ts
12+
.github/

eggs-ai/.github/workflows/ci.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
pull_request:
7+
branches: [master, main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
node-version: [18, 20, 22]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Use Node.js ${{ matrix.node-version }}
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: ${{ matrix.node-version }}
23+
cache: npm
24+
25+
- name: Install dependencies
26+
run: npm ci
27+
28+
- name: Build
29+
run: npm run build
30+
31+
- name: Run tests
32+
run: npm test
33+
34+
publish-dry-run:
35+
runs-on: ubuntu-latest
36+
needs: test
37+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
38+
steps:
39+
- uses: actions/checkout@v4
40+
41+
- uses: actions/setup-node@v4
42+
with:
43+
node-version: 22
44+
cache: npm
45+
46+
- run: npm ci
47+
- run: npm run build
48+
- run: npm pack --dry-run

eggs-ai/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules/
2+
dist/
3+
build/
4+
.env
5+
.env.local
6+
.env.*.local
7+
*.log
8+
npm-debug.log*
9+
.DS_Store
10+
*.tsbuildinfo

eggs-ai/.npmignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
src/
2+
test/
3+
examples/
4+
packaging/
5+
.gitignore
6+
vitest.config.ts
7+
tsconfig.json
8+
*.tsbuildinfo
9+
.eggs-ai.yaml

eggs-ai/CONTRIBUTING.md

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# Contributing to Eggs-AI
2+
3+
## Quick start
4+
5+
```bash
6+
git clone https://github.com/Interested-Deving-1896/eggs-ai.git
7+
cd eggs-ai
8+
npm install
9+
npm run build
10+
npm test # 80 tests, should all pass
11+
npm run dev -- status # verify CLI works
12+
```
13+
14+
Node.js 18+ required. No LLM key needed for development — tests use mocks.
15+
16+
## Project layout
17+
18+
```
19+
src/
20+
├── agents/ # 6 domain agents — each takes an LLMProvider + args, returns a string
21+
├── providers/ # LLM abstraction — registry, 7 built-in providers, config loader
22+
├── knowledge/ # Static + dynamic domain knowledge (commands, issues, distro guides)
23+
├── server/ # HTTP API server (REST + SSE streaming)
24+
├── mcp/ # Model Context Protocol server (stdio JSON-RPC)
25+
├── bridge/ # eggs-gui daemon client + JSON-RPC method definitions
26+
├── sdk/ # TypeScript client SDK for the HTTP API
27+
├── tools/ # System inspection and eggs CLI wrappers
28+
└── index.ts # CLI entry point (Commander)
29+
30+
test/ # Vitest tests — unit, integration, E2E, MCP
31+
integrations/ # Drop-in code for eggs-gui frontends (Python, Go)
32+
examples/ # Sample configs and programmatic usage
33+
packaging/ # systemd service, .desktop file
34+
proto/ # JSON-RPC schema for eggs-gui integration
35+
```
36+
37+
## Development workflow
38+
39+
### Build and test
40+
41+
```bash
42+
npm run build # TypeScript → dist/
43+
npm test # Run all tests once
44+
npm run test:watch # Watch mode
45+
npm run dev -- <command> # Run CLI without building (uses tsx)
46+
```
47+
48+
### Run the API server
49+
50+
```bash
51+
npm run serve # Starts on http://127.0.0.1:3737
52+
# or
53+
npm run dev -- serve --port 3737
54+
```
55+
56+
### Run the MCP server
57+
58+
```bash
59+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | npx tsx src/mcp/server.ts
60+
```
61+
62+
## How things connect
63+
64+
```
65+
User input
66+
67+
68+
CLI (src/index.ts) ──or── API Server (src/server/api.ts) ──or── MCP Server (src/mcp/server.ts)
69+
│ │ │
70+
▼ ▼ ▼
71+
Agent (src/agents/*.ts) Agent (same) Direct knowledge lookup
72+
│ │ (no LLM needed for MCP)
73+
├── Knowledge (src/knowledge/) │
74+
├── System inspect (src/tools/)│
75+
│ │
76+
▼ ▼
77+
LLMProvider.chat(messages) LLMProvider.chat(messages)
78+
│ │
79+
▼ ▼
80+
ProviderRegistry → Gemini / OpenAI / Anthropic / Ollama / ...
81+
```
82+
83+
Key distinction: **agents** need an LLM provider (they construct prompts and call `provider.chat()`). **MCP tools** return knowledge directly without an LLM — the calling AI agent (Cursor, Claude Desktop, etc.) is the LLM.
84+
85+
## Adding a new LLM provider
86+
87+
1. Create `src/providers/myservice.ts`:
88+
89+
```typescript
90+
import type { LLMProvider, Message } from './base.js';
91+
92+
export class MyServiceProvider implements LLMProvider {
93+
name = 'myservice';
94+
95+
constructor(private apiKey: string, private model = 'default-model') {}
96+
97+
async chat(messages: Message[]): Promise<string> {
98+
// Call your API, return the response text
99+
}
100+
101+
// Optional: streaming support
102+
async chatStream?(messages: Message[], onChunk: (chunk: string) => void): Promise<string> {
103+
// Stream tokens, call onChunk for each, return full text
104+
}
105+
106+
async isAvailable(): Promise<boolean> {
107+
// Quick check — can we reach the API?
108+
}
109+
}
110+
```
111+
112+
2. Register it in `src/providers/index.ts`:
113+
114+
```typescript
115+
import { MyServiceProvider } from './myservice.js';
116+
117+
ProviderRegistry.register('myservice', (config) => {
118+
const key = config.apiKey || process.env.MYSERVICE_API_KEY;
119+
if (!key) throw new Error('API key required. Set MYSERVICE_API_KEY.');
120+
return new MyServiceProvider(key, config.model);
121+
});
122+
```
123+
124+
3. Add it to the auto-detection list in `autoDetectProvider()` if it has an env var.
125+
126+
4. Add tests in `test/providers-impl.test.ts` — mock `fetch`, verify request format and headers.
127+
128+
If the API is OpenAI-compatible (most are), you can skip step 1 and just register it with the `CustomProvider`:
129+
130+
```typescript
131+
ProviderRegistry.register('myservice', (config) => {
132+
return new CustomProvider('myservice', 'https://api.myservice.com/v1', config.model || 'default', config.apiKey);
133+
});
134+
```
135+
136+
## Adding a new agent
137+
138+
Agents live in `src/agents/`. Each one:
139+
- Takes an `LLMProvider` and agent-specific arguments
140+
- Builds a prompt using knowledge from `src/knowledge/`
141+
- Calls `provider.chat(messages)` and returns the result
142+
143+
1. Create `src/agents/myagent.ts`:
144+
145+
```typescript
146+
import type { LLMProvider, Message } from '../providers/index.js';
147+
import { SYSTEM_PROMPT } from '../knowledge/eggs-reference.js';
148+
import { inspectSystem, formatSystemInfo } from '../tools/system-inspect.js';
149+
150+
export async function runMyAgent(provider: LLMProvider, userInput: string): Promise<string> {
151+
const systemInfo = inspectSystem();
152+
153+
const prompt = `
154+
## System
155+
${formatSystemInfo(systemInfo)}
156+
157+
## Task
158+
${userInput}
159+
160+
Provide specific, actionable output.
161+
`;
162+
163+
const messages: Message[] = [
164+
{ role: 'system', content: SYSTEM_PROMPT },
165+
{ role: 'user', content: prompt },
166+
];
167+
168+
return provider.chat(messages);
169+
}
170+
```
171+
172+
2. Wire it into the CLI in `src/index.ts` — add a new Commander command.
173+
174+
3. Wire it into the API server in `src/server/api.ts` — add a new endpoint.
175+
176+
4. Add tests in `test/agents.test.ts` — use a mock provider, verify the prompt contains the right context.
177+
178+
## Adding a new MCP tool
179+
180+
MCP tools in `src/mcp/server.ts` return knowledge directly — no LLM call. They're for other AI agents to use.
181+
182+
1. Add the tool definition to the `TOOLS` array:
183+
184+
```typescript
185+
{
186+
name: 'eggs_my_tool',
187+
description: 'What this tool does (be specific — the AI agent reads this)',
188+
inputSchema: {
189+
type: 'object',
190+
properties: {
191+
param: { type: 'string', description: 'What this param is for' },
192+
},
193+
required: ['param'],
194+
},
195+
},
196+
```
197+
198+
2. Add a handler case in `handleTool()`:
199+
200+
```typescript
201+
case 'eggs_my_tool': {
202+
const param = args.param as string;
203+
// Look up knowledge, inspect system, etc.
204+
return text(`Result for ${param}`);
205+
}
206+
```
207+
208+
3. Add tests in `test/mcp-server.test.ts` — send a JSON-RPC request, verify the response.
209+
210+
## Adding knowledge
211+
212+
### Static knowledge (src/knowledge/)
213+
214+
- `eggs-reference.ts` — commands, config fields, common issues, supported distros, calamares modules, wardrobe costumes, system prompt
215+
- `distro-guides.ts` — per-distro install guides, advanced workflows, additional troubleshooting
216+
217+
Add new entries to the existing data structures. The agents and MCP tools reference these at runtime.
218+
219+
### Dynamic knowledge (src/knowledge/updater.ts)
220+
221+
Fetches from GitHub API and caches locally:
222+
- Recent issues (20 most recent)
223+
- Latest release info
224+
- README excerpt
225+
226+
Cache lives at `~/.cache/eggs-ai/` with a 24-hour TTL. Run `eggs-ai update` to refresh manually.
227+
228+
## Testing
229+
230+
### Test structure
231+
232+
| File | What it tests |
233+
|------|--------------|
234+
| `providers-registry.test.ts` | Registry registration, lookup, env key resolution |
235+
| `providers-impl.test.ts` | All 6 provider implementations (mocked fetch) |
236+
| `agents.test.ts` | All 5 agents with mock providers |
237+
| `knowledge.test.ts` | Knowledge base structure validation |
238+
| `system-inspect.test.ts` | System detection on live system |
239+
| `sdk-client.test.ts` | SDK client with mocked HTTP |
240+
| `api-server.test.ts` | Integration tests against running API server |
241+
| `mcp-server.test.ts` | MCP protocol tests via subprocess |
242+
| `e2e.test.ts` | Full pipeline validation (prompt construction, history, error handling) |
243+
244+
### Writing tests
245+
246+
- Use `vitest` (already configured)
247+
- Mock `fetch` with `vi.stubGlobal('fetch', mockFetch)` for provider tests
248+
- Use `createMockProvider()` pattern for agent tests — captures messages sent to the LLM
249+
- MCP tests shell out to `npx tsx src/mcp/server.ts` with JSON-RPC input
250+
- API tests hit `http://127.0.0.1:3737` (requires the server to be running)
251+
252+
### Running specific tests
253+
254+
```bash
255+
npx vitest run test/agents.test.ts
256+
npx vitest run -t "doctor agent" # by test name
257+
```
258+
259+
## Code conventions
260+
261+
- TypeScript strict mode, ES2022 target, Node16 module resolution
262+
- ESM only (`"type": "module"` in package.json)
263+
- Imports use `.js` extension (required for Node ESM)
264+
- No classes for agents — plain async functions
265+
- Classes for providers (they hold state: API keys, model names)
266+
- `ProviderRegistry` is a singleton — register once, use everywhere
267+
- Knowledge is exported as plain objects/arrays, not classes
268+
269+
## Pull request guidelines
270+
271+
1. Branch from `master`
272+
2. Run `npm test` — all tests must pass
273+
3. Run `npm run build` — must compile clean
274+
4. Add tests for new functionality
275+
5. Keep commits focused — one logical change per commit
276+
6. Commit message format: short summary line, blank line, details if needed
277+
278+
## Deployment options
279+
280+
| Method | Command |
281+
|--------|---------|
282+
| CLI | `npm run dev -- <command>` |
283+
| API server | `eggs-ai serve --port 3737` |
284+
| MCP server | `eggs-ai mcp` (stdio) |
285+
| Docker | `docker compose up -d` |
286+
| systemd | `sudo cp packaging/eggs-ai.service /etc/systemd/system/ && sudo systemctl enable --now eggs-ai` |
287+
| Install script | `sudo bash install.sh` |

eggs-ai/Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM node:22-slim AS builder
2+
3+
WORKDIR /app
4+
COPY package.json package-lock.json ./
5+
RUN npm ci
6+
COPY tsconfig.json ./
7+
COPY src/ src/
8+
RUN npm run build
9+
10+
FROM node:22-slim
11+
12+
WORKDIR /app
13+
COPY package.json package-lock.json ./
14+
RUN npm ci --production
15+
COPY --from=builder /app/dist/ dist/
16+
COPY bin/ bin/
17+
COPY proto/ proto/
18+
19+
EXPOSE 3737
20+
21+
# Default: run the HTTP API server
22+
CMD ["node", "dist/index.js", "serve", "--port", "3737", "--host", "0.0.0.0"]

0 commit comments

Comments
 (0)