genaicode 2.17.2 → 2.18.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.
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - `genaicode embed [request]`: your own agent adds genaicode to the project in the current
8
+ directory. It gets instructions (the Vite overlay; a development-only panel through
9
+ `genaicode/ui` for webpack, Next.js, Express and other Node dev servers; the in-app agent;
10
+ or `genaicode ui` alongside) and decides from what it reads in the project. The chat opens
11
+ in the terminal (`--ui`: browser), headless without one; `--print` prints the
12
+ instructions for any other agent. They ship as a Claude Code skill too,
13
+ `skills/genaicode-embed/SKILL.md`.
14
+ - One session in the terminal and the browser: `/web` in `genaicode chat` opens it in the
15
+ browser (`/web off` stops the server), `genaicode chat --web` does so at start, and Enter
16
+ in the terminal running `genaicode ui` continues there with the browser still connected.
17
+
7
18
  - `<AgentChat foldSteps>` (and `<AppAgentChat foldSteps>`): each turn's work (tools, edits,
8
19
  interim text) folds into one "N steps" line that opens on click, naming the running tool.
9
20
  The answer, errors, steering input and open approvals stay in view.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genaicode",
3
- "version": "2.17.2",
3
+ "version": "2.18.0",
4
4
  "description": "Drive the coding-agent CLIs you already have (Claude Code, Codex, Copilot, Cursor, Gemini, opencode...) behind one API and one CLI, plus a thin provider-neutral LLM layer.",
5
5
  "author": "Grzegorz Tańczyk",
6
6
  "repository": {
@@ -35,6 +35,7 @@
35
35
  "docs",
36
36
  "!docs/examples",
37
37
  "!docs/media",
38
+ "skills",
38
39
  "README.md",
39
40
  "LICENSE"
40
41
  ],
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: genaicode-embed
3
+ description: Embed genaicode in a project so its developers chat with their own coding agent (Claude Code, Codex, Copilot...) from inside the running app. Use when asked to add genaicode, its Vite overlay, a dev agent panel, or an in-app agent drawer (genaicode/app-agent) to a project, with or without Vite.
4
+ ---
5
+
6
+ # Embed genaicode in this project
7
+
8
+ genaicode (npm package `genaicode`, Apache-2.0) drives the coding-agent CLIs a developer
9
+ already has installed (Claude Code, Codex, Copilot CLI, Cursor, Gemini, opencode...) behind
10
+ one API. Your job is to wire it into the project in the current directory so the
11
+ project's developers can talk to their agent from inside the running app.
12
+
13
+ There are three ways in, plus a fallback. Pick by what the project is, not by what is
14
+ more impressive.
15
+
16
+ 1. **Vite dev overlay** (`genaicode/vite`): for any app built with Vite. One plugin line;
17
+ in `vite dev` the page gets a wolf button that opens a chat with the agent working in
18
+ the project root, and the page's errors can be sent to the agent with one click.
19
+ Builds are untouched, nothing reaches production.
20
+ 2. **Dev panel without Vite** (`genaicode/ui`): the same chat for apps on webpack, Next.js,
21
+ Express, Fastify, Remix, Astro or any other Node dev server. The dev server process
22
+ starts the chat on 127.0.0.1 with `startEmbeddedWeb`, and a development-only component
23
+ on the page shows it in a frame. A few more lines than the plugin, same result.
24
+ 3. **In-app agent** (`genaicode/app-agent` on the server, `<AppAgentChat>` from
25
+ `genaicode/react` on the page): for a local app with its own Node server (an admin
26
+ console, an internal tool, an editor) whose users want the agent to act on the app's
27
+ data. The agent works the app through tools the app serves over MCP, every write waits
28
+ for the user's Approve, and an optional `dev` mode lets the agent edit the app's own
29
+ code and propose a commit.
30
+ 4. **Fallback**: no Node process to host it (a Rails, Django, Go or static-site project,
31
+ or a stack you cannot change safely): tell the user to run `npx genaicode ui` (browser)
32
+ or `npx genaicode` (terminal) in the project next to their dev server.
33
+
34
+ The agent CLI always runs on the machine that runs the server, with that machine's
35
+ login and files. Options 1 and 2 exist only in development. The in-app agent is for apps
36
+ that run on the developer's own machine (or a trusted internal host behind real auth).
37
+ Never wire any of them into a public production site, and never expose them without auth.
38
+
39
+ ## Steps
40
+
41
+ 1. **Look before you change anything.** Read `package.json` (dependencies, scripts,
42
+ `workspaces`), the lockfile, the build and dev server config (`vite.config.*`,
43
+ `next.config.*`, `webpack.config.*`...), the server entry if there is one, and the root
44
+ component of the UI. Find out what actually serves the app in development. In a
45
+ monorepo, work in the package that owns the app; ask if that is unclear.
46
+ 2. **Choose the integration** (the user's request may already say which):
47
+ - Vite serves the app in development (React, Vue, Svelte, Solid, Preact, vanilla, also
48
+ Vite in front of a backend, Remix/React Router/Astro/SvelteKit on Vite): option 1.
49
+ - Another Node dev server: option 2.
50
+ - The user wants the agent to use the app itself (its data, its actions), or the app
51
+ is an internal tool with a Node server and a React UI: option 3, alone or next to
52
+ option 1 or 2.
53
+ - Otherwise: option 4. Say why.
54
+ - If genaicode is already wired in, extend or fix what is there instead of adding a
55
+ second copy.
56
+ 3. **Install** `genaicode` as a dev dependency for options 1 and 2, as a regular
57
+ dependency when the server imports `genaicode/app-agent`. Use the project's package
58
+ manager (lockfile: `package-lock.json` npm, `pnpm-lock.yaml` pnpm, `yarn.lock` yarn,
59
+ `bun.lockb`/`bun.lock` bun). If you may not run the install, add the dependency to
60
+ `package.json` with the version from your task and tell the user to run it. genaicode
61
+ needs Node 20+ and declares no React peer: the app brings React 18+ for
62
+ `genaicode/react`.
63
+ 4. **Wire it in** (recipes below), matching the project's style: TypeScript or
64
+ JavaScript, ESM or CJS, its formatter, its folder layout.
65
+ 5. **Check it**: run the project's type check, lint and tests if it has them, and a
66
+ production build. For options 1 and 2 the production output must not contain
67
+ genaicode. Fix what you broke; do not "fix" unrelated failures. Remove build output
68
+ you created that the project does not track.
69
+ 6. **Report**: what you changed, file by file, and how to try it (the dev command, where
70
+ to click). Say that the agent uses its own CLI login and billing.
71
+
72
+ ## Recipe: Vite overlay
73
+
74
+ ```ts
75
+ // vite.config.ts
76
+ import { defineConfig } from 'vite';
77
+ import genaicode from 'genaicode/vite';
78
+
79
+ export default defineConfig({
80
+ plugins: [
81
+ // ...the project's plugins stay as they are
82
+ genaicode(),
83
+ ],
84
+ });
85
+ ```
86
+
87
+ - Add it to the existing `plugins` array; keep everything else. With a config function
88
+ (`defineConfig(({ mode }) => ...)`) it goes in the returned object the same way.
89
+ - A `vite.config.js` in a CommonJS package (no `"type": "module"`) still uses `import`:
90
+ Vite bundles the config. genaicode is ESM only (`require('genaicode/vite')` fails), so a
91
+ `vite.config.cjs` loads it with a dynamic import from an async config:
92
+ `module.exports = async () => ({ plugins: [react(), (await import('genaicode/vite')).default()] })`,
93
+ keeping the rest of the existing config in that object.
94
+ - Options, all optional: `agent` (e.g. `'claude'`, `'codex'`; default: first installed),
95
+ `model`, `effort`, `approveAll` (skip the agent's permission prompts; only if asked),
96
+ `port`, `captureErrors: false`. Leave the defaults unless the user asked.
97
+ - The chat runs on its own 127.0.0.1 port, only the app's own pages can frame it, and it
98
+ takes requests only from the same machine. No env vars or secrets are needed.
99
+
100
+ ## Recipe: dev panel without Vite
101
+
102
+ Server side, in the Node process that serves the app in development, and only there:
103
+
104
+ ```ts
105
+ import type { IncomingMessage, ServerResponse } from 'node:http';
106
+
107
+ // Development only: production never imports genaicode.
108
+ // The origins the app was opened at; the chat lets only these frame it, read per request.
109
+ const origins = new Set<string>();
110
+ let panel: Promise<{ url: string }> | undefined;
111
+ export function genaicodePanel() {
112
+ panel ??= import('genaicode/ui').then(({ startEmbeddedWeb }) =>
113
+ startEmbeddedWeb({ cwd: process.cwd(), frameAncestors: () => [...origins] }),
114
+ );
115
+ return panel;
116
+ }
117
+
118
+ // GET /__genaicode/session -> { url }. The url carries the chat's access token: hand it only
119
+ // to the app's own pages on this machine.
120
+ export async function genaicodeSession(req: IncomingMessage, res: ServerResponse) {
121
+ const address = req.socket.remoteAddress ?? '';
122
+ const local = address === '::1' || address.startsWith('127.') || address.startsWith('::ffff:127.');
123
+ const host = req.headers.host ?? '';
124
+ const loopbackHost = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(host);
125
+ if (!local || !loopbackHost || req.headers['sec-fetch-site'] !== 'same-origin') {
126
+ res.writeHead(403).end();
127
+ return;
128
+ }
129
+ origins.add(`http://${host}`); // localhost and 127.0.0.1 can both frame it, without a restart
130
+ const { url } = await genaicodePanel();
131
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
132
+ res.end(JSON.stringify({ url }));
133
+ }
134
+ ```
135
+
136
+ - `frameAncestors` is read per request and must list every origin the app is opened at in
137
+ development (`http://localhost:3000`, `http://127.0.0.1:3000`...); the chat refuses other
138
+ frames. Collect them as above rather than capturing the first one. Behind a dev host name
139
+ (`app.localhost`, https), allow that name explicitly.
140
+ `startEmbeddedWeb` binds to 127.0.0.1 on a free port (`port` to fix one) and takes
141
+ `agent`, `model`, `effort`, `approveAll` like the Vite plugin.
142
+ - Start it once per process. Dev servers that reload server code (Next.js, nodemon...)
143
+ re-run modules: keep the promise on `globalThis` so a reload does not start a second chat.
144
+ - Where to hook the route:
145
+ - Express / Connect / node:http dev server: before the app's routes, behind
146
+ `process.env.NODE_ENV !== 'production'`.
147
+ - webpack-dev-server: `devServer.setupMiddlewares(middlewares, devServer)`, push a
148
+ middleware for `/__genaicode/session`.
149
+ - Next.js: a route handler such as `app/api/genaicode/route.ts` (folders starting with
150
+ `_` are private in the app router) that returns 404 unless
151
+ `process.env.NODE_ENV === 'development'`. It has no socket address, so check that the
152
+ `Host` header is `localhost` / `127.0.0.1` / `[::1]` with a port and that
153
+ `sec-fetch-site` is `same-origin`. Start the chat from the route on first use (Node
154
+ runtime: `export const runtime = 'nodejs'`), cached on `globalThis`.
155
+ - Fastify: a route under `if (dev)`; Koa: a middleware under `if (dev)`.
156
+
157
+ Page side, a component rendered only in development (`process.env.NODE_ENV === 'development'`,
158
+ `import.meta.env.DEV`...), loaded so the production bundle drops it (a dynamic `import()`
159
+ behind that check, or the framework's dev-only slot):
160
+
161
+ ```tsx
162
+ import { useEffect, useState } from 'react';
163
+
164
+ export function GenaicodePanel() {
165
+ const [url, setUrl] = useState<string>();
166
+ const [open, setOpen] = useState(false);
167
+ useEffect(() => {
168
+ fetch('/__genaicode/session')
169
+ .then((r) => (r.ok ? r.json() : undefined))
170
+ .then((s) => setUrl(s?.url));
171
+ }, []);
172
+ if (!url) return null;
173
+ return (
174
+ <>
175
+ <button onClick={() => setOpen(!open)} style={{ position: 'fixed', right: 16, bottom: 16, zIndex: 2147483647 }}>
176
+ Agent
177
+ </button>
178
+ {open && (
179
+ <iframe
180
+ src={url}
181
+ title="genaicode"
182
+ style={{
183
+ position: 'fixed',
184
+ right: 16,
185
+ bottom: 64,
186
+ width: 420,
187
+ height: '70vh',
188
+ zIndex: 2147483647,
189
+ border: 0,
190
+ borderRadius: 12,
191
+ boxShadow: '0 8px 32px #0004',
192
+ }}
193
+ />
194
+ )}
195
+ </>
196
+ );
197
+ }
198
+ ```
199
+
200
+ Not React? The same in the app's own framework or plain DOM: fetch the session, then a
201
+ fixed button that toggles a fixed iframe. Keep the token URL out of logs, `localStorage`
202
+ and anything the page sends elsewhere.
203
+
204
+ ## Recipe: in-app agent
205
+
206
+ Server side, wherever the Node server is created:
207
+
208
+ ```ts
209
+ import { createAppAgent, createAppAgentHandler } from 'genaicode/app-agent';
210
+
211
+ const agent = createAppAgent({
212
+ name: 'the shop admin', // how the agent calls the app
213
+ mcpUrl: () => `http://127.0.0.1:${port}/api/agent/mcp`, // this server, reachable from this machine
214
+ tools: [
215
+ { name: 'orders', description: 'List open orders.', run: () => JSON.stringify(listOrders()) },
216
+ {
217
+ name: 'refund',
218
+ description: 'Refund an order.',
219
+ write: true, // shows Approve / Reject; runs only after Approve
220
+ inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
221
+ run: ({ id }) => refund(String(id)),
222
+ },
223
+ ],
224
+ pages: () => ['/', '/orders'], // optional: page context and "take me there" chips
225
+ dev: { cwd: process.cwd(), commands: ['npm test'] }, // optional: agent may edit the app's code
226
+ });
227
+ const handleAgent = createAppAgentHandler(agent, {
228
+ // Required as soon as the server listens beyond loopback: reuse the app's own auth check.
229
+ // authorize: (req) => isAdmin(req),
230
+ });
231
+ ```
232
+
233
+ Mount `handleAgent(req, res)` before the app's own routes; it returns `true` when it
234
+ answered (everything under `/api/agent`, or `basePath`):
235
+
236
+ - node:http: `if (await handleAgent(req, res)) return;` at the top of the request handler.
237
+ - Express / Connect: `app.use((req, res, next) => handleAgent(req, res).then((done) => done || next(), next));`
238
+ (a JSON body parser before it is fine: it reads `req.body` when one is there).
239
+ - Fastify: an `onRequest` hook that calls `reply.hijack()` and `handleAgent(request.raw, reply.raw)`
240
+ for URLs under the base path.
241
+ - Vite dev server with no backend: a small plugin whose `configureServer(server)` adds the
242
+ same middleware to `server.middlewares`; build `mcpUrl` from the server's real port
243
+ (`server.httpServer.address()`), not the configured one.
244
+ - Next.js pages router: `pages/api/agent/[...path].ts` with
245
+ `export const config = { api: { bodyParser: false } }` calling `handleAgent(req, res)`.
246
+ The app router has no Node `req`/`res`: use a custom server or the pages route.
247
+ - Call `handleAgent.close()` when the server shuts down.
248
+
249
+ Tools are the point of the in-app agent: expose a few read tools over the app's existing
250
+ data or services and, only where it makes sense, write tools with `write: true`. Reuse the
251
+ app's own functions; never hand the agent raw database or shell access through a tool. If
252
+ the app has nothing worth exposing yet, start with `pages` and one read tool, and tell the
253
+ user where to add more.
254
+
255
+ Page side, in the React tree (React 18+):
256
+
257
+ ```tsx
258
+ import { AppAgentChat } from 'genaicode/react';
259
+ import 'genaicode/react/styles.css';
260
+
261
+ // Inside a fixed-height container: a drawer, a side panel, a route of its own.
262
+ <div style={{ position: 'fixed', right: 0, top: 0, bottom: 0, width: 420 }}>
263
+ <AppAgentChat currentPath={location.pathname} onNavigate={(path) => navigate(path)} />
264
+ </div>;
265
+ ```
266
+
267
+ - Use the app's router for `currentPath` and `onNavigate` (React Router `useLocation` /
268
+ `useNavigate`, Next `useRouter`...). Without a router, `location.pathname` and
269
+ `location.assign`.
270
+ - Put the drawer behind a toggle button that matches the app's UI, closed by default.
271
+ - Theme with `--gc-*` CSS custom properties (`--gc-bg`, `--gc-text`, `--gc-accent`,
272
+ `--gc-font`...) or `theme="light" | "dark"`, to match the app.
273
+ - Not React? Use the HTTP routes directly: `GET /api/agent/events` (server-sent
274
+ `AppAgentState`), `POST /api/agent/send {text}`, `/stop`, `/reset`, `/actions/:id`.
275
+
276
+ ## Rules
277
+
278
+ - Keep changes small and in the project's own style. Do not restructure the app, upgrade
279
+ unrelated dependencies, or reformat files you did not need to touch.
280
+ - Do not commit, push, or create branches unless the user asks.
281
+ - Do not add API keys, tokens or env vars: the agent CLI brings its own login.
282
+ - Do not turn on `approveAll` or the agents' "yolo" modes unless the user asks.
283
+ - These recipes are starting points, not templates to paste: adapt them to what the
284
+ project already does. If something does not fit, say what and why instead of forcing it.
285
+ - Docs: https://github.com/gtanczyk/genaicode#readme