@guuey/create-agentic-app 0.1.1 → 0.1.3

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 (37) hide show
  1. package/dist/cli.cjs +1 -1
  2. package/dist/cli.js +1 -1
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/templates/claude-agent-sdk/package.json +3 -3
  6. package/dist/templates/claude-agent-sdk/web/package.json +1 -1
  7. package/dist/templates/google-adk/.env.example +4 -0
  8. package/dist/templates/google-adk/.mcp.json +1 -0
  9. package/dist/templates/google-adk/README.md +74 -0
  10. package/dist/templates/google-adk/ggui/blueprints/.gitkeep +0 -0
  11. package/dist/templates/google-adk/ggui/ggui.json +15 -0
  12. package/dist/templates/google-adk/ggui/themes/.gitkeep +0 -0
  13. package/dist/templates/google-adk/guuey.json +14 -0
  14. package/dist/templates/google-adk/mcps/todo/Dockerfile +46 -0
  15. package/dist/templates/google-adk/mcps/todo/package.json +22 -0
  16. package/dist/templates/google-adk/mcps/todo/src/server.ts +173 -0
  17. package/dist/templates/google-adk/mcps/todo/tsconfig.json +15 -0
  18. package/dist/templates/google-adk/package.json +22 -0
  19. package/dist/templates/google-adk/pnpm-workspace.yaml +34 -0
  20. package/dist/templates/google-adk/prompts/system.md +4 -0
  21. package/dist/templates/google-adk/scripts/dev.mjs +44 -0
  22. package/dist/templates/google-adk/src/agent.ts +39 -0
  23. package/dist/templates/google-adk/tsconfig.json +15 -0
  24. package/dist/templates/google-adk/tsup.config.ts +13 -0
  25. package/dist/templates/google-adk/web/.env.example +17 -0
  26. package/dist/templates/google-adk/web/index.html +13 -0
  27. package/dist/templates/google-adk/web/package.json +25 -0
  28. package/dist/templates/google-adk/web/sandbox-proxy.ts +239 -0
  29. package/dist/templates/google-adk/web/src/App.tsx +272 -0
  30. package/dist/templates/google-adk/web/src/main.tsx +12 -0
  31. package/dist/templates/google-adk/web/src/useAgentChat.ts +122 -0
  32. package/dist/templates/google-adk/web/src/vite-env.d.ts +27 -0
  33. package/dist/templates/google-adk/web/tsconfig.json +20 -0
  34. package/dist/templates/google-adk/web/vite.config.ts +12 -0
  35. package/dist/templates/openai-agents-sdk/package.json +3 -3
  36. package/dist/templates/openai-agents-sdk/web/package.json +1 -1
  37. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -171,7 +171,7 @@ var packageRoot2 = (0, import_node_path2.join)(here2, "..");
171
171
  var defaultTemplatesDir2 = (0, import_node_path2.join)(packageRoot2, "dist", "templates");
172
172
 
173
173
  // src/cli.ts
174
- var FRAMEWORKS = ["claude-agent-sdk", "openai-agents-sdk"];
174
+ var FRAMEWORKS = ["claude-agent-sdk", "openai-agents-sdk", "google-adk"];
175
175
  function isFramework(value) {
176
176
  return FRAMEWORKS.includes(value);
177
177
  }
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  // src/cli.ts
7
7
  import { createInterface } from "readline/promises";
8
8
  import { stdin, stdout } from "process";
9
- var FRAMEWORKS = ["claude-agent-sdk", "openai-agents-sdk"];
9
+ var FRAMEWORKS = ["claude-agent-sdk", "openai-agents-sdk", "google-adk"];
10
10
  function isFramework(value) {
11
11
  return FRAMEWORKS.includes(value);
12
12
  }
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Frameworks the scaffolder ships templates for. */
2
- type Framework = 'claude-agent-sdk' | 'openai-agents-sdk';
2
+ type Framework = 'claude-agent-sdk' | 'openai-agents-sdk' | 'google-adk';
3
3
  interface ScaffoldOptions {
4
4
  /** Absolute or cwd-relative path to create/populate the new project in. */
5
5
  targetDir: string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Frameworks the scaffolder ships templates for. */
2
- type Framework = 'claude-agent-sdk' | 'openai-agents-sdk';
2
+ type Framework = 'claude-agent-sdk' | 'openai-agents-sdk' | 'google-adk';
3
3
  interface ScaffoldOptions {
4
4
  /** Absolute or cwd-relative path to create/populate the new project in. */
5
5
  targetDir: string;
@@ -10,11 +10,11 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@anthropic-ai/claude-agent-sdk": "^0.3.199",
13
- "@guuey/config": "0.0.1",
14
- "@guuey/worker": "0.0.1"
13
+ "@guuey/config": "0.1.1",
14
+ "@guuey/worker": "0.1.1"
15
15
  },
16
16
  "devDependencies": {
17
- "@guuey/cli": "0.0.1",
17
+ "@guuey/cli": "0.1.3",
18
18
  "tsup": "^8.5.0",
19
19
  "tsx": "^4.19.0",
20
20
  "typescript": "^5.8.0"
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@mcp-ui/client": "^7.1.1",
13
- "@silverprotocol/core": "0.1.0",
13
+ "@silverprotocol/core": "0.3.2",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0"
16
16
  },
@@ -0,0 +1,4 @@
1
+ # LLM key for local dev — the deployed agent uses guuey's managed broker instead.
2
+ ANTHROPIC_API_KEY=
3
+ # Only needed for the openai-agents-sdk template:
4
+ OPENAI_API_KEY=
@@ -0,0 +1 @@
1
+ { "mcpServers": { "ggui-dev": { "type": "http", "url": "https://mcp.ggui.ai/dev" } } }
@@ -0,0 +1,74 @@
1
+ # Your Google-ADK agent on Guuey
2
+
3
+ You write **pure [Google ADK](https://google.github.io/adk-docs/) code** —
4
+ `src/agent.ts` exports your agent, and Guuey runs it. There is no Guuey SDK
5
+ to learn, no worker loop, no event plumbing: the platform injects the
6
+ harness around your export, locally (`pnpm dev`) and in production
7
+ (`guuey deploy`) identically.
8
+
9
+ ```ts
10
+ import type { GuueyContext } from "@guuey/config";
11
+
12
+ export default (guuey: GuueyContext<MCPToolset>) =>
13
+ new LlmAgent({
14
+ model: guuey.model,
15
+ instruction: guuey.instruction,
16
+ tools: [myTool, ...guuey.mcpToolsets],
17
+ });
18
+ ```
19
+
20
+ ## Quick start
21
+
22
+ ```bash
23
+ pnpm install
24
+ cp .env.example .env.local # set GEMINI_API_KEY
25
+ pnpm dev # local stack: your agent + the todo MCP + chat
26
+ guuey deploy # same code, hosted
27
+ ```
28
+
29
+ ## The GuueyContext (everything the platform hands you)
30
+
31
+ Your factory runs **once per turn** and receives:
32
+
33
+ | field | what it is |
34
+ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
35
+ | `model` | resolved model id (from `guuey.json`, registry default otherwise) |
36
+ | `instruction` | your system prompt **with the conversation preamble already prepended** — feed it to the agent and it is conversational with zero state code |
37
+ | `mcpToolsets` | ready-to-use ADK `MCPToolset`s for every server in `guuey.json#mcpServers` — credentials, URLs, and auth headers already resolved by the platform |
38
+ | `user` | the end user this turn serves: `{ id, authMode }` — build multi-tenant behavior on `user.id` |
39
+ | `files` | three storage tiers (absolute paths — see the state map below) |
40
+ | `history` / `memory` / `workingState` | the raw conversation state, if you want to render context yourself instead of using `instruction` |
41
+
42
+ ## Where state lives (the three-tier map)
43
+
44
+ | you want to… | use | persistence |
45
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | save/read **files** from agent code or tools | `guuey.files.home` (per-USER durable) · `guuey.files.session` (per-session scratch) · `guuey.files.app` (read-only app assets) | `home` survives across sessions per user |
47
+ | have the agent **remember the conversation** | nothing — `instruction` already carries history + thread memory + working state | automatic (Guuey folds every turn) |
48
+ | store data from your **MCP server's tools** (e.g. the todo list) | `@guuey/state` KV inside the MCP server — scoped per `(user, server)` | **in-memory in the current release** (durable managed KV + console export/delete is on the roadmap) — persist anything you can't lose to `guuey.files.home` for now |
49
+
50
+ Two honest notes:
51
+
52
+ - **ADK-native session state does not persist across turns here.** Guuey
53
+ runs your agent with a fresh `InMemoryRunner` per turn (that's what makes
54
+ turns cheap and stateless); `session.state` / `outputKey` /
55
+ `DatabaseSessionService` won't carry data forward. Use the table above —
56
+ history and thread memory ARE the persistence, and they arrive
57
+ pre-rendered in `instruction`.
58
+ - **Writing `workingState` for the next turn** is a platform feature in
59
+ flight; today the fold carries what the conversation itself establishes.
60
+
61
+ ## MCP servers
62
+
63
+ `guuey.json#mcpServers` declares them; the platform connects them and hands
64
+ you `guuey.mcpToolsets`. Locally, `pnpm dev` boots the colocated `mcps/todo`
65
+ server on its `devPort`. Note: ADK speaks **Streamable HTTP** only — an
66
+ `sse` transport server will be rejected with a clear error.
67
+
68
+ ## When you outgrow the factory
69
+
70
+ If you need to own the turn loop itself (custom streaming, multiple agents
71
+ per turn, your own protocol), graduate to a full worker: add a
72
+ `guuey.worker.js` build via `@guuey/worker`'s `serveNative()` — see the
73
+ Guuey docs. `agent.entry` and a full worker are mutually exclusive; the
74
+ worker wins if both exist.
@@ -0,0 +1,15 @@
1
+ {
2
+ "schema": "1",
3
+ "protocol": "draft-2026-06-12",
4
+ "app": {
5
+ "slug": "agentic-app-template",
6
+ "name": "agentic-app-template"
7
+ },
8
+ "generation": {
9
+ "model": "anthropic:claude-haiku-4-5-20251001"
10
+ },
11
+ "theme": {
12
+ "preset": "indigo",
13
+ "mode": "dark"
14
+ }
15
+ }
File without changes
@@ -0,0 +1,14 @@
1
+ {
2
+ "schema": "1",
3
+ "agent": {
4
+ "mode": "code",
5
+ "framework": "google-adk",
6
+ "model": "gemini-3.5-flash",
7
+ "entry": "agent.js",
8
+ "systemPrompt": { "file": "prompts/system.md" },
9
+ "mcpServers": {
10
+ "todo": { "kind": "hosted", "source": "./mcps/todo", "devPort": 6782 }
11
+ }
12
+ },
13
+ "ggui": { "configFile": "./ggui/ggui.json" }
14
+ }
@@ -0,0 +1,46 @@
1
+ # todo-mcp — guuey hosted MCP server image.
2
+ #
3
+ # Self-sufficient multi-stage build: `guuey mcp deploy` packs this directory
4
+ # into a tarball and hands it straight to the builder with no external
5
+ # pre-build step and no build args — this Dockerfile installs its own deps
6
+ # and builds itself. (Different from guuey's own first-party MCP servers,
7
+ # which use a "copy-prebuilt" pattern that expects `dist/` + `node_modules/`
8
+ # to already exist before `docker build` runs — that pattern does not apply
9
+ # to a standalone project like this one.)
10
+ #
11
+ # Build + run locally:
12
+ #
13
+ # docker build -t todo-mcp .
14
+ # docker run -p 8080:8080 todo-mcp
15
+ # curl http://localhost:8080/health
16
+ #
17
+ # Deploy to guuey:
18
+ #
19
+ # cd mcps/todo && guuey mcp deploy
20
+
21
+ FROM node:22-slim AS build
22
+ WORKDIR /app
23
+ COPY package.json ./
24
+ RUN npm install
25
+ COPY tsconfig.json ./
26
+ COPY src ./src
27
+ RUN npm run build
28
+
29
+ FROM node:22-slim
30
+ WORKDIR /app
31
+ ENV NODE_ENV=production
32
+ # guuey's hosted-MCP runtime injects PORT=8080 and probes GET /health at that
33
+ # port — this default matches so `docker run` alone also works.
34
+ ENV PORT=8080
35
+
36
+ COPY package.json ./
37
+ RUN npm install --omit=dev
38
+ COPY --from=build /app/dist ./dist
39
+
40
+ USER node
41
+ EXPOSE 8080
42
+
43
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
44
+ CMD node -e "fetch('http://localhost:' + (process.env.PORT || 8080) + '/health').then(r => { if (!r.ok) throw 1 }).catch(() => process.exit(1))"
45
+
46
+ CMD ["node", "dist/server.js"]
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@agentic-app-template/todo-mcp",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "tsx src/server.ts",
8
+ "build": "tsup src/server.ts --format esm -d dist",
9
+ "start": "node dist/server.js",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.27.0",
14
+ "zod": "^4.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.0.0",
18
+ "tsx": "^4.19.0",
19
+ "tsup": "^8.5.0",
20
+ "typescript": "^5.8.0"
21
+ }
22
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * todo-mcp — the copy-me example MCP server for @agentic-app-template.
3
+ *
4
+ * A minimal, stateless Streamable HTTP MCP server: four tools over an
5
+ * in-memory todo list. Swap the `Map` for a real database when you outgrow
6
+ * a single replica; everything else (transport wiring, tool shape) stays
7
+ * the same.
8
+ *
9
+ * Transport pattern mirrors guuey's canonical hosted-MCP scaffold
10
+ * (`mcp-servers/_template` in the guuey monorepo): a fresh `McpServer` +
11
+ * `StreamableHTTPServerTransport` are created **per request** and torn down
12
+ * when the response closes. Stateless mode — the guuey proxy (and, locally,
13
+ * whatever MCP client you point at this server) handles sessions, so this
14
+ * server never keeps request state around between calls.
15
+ *
16
+ * Runtime contract when deployed via `guuey mcp deploy` (see mcps/todo's
17
+ * Dockerfile): the platform injects `PORT=8080` and probes `GET /health`
18
+ * for both liveness and readiness. Locally, `pnpm dev` runs this on
19
+ * `PORT=6782` (see ../../scripts/dev.mjs).
20
+ */
21
+ import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
22
+ import { randomUUID } from "node:crypto";
23
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
25
+ import { z } from "zod";
26
+
27
+ interface Todo {
28
+ id: string;
29
+ title: string;
30
+ done: boolean;
31
+ }
32
+
33
+ const todos = new Map<string, Todo>();
34
+
35
+ const todoShape = {
36
+ id: z.string(),
37
+ title: z.string(),
38
+ done: z.boolean(),
39
+ };
40
+
41
+ /** Wrap a structured result as both `structuredContent` and a text block. */
42
+ function toolResult(data: Record<string, unknown>) {
43
+ return {
44
+ structuredContent: data,
45
+ content: [{ type: "text" as const, text: JSON.stringify(data) }],
46
+ };
47
+ }
48
+
49
+ function buildMcpServer(): McpServer {
50
+ const server = new McpServer({
51
+ name: "todo-mcp",
52
+ version: "0.0.0",
53
+ description: "Create, list, toggle, and delete todos.",
54
+ });
55
+
56
+ server.registerTool(
57
+ "todo_list",
58
+ {
59
+ title: "List todos",
60
+ description: "List every todo, in creation order.",
61
+ inputSchema: {},
62
+ outputSchema: { todos: z.array(z.object(todoShape)) },
63
+ },
64
+ async () => toolResult({ todos: [...todos.values()] }),
65
+ );
66
+
67
+ server.registerTool(
68
+ "todo_create",
69
+ {
70
+ title: "Create todo",
71
+ description: "Create a new todo item.",
72
+ inputSchema: { title: z.string().min(1).describe("Todo title.") },
73
+ outputSchema: todoShape,
74
+ },
75
+ async ({ title }) => {
76
+ const todo: Todo = { id: randomUUID(), title, done: false };
77
+ todos.set(todo.id, todo);
78
+ // Spread into a fresh object literal — `toolResult` takes
79
+ // `Record<string, unknown>`, and a named interface like `Todo` isn't
80
+ // structurally assignable to that without an (unwanted) index
81
+ // signature of its own; a fresh literal is.
82
+ return toolResult({ ...todo });
83
+ },
84
+ );
85
+
86
+ server.registerTool(
87
+ "todo_toggle",
88
+ {
89
+ title: "Toggle todo",
90
+ description: "Flip a todo's done state.",
91
+ inputSchema: { id: z.string().describe("Todo id.") },
92
+ outputSchema: todoShape,
93
+ },
94
+ async ({ id }) => {
95
+ const todo = todos.get(id);
96
+ if (!todo) throw new Error(`todo not found: ${id}`);
97
+ todo.done = !todo.done;
98
+ return toolResult({ ...todo }); // fresh literal — see the create() comment above
99
+ },
100
+ );
101
+
102
+ server.registerTool(
103
+ "todo_delete",
104
+ {
105
+ title: "Delete todo",
106
+ description: "Delete a todo by id.",
107
+ inputSchema: { id: z.string().describe("Todo id.") },
108
+ outputSchema: { id: z.string(), deleted: z.boolean() },
109
+ },
110
+ async ({ id }) => {
111
+ const deleted = todos.delete(id);
112
+ return toolResult({ id, deleted });
113
+ },
114
+ );
115
+
116
+ return server;
117
+ }
118
+
119
+ const PORT = Number(process.env.PORT ?? 6782);
120
+
121
+ const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {
122
+ if (req.method === "GET" && req.url === "/health") {
123
+ res.writeHead(200, { "content-type": "application/json" });
124
+ res.end(JSON.stringify({ status: "ok", server: "todo-mcp", version: "0.0.0" }));
125
+ return;
126
+ }
127
+
128
+ if (req.url !== "/mcp") {
129
+ res.writeHead(404, { "content-type": "application/json" }).end(
130
+ JSON.stringify({ error: "not found" }),
131
+ );
132
+ return;
133
+ }
134
+
135
+ // Fresh server + transport per request (stateless mode) — see module doc.
136
+ const mcp = buildMcpServer();
137
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
138
+
139
+ res.on("close", () => {
140
+ transport.close().catch(() => undefined);
141
+ mcp.close().catch(() => undefined);
142
+ });
143
+
144
+ try {
145
+ await mcp.connect(transport);
146
+ await transport.handleRequest(req, res);
147
+ } catch (err) {
148
+ console.error("mcp_handle_failed", err);
149
+ if (!res.headersSent) {
150
+ res.writeHead(500, { "content-type": "application/json" });
151
+ res.end(
152
+ JSON.stringify({
153
+ jsonrpc: "2.0",
154
+ error: { code: -32603, message: "Internal server error" },
155
+ id: null,
156
+ }),
157
+ );
158
+ }
159
+ }
160
+ });
161
+
162
+ httpServer.listen(PORT, () => {
163
+ console.log(`todo-mcp listening on :${PORT}`);
164
+ });
165
+
166
+ function shutdown(signal: string): void {
167
+ console.log(`${signal} received — shutting down`);
168
+ httpServer.close(() => process.exit(0));
169
+ // Force-exit if connections don't drain in time.
170
+ setTimeout(() => process.exit(1), 10_000).unref();
171
+ }
172
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
173
+ process.on("SIGINT", () => shutdown("SIGINT"));
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "resolveJsonModule": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist"]
15
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "agentic-app-template",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "node scripts/dev.mjs",
8
+ "build": "tsup",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@google/adk": "^1.3.0",
13
+ "zod": "^4.2.1"
14
+ },
15
+ "devDependencies": {
16
+ "@guuey/cli": "0.1.3",
17
+ "tsup": "^8.5.0",
18
+ "tsx": "^4.19.0",
19
+ "typescript": "^5.8.0",
20
+ "@guuey/config": "0.1.1"
21
+ }
22
+ }
@@ -0,0 +1,34 @@
1
+ packages:
2
+ - "mcps/*"
3
+ - "web"
4
+
5
+ # pnpm 11 default `strictDepBuilds: true` blocks install scripts for any
6
+ # dependency with a build script (esbuild's native binary fetch, sharp,
7
+ # etc.) unless individually allowlisted, and fails the whole `install`
8
+ # with ERR_PNPM_IGNORED_BUILDS otherwise. Scaffolded apps pull in build-
9
+ # script deps transitively (tsup/vite -> esbuild, mcp/ui deps, etc.) that
10
+ # we can't enumerate up front, so match pnpm 9/10 behavior here.
11
+ strictDepBuilds: false
12
+
13
+ # pnpm 11 default `verifyDepsBeforeRun: 'install'` re-verifies the
14
+ # lockfile against supply-chain policies (including minimumReleaseAge)
15
+ # before every `pnpm run <script>`, hitting the registry even for
16
+ # already-installed deps. That check 404s for any dependency that only
17
+ # exists as a local tarball (not yet published), which is expected
18
+ # during local/dev/CI installs. Restore pnpm 9/10 behavior: only verify
19
+ # on an explicit `pnpm install`.
20
+ verifyDepsBeforeRun: false
21
+
22
+ # See above — same registry-lookback problem hits a plain `pnpm install`
23
+ # too when minimumReleaseAge is nonzero; disable it for scaffolded apps.
24
+ minimumReleaseAge: 0
25
+
26
+ # @google/adk declares five NON-optional @mikro-orm/* database-driver peer
27
+ # dependencies (mariadb/mssql/mysql/postgresql/sqlite). Auto-installing
28
+ # them drags in sqlite3 — a NATIVE module whose install script needs a
29
+ # GitHub prebuild download or a full node-gyp toolchain, neither of which
30
+ # exists in the platform's image build. None of the drivers is needed:
31
+ # they back DatabaseSessionService, and Guuey runs the ADK with
32
+ # InMemoryRunner (conversation state rides Guuey's own history/memory
33
+ # fold). The unmet-peer warnings this produces are expected and harmless.
34
+ autoInstallPeers: false
@@ -0,0 +1,4 @@
1
+ You are the agentic-app-template assistant. You have a todo tool server —
2
+ use it to create, list, toggle, and delete the user's todos. When the ggui
3
+ server is available, prefer rendering rich UI for lists and forms over
4
+ plain text.
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ // pnpm dev — boots the whole local stack. Ctrl-C tears everything down.
3
+ import { spawn } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+
6
+ const procs = [];
7
+ function boot(name, command, args, opts = {}) {
8
+ const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"], ...opts });
9
+ const prefix = `[${name}]`.padEnd(9);
10
+ child.stdout.on("data", (d) => process.stdout.write(String(d).replace(/^/gm, prefix)));
11
+ child.stderr.on("data", (d) => process.stderr.write(String(d).replace(/^/gm, prefix)));
12
+ child.on("exit", (code) => {
13
+ if (code !== 0 && !shuttingDown) shutdown(`${name} exited (${code})`, 1);
14
+ });
15
+ procs.push(child);
16
+ }
17
+ let shuttingDown = false;
18
+ function shutdown(reason, code = 0) {
19
+ if (shuttingDown) return;
20
+ shuttingDown = true;
21
+ console.log(`\n${reason} — shutting down`);
22
+ for (const p of procs) p.kill("SIGTERM");
23
+ setTimeout(() => process.exit(code), 500);
24
+ }
25
+ process.on("SIGINT", () => shutdown("interrupted"));
26
+ process.on("SIGTERM", () => shutdown("terminated"));
27
+
28
+ if (!existsSync(".env.local"))
29
+ console.warn("hint: cp .env.example .env.local and set your LLM key");
30
+
31
+ boot("worker", "pnpm", ["exec", "tsup", "--watch"]); // rebuilds guuey.worker.js on change
32
+ boot("agent", "pnpm", ["exec", "guuey", "dev", "--serve", "--port", "6790"]);
33
+ boot("todo", "pnpm", ["--filter", "@agentic-app-template/todo-mcp", "dev"], {
34
+ env: { ...process.env, PORT: "6782" },
35
+ });
36
+ boot("ggui", "pnpm", ["exec", "ggui", "serve", "--mcp-only", "--dev-allow-all", "--port", "6781"], {
37
+ cwd: "ggui",
38
+ });
39
+ boot("web", "pnpm", ["--filter", "@agentic-app-template/web", "dev"], {
40
+ env: { ...process.env, PORT: "6890" },
41
+ });
42
+
43
+ console.log("\n agent http://localhost:6790 todo-mcp http://localhost:6782");
44
+ console.log(" ggui http://localhost:6781 web http://localhost:6890\n");
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Your agent — pure Google-ADK code. No Guuey SDK, no harness, no event
3
+ * plumbing: export it and the platform runs it (locally via `pnpm dev`,
4
+ * hosted via `guuey deploy`).
5
+ *
6
+ * The default export is a FACTORY receiving everything Guuey resolved for
7
+ * the current turn (`GuueyContext`): the model + instruction from
8
+ * guuey.json, ready-to-use MCP toolsets for every server declared there,
9
+ * the end user's identity, file storage paths, and the conversation state.
10
+ * See the README for the full context tour.
11
+ */
12
+ import { FunctionTool, LlmAgent, type MCPToolset } from "@google/adk";
13
+ import { z } from "zod";
14
+ import type { GuueyContext } from "@guuey/config";
15
+
16
+ /** A plain ADK tool — everything the ADK offers works unchanged. */
17
+ const rollDice = new FunctionTool({
18
+ name: "roll_dice",
19
+ description: "Roll an N-sided die and return the result.",
20
+ parameters: z.object({
21
+ sides: z.number().int().min(2).describe("number of faces on the die"),
22
+ }),
23
+ execute: async ({ sides }) => ({
24
+ result: 1 + Math.floor(Math.random() * sides),
25
+ }),
26
+ });
27
+
28
+ export default (guuey: GuueyContext<MCPToolset>) =>
29
+ new LlmAgent({
30
+ name: "my_agent",
31
+ model: guuey.model,
32
+ // `instruction` already carries your system prompt PLUS the conversation
33
+ // preamble (history, thread memory, working state) — your agent is
34
+ // conversational without writing any state code.
35
+ instruction: guuey.instruction,
36
+ // Your own tools compose with the MCP servers from guuey.json. Drop
37
+ // `...guuey.mcpToolsets` if you want a fully self-contained agent.
38
+ tools: [rollDice, ...guuey.mcpToolsets],
39
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "resolveJsonModule": true,
11
+ "isolatedModules": true
12
+ },
13
+ "include": ["src"],
14
+ "exclude": ["node_modules", "dist"]
15
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: { agent: "src/agent.ts" },
5
+ format: ["esm"],
6
+ outDir: ".",
7
+ clean: false,
8
+ // Dependencies stay EXTERNAL (tsup's default): the platform installs them
9
+ // from the lockfile at image-build time and the Guuey host imports your
10
+ // agent.js — with @google/adk resolved from YOUR node_modules (your pinned
11
+ // version wins). Do NOT bundle @google/adk; its dependency tree is not
12
+ // bundler-safe.
13
+ });
@@ -0,0 +1,17 @@
1
+ # Vite env for the web chat client. Copy to `web/.env.local` and UNCOMMENT
2
+ # the vars you want to override. (Vite reads env files from this directory —
3
+ # not the project root. Keep unused vars commented out: an empty
4
+ # `VITE_X=` declaration loads as "" — not undefined.)
5
+
6
+ # Agent base URL. Defaults to the local `guuey dev` router (:6790).
7
+ # VITE_AGENT_ENDPOINT_URL=http://localhost:6790
8
+
9
+ # Second-origin MCP-Apps sandbox page for @mcp-ui/client's <AppRenderer>.
10
+ # The MCP Apps spec requires UI resources to mount from a DIFFERENT origin
11
+ # than the app itself (double-iframe sandbox). In local dev this is served
12
+ # automatically on http://127.0.0.1:6891/sandbox.html by the
13
+ # sandboxProxyPlugin in vite.config.ts — leave unset. For a DEPLOYED build,
14
+ # host ../sandbox-proxy.ts's sandbox page on its own origin and point this
15
+ # at it; if unset in production, UI resources show a plain-text notice
16
+ # instead of rendering.
17
+ # VITE_SANDBOX_URL=https://sandbox.example.com/sandbox.html
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>agentic-app-template</title>
7
+ <meta name="description" content="Local dev chat client for your guuey agent." />
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>