@guuey/create-agentic-app 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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/dist/chunk-JD4RAKEG.js +203 -0
  4. package/dist/chunk-U7JDNY6B.js +149 -0
  5. package/dist/cli.cjs +293 -0
  6. package/dist/cli.d.cts +1 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.js +128 -0
  9. package/dist/index.cjs +233 -0
  10. package/dist/index.d.cts +76 -0
  11. package/dist/index.d.ts +76 -0
  12. package/dist/index.js +10 -0
  13. package/dist/templates/claude-agent-sdk/.env.example +4 -0
  14. package/dist/templates/claude-agent-sdk/.mcp.json +1 -0
  15. package/dist/templates/claude-agent-sdk/README.md +107 -0
  16. package/dist/templates/claude-agent-sdk/ggui/blueprints/.gitkeep +0 -0
  17. package/dist/templates/claude-agent-sdk/ggui/ggui.json +15 -0
  18. package/dist/templates/claude-agent-sdk/ggui/themes/.gitkeep +0 -0
  19. package/dist/templates/claude-agent-sdk/guuey.json +13 -0
  20. package/dist/templates/claude-agent-sdk/mcps/todo/Dockerfile +46 -0
  21. package/dist/templates/claude-agent-sdk/mcps/todo/package.json +22 -0
  22. package/dist/templates/claude-agent-sdk/mcps/todo/src/server.ts +173 -0
  23. package/dist/templates/claude-agent-sdk/mcps/todo/tsconfig.json +15 -0
  24. package/dist/templates/claude-agent-sdk/package.json +22 -0
  25. package/dist/templates/claude-agent-sdk/pnpm-workspace.yaml +24 -0
  26. package/dist/templates/claude-agent-sdk/prompts/system.md +4 -0
  27. package/dist/templates/claude-agent-sdk/scripts/dev.mjs +44 -0
  28. package/dist/templates/claude-agent-sdk/src/agent-config.ts +70 -0
  29. package/dist/templates/claude-agent-sdk/src/worker.ts +89 -0
  30. package/dist/templates/claude-agent-sdk/tsconfig.json +15 -0
  31. package/dist/templates/claude-agent-sdk/tsup.config.ts +9 -0
  32. package/dist/templates/claude-agent-sdk/web/.env.example +17 -0
  33. package/dist/templates/claude-agent-sdk/web/index.html +13 -0
  34. package/dist/templates/claude-agent-sdk/web/package.json +25 -0
  35. package/dist/templates/claude-agent-sdk/web/sandbox-proxy.ts +239 -0
  36. package/dist/templates/claude-agent-sdk/web/src/App.tsx +272 -0
  37. package/dist/templates/claude-agent-sdk/web/src/main.tsx +12 -0
  38. package/dist/templates/claude-agent-sdk/web/src/useAgentChat.ts +122 -0
  39. package/dist/templates/claude-agent-sdk/web/src/vite-env.d.ts +27 -0
  40. package/dist/templates/claude-agent-sdk/web/tsconfig.json +20 -0
  41. package/dist/templates/claude-agent-sdk/web/vite.config.ts +12 -0
  42. package/dist/templates/mcp-base/Dockerfile +46 -0
  43. package/dist/templates/mcp-base/package.json +22 -0
  44. package/dist/templates/mcp-base/src/server.ts +113 -0
  45. package/dist/templates/mcp-base/tsconfig.json +15 -0
  46. package/dist/templates/openai-agents-sdk/.env.example +4 -0
  47. package/dist/templates/openai-agents-sdk/.mcp.json +1 -0
  48. package/dist/templates/openai-agents-sdk/README.md +107 -0
  49. package/dist/templates/openai-agents-sdk/ggui/blueprints/.gitkeep +0 -0
  50. package/dist/templates/openai-agents-sdk/ggui/ggui.json +15 -0
  51. package/dist/templates/openai-agents-sdk/ggui/themes/.gitkeep +0 -0
  52. package/dist/templates/openai-agents-sdk/guuey.json +13 -0
  53. package/dist/templates/openai-agents-sdk/mcps/todo/Dockerfile +46 -0
  54. package/dist/templates/openai-agents-sdk/mcps/todo/package.json +22 -0
  55. package/dist/templates/openai-agents-sdk/mcps/todo/src/server.ts +173 -0
  56. package/dist/templates/openai-agents-sdk/mcps/todo/tsconfig.json +15 -0
  57. package/dist/templates/openai-agents-sdk/package.json +22 -0
  58. package/dist/templates/openai-agents-sdk/pnpm-workspace.yaml +24 -0
  59. package/dist/templates/openai-agents-sdk/prompts/system.md +4 -0
  60. package/dist/templates/openai-agents-sdk/scripts/dev.mjs +44 -0
  61. package/dist/templates/openai-agents-sdk/src/agent-config.ts +70 -0
  62. package/dist/templates/openai-agents-sdk/src/worker.ts +75 -0
  63. package/dist/templates/openai-agents-sdk/tsconfig.json +15 -0
  64. package/dist/templates/openai-agents-sdk/tsup.config.ts +9 -0
  65. package/dist/templates/openai-agents-sdk/web/.env.example +17 -0
  66. package/dist/templates/openai-agents-sdk/web/index.html +13 -0
  67. package/dist/templates/openai-agents-sdk/web/package.json +25 -0
  68. package/dist/templates/openai-agents-sdk/web/sandbox-proxy.ts +239 -0
  69. package/dist/templates/openai-agents-sdk/web/src/App.tsx +272 -0
  70. package/dist/templates/openai-agents-sdk/web/src/main.tsx +12 -0
  71. package/dist/templates/openai-agents-sdk/web/src/useAgentChat.ts +122 -0
  72. package/dist/templates/openai-agents-sdk/web/src/vite-env.d.ts +27 -0
  73. package/dist/templates/openai-agents-sdk/web/tsconfig.json +20 -0
  74. package/dist/templates/openai-agents-sdk/web/vite.config.ts +12 -0
  75. package/package.json +45 -0
package/dist/cli.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ scaffold
4
+ } from "./chunk-JD4RAKEG.js";
5
+
6
+ // src/cli.ts
7
+ import { createInterface } from "readline/promises";
8
+ import { stdin, stdout } from "process";
9
+ var FRAMEWORKS = ["claude-agent-sdk", "openai-agents-sdk"];
10
+ function isFramework(value) {
11
+ return FRAMEWORKS.includes(value);
12
+ }
13
+ function parseArgs(argv) {
14
+ const positional = [];
15
+ const flags = {};
16
+ for (let i = 0; i < argv.length; i++) {
17
+ const arg = argv[i];
18
+ if (arg.startsWith("--")) {
19
+ const key = arg.slice(2);
20
+ const next = argv[i + 1];
21
+ if (next && !next.startsWith("--")) {
22
+ flags[key] = next;
23
+ i++;
24
+ } else {
25
+ flags[key] = true;
26
+ }
27
+ } else {
28
+ positional.push(arg);
29
+ }
30
+ }
31
+ return { positional, flags };
32
+ }
33
+ function printHelp() {
34
+ console.log(`create-agentic-app -- scaffold a guuey agentic app
35
+
36
+ Usage: create-agentic-app [target] [options]
37
+
38
+ Arguments:
39
+ target Directory to create the project in (prompted if omitted)
40
+
41
+ Options:
42
+ --name <name> Project name (default: derived from target)
43
+ --scope <scope> npm scope for @<scope>/* packages (default: name)
44
+ --framework <f> Framework: ${FRAMEWORKS.join(" | ")}
45
+ --agent <f> Alias for --framework
46
+ --install Run "pnpm install" after scaffolding
47
+ --no-git Skip "git init" + initial commit
48
+ --force Scaffold into a non-empty target directory
49
+ --list-agents List available frameworks and exit
50
+ --help Show this help
51
+
52
+ Examples:
53
+ create-agentic-app my-app --framework claude-agent-sdk
54
+ create-agentic-app my-app --agent openai-agents-sdk --install
55
+ `);
56
+ }
57
+ function deriveName(target) {
58
+ const base = target.split(/[\\/]/).filter(Boolean).pop() ?? target;
59
+ const slug = base.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+/, "");
60
+ return slug || "agentic-app";
61
+ }
62
+ async function promptFor(question) {
63
+ const rl = createInterface({ input: stdin, output: stdout });
64
+ try {
65
+ return (await rl.question(question)).trim();
66
+ } finally {
67
+ rl.close();
68
+ }
69
+ }
70
+ async function main() {
71
+ const { positional, flags } = parseArgs(process.argv.slice(2));
72
+ if (flags.help) {
73
+ printHelp();
74
+ return;
75
+ }
76
+ if (flags["list-agents"]) {
77
+ console.log(FRAMEWORKS.join("\n"));
78
+ return;
79
+ }
80
+ let target = positional[0];
81
+ if (!target) {
82
+ target = await promptFor("Project directory: ");
83
+ }
84
+ if (!target) {
85
+ console.error("A target directory is required.");
86
+ process.exit(1);
87
+ return;
88
+ }
89
+ const frameworkFlag = flags.framework ?? flags.agent;
90
+ let frameworkInput = typeof frameworkFlag === "string" ? frameworkFlag : void 0;
91
+ if (!frameworkInput) {
92
+ frameworkInput = await promptFor(`Framework (${FRAMEWORKS.join(" | ")}): `);
93
+ }
94
+ if (!isFramework(frameworkInput)) {
95
+ console.error(`Unknown framework "${frameworkInput}". Available: ${FRAMEWORKS.join(", ")}`);
96
+ process.exit(1);
97
+ return;
98
+ }
99
+ const framework = frameworkInput;
100
+ const name = typeof flags.name === "string" ? flags.name : deriveName(target);
101
+ const scope = typeof flags.scope === "string" ? flags.scope : void 0;
102
+ const install = flags.install === true;
103
+ const { projectDir } = await scaffold({
104
+ targetDir: target,
105
+ name,
106
+ framework,
107
+ scope,
108
+ install,
109
+ git: flags["no-git"] !== true,
110
+ force: flags.force === true
111
+ });
112
+ console.log(`
113
+ Scaffolded "${name}" in ${projectDir}
114
+ `);
115
+ console.log("Next steps:");
116
+ console.log(` cd ${projectDir}`);
117
+ if (!install) console.log(" pnpm install");
118
+ console.log(" pnpm dev");
119
+ console.log(" guuey login && guuey deploy");
120
+ }
121
+ main().catch((err) => {
122
+ if (err instanceof Error) {
123
+ console.error(`\u2717 ${err.message}`);
124
+ } else {
125
+ console.error(`\u2717 ${err}`);
126
+ }
127
+ process.exit(1);
128
+ });
package/dist/index.cjs ADDED
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ isNpmSafeName: () => isNpmSafeName,
24
+ scaffold: () => scaffold,
25
+ scaffoldMcp: () => scaffoldMcp
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/scaffold.ts
30
+ var import_node_fs2 = require("fs");
31
+ var import_node_path = require("path");
32
+ var import_node_url = require("url");
33
+ var import_node_child_process = require("child_process");
34
+ var import_node_util = require("util");
35
+
36
+ // src/rename.ts
37
+ function renameContent(content, name, scope) {
38
+ return content.replaceAll("@agentic-app-template/", `@${scope}/`).replaceAll("agentic-app-template", name);
39
+ }
40
+ function isProbablyText(buf) {
41
+ const window = buf.subarray(0, 8192);
42
+ return !window.includes(0);
43
+ }
44
+
45
+ // src/shared.ts
46
+ var import_node_fs = require("fs");
47
+ var NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
48
+ function isNpmSafeName(name) {
49
+ return NAME_PATTERN.test(name);
50
+ }
51
+ function assertNpmSafeName(name, label) {
52
+ if (!isNpmSafeName(name)) {
53
+ throw new Error(
54
+ `Invalid ${label} name "${name}": must be npm-safe (match ${NAME_PATTERN.toString()}).`
55
+ );
56
+ }
57
+ }
58
+ function isErrnoException(err) {
59
+ return err instanceof Error && "code" in err;
60
+ }
61
+ async function pathExists(path) {
62
+ try {
63
+ await import_node_fs.promises.access(path);
64
+ return true;
65
+ } catch (err) {
66
+ if (isErrnoException(err) && err.code === "ENOENT") return false;
67
+ throw err;
68
+ }
69
+ }
70
+ async function ensureTargetDir(targetDir, force) {
71
+ if (!await pathExists(targetDir)) {
72
+ await import_node_fs.promises.mkdir(targetDir, { recursive: true });
73
+ return;
74
+ }
75
+ const entries = await import_node_fs.promises.readdir(targetDir);
76
+ if (entries.length > 0 && !force) {
77
+ throw new Error(
78
+ `Target directory "${targetDir}" is not empty. Pass force: true (or --force) to scaffold into it anyway.`
79
+ );
80
+ }
81
+ }
82
+
83
+ // src/scaffold.ts
84
+ var import_meta = {};
85
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
86
+ var here = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
87
+ var packageRoot = (0, import_node_path.join)(here, "..");
88
+ var defaultTemplatesDir = (0, import_node_path.join)(packageRoot, "dist", "templates");
89
+ async function listAvailableFrameworks(templatesDir) {
90
+ try {
91
+ const entries = await import_node_fs2.promises.readdir(templatesDir, { withFileTypes: true });
92
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
93
+ } catch (err) {
94
+ if (isErrnoException(err) && err.code === "ENOENT") return [];
95
+ throw err;
96
+ }
97
+ }
98
+ async function resolveTemplateDir(templatesDir, framework) {
99
+ const dir = (0, import_node_path.join)(templatesDir, framework);
100
+ if (await pathExists(dir)) return dir;
101
+ const available = await listAvailableFrameworks(templatesDir);
102
+ const list = available.length > 0 ? available.join(", ") : `(none found under ${templatesDir})`;
103
+ throw new Error(`No template for framework "${framework}". Available frameworks: ${list}`);
104
+ }
105
+ async function copyTree(src, dest, name, scope) {
106
+ await import_node_fs2.promises.mkdir(dest, { recursive: true });
107
+ const entries = await import_node_fs2.promises.readdir(src, { withFileTypes: true });
108
+ for (const entry of entries) {
109
+ const destName = renameContent(entry.name, name, scope);
110
+ const srcPath = (0, import_node_path.join)(src, entry.name);
111
+ const destPath = (0, import_node_path.join)(dest, destName);
112
+ if (entry.isDirectory()) {
113
+ await copyTree(srcPath, destPath, name, scope);
114
+ } else {
115
+ const buf = await import_node_fs2.promises.readFile(srcPath);
116
+ if (isProbablyText(buf)) {
117
+ await import_node_fs2.promises.writeFile(destPath, renameContent(buf.toString("utf8"), name, scope), "utf8");
118
+ } else {
119
+ await import_node_fs2.promises.writeFile(destPath, buf);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ async function seedEnvLocal(projectDir) {
125
+ const envExample = (0, import_node_path.join)(projectDir, ".env.example");
126
+ const envLocal = (0, import_node_path.join)(projectDir, ".env.local");
127
+ if (!await pathExists(envExample)) return;
128
+ if (await pathExists(envLocal)) return;
129
+ await import_node_fs2.promises.copyFile(envExample, envLocal);
130
+ }
131
+ async function initGit(projectDir) {
132
+ try {
133
+ await execFileAsync("git", ["init"], { cwd: projectDir });
134
+ await execFileAsync("git", ["add", "-A"], { cwd: projectDir });
135
+ await execFileAsync(
136
+ "git",
137
+ [
138
+ "-c",
139
+ "user.name=guuey",
140
+ "-c",
141
+ "user.email=scaffold@guuey.com",
142
+ "commit",
143
+ "-m",
144
+ "chore: scaffold"
145
+ ],
146
+ { cwd: projectDir }
147
+ );
148
+ } catch {
149
+ console.error(
150
+ `Warning: git init failed; the project was scaffolded without a repo. Initialize it manually:
151
+ cd ${projectDir}
152
+ git init && git add -A && git commit -m "chore: scaffold"`
153
+ );
154
+ }
155
+ }
156
+ async function runInstall(projectDir) {
157
+ try {
158
+ await execFileAsync("pnpm", ["install"], { cwd: projectDir });
159
+ } catch {
160
+ console.error(
161
+ `Warning: "pnpm install" failed to run automatically. Run it manually:
162
+ cd ${projectDir}
163
+ pnpm install`
164
+ );
165
+ }
166
+ }
167
+ async function scaffold(opts) {
168
+ assertNpmSafeName(opts.name, "project");
169
+ const scope = opts.scope ?? opts.name;
170
+ const templatesDir = opts.templatesDir ?? defaultTemplatesDir;
171
+ const templateDir = await resolveTemplateDir(templatesDir, opts.framework);
172
+ const projectDir = (0, import_node_path.resolve)(opts.targetDir);
173
+ await ensureTargetDir(projectDir, opts.force);
174
+ await copyTree(templateDir, projectDir, opts.name, scope);
175
+ await seedEnvLocal(projectDir);
176
+ if (opts.git !== false) {
177
+ await initGit(projectDir);
178
+ }
179
+ if (opts.install) {
180
+ await runInstall(projectDir);
181
+ }
182
+ return { projectDir };
183
+ }
184
+
185
+ // src/scaffold-mcp.ts
186
+ var import_node_fs3 = require("fs");
187
+ var import_node_path2 = require("path");
188
+ var import_node_url2 = require("url");
189
+ var import_meta2 = {};
190
+ var here2 = (0, import_node_path2.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
191
+ var packageRoot2 = (0, import_node_path2.join)(here2, "..");
192
+ var defaultTemplatesDir2 = (0, import_node_path2.join)(packageRoot2, "dist", "templates");
193
+ function renameMcpTokens(content, name, scope) {
194
+ return renameContent(content.replaceAll("NAME_PLACEHOLDER", name), scope, scope);
195
+ }
196
+ async function copyMcpTree(src, dest, name, scope) {
197
+ await import_node_fs3.promises.mkdir(dest, { recursive: true });
198
+ const entries = await import_node_fs3.promises.readdir(src, { withFileTypes: true });
199
+ for (const entry of entries) {
200
+ const destName = renameMcpTokens(entry.name, name, scope);
201
+ const srcPath = (0, import_node_path2.join)(src, entry.name);
202
+ const destPath = (0, import_node_path2.join)(dest, destName);
203
+ if (entry.isDirectory()) {
204
+ await copyMcpTree(srcPath, destPath, name, scope);
205
+ } else {
206
+ const buf = await import_node_fs3.promises.readFile(srcPath);
207
+ if (isProbablyText(buf)) {
208
+ await import_node_fs3.promises.writeFile(destPath, renameMcpTokens(buf.toString("utf8"), name, scope), "utf8");
209
+ } else {
210
+ await import_node_fs3.promises.writeFile(destPath, buf);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ async function scaffoldMcp(opts) {
216
+ assertNpmSafeName(opts.name, "MCP");
217
+ const scope = opts.scope ?? opts.name;
218
+ const templatesDir = opts.templatesDir ?? defaultTemplatesDir2;
219
+ const templateDir = (0, import_node_path2.join)(templatesDir, "mcp-base");
220
+ if (!await pathExists(templateDir)) {
221
+ throw new Error(`No mcp-base template found at ${templateDir}.`);
222
+ }
223
+ const mcpDir = (0, import_node_path2.resolve)(opts.targetDir);
224
+ await ensureTargetDir(mcpDir, void 0);
225
+ await copyMcpTree(templateDir, mcpDir, opts.name, scope);
226
+ return { mcpDir };
227
+ }
228
+ // Annotate the CommonJS export names for ESM import in node:
229
+ 0 && (module.exports = {
230
+ isNpmSafeName,
231
+ scaffold,
232
+ scaffoldMcp
233
+ });
@@ -0,0 +1,76 @@
1
+ /** Frameworks the scaffolder ships templates for. */
2
+ type Framework = 'claude-agent-sdk' | 'openai-agents-sdk';
3
+ interface ScaffoldOptions {
4
+ /** Absolute or cwd-relative path to create/populate the new project in. */
5
+ targetDir: string;
6
+ /** npm-safe project name (also used as the default scope). */
7
+ name: string;
8
+ framework: Framework;
9
+ /** Package scope for `@<scope>/*` packages. Default: `name`. */
10
+ scope?: string;
11
+ /** Run `pnpm install` in the new project after scaffolding. Default: false. */
12
+ install?: boolean;
13
+ /** Run `git init` + an initial commit in the new project. Default: true. */
14
+ git?: boolean;
15
+ /** Scaffold into a non-empty targetDir anyway. Default: false. */
16
+ force?: boolean;
17
+ /** Root directory holding one subdirectory per framework. Default: dist/templates. */
18
+ templatesDir?: string;
19
+ }
20
+ interface ScaffoldResult {
21
+ projectDir: string;
22
+ }
23
+ /**
24
+ * Scaffold a new agentic-app project from a bundled template.
25
+ *
26
+ * Copies the template tree for `opts.framework`, rewrites the
27
+ * `agentic-app-template` / `@agentic-app-template` placeholder tokens to the
28
+ * requested project name/scope (in file contents and in file/dir names),
29
+ * seeds `.env.local` from `.env.example` when absent, and optionally runs
30
+ * `git init` + an initial commit and/or `pnpm install` in the new project.
31
+ */
32
+ declare function scaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
33
+
34
+ interface ScaffoldMcpOptions {
35
+ /** Absolute or cwd-relative path to create the MCP package in. */
36
+ targetDir: string;
37
+ /** npm-safe MCP name (also the default scope in standalone mode). */
38
+ name: string;
39
+ /**
40
+ * Package scope for the emitted `@<scope>/<name>-mcp` name. Project-mode
41
+ * callers (`guuey mcp new` inside a scaffolded app) pass the surrounding
42
+ * project's own scope — its root `package.json` name, since a scaffolded
43
+ * app's own `scope` defaults to its (unscoped) project name (see
44
+ * `scaffold()`'s `scope ?? name`). Standalone-mode callers default this
45
+ * to `name`.
46
+ */
47
+ scope?: string;
48
+ /** Root directory holding `mcp-base/`. Default: `dist/templates`. */
49
+ templatesDir?: string;
50
+ }
51
+ interface ScaffoldMcpResult {
52
+ mcpDir: string;
53
+ }
54
+ /**
55
+ * Scaffold a single hosted-MCP package from the shared `mcp-base` template
56
+ * (`templates-src/mcp-base/`, emitted verbatim with `NAME_PLACEHOLDER`
57
+ * unresolved to `dist/templates/mcp-base/` by `build-templates.mjs`; see
58
+ * the mcp-lifecycle design spec §5).
59
+ *
60
+ * The sole consumer today is `guuey mcp new` (`@guuey/cli`):
61
+ * - **Project mode** — `targetDir: <project>/mcps/<name>`, `scope`: the
62
+ * surrounding project's own scope.
63
+ * - **Standalone mode** — `targetDir: ./<name>`, `scope` left `undefined`
64
+ * (defaults to `name`).
65
+ *
66
+ * Refuses (via {@link ensureTargetDir}) a non-empty `targetDir` — callers
67
+ * that want a stricter "refuse ANY existing directory" rule (project mode's
68
+ * "refuse if `mcps/<name>` exists") check `existsSync(targetDir)` themselves
69
+ * before calling this.
70
+ */
71
+ declare function scaffoldMcp(opts: ScaffoldMcpOptions): Promise<ScaffoldMcpResult>;
72
+
73
+ /** Whether `name` matches the npm-safe {@link NAME_PATTERN} rule. */
74
+ declare function isNpmSafeName(name: string): boolean;
75
+
76
+ export { type Framework, type ScaffoldMcpOptions, type ScaffoldMcpResult, type ScaffoldOptions, type ScaffoldResult, isNpmSafeName, scaffold, scaffoldMcp };
@@ -0,0 +1,76 @@
1
+ /** Frameworks the scaffolder ships templates for. */
2
+ type Framework = 'claude-agent-sdk' | 'openai-agents-sdk';
3
+ interface ScaffoldOptions {
4
+ /** Absolute or cwd-relative path to create/populate the new project in. */
5
+ targetDir: string;
6
+ /** npm-safe project name (also used as the default scope). */
7
+ name: string;
8
+ framework: Framework;
9
+ /** Package scope for `@<scope>/*` packages. Default: `name`. */
10
+ scope?: string;
11
+ /** Run `pnpm install` in the new project after scaffolding. Default: false. */
12
+ install?: boolean;
13
+ /** Run `git init` + an initial commit in the new project. Default: true. */
14
+ git?: boolean;
15
+ /** Scaffold into a non-empty targetDir anyway. Default: false. */
16
+ force?: boolean;
17
+ /** Root directory holding one subdirectory per framework. Default: dist/templates. */
18
+ templatesDir?: string;
19
+ }
20
+ interface ScaffoldResult {
21
+ projectDir: string;
22
+ }
23
+ /**
24
+ * Scaffold a new agentic-app project from a bundled template.
25
+ *
26
+ * Copies the template tree for `opts.framework`, rewrites the
27
+ * `agentic-app-template` / `@agentic-app-template` placeholder tokens to the
28
+ * requested project name/scope (in file contents and in file/dir names),
29
+ * seeds `.env.local` from `.env.example` when absent, and optionally runs
30
+ * `git init` + an initial commit and/or `pnpm install` in the new project.
31
+ */
32
+ declare function scaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
33
+
34
+ interface ScaffoldMcpOptions {
35
+ /** Absolute or cwd-relative path to create the MCP package in. */
36
+ targetDir: string;
37
+ /** npm-safe MCP name (also the default scope in standalone mode). */
38
+ name: string;
39
+ /**
40
+ * Package scope for the emitted `@<scope>/<name>-mcp` name. Project-mode
41
+ * callers (`guuey mcp new` inside a scaffolded app) pass the surrounding
42
+ * project's own scope — its root `package.json` name, since a scaffolded
43
+ * app's own `scope` defaults to its (unscoped) project name (see
44
+ * `scaffold()`'s `scope ?? name`). Standalone-mode callers default this
45
+ * to `name`.
46
+ */
47
+ scope?: string;
48
+ /** Root directory holding `mcp-base/`. Default: `dist/templates`. */
49
+ templatesDir?: string;
50
+ }
51
+ interface ScaffoldMcpResult {
52
+ mcpDir: string;
53
+ }
54
+ /**
55
+ * Scaffold a single hosted-MCP package from the shared `mcp-base` template
56
+ * (`templates-src/mcp-base/`, emitted verbatim with `NAME_PLACEHOLDER`
57
+ * unresolved to `dist/templates/mcp-base/` by `build-templates.mjs`; see
58
+ * the mcp-lifecycle design spec §5).
59
+ *
60
+ * The sole consumer today is `guuey mcp new` (`@guuey/cli`):
61
+ * - **Project mode** — `targetDir: <project>/mcps/<name>`, `scope`: the
62
+ * surrounding project's own scope.
63
+ * - **Standalone mode** — `targetDir: ./<name>`, `scope` left `undefined`
64
+ * (defaults to `name`).
65
+ *
66
+ * Refuses (via {@link ensureTargetDir}) a non-empty `targetDir` — callers
67
+ * that want a stricter "refuse ANY existing directory" rule (project mode's
68
+ * "refuse if `mcps/<name>` exists") check `existsSync(targetDir)` themselves
69
+ * before calling this.
70
+ */
71
+ declare function scaffoldMcp(opts: ScaffoldMcpOptions): Promise<ScaffoldMcpResult>;
72
+
73
+ /** Whether `name` matches the npm-safe {@link NAME_PATTERN} rule. */
74
+ declare function isNpmSafeName(name: string): boolean;
75
+
76
+ export { type Framework, type ScaffoldMcpOptions, type ScaffoldMcpResult, type ScaffoldOptions, type ScaffoldResult, isNpmSafeName, scaffold, scaffoldMcp };
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ isNpmSafeName,
3
+ scaffold,
4
+ scaffoldMcp
5
+ } from "./chunk-JD4RAKEG.js";
6
+ export {
7
+ isNpmSafeName,
8
+ scaffold,
9
+ scaffoldMcp
10
+ };
@@ -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,107 @@
1
+ # agentic-app-template
2
+
3
+ A guuey agentic app: a code-mode agent, a custom MCP server you can copy to
4
+ add your own tools, and a `ggui` config for generative UI — all runnable
5
+ locally with one command, and deployable to guuey with one more.
6
+
7
+ ## What got scaffolded
8
+
9
+ ```
10
+ .
11
+ ├── guuey.json # the deploy contract: agent framework/model, system prompt,
12
+ │ # mcpServers (name → local dev port / hosted source), ggui config
13
+ ├── package.json # agent deps + the pnpm workspace root (workspaces: mcps/*, web)
14
+ ├── src/worker.ts # your agent code (code-mode worker); build emits ./guuey.worker.js
15
+ ├── prompts/system.md # system prompt, referenced from guuey.json#agent.systemPrompt.file
16
+ ├── mcps/todo/ # the "copy-me" custom MCP server — @modelcontextprotocol/sdk,
17
+ │ # Streamable HTTP, in-memory todo list, port :6782
18
+ ├── ggui/ # ggui.json + blueprints/ + themes/ — generative-UI config.
19
+ │ # `ggui serve` runs against this locally; it's pushed to your
20
+ │ # deployed app's guuey-managed ggui instance on `guuey deploy`.
21
+ ├── web/ # a small Vite SPA chat client — the local dev surface, and a
22
+ │ # worked example of the bring-your-own-frontend path
23
+ ├── scripts/dev.mjs # `pnpm dev` orchestrator — boots the whole local stack
24
+ ├── .env.example # ANTHROPIC_API_KEY / OPENAI_API_KEY for local dev
25
+ └── .mcp.json # Claude Code convenience wiring (mcp.ggui.ai/dev)
26
+ ```
27
+
28
+ The project root **is** the agent package — `guuey deploy` packs this directory
29
+ directly as the deploy tarball, so `guuey.json` and `src/worker.ts` live at the top
30
+ level rather than nested under a `servers/` or `apps/` folder.
31
+
32
+ ## Quick start
33
+
34
+ ```bash
35
+ pnpm install
36
+ cp .env.example .env.local # done automatically on scaffold if .env.local is absent
37
+ # set ANTHROPIC_API_KEY (or OPENAI_API_KEY, for the openai-agents-sdk template) in .env.local
38
+ pnpm dev
39
+ ```
40
+
41
+ `pnpm dev` (`scripts/dev.mjs`) boots five processes with prefixed, interleaved
42
+ logs. Ctrl-C tears all of them down together.
43
+
44
+ | process | port | what |
45
+ | ------------ | ----- | ------------------------------------------------------------ |
46
+ | `worker` | — | `tsup --watch` — rebuilds `guuey.worker.js` on every save |
47
+ | `guuey dev` | :6790 | local router: spawns your worker per turn, streams SSE |
48
+ | `mcps/todo` | :6782 | the example MCP server (copy this directory to add your own) |
49
+ | `ggui serve` | :6781 | local generative-UI server, over `ggui/` |
50
+ | `web` | :6890 | the Vite chat SPA |
51
+
52
+ Open http://localhost:6890 to chat with your agent locally.
53
+
54
+ ## Local dev vs. deployed — what's different
55
+
56
+ `guuey dev` is the open, local equivalent of the pod router that runs your
57
+ worker in production. It mirrors the real spawn/stream contract closely — the
58
+ same normalizer, the same SSE shape — but it deliberately cuts three corners
59
+ that only matter once real users and real money are involved:
60
+
61
+ - **No sandboxing.** Locally your worker runs as a plain child process — no
62
+ `bwrap`/gVisor isolation. In production every invocation runs inside a
63
+ gVisor-isolated pod.
64
+ - **Permissive auth.** `ggui serve --dev-allow-all` accepts any bearer
65
+ locally. The deployed ggui instance enforces real auth.
66
+ - **No metering or history.** Local runs aren't billed and aren't persisted.
67
+ Deployed conversations are metered and their history is written to
68
+ DynamoDB so users can resume a thread.
69
+
70
+ None of this changes your code — `guuey.json` is the same file in both
71
+ worlds; only how it's resolved differs (`guuey dev` points MCP server names
72
+ at `localhost:<devPort>`, `guuey deploy` points them at the platform's
73
+ federated URLs).
74
+
75
+ ## Deploying
76
+
77
+ ```bash
78
+ guuey login # device-flow auth; stores a token in ~/.guuey/auth.json
79
+ guuey deploy # ships everything
80
+ ```
81
+
82
+ `guuey deploy` runs four legs, in order, so a hard failure aborts before
83
+ anything user-visible changes:
84
+
85
+ 1. **MCP leg** — deploys each `hosted` entry under `guuey.json#agent.mcpServers`
86
+ (e.g. `mcps/todo`) as its own hosted MCP server (build → deploy → registry),
87
+ then writes the resulting server id back into `guuey.json`.
88
+ 2. **ggui asset leg** — pushes `ggui/` (ggui.json, blueprints, themes) to your
89
+ app's guuey-managed ggui instance.
90
+ 3. **Agent leg** — builds `src/worker.ts` into `guuey.worker.js`, packs the
91
+ project root, and deploys it as a gVisor-isolated, scale-to-zero pod.
92
+ 4. **Output** — prints your agent's endpoint URL and a Portal deep link.
93
+
94
+ Re-running `guuey deploy` converges: unchanged pieces are skipped or reused,
95
+ nothing is duplicated.
96
+
97
+ ## How people talk to your agent
98
+
99
+ - **guuey Portal** — a Telegram-like agent App Store and universal chat
100
+ client. Once deployed, your agent is reachable from Portal with **zero
101
+ frontend code** — most builders never need to touch `web/` at all.
102
+ - **`web/`** — ships anyway, because the local dev loop needs a chat surface,
103
+ and it's a worked example of the bring-your-own-frontend path if you want
104
+ to embed your agent somewhere Portal doesn't reach.
105
+ - **Future: guuey widgets** — an embeddable web-chat widget for dropping
106
+ your agent into an existing site is planned as a separate feature; `web/`
107
+ is designed to make that graduation straightforward when it lands.
@@ -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
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schema": "1",
3
+ "agent": {
4
+ "mode": "code",
5
+ "framework": "claude-agent-sdk",
6
+ "model": "claude-sonnet-5",
7
+ "systemPrompt": { "file": "prompts/system.md" },
8
+ "mcpServers": {
9
+ "todo": { "kind": "hosted", "source": "./mcps/todo", "devPort": 6782 }
10
+ }
11
+ },
12
+ "ggui": { "configFile": "./ggui/ggui.json" }
13
+ }