create-bk-agent 0.16.1 → 0.17.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/README.md +93 -29
- package/dist/src/add/detector.js +80 -0
- package/dist/src/add/index.js +67 -0
- package/dist/src/add/modules/mcp-client.js +65 -0
- package/dist/src/add/modules/server.js +100 -0
- package/dist/src/add/modules/specialist.js +98 -0
- package/dist/src/add/modules/tools.js +102 -0
- package/dist/src/add/patcher.js +168 -0
- package/dist/src/index.js +55 -9
- package/dist/src/prompts.js +102 -25
- package/dist/src/scaffold.js +90 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,54 +1,118 @@
|
|
|
1
1
|
# create-bk-agent
|
|
2
2
|
|
|
3
|
-
Scaffold a new [BackendKit agent](https://github.com/BackendKit-labs/backendkit-agent-framework) project in seconds.
|
|
3
|
+
Scaffold a new [BackendKit agent](https://github.com/BackendKit-labs/backendkit-agent-framework) project in seconds — or add capabilities to an existing one.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Create a project
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx create-bk-agent my-project
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
The CLI asks three questions and generates a production-ready project:
|
|
12
|
+
|
|
11
13
|
```
|
|
12
|
-
✨ create-bk-agent v0.
|
|
14
|
+
✨ create-bk-agent v0.17.0
|
|
13
15
|
|
|
14
|
-
? Project name:
|
|
15
|
-
?
|
|
16
|
-
?
|
|
16
|
+
? Project name: my-project
|
|
17
|
+
? Architecture: Multi-agent (orchestrator + specialists)
|
|
18
|
+
? Skill packs: Node.js / TypeScript
|
|
19
|
+
? How will it be used? MCP Server (HTTP)
|
|
17
20
|
|
|
18
|
-
Creating
|
|
21
|
+
Creating Multi-agent — MCP Server (HTTP) agent in ./my-project...
|
|
19
22
|
Installing dependencies...
|
|
20
23
|
|
|
21
|
-
✅ Done!
|
|
24
|
+
✅ Done! Your agent is ready.
|
|
25
|
+
|
|
22
26
|
cd my-project
|
|
23
|
-
cp .env.example .env
|
|
24
|
-
npm run
|
|
27
|
+
cp .env.example .env # add your API key
|
|
28
|
+
npm run server # starts HTTP MCP on port 3001
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
##
|
|
31
|
+
## Architectures
|
|
32
|
+
|
|
33
|
+
| Architecture | What it gives you | Best for |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `multi-agent` | Orchestrator + specialists with delegation | Domain agents (DevOps, finance, data, etc.) |
|
|
36
|
+
| `single` | One agent with a tool registry | Simple task automation |
|
|
37
|
+
| `coding` | 10 pre-built agents + all coding tools | Coding assistants, ready out of the box |
|
|
38
|
+
| `autonomous` | Trigger system (cron / webhook / event) | Scheduled or event-driven daemons |
|
|
39
|
+
| `minimal` | Bare engine, no entry point | Library embedding |
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
10 specialized agents (backend, frontend, QA, security, architecture…) and all
|
|
31
|
-
coding tools (read/write files, run commands, git, search) are already configured.
|
|
32
|
-
Add your API key, describe your project in `AGENT.md`, and it works out of the box.
|
|
33
|
-
**Choose this when you want a coding assistant, not when you want to build one.**
|
|
41
|
+
## Deploy modes
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
Orchestrator + specialists pattern with no pre-built agents or tools. You define
|
|
37
|
-
the agents and tools for your specific domain (Docker, AWS, databases, APIs, etc.).
|
|
38
|
-
The framework handles orchestration, delegation, and parallel execution.
|
|
39
|
-
**Choose this when you are building a new type of agent from scratch.**
|
|
43
|
+
Available for `multi-agent` and `single` architectures:
|
|
40
44
|
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
45
|
+
| Mode | Entry point | Use case |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `repl` | `src/index.ts` — readline loop | Local terminal tool |
|
|
48
|
+
| `mcp-http` | `src/server.ts` — HTTP MCP server | Connect to Claude Desktop, Cursor, etc. |
|
|
49
|
+
| `mcp-stdio` | `src/server.ts` — stdio MCP server | Subprocess / pipe-based MCP |
|
|
50
|
+
| `hybrid` | Both `index.ts` + `server.ts` | REPL for dev, server for production |
|
|
51
|
+
| `library` | Only `src/engine.ts` exported | Import the engine in your own app |
|
|
48
52
|
|
|
49
53
|
## Providers
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
Anthropic (Claude), OpenAI, DeepSeek / any OpenAI-compatible endpoint, and Ollama (local).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Add capabilities to an existing project
|
|
60
|
+
|
|
61
|
+
Once a project is created, use `add` to extend it without editing code manually:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx create-bk-agent add <module> [options]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `add server` — expose the agent as an HTTP MCP server
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx create-bk-agent add server
|
|
71
|
+
npx create-bk-agent add server --port 4000 --name my-agent
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Generates `src/server.ts` wired to the existing engine
|
|
75
|
+
- Adds `@backendkit-labs/mcp-server` to `dependencies`
|
|
76
|
+
- Adds `"server"` script to `package.json`
|
|
77
|
+
|
|
78
|
+
### `add specialist <name>` — add a specialist agent (multi-agent projects)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx create-bk-agent add specialist data-analyst
|
|
82
|
+
npx create-bk-agent add specialist git-expert
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- Creates `src/agents/<name>.ts` with an `AgentProfile` stub
|
|
86
|
+
- Patches `src/engine.ts`: adds the import and `agents.register()`
|
|
87
|
+
|
|
88
|
+
### `add tools <domain>` — add a tool file for a new domain
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx create-bk-agent add tools github
|
|
92
|
+
npx create-bk-agent add tools my-database
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- Creates `src/tools/<domain>-tools.ts` with two stub tools (action + query)
|
|
96
|
+
- Patches `src/engine.ts`: adds the import and `tools.register()`
|
|
97
|
+
|
|
98
|
+
### `add mcp-client <url>` — connect to a remote MCP server
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx create-bk-agent add mcp-client http://localhost:3001/mcp
|
|
102
|
+
npx create-bk-agent add mcp-client http://billing-agent/mcp --name billing
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Patches `src/engine.ts`: adds the entry to `mcpServers: [...]` inside `AgentEngine`
|
|
106
|
+
- Creates the `mcpServers` array if it doesn't exist yet
|
|
107
|
+
|
|
108
|
+
All `add` commands are **idempotent** — running the same command twice won't duplicate entries.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Requirements
|
|
113
|
+
|
|
114
|
+
- Node.js >= 20
|
|
115
|
+
- An API key for your chosen provider (Anthropic, OpenAI, DeepSeek, or Ollama)
|
|
52
116
|
|
|
53
117
|
## License
|
|
54
118
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectProject = detectProject;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function detectProject(cwd) {
|
|
40
|
+
const pkgFile = path.join(cwd, 'package.json');
|
|
41
|
+
if (!fs.existsSync(pkgFile))
|
|
42
|
+
return null;
|
|
43
|
+
let pkg;
|
|
44
|
+
try {
|
|
45
|
+
pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const deps = {
|
|
51
|
+
...(pkg.dependencies ?? {}),
|
|
52
|
+
...(pkg.devDependencies ?? {}),
|
|
53
|
+
};
|
|
54
|
+
if (!deps['@backendkit-labs/agent-core'])
|
|
55
|
+
return null;
|
|
56
|
+
const engineFile = path.join(cwd, 'src', 'engine.ts');
|
|
57
|
+
let architecture = 'unknown';
|
|
58
|
+
let engineFnName = 'createEngine';
|
|
59
|
+
if (fs.existsSync(engineFile)) {
|
|
60
|
+
const content = fs.readFileSync(engineFile, 'utf-8');
|
|
61
|
+
if (content.includes('ORCHESTRATOR_PROFILE') || content.includes("id: 'orchestrator'")) {
|
|
62
|
+
architecture = 'multi-agent';
|
|
63
|
+
}
|
|
64
|
+
else if (content.includes('export function create')) {
|
|
65
|
+
architecture = 'single';
|
|
66
|
+
}
|
|
67
|
+
const match = content.match(/export function (\w+)\s*\(/);
|
|
68
|
+
if (match)
|
|
69
|
+
engineFnName = match[1];
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
cwd,
|
|
73
|
+
architecture,
|
|
74
|
+
engineFile,
|
|
75
|
+
indexFile: path.join(cwd, 'src', 'index.ts'),
|
|
76
|
+
serverFile: path.join(cwd, 'src', 'server.ts'),
|
|
77
|
+
pkgFile,
|
|
78
|
+
engineFnName,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runAdd = runAdd;
|
|
4
|
+
const detector_1 = require("./detector");
|
|
5
|
+
const server_1 = require("./modules/server");
|
|
6
|
+
const specialist_1 = require("./modules/specialist");
|
|
7
|
+
const tools_1 = require("./modules/tools");
|
|
8
|
+
const mcp_client_1 = require("./modules/mcp-client");
|
|
9
|
+
const HELP = `
|
|
10
|
+
Usage: npx create-bk-agent add <module> [options]
|
|
11
|
+
|
|
12
|
+
Modules:
|
|
13
|
+
server Add an HTTP MCP server (src/server.ts)
|
|
14
|
+
--port <n> Port to listen on (default: 3001)
|
|
15
|
+
--name <s> MCP server name (default: project directory name)
|
|
16
|
+
|
|
17
|
+
specialist <name> Add a new specialist agent to a multi-agent project
|
|
18
|
+
(creates src/agents/<name>.ts and patches engine.ts)
|
|
19
|
+
|
|
20
|
+
tools <domain> Add a tool file for a new domain
|
|
21
|
+
(creates src/tools/<domain>-tools.ts and patches engine.ts)
|
|
22
|
+
|
|
23
|
+
mcp-client <url> Add a remote MCP server to the engine
|
|
24
|
+
--name <s> Name for the mcpServers entry (default: last URL segment)
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
npx create-bk-agent add server --port 4000
|
|
28
|
+
npx create-bk-agent add specialist data-analyst
|
|
29
|
+
npx create-bk-agent add tools github
|
|
30
|
+
npx create-bk-agent add mcp-client http://localhost:3001/mcp --name billing
|
|
31
|
+
`;
|
|
32
|
+
function runAdd(args, cwd) {
|
|
33
|
+
const [module, ...rest] = args;
|
|
34
|
+
if (!module || module === '--help' || module === '-h') {
|
|
35
|
+
console.log(HELP);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
const info = (0, detector_1.detectProject)(cwd);
|
|
39
|
+
if (!info) {
|
|
40
|
+
console.error('\n✗ No bk-agent project detected in the current directory.\n' +
|
|
41
|
+
' Make sure you are inside a project created with npx create-bk-agent.\n');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
switch (module) {
|
|
45
|
+
case 'server':
|
|
46
|
+
(0, server_1.addServer)(info, rest);
|
|
47
|
+
break;
|
|
48
|
+
case 'specialist':
|
|
49
|
+
if (info.architecture !== 'multi-agent') {
|
|
50
|
+
console.error(`\n⚠ "add specialist" is designed for multi-agent projects.\n` +
|
|
51
|
+
` Detected architecture: ${info.architecture}.\n` +
|
|
52
|
+
` Proceeding anyway — verify engine.ts manually.\n`);
|
|
53
|
+
}
|
|
54
|
+
(0, specialist_1.addSpecialist)(info, rest);
|
|
55
|
+
break;
|
|
56
|
+
case 'tools':
|
|
57
|
+
(0, tools_1.addTools)(info, rest);
|
|
58
|
+
break;
|
|
59
|
+
case 'mcp-client':
|
|
60
|
+
(0, mcp_client_1.addMcpClient)(info, rest);
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
console.error(`\n✗ Unknown module: "${module}"\n`);
|
|
64
|
+
console.log(HELP);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addMcpClient = addMcpClient;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const patcher_1 = require("../patcher");
|
|
39
|
+
function addMcpClient(info, args) {
|
|
40
|
+
const url = args[0];
|
|
41
|
+
if (!url) {
|
|
42
|
+
console.error('\n✗ Usage: add mcp-client <url> [--name <name>]\n');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
let name = url.split('/').filter(Boolean).pop() ?? 'remote-agent';
|
|
46
|
+
for (let i = 1; i < args.length; i++) {
|
|
47
|
+
if (args[i] === '--name' && args[i + 1]) {
|
|
48
|
+
name = args[++i];
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!fs.existsSync(info.engineFile)) {
|
|
53
|
+
console.error('\n✗ src/engine.ts not found.\n');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const ok = (0, patcher_1.addMcpServerEntry)(info.engineFile, name, url);
|
|
57
|
+
if (ok) {
|
|
58
|
+
console.log(`\n✓ Patched src/engine.ts (mcpServers: { name: '${name}', url: '${url}' })`);
|
|
59
|
+
console.log(`\nNext steps:\n npm run dev\n`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.error('\n✗ Could not locate AgentEngine constructor to inject mcpServers.\n');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addServer = addServer;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const patcher_1 = require("../patcher");
|
|
40
|
+
function parseArgs(args, defaultName) {
|
|
41
|
+
let port = 3001;
|
|
42
|
+
let name = defaultName;
|
|
43
|
+
for (let i = 0; i < args.length; i++) {
|
|
44
|
+
if (args[i] === '--port' && args[i + 1])
|
|
45
|
+
port = parseInt(args[++i]);
|
|
46
|
+
if (args[i] === '--name' && args[i + 1])
|
|
47
|
+
name = args[++i];
|
|
48
|
+
}
|
|
49
|
+
return { port, name };
|
|
50
|
+
}
|
|
51
|
+
function generateServerTs(info, opts) {
|
|
52
|
+
return `import 'dotenv/config';
|
|
53
|
+
import { AgentMCPServer } from '@backendkit-labs/mcp-server';
|
|
54
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
55
|
+
import { z } from 'zod';
|
|
56
|
+
import { ${info.engineFnName} } from './engine';
|
|
57
|
+
|
|
58
|
+
const server = new AgentMCPServer({
|
|
59
|
+
name: '${opts.name}',
|
|
60
|
+
version: '1.0.0',
|
|
61
|
+
port: parseInt(process.env.MCP_PORT ?? '${opts.port}'),
|
|
62
|
+
|
|
63
|
+
tools: [
|
|
64
|
+
{
|
|
65
|
+
name: 'agent_request',
|
|
66
|
+
description: 'TODO: describe what requests this agent handles',
|
|
67
|
+
schema: {
|
|
68
|
+
request: z.string().describe('Natural-language request for the agent'),
|
|
69
|
+
},
|
|
70
|
+
buildPrompt: ({ request }: { request: string }) => request,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
engineFactory: (transport: CallbackTransport) => ${info.engineFnName}(transport),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await server.start();
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
function addServer(info, args) {
|
|
81
|
+
const opts = parseArgs(args, path.basename(info.cwd));
|
|
82
|
+
if (fs.existsSync(info.serverFile)) {
|
|
83
|
+
console.error(`\n✗ src/server.ts already exists. Remove it first to regenerate.\n`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
fs.writeFileSync(info.serverFile, generateServerTs(info, opts));
|
|
87
|
+
console.log(`\n✓ Created src/server.ts (HTTP MCP on port ${opts.port})`);
|
|
88
|
+
const added = (0, patcher_1.addPackageDependency)(info.pkgFile, '@backendkit-labs/mcp-server', '^0.16.0');
|
|
89
|
+
(0, patcher_1.addPackageDependency)(info.pkgFile, 'zod', '^3.25.0');
|
|
90
|
+
(0, patcher_1.addPackageScript)(info.pkgFile, 'server', 'ts-node src/server.ts');
|
|
91
|
+
if (added) {
|
|
92
|
+
console.log(`✓ package.json (@backendkit-labs/mcp-server added)`);
|
|
93
|
+
}
|
|
94
|
+
console.log(`✓ package.json (script "server" added)`);
|
|
95
|
+
console.log(`
|
|
96
|
+
Next steps:
|
|
97
|
+
npm install
|
|
98
|
+
npm run server # starts HTTP MCP on port ${opts.port}
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addSpecialist = addSpecialist;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const patcher_1 = require("../patcher");
|
|
40
|
+
function toConstName(name) {
|
|
41
|
+
return name.replace(/-/g, '_').toUpperCase() + '_PROFILE';
|
|
42
|
+
}
|
|
43
|
+
function toDisplayName(name) {
|
|
44
|
+
return name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
45
|
+
}
|
|
46
|
+
function generateSpecialistTs(name) {
|
|
47
|
+
const constName = toConstName(name);
|
|
48
|
+
return `import type { AgentProfile } from '@backendkit-labs/agent-core';
|
|
49
|
+
|
|
50
|
+
// TODO: Rename this file and export to match your domain
|
|
51
|
+
export const ${constName}: AgentProfile = {
|
|
52
|
+
id: '${name}',
|
|
53
|
+
name: '${toDisplayName(name)}',
|
|
54
|
+
icon: '🔧',
|
|
55
|
+
description: 'TODO: describe what this specialist does',
|
|
56
|
+
delegatesTo: [],
|
|
57
|
+
allowedTools: [
|
|
58
|
+
// TODO: list the tool names this agent can use
|
|
59
|
+
],
|
|
60
|
+
systemPrompt: \`You are a specialist in TODO: <your domain area>.
|
|
61
|
+
|
|
62
|
+
Use the available tools to complete the task delegated to you.
|
|
63
|
+
Be precise and report results clearly.\`,
|
|
64
|
+
};
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
function addSpecialist(info, args) {
|
|
68
|
+
const name = args[0];
|
|
69
|
+
if (!name) {
|
|
70
|
+
console.error('\n✗ Usage: add specialist <name>\n');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const agentFile = path.join(info.cwd, 'src', 'agents', `${name}.ts`);
|
|
74
|
+
if (fs.existsSync(agentFile)) {
|
|
75
|
+
console.error(`\n✗ src/agents/${name}.ts already exists.\n`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
fs.writeFileSync(agentFile, generateSpecialistTs(name));
|
|
79
|
+
console.log(`\n✓ Created src/agents/${name}.ts`);
|
|
80
|
+
if (fs.existsSync(info.engineFile)) {
|
|
81
|
+
const constName = toConstName(name);
|
|
82
|
+
(0, patcher_1.addImportLine)(info.engineFile, `import { ${constName} } from './agents/${name}';`);
|
|
83
|
+
// Primary: insert after TODO comment; fallback: after last .register( call
|
|
84
|
+
const inserted = (0, patcher_1.insertAfterPattern)(info.engineFile, /\/\/ TODO: register more specialists/, ` agents.register(${constName});`) ||
|
|
85
|
+
(0, patcher_1.insertAfterPattern)(info.engineFile, /agents\.register\(/, ` agents.register(${constName});`);
|
|
86
|
+
if (inserted) {
|
|
87
|
+
console.log(`✓ Patched src/engine.ts (agents.register(${constName}))`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log(`⚠ Could not auto-patch engine.ts — add manually: agents.register(${constName})`);
|
|
91
|
+
}
|
|
92
|
+
const orchestratorFile = path.join(info.cwd, 'src', 'agents', 'orchestrator.ts');
|
|
93
|
+
if (fs.existsSync(orchestratorFile)) {
|
|
94
|
+
console.log(`\n Reminder: add '${name}' to orchestrator.ts delegatesTo[] and update the systemPrompt.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
console.log(`\nNext steps:\n Implement src/agents/${name}.ts\n npm run dev\n`);
|
|
98
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addTools = addTools;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const patcher_1 = require("../patcher");
|
|
40
|
+
function toCamelCase(name) {
|
|
41
|
+
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
42
|
+
}
|
|
43
|
+
function generateToolsTs(domain) {
|
|
44
|
+
const fn = toCamelCase(domain);
|
|
45
|
+
return `import { defineTool, z } from '@backendkit-labs/agent-core';
|
|
46
|
+
|
|
47
|
+
// TODO: replace with real ${domain} SDK/API calls
|
|
48
|
+
|
|
49
|
+
export const ${fn}Action = defineTool({
|
|
50
|
+
name: '${domain}_action',
|
|
51
|
+
description: 'TODO: describe what this tool does',
|
|
52
|
+
input: z.object({
|
|
53
|
+
target: z.string().describe('Target to act on'),
|
|
54
|
+
// TODO: add more parameters
|
|
55
|
+
}),
|
|
56
|
+
execute: async ({ target }) => {
|
|
57
|
+
// TODO: implement
|
|
58
|
+
return \`${domain} action on: \${target}\`;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const ${fn}Query = defineTool({
|
|
63
|
+
name: '${domain}_query',
|
|
64
|
+
description: 'TODO: describe what this query returns',
|
|
65
|
+
input: z.object({
|
|
66
|
+
filter: z.string().optional().describe('Optional filter'),
|
|
67
|
+
}),
|
|
68
|
+
execute: async ({ filter }) => {
|
|
69
|
+
// TODO: implement
|
|
70
|
+
return JSON.stringify({ results: [], filter });
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
75
|
+
function addTools(info, args) {
|
|
76
|
+
const domain = args[0];
|
|
77
|
+
if (!domain) {
|
|
78
|
+
console.error('\n✗ Usage: add tools <domain>\n');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const toolsFile = path.join(info.cwd, 'src', 'tools', `${domain}-tools.ts`);
|
|
82
|
+
if (fs.existsSync(toolsFile)) {
|
|
83
|
+
console.error(`\n✗ src/tools/${domain}-tools.ts already exists.\n`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
fs.mkdirSync(path.dirname(toolsFile), { recursive: true });
|
|
87
|
+
fs.writeFileSync(toolsFile, generateToolsTs(domain));
|
|
88
|
+
console.log(`\n✓ Created src/tools/${domain}-tools.ts`);
|
|
89
|
+
if (fs.existsSync(info.engineFile)) {
|
|
90
|
+
const fn = toCamelCase(domain);
|
|
91
|
+
(0, patcher_1.addImportLine)(info.engineFile, `import { ${fn}Action, ${fn}Query } from './tools/${domain}-tools';`);
|
|
92
|
+
const inserted = (0, patcher_1.insertAfterPattern)(info.engineFile, /\/\/ TODO: register more tools/, ` tools.register(${fn}Action).register(${fn}Query);`) ||
|
|
93
|
+
(0, patcher_1.insertAfterPattern)(info.engineFile, /tools\.register\(/, ` tools.register(${fn}Action).register(${fn}Query);`);
|
|
94
|
+
if (inserted) {
|
|
95
|
+
console.log(`✓ Patched src/engine.ts (tools.register(${fn}Action, ${fn}Query))`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(`⚠ Could not auto-patch engine.ts — add manually: tools.register(${fn}Action).register(${fn}Query)`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
console.log(`\nNext steps:\n Implement src/tools/${domain}-tools.ts\n npm run dev\n`);
|
|
102
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addImportLine = addImportLine;
|
|
37
|
+
exports.insertAfterPattern = insertAfterPattern;
|
|
38
|
+
exports.insertBeforePattern = insertBeforePattern;
|
|
39
|
+
exports.addPackageScript = addPackageScript;
|
|
40
|
+
exports.addPackageDependency = addPackageDependency;
|
|
41
|
+
exports.addMcpServerEntry = addMcpServerEntry;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
// ── Import block ──────────────────────────────────────────────────────────────
|
|
44
|
+
/** Adds `importLine` after the last import statement. No-op if already present. */
|
|
45
|
+
function addImportLine(file, importLine) {
|
|
46
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
47
|
+
if (content.includes(importLine.trim()))
|
|
48
|
+
return;
|
|
49
|
+
const lines = content.split('\n');
|
|
50
|
+
let lastImportIdx = -1;
|
|
51
|
+
for (let i = 0; i < lines.length; i++) {
|
|
52
|
+
if (/^import\s/.test(lines[i]))
|
|
53
|
+
lastImportIdx = i;
|
|
54
|
+
}
|
|
55
|
+
if (lastImportIdx === -1) {
|
|
56
|
+
fs.writeFileSync(file, importLine + '\n' + content);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
lines.splice(lastImportIdx + 1, 0, importLine);
|
|
60
|
+
fs.writeFileSync(file, lines.join('\n'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ── Line insertion ────────────────────────────────────────────────────────────
|
|
64
|
+
/**
|
|
65
|
+
* Inserts `newLine` after the LAST line that matches `pattern`.
|
|
66
|
+
* Returns true if the insertion was done, false if the pattern wasn't found.
|
|
67
|
+
* No-op if `newLine` (trimmed) already exists in the file.
|
|
68
|
+
*/
|
|
69
|
+
function insertAfterPattern(file, pattern, newLine) {
|
|
70
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
71
|
+
if (content.includes(newLine.trim()))
|
|
72
|
+
return true;
|
|
73
|
+
const lines = content.split('\n');
|
|
74
|
+
let lastIdx = -1;
|
|
75
|
+
for (let i = 0; i < lines.length; i++) {
|
|
76
|
+
if (pattern.test(lines[i]))
|
|
77
|
+
lastIdx = i;
|
|
78
|
+
}
|
|
79
|
+
if (lastIdx === -1)
|
|
80
|
+
return false;
|
|
81
|
+
lines.splice(lastIdx + 1, 0, newLine);
|
|
82
|
+
fs.writeFileSync(file, lines.join('\n'));
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Inserts `newLine` before the FIRST line that matches `pattern`.
|
|
87
|
+
* Returns true if the insertion was done, false if the pattern wasn't found.
|
|
88
|
+
* No-op if `newLine` (trimmed) already exists in the file.
|
|
89
|
+
*/
|
|
90
|
+
function insertBeforePattern(file, pattern, newLine) {
|
|
91
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
92
|
+
if (content.includes(newLine.trim()))
|
|
93
|
+
return true;
|
|
94
|
+
const lines = content.split('\n');
|
|
95
|
+
let firstIdx = -1;
|
|
96
|
+
for (let i = 0; i < lines.length; i++) {
|
|
97
|
+
if (pattern.test(lines[i])) {
|
|
98
|
+
firstIdx = i;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (firstIdx === -1)
|
|
103
|
+
return false;
|
|
104
|
+
lines.splice(firstIdx, 0, newLine);
|
|
105
|
+
fs.writeFileSync(file, lines.join('\n'));
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// ── package.json helpers ──────────────────────────────────────────────────────
|
|
109
|
+
function addPackageScript(pkgFile, name, cmd) {
|
|
110
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
111
|
+
pkg.scripts ??= {};
|
|
112
|
+
pkg.scripts[name] = cmd;
|
|
113
|
+
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + '\n');
|
|
114
|
+
}
|
|
115
|
+
/** Adds `depName@version` to dependencies if not already present. */
|
|
116
|
+
function addPackageDependency(pkgFile, depName, version) {
|
|
117
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
118
|
+
pkg.dependencies ??= {};
|
|
119
|
+
if (pkg.dependencies[depName])
|
|
120
|
+
return false;
|
|
121
|
+
pkg.dependencies[depName] = version;
|
|
122
|
+
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + '\n');
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// ── AgentEngine options patcher ───────────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Adds `mcpServers: [{ name, url }]` to the first AgentEngine constructor call.
|
|
128
|
+
* If mcpServers already exists, appends a new entry to the existing array.
|
|
129
|
+
* Returns true on success.
|
|
130
|
+
*/
|
|
131
|
+
function addMcpServerEntry(file, name, url) {
|
|
132
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
133
|
+
const entry = ` { name: '${name}', url: '${url}' },`;
|
|
134
|
+
if (content.includes(`url: '${url}'`))
|
|
135
|
+
return true; // already registered
|
|
136
|
+
if (content.includes('mcpServers:')) {
|
|
137
|
+
// Append into existing array — insert after 'mcpServers: ['
|
|
138
|
+
const updated = content.replace(/(mcpServers:\s*\[)/, `$1\n${entry}`);
|
|
139
|
+
if (updated === content)
|
|
140
|
+
return false;
|
|
141
|
+
fs.writeFileSync(file, updated);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
// Insert new mcpServers block before the closing of AgentEngine({ ... })
|
|
145
|
+
const lines = content.split('\n');
|
|
146
|
+
let depth = 0;
|
|
147
|
+
let engineStart = -1;
|
|
148
|
+
let closeIdx = -1;
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
if (lines[i].includes('new AgentEngine({')) {
|
|
151
|
+
engineStart = i;
|
|
152
|
+
depth = 0;
|
|
153
|
+
}
|
|
154
|
+
if (engineStart !== -1) {
|
|
155
|
+
depth += (lines[i].match(/\{/g) ?? []).length;
|
|
156
|
+
depth -= (lines[i].match(/\}/g) ?? []).length;
|
|
157
|
+
if (depth === 0 && i > engineStart) {
|
|
158
|
+
closeIdx = i;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (closeIdx === -1)
|
|
164
|
+
return false;
|
|
165
|
+
lines.splice(closeIdx, 0, ` mcpServers: [\n${entry}\n ],`);
|
|
166
|
+
fs.writeFileSync(file, lines.join('\n'));
|
|
167
|
+
return true;
|
|
168
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -38,6 +38,7 @@ const path = __importStar(require("path"));
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const prompts_1 = require("./prompts");
|
|
40
40
|
const scaffold_1 = require("./scaffold");
|
|
41
|
+
const index_1 = require("./add/index");
|
|
41
42
|
// Read version from package.json so it always matches the published version
|
|
42
43
|
const FRAMEWORK_VERSION = (() => {
|
|
43
44
|
try {
|
|
@@ -48,14 +49,59 @@ const FRAMEWORK_VERSION = (() => {
|
|
|
48
49
|
return '0.16.0';
|
|
49
50
|
}
|
|
50
51
|
})();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
// ── Human-readable label for the scaffold banner ──────────────────────────────
|
|
53
|
+
function describeChoices(choices) {
|
|
54
|
+
const arch = {
|
|
55
|
+
'multi-agent': 'Multi-agent',
|
|
56
|
+
single: 'Single-agent',
|
|
57
|
+
coding: 'Coding assistant',
|
|
58
|
+
autonomous: 'Autonomous daemon',
|
|
59
|
+
minimal: 'Minimal / embedded',
|
|
60
|
+
};
|
|
61
|
+
const deploy = {
|
|
62
|
+
repl: 'Terminal REPL',
|
|
63
|
+
'mcp-http': 'MCP Server (HTTP)',
|
|
64
|
+
'mcp-stdio': 'MCP Server (stdio)',
|
|
65
|
+
hybrid: 'REPL + MCP Server',
|
|
66
|
+
library: 'Library',
|
|
67
|
+
};
|
|
68
|
+
const archLabel = arch[choices.architecture] ?? choices.architecture;
|
|
69
|
+
if (!choices.deployMode)
|
|
70
|
+
return archLabel;
|
|
71
|
+
return `${archLabel} — ${deploy[choices.deployMode] ?? choices.deployMode}`;
|
|
72
|
+
}
|
|
73
|
+
// ── Next steps after scaffold ─────────────────────────────────────────────────
|
|
74
|
+
function buildNextSteps(choices) {
|
|
75
|
+
const { architecture: arch, deployMode: mode } = choices;
|
|
76
|
+
// Self-contained architectures
|
|
77
|
+
if (arch === 'coding')
|
|
78
|
+
return ['Edit AGENT.md to describe your project', 'npm run dev'];
|
|
79
|
+
if (arch === 'autonomous')
|
|
80
|
+
return ['Configure triggers in src/index.ts', 'npm run dev'];
|
|
81
|
+
if (arch === 'minimal')
|
|
82
|
+
return ['Implement your LLM provider in src/index.ts', 'npm run dev'];
|
|
83
|
+
// Layered architectures (multi-agent, single) — vary by deploy mode
|
|
84
|
+
const domainSteps = arch === 'multi-agent'
|
|
85
|
+
? ['Rename agents/ and tools/ to match your domain', 'Implement tools in src/tools/']
|
|
86
|
+
: ['Implement tools in src/tools/'];
|
|
87
|
+
if (mode === 'repl')
|
|
88
|
+
return [...domainSteps, 'npm run dev'];
|
|
89
|
+
if (mode === 'mcp-http')
|
|
90
|
+
return [...domainSteps, 'npm run server # starts HTTP MCP on port 3001'];
|
|
91
|
+
if (mode === 'mcp-stdio')
|
|
92
|
+
return [...domainSteps, 'npm run build', 'node dist/server.js'];
|
|
93
|
+
if (mode === 'hybrid')
|
|
94
|
+
return [...domainSteps, 'npm run dev', '# in another terminal:', 'npm run server'];
|
|
95
|
+
if (mode === 'library')
|
|
96
|
+
return [...domainSteps, '# import { createEngine } from \'./src/engine\' in your app'];
|
|
97
|
+
return ['npm run dev'];
|
|
98
|
+
}
|
|
99
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
58
100
|
async function main(argv) {
|
|
101
|
+
if (argv[2] === 'add') {
|
|
102
|
+
(0, index_1.runAdd)(argv.slice(3), process.cwd());
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
59
105
|
const projectArg = argv[2];
|
|
60
106
|
console.log(`\n✨ create-bk-agent v${FRAMEWORK_VERSION}\n`);
|
|
61
107
|
const defaultName = projectArg ?? 'my-agent';
|
|
@@ -69,7 +115,7 @@ async function main(argv) {
|
|
|
69
115
|
console.error(`\n✗ Directory already exists: ${targetDir}`);
|
|
70
116
|
process.exit(1);
|
|
71
117
|
}
|
|
72
|
-
console.log(`\nCreating ${choices
|
|
118
|
+
console.log(`\nCreating ${describeChoices(choices)} agent in ./${choices.projectName} ...`);
|
|
73
119
|
try {
|
|
74
120
|
(0, scaffold_1.scaffold)(choices, targetDir);
|
|
75
121
|
}
|
|
@@ -79,7 +125,7 @@ async function main(argv) {
|
|
|
79
125
|
}
|
|
80
126
|
console.log('Installing dependencies...\n');
|
|
81
127
|
(0, scaffold_1.installDeps)(targetDir);
|
|
82
|
-
const steps =
|
|
128
|
+
const steps = buildNextSteps(choices);
|
|
83
129
|
console.log(`
|
|
84
130
|
✅ Done! Your agent is ready.
|
|
85
131
|
|
package/dist/src/prompts.js
CHANGED
|
@@ -5,21 +5,81 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.askQuestions = askQuestions;
|
|
7
7
|
const prompts_1 = __importDefault(require("prompts"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
// ── Option definitions ────────────────────────────────────────────────────────
|
|
9
|
+
const p = (s, n) => s.padEnd(n);
|
|
10
|
+
const ARCHITECTURE_CHOICES = [
|
|
11
|
+
{
|
|
12
|
+
title: p('Multi-agent', 22) + 'Orchestrator delegates to specialized sub-agents',
|
|
13
|
+
value: 'multi-agent',
|
|
14
|
+
description: 'Best for complex domains (git: commit / branch / merge / recovery)',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
title: p('Single agent', 22) + 'One agent handles all requests with tools',
|
|
18
|
+
value: 'single',
|
|
19
|
+
description: 'Best for focused single-domain tasks and quick prototypes',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
title: p('Coding assistant', 22) + '10 pre-built agents for software dev — ready to run',
|
|
23
|
+
value: 'coding',
|
|
24
|
+
description: 'Just add your API key and run',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: p('Autonomous daemon', 22) + 'Cron / webhook triggers, no human input',
|
|
28
|
+
value: 'autonomous',
|
|
29
|
+
description: 'Best for background monitoring, alerts, automated reports',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: p('Minimal / embedded', 22) + 'Bare engine factory, full control',
|
|
33
|
+
value: 'minimal',
|
|
34
|
+
description: 'Best for embedding in an existing app or custom architecture',
|
|
35
|
+
},
|
|
14
36
|
];
|
|
15
|
-
const
|
|
16
|
-
{ title: '
|
|
17
|
-
{ title: '
|
|
18
|
-
{ title: '
|
|
19
|
-
{ title:
|
|
37
|
+
const SKILL_PACK_CHOICES = [
|
|
38
|
+
{ title: p('Node.js / TypeScript', 24) + 'npm, jest, tsc, eslint', value: 'node' },
|
|
39
|
+
{ title: p('Python', 24) + 'pytest, ruff, mypy, pip', value: 'python' },
|
|
40
|
+
{ title: p('Go', 24) + 'go test, golangci-lint', value: 'go' },
|
|
41
|
+
{ title: p('Docker / K8s', 24) + 'docker, kubectl, helm', value: 'docker' },
|
|
20
42
|
];
|
|
43
|
+
const DEPLOY_MODE_CHOICES = [
|
|
44
|
+
{
|
|
45
|
+
title: p('Terminal REPL', 28) + 'Humans type requests, agent responds in the terminal',
|
|
46
|
+
value: 'repl',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: p('MCP Server — HTTP', 28) + 'Other agents / tools connect via HTTP protocol',
|
|
50
|
+
value: 'mcp-http',
|
|
51
|
+
description: 'e.g. git-agent server that another bk-agent connects to',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: p('MCP Server — stdio', 28) + 'Claude Desktop / Cursor / VS Code integration',
|
|
55
|
+
value: 'mcp-stdio',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: p('REPL + MCP Server', 28) + 'Hybrid — humans use it AND other agents connect',
|
|
59
|
+
value: 'hybrid',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
title: p('Library only', 28) + 'No entry point — imported programmatically',
|
|
63
|
+
value: 'library',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
const PROVIDER_CHOICES = [
|
|
67
|
+
{ title: p('Anthropic (Claude)', 32) + 'claude-sonnet-4-6 recommended', value: 'anthropic' },
|
|
68
|
+
{ title: p('OpenAI', 32) + 'gpt-4o recommended', value: 'openai' },
|
|
69
|
+
{ title: p('DeepSeek / OpenAI-compatible', 32) + 'deepseek-chat or any OpenAI-compatible endpoint', value: 'deepseek' },
|
|
70
|
+
{ title: p('Ollama (local)', 32) + 'llama3.2, codellama — no API key needed', value: 'ollama' },
|
|
71
|
+
];
|
|
72
|
+
// ── Conditional question helpers ──────────────────────────────────────────────
|
|
73
|
+
const NEEDS_SKILLS = ['multi-agent', 'single'];
|
|
74
|
+
const NEEDS_DEPLOY = ['multi-agent', 'single', 'coding'];
|
|
75
|
+
// @types/prompts doesn't fully type the function form of `type` — cast at call site.
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
const whenSkills = (_prev, v) => NEEDS_SKILLS.includes(v.architecture) ? 'multiselect' : null;
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
const whenDeploy = (_prev, v) => NEEDS_DEPLOY.includes(v.architecture) ? 'select' : null;
|
|
80
|
+
// ── Main question flow ────────────────────────────────────────────────────────
|
|
21
81
|
async function askQuestions(defaultName) {
|
|
22
|
-
const
|
|
82
|
+
const raw = await (0, prompts_1.default)([
|
|
23
83
|
{
|
|
24
84
|
type: 'text',
|
|
25
85
|
name: 'projectName',
|
|
@@ -29,26 +89,43 @@ async function askQuestions(defaultName) {
|
|
|
29
89
|
},
|
|
30
90
|
{
|
|
31
91
|
type: 'select',
|
|
32
|
-
name: '
|
|
33
|
-
message: '
|
|
34
|
-
choices:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
92
|
+
name: 'architecture',
|
|
93
|
+
message: 'What type of agent are you building?',
|
|
94
|
+
choices: ARCHITECTURE_CHOICES,
|
|
95
|
+
initial: 0,
|
|
96
|
+
},
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
{
|
|
99
|
+
type: whenSkills,
|
|
100
|
+
name: 'skillPacks',
|
|
101
|
+
message: 'Include skill packs? (space to toggle, enter to confirm)',
|
|
102
|
+
choices: SKILL_PACK_CHOICES,
|
|
103
|
+
instructions: false,
|
|
104
|
+
hint: 'Adds domain-specific instructions to your agents. Can be skipped.',
|
|
105
|
+
},
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
{
|
|
108
|
+
type: whenDeploy,
|
|
109
|
+
name: 'deployMode',
|
|
110
|
+
message: 'How will this agent be used?',
|
|
111
|
+
choices: DEPLOY_MODE_CHOICES,
|
|
38
112
|
initial: 0,
|
|
39
113
|
},
|
|
40
114
|
{
|
|
41
115
|
type: 'select',
|
|
42
116
|
name: 'provider',
|
|
43
117
|
message: 'LLM provider:',
|
|
44
|
-
choices:
|
|
45
|
-
title: p.title.padEnd(30) + ' ' + p.description,
|
|
46
|
-
value: p.value,
|
|
47
|
-
})),
|
|
118
|
+
choices: PROVIDER_CHOICES,
|
|
48
119
|
initial: 0,
|
|
49
120
|
},
|
|
50
|
-
], { onCancel: () =>
|
|
51
|
-
if (!
|
|
121
|
+
], { onCancel: () => false });
|
|
122
|
+
if (!raw.projectName || !raw.architecture || !raw.provider)
|
|
52
123
|
return null;
|
|
53
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
projectName: raw.projectName,
|
|
126
|
+
architecture: raw.architecture,
|
|
127
|
+
skillPacks: (raw.skillPacks ?? []),
|
|
128
|
+
deployMode: (raw.deployMode ?? undefined),
|
|
129
|
+
provider: raw.provider,
|
|
130
|
+
};
|
|
54
131
|
}
|
package/dist/src/scaffold.js
CHANGED
|
@@ -39,6 +39,11 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
41
|
const TEMPLATES_DIR = path.resolve(__dirname, '../../templates');
|
|
42
|
+
const BASE_DIR = path.join(TEMPLATES_DIR, 'base');
|
|
43
|
+
const DEPLOY_DIR = path.join(TEMPLATES_DIR, 'deploy');
|
|
44
|
+
// Only these architectures use the base + deploy layer pattern.
|
|
45
|
+
// The rest (coding, autonomous, minimal) are self-contained in base/.
|
|
46
|
+
const LAYERED = ['multi-agent', 'single'];
|
|
42
47
|
// ── Provider env var hints ────────────────────────────────────────────────────
|
|
43
48
|
const PROVIDER_ENV = {
|
|
44
49
|
anthropic: { varName: 'ANTHROPIC_API_KEY', placeholder: 'sk-ant-...', model: 'claude-sonnet-4-6' },
|
|
@@ -46,54 +51,118 @@ const PROVIDER_ENV = {
|
|
|
46
51
|
deepseek: { varName: 'LLM_API_KEY', placeholder: 'sk-...', model: 'deepseek-chat' },
|
|
47
52
|
ollama: { varName: 'OLLAMA_BASE_URL', placeholder: 'http://localhost:11434', model: 'llama3.2' },
|
|
48
53
|
};
|
|
49
|
-
// ── Copy
|
|
54
|
+
// ── Copy directory (overlay-safe) ─────────────────────────────────────────────
|
|
55
|
+
// dst may already exist from the base layer — files are overwritten, dirs merged.
|
|
50
56
|
function copyDir(src, dst) {
|
|
51
57
|
fs.mkdirSync(dst, { recursive: true });
|
|
52
58
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
53
59
|
const srcPath = path.join(src, entry.name);
|
|
54
60
|
const dstPath = path.join(dst, entry.name);
|
|
55
|
-
|
|
56
|
-
copyDir(srcPath, dstPath);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
fs.copyFileSync(srcPath, dstPath);
|
|
60
|
-
}
|
|
61
|
+
entry.isDirectory() ? copyDir(srcPath, dstPath) : fs.copyFileSync(srcPath, dstPath);
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
|
-
// ──
|
|
64
|
-
|
|
64
|
+
// ── Patch package.json — name + scripts ──────────────────────────────────────
|
|
65
|
+
const DEPLOY_SCRIPTS = {
|
|
66
|
+
repl: (s) => {
|
|
67
|
+
delete s['server'];
|
|
68
|
+
},
|
|
69
|
+
'mcp-http': (s) => {
|
|
70
|
+
s['dev'] = 'ts-node src/server.ts';
|
|
71
|
+
s['start'] = 'node dist/server.js';
|
|
72
|
+
delete s['server'];
|
|
73
|
+
},
|
|
74
|
+
'mcp-stdio': (s) => {
|
|
75
|
+
s['dev'] = 'ts-node src/server.ts';
|
|
76
|
+
s['start'] = 'node dist/server.js';
|
|
77
|
+
delete s['server'];
|
|
78
|
+
},
|
|
79
|
+
// hybrid: keep both dev (REPL) and server (MCP) — no changes
|
|
80
|
+
library: (s) => {
|
|
81
|
+
delete s['dev'];
|
|
82
|
+
delete s['server'];
|
|
83
|
+
delete s['start'];
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
function patchPackageJson(dir, projectName, deployMode) {
|
|
65
87
|
const pkgPath = path.join(dir, 'package.json');
|
|
66
88
|
if (!fs.existsSync(pkgPath))
|
|
67
89
|
return;
|
|
68
90
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
69
91
|
pkg.name = projectName;
|
|
92
|
+
if (deployMode && DEPLOY_SCRIPTS[deployMode]) {
|
|
93
|
+
pkg.scripts ??= {};
|
|
94
|
+
DEPLOY_SCRIPTS[deployMode](pkg.scripts);
|
|
95
|
+
}
|
|
70
96
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
71
97
|
}
|
|
72
|
-
// ── Write .env
|
|
98
|
+
// ── Write .env stub ───────────────────────────────────────────────────────────
|
|
73
99
|
function writeEnv(dir, provider) {
|
|
74
100
|
const envExample = path.join(dir, '.env.example');
|
|
75
101
|
const envFile = path.join(dir, '.env');
|
|
76
|
-
// Only create .env if .env.example exists and .env doesn't
|
|
77
102
|
if (!fs.existsSync(envExample) || fs.existsSync(envFile))
|
|
78
103
|
return;
|
|
79
104
|
const p = PROVIDER_ENV[provider];
|
|
80
|
-
const content =
|
|
105
|
+
const content = [
|
|
106
|
+
'# Generated by create-bk-agent',
|
|
107
|
+
`${p.varName}=${p.placeholder}`,
|
|
108
|
+
`LLM_PROVIDER=${provider}`,
|
|
109
|
+
`LLM_MODEL=${p.model}`,
|
|
110
|
+
...(provider !== 'anthropic' && provider !== 'ollama'
|
|
111
|
+
? ['LLM_BASE_URL=https://api.openai.com/v1']
|
|
112
|
+
: []),
|
|
113
|
+
'',
|
|
114
|
+
].join('\n');
|
|
81
115
|
fs.writeFileSync(envFile, content);
|
|
82
116
|
}
|
|
117
|
+
// ── Inject skill pack hint into engine.ts ─────────────────────────────────────
|
|
118
|
+
// Appends a clearly marked comment block so the developer knows what to wire.
|
|
119
|
+
// Full YAML-based skill injection is handled by `create-bk-agent add skills`.
|
|
120
|
+
const SKILL_HINTS = {
|
|
121
|
+
node: 'npm, jest, tsc, eslint — Node.js / TypeScript conventions',
|
|
122
|
+
python: 'pytest, ruff, mypy, pip — Python conventions',
|
|
123
|
+
go: 'go test, golangci-lint — Go conventions',
|
|
124
|
+
docker: 'docker, kubectl, helm — Docker / K8s conventions',
|
|
125
|
+
};
|
|
126
|
+
function injectSkillPack(targetDir, pack) {
|
|
127
|
+
const engineFile = path.join(targetDir, 'src', 'engine.ts');
|
|
128
|
+
if (!fs.existsSync(engineFile))
|
|
129
|
+
return;
|
|
130
|
+
const block = [
|
|
131
|
+
'',
|
|
132
|
+
`// ── Skill pack: ${pack} ──────────────────────────────────────────────────────`,
|
|
133
|
+
`// ${SKILL_HINTS[pack]}`,
|
|
134
|
+
`// To activate: add YAML skill files in .backendkit-agent/skills/`,
|
|
135
|
+
`// and wire SkillLoader + SkillActivator into the engine above.`,
|
|
136
|
+
].join('\n');
|
|
137
|
+
fs.appendFileSync(engineFile, block + '\n');
|
|
138
|
+
}
|
|
83
139
|
// ── Main scaffold ─────────────────────────────────────────────────────────────
|
|
84
140
|
function scaffold(choices, targetDir) {
|
|
85
|
-
const templateDir = path.join(TEMPLATES_DIR, choices.template);
|
|
86
|
-
if (!fs.existsSync(templateDir)) {
|
|
87
|
-
throw new Error(`Template not found: ${choices.template} (looked in ${templateDir})`);
|
|
88
|
-
}
|
|
89
141
|
if (fs.existsSync(targetDir)) {
|
|
90
142
|
throw new Error(`Directory already exists: ${targetDir}`);
|
|
91
143
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
144
|
+
const isLayered = LAYERED.includes(choices.architecture);
|
|
145
|
+
// 1. Base layer — always copied
|
|
146
|
+
const baseDir = path.join(BASE_DIR, choices.architecture);
|
|
147
|
+
if (!fs.existsSync(baseDir)) {
|
|
148
|
+
throw new Error(`Base template not found: ${choices.architecture} (in ${baseDir})`);
|
|
149
|
+
}
|
|
150
|
+
copyDir(baseDir, targetDir);
|
|
151
|
+
// 2. Deploy layer — overlaid on top of base (layered architectures only)
|
|
152
|
+
if (isLayered && choices.deployMode && choices.deployMode !== 'library') {
|
|
153
|
+
const deployDir = path.join(DEPLOY_DIR, choices.deployMode);
|
|
154
|
+
if (!fs.existsSync(deployDir)) {
|
|
155
|
+
throw new Error(`Deploy template not found: ${choices.deployMode} (in ${deployDir})`);
|
|
156
|
+
}
|
|
157
|
+
copyDir(deployDir, targetDir);
|
|
158
|
+
}
|
|
159
|
+
// 3. Skill packs — appended to engine.ts
|
|
160
|
+
for (const pack of choices.skillPacks) {
|
|
161
|
+
injectSkillPack(targetDir, pack);
|
|
162
|
+
}
|
|
163
|
+
// 4. Patch package.json (name + deploy-mode script adjustments)
|
|
164
|
+
patchPackageJson(targetDir, choices.projectName, isLayered ? choices.deployMode : undefined);
|
|
165
|
+
// 5. .env stub
|
|
97
166
|
writeEnv(targetDir, choices.provider);
|
|
98
167
|
}
|
|
99
168
|
function installDeps(targetDir) {
|