@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.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/chunk-JD4RAKEG.js +203 -0
- package/dist/chunk-U7JDNY6B.js +149 -0
- package/dist/cli.cjs +293 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -0
- package/dist/index.cjs +233 -0
- package/dist/index.d.cts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +10 -0
- package/dist/templates/claude-agent-sdk/.env.example +4 -0
- package/dist/templates/claude-agent-sdk/.mcp.json +1 -0
- package/dist/templates/claude-agent-sdk/README.md +107 -0
- package/dist/templates/claude-agent-sdk/ggui/blueprints/.gitkeep +0 -0
- package/dist/templates/claude-agent-sdk/ggui/ggui.json +15 -0
- package/dist/templates/claude-agent-sdk/ggui/themes/.gitkeep +0 -0
- package/dist/templates/claude-agent-sdk/guuey.json +13 -0
- package/dist/templates/claude-agent-sdk/mcps/todo/Dockerfile +46 -0
- package/dist/templates/claude-agent-sdk/mcps/todo/package.json +22 -0
- package/dist/templates/claude-agent-sdk/mcps/todo/src/server.ts +173 -0
- package/dist/templates/claude-agent-sdk/mcps/todo/tsconfig.json +15 -0
- package/dist/templates/claude-agent-sdk/package.json +22 -0
- package/dist/templates/claude-agent-sdk/pnpm-workspace.yaml +24 -0
- package/dist/templates/claude-agent-sdk/prompts/system.md +4 -0
- package/dist/templates/claude-agent-sdk/scripts/dev.mjs +44 -0
- package/dist/templates/claude-agent-sdk/src/agent-config.ts +70 -0
- package/dist/templates/claude-agent-sdk/src/worker.ts +89 -0
- package/dist/templates/claude-agent-sdk/tsconfig.json +15 -0
- package/dist/templates/claude-agent-sdk/tsup.config.ts +9 -0
- package/dist/templates/claude-agent-sdk/web/.env.example +17 -0
- package/dist/templates/claude-agent-sdk/web/index.html +13 -0
- package/dist/templates/claude-agent-sdk/web/package.json +25 -0
- package/dist/templates/claude-agent-sdk/web/sandbox-proxy.ts +239 -0
- package/dist/templates/claude-agent-sdk/web/src/App.tsx +272 -0
- package/dist/templates/claude-agent-sdk/web/src/main.tsx +12 -0
- package/dist/templates/claude-agent-sdk/web/src/useAgentChat.ts +122 -0
- package/dist/templates/claude-agent-sdk/web/src/vite-env.d.ts +27 -0
- package/dist/templates/claude-agent-sdk/web/tsconfig.json +20 -0
- package/dist/templates/claude-agent-sdk/web/vite.config.ts +12 -0
- package/dist/templates/mcp-base/Dockerfile +46 -0
- package/dist/templates/mcp-base/package.json +22 -0
- package/dist/templates/mcp-base/src/server.ts +113 -0
- package/dist/templates/mcp-base/tsconfig.json +15 -0
- package/dist/templates/openai-agents-sdk/.env.example +4 -0
- package/dist/templates/openai-agents-sdk/.mcp.json +1 -0
- package/dist/templates/openai-agents-sdk/README.md +107 -0
- package/dist/templates/openai-agents-sdk/ggui/blueprints/.gitkeep +0 -0
- package/dist/templates/openai-agents-sdk/ggui/ggui.json +15 -0
- package/dist/templates/openai-agents-sdk/ggui/themes/.gitkeep +0 -0
- package/dist/templates/openai-agents-sdk/guuey.json +13 -0
- package/dist/templates/openai-agents-sdk/mcps/todo/Dockerfile +46 -0
- package/dist/templates/openai-agents-sdk/mcps/todo/package.json +22 -0
- package/dist/templates/openai-agents-sdk/mcps/todo/src/server.ts +173 -0
- package/dist/templates/openai-agents-sdk/mcps/todo/tsconfig.json +15 -0
- package/dist/templates/openai-agents-sdk/package.json +22 -0
- package/dist/templates/openai-agents-sdk/pnpm-workspace.yaml +24 -0
- package/dist/templates/openai-agents-sdk/prompts/system.md +4 -0
- package/dist/templates/openai-agents-sdk/scripts/dev.mjs +44 -0
- package/dist/templates/openai-agents-sdk/src/agent-config.ts +70 -0
- package/dist/templates/openai-agents-sdk/src/worker.ts +75 -0
- package/dist/templates/openai-agents-sdk/tsconfig.json +15 -0
- package/dist/templates/openai-agents-sdk/tsup.config.ts +9 -0
- package/dist/templates/openai-agents-sdk/web/.env.example +17 -0
- package/dist/templates/openai-agents-sdk/web/index.html +13 -0
- package/dist/templates/openai-agents-sdk/web/package.json +25 -0
- package/dist/templates/openai-agents-sdk/web/sandbox-proxy.ts +239 -0
- package/dist/templates/openai-agents-sdk/web/src/App.tsx +272 -0
- package/dist/templates/openai-agents-sdk/web/src/main.tsx +12 -0
- package/dist/templates/openai-agents-sdk/web/src/useAgentChat.ts +122 -0
- package/dist/templates/openai-agents-sdk/web/src/vite-env.d.ts +27 -0
- package/dist/templates/openai-agents-sdk/web/tsconfig.json +20 -0
- package/dist/templates/openai-agents-sdk/web/vite.config.ts +12 -0
- 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
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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 @@
|
|
|
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.
|
|
File without changes
|
|
@@ -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,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
|
+
}
|