@elizaos/cli 1.0.8 → 1.0.10
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 +9 -16
- package/dist/assets/{index-CZAd5zm2.js → index-CmuPnu0u.js} +72 -89
- package/dist/assets/index-CmuPnu0u.js.br +0 -0
- package/dist/assets/index-CmuPnu0u.js.map +1 -0
- package/dist/assets/{index-CaEsCLCC.js → index-DDQnwxzL.js} +28798 -16391
- package/dist/assets/{index-CaEsCLCC.js.map → index-DDQnwxzL.js.map} +1 -1
- package/dist/assets/index-Df1AFSuJ.css +1 -0
- package/dist/assets/index-Df1AFSuJ.css.br +0 -0
- package/dist/assets/vendor-DSdxb8P-.js.map +1 -1
- package/dist/{chunk-REBZFQYE.js → chunk-7HYEGM5V.js} +967 -1597
- package/dist/{chunk-W3HS2NP6.js → chunk-B4KJXECB.js} +13 -18
- package/dist/{chunk-33BHGAF7.js → chunk-GWQB7PBK.js} +59 -32
- package/dist/{chunk-CVADLFW6.js → chunk-LQ6XHF53.js} +4543 -3043
- package/dist/{chunk-GYTAJJOD.js → chunk-RIAWNDYI.js} +16 -2
- package/dist/{chunk-IEKLJDUU.js → chunk-WS4DWCDZ.js} +54 -32
- package/dist/commands/agent/actions/index.d.ts +5 -1
- package/dist/commands/agent/actions/index.js +5 -4
- package/dist/commands/agent/index.js +3 -4
- package/dist/commands/create/actions/index.js +4 -5
- package/dist/commands/create/index.js +5 -6
- package/dist/{fileFromPath-DCRQMDLJ.js → fileFromPath-KB6XMTJ4.js} +1 -0
- package/dist/index.html +2 -2
- package/dist/index.js +9346 -102098
- package/dist/{migrator-KZLCVEIH.js → migrator-JREQPDN3.js} +42 -220
- package/dist/pglite.data +0 -0
- package/dist/pglite.wasm +0 -0
- package/dist/plugin-creator-T4K2673C.js +910 -0
- package/dist/{registry-XFOSZFU4.js → registry-CBMRMYCG.js} +3 -4
- package/dist/templates/plugin-starter/README.md +255 -0
- package/dist/templates/plugin-starter/bunfig.toml +6 -0
- package/dist/templates/plugin-starter/cypress.config.ts +18 -0
- package/dist/templates/plugin-starter/index.html +13 -0
- package/dist/templates/plugin-starter/package.json +95 -0
- package/dist/templates/plugin-starter/postcss.config.js +3 -0
- package/dist/templates/plugin-starter/scripts/test-e2e-manual.js +201 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/component/ExampleRoute.cy.tsx +404 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/component/PanelComponent.cy.tsx +287 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/support/commands.ts +38 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/support/component-index.html +11 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/support/component.ts +33 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/support/e2e.ts +11 -0
- package/dist/templates/plugin-starter/src/__tests__/cypress/tsconfig.json +10 -0
- package/dist/templates/plugin-starter/src/__tests__/e2e/README.md +47 -0
- package/dist/templates/plugin-starter/src/__tests__/e2e/starter-plugin.ts +320 -0
- package/{templates/plugin-starter → dist/templates/plugin-starter/src}/__tests__/integration.test.ts +22 -17
- package/{templates/plugin-starter → dist/templates/plugin-starter/src}/__tests__/plugin.test.ts +8 -8
- package/{templates/plugin-starter → dist/templates/plugin-starter/src}/__tests__/test-utils.ts +17 -17
- package/dist/templates/plugin-starter/src/frontend/index.css +77 -0
- package/dist/templates/plugin-starter/src/frontend/index.tsx +164 -0
- package/dist/templates/plugin-starter/src/frontend/utils.ts +6 -0
- package/dist/templates/plugin-starter/src/index.ts +274 -0
- package/dist/templates/plugin-starter/src/tests.ts +6 -0
- package/dist/templates/plugin-starter/tailwind.config.js +62 -0
- package/dist/templates/plugin-starter/tsconfig.build.json +11 -0
- package/dist/templates/plugin-starter/tsconfig.json +28 -0
- package/dist/templates/plugin-starter/tsup.config.ts +20 -0
- package/dist/templates/plugin-starter/vite.config.ts +20 -0
- package/dist/templates/project-starter/.env.example +153 -0
- package/dist/templates/project-starter/README.md +109 -0
- package/dist/templates/project-starter/bunfig.toml +6 -0
- package/dist/templates/project-starter/cypress.config.ts +31 -0
- package/dist/templates/project-starter/index.html +13 -0
- package/dist/templates/project-starter/package.json +83 -0
- package/dist/templates/project-starter/postcss.config.js +3 -0
- package/dist/templates/project-starter/scripts/test-all.sh +101 -0
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/actions.test.ts +6 -6
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/character.test.ts +3 -3
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/config.test.ts +18 -18
- package/dist/templates/project-starter/src/__tests__/cypress/component/Accessibility.cy.tsx +271 -0
- package/dist/templates/project-starter/src/__tests__/cypress/component/ApiIntegration.cy.tsx +220 -0
- package/dist/templates/project-starter/src/__tests__/cypress/component/ExampleRoute.cy.tsx +146 -0
- package/dist/templates/project-starter/src/__tests__/cypress/component/PanelComponent.cy.tsx +51 -0
- package/dist/templates/project-starter/src/__tests__/cypress/e2e/agent-chat.cy.ts +235 -0
- package/dist/templates/project-starter/src/__tests__/cypress/e2e/dashboard.cy.ts +146 -0
- package/dist/templates/project-starter/src/__tests__/cypress/e2e/user-workflow.cy.ts +257 -0
- package/dist/templates/project-starter/src/__tests__/cypress/support/commands.ts +44 -0
- package/dist/templates/project-starter/src/__tests__/cypress/support/component-index.html +11 -0
- package/dist/templates/project-starter/src/__tests__/cypress/support/component.ts +33 -0
- package/dist/templates/project-starter/src/__tests__/cypress/support/e2e.ts +179 -0
- package/dist/templates/project-starter/src/__tests__/e2e/index.ts +14 -0
- package/dist/templates/project-starter/src/__tests__/e2e/natural-language.test.ts +246 -0
- package/dist/templates/project-starter/src/__tests__/e2e/project.test.ts +155 -0
- package/dist/templates/project-starter/src/__tests__/e2e/starter-plugin.test.ts +421 -0
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/env.test.ts +2 -2
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/error-handling.test.ts +17 -17
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/events.test.ts +7 -7
- package/dist/templates/project-starter/src/__tests__/file-structure.test.ts +135 -0
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/integration.test.ts +25 -25
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/models.test.ts +6 -6
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/plugin.test.ts +9 -9
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/provider.test.ts +7 -7
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/routes.test.ts +3 -3
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/test-utils.ts +17 -17
- package/{templates/project-starter → dist/templates/project-starter/src}/__tests__/utils/core-test-utils.ts +3 -3
- package/dist/templates/project-starter/src/frontend/index.css +77 -0
- package/dist/templates/project-starter/src/frontend/index.html +19 -0
- package/dist/templates/project-starter/src/frontend/index.tsx +98 -0
- package/dist/templates/project-starter/src/frontend/utils.ts +6 -0
- package/dist/templates/project-starter/src/index.ts +153 -0
- package/dist/templates/project-starter/src/plugin.ts +255 -0
- package/dist/templates/project-starter/tailwind.config.js +62 -0
- package/dist/templates/project-starter/tsconfig.build.json +20 -0
- package/dist/templates/project-starter/tsconfig.json +39 -0
- package/dist/templates/project-starter/tsup.config.ts +19 -0
- package/dist/templates/project-starter/vite.config.ts +39 -0
- package/dist/templates/project-tee-starter/.dockerignore +20 -0
- package/dist/templates/project-tee-starter/.env.example +55 -0
- package/dist/templates/project-tee-starter/Dockerfile +66 -0
- package/dist/templates/project-tee-starter/GUIDE.md +235 -0
- package/dist/templates/project-tee-starter/README.md +173 -0
- package/dist/templates/project-tee-starter/__tests__/actions.test.ts +9 -0
- package/dist/templates/project-tee-starter/__tests__/character.test.ts +86 -0
- package/dist/templates/project-tee-starter/__tests__/config.test.ts +31 -0
- package/dist/templates/project-tee-starter/__tests__/env.test.ts +87 -0
- package/dist/templates/project-tee-starter/__tests__/error-handling.test.ts +30 -0
- package/dist/templates/project-tee-starter/__tests__/events.test.ts +21 -0
- package/{templates/project-starter → dist/templates/project-tee-starter}/__tests__/file-structure.test.ts +6 -6
- package/dist/templates/project-tee-starter/__tests__/integration.test.ts +205 -0
- package/dist/templates/project-tee-starter/__tests__/models.test.ts +22 -0
- package/dist/templates/project-tee-starter/__tests__/plugin.test.ts +38 -0
- package/dist/templates/project-tee-starter/__tests__/provider.test.ts +189 -0
- package/dist/templates/project-tee-starter/__tests__/routes.test.ts +21 -0
- package/dist/templates/project-tee-starter/__tests__/test-utils.ts +121 -0
- package/dist/templates/project-tee-starter/__tests__/utils/core-test-utils.ts +167 -0
- package/dist/templates/project-tee-starter/assets/mr-tee-portrait.jpg +0 -0
- package/dist/templates/project-tee-starter/bunfig.toml +6 -0
- package/dist/templates/project-tee-starter/docker-compose.yaml +57 -0
- package/dist/templates/project-tee-starter/e2e/project.test.ts +38 -0
- package/dist/templates/project-tee-starter/e2e/starter-plugin.test.ts +92 -0
- package/dist/templates/project-tee-starter/package.json +74 -0
- package/dist/templates/project-tee-starter/src/character.ts +257 -0
- package/dist/templates/project-tee-starter/src/index.ts +33 -0
- package/dist/templates/project-tee-starter/src/plugin.ts +169 -0
- package/dist/templates/project-tee-starter/tsconfig.build.json +13 -0
- package/dist/templates/project-tee-starter/tsconfig.json +30 -0
- package/dist/templates/project-tee-starter/tsup.config.ts +19 -0
- package/dist/{utils-DIZZ3HNZ.js → utils-TIALZU53.js} +9 -8
- package/package.json +29 -12
- package/templates/plugin-starter/README.md +38 -13
- package/templates/plugin-starter/bunfig.toml +6 -0
- package/templates/plugin-starter/cypress.config.ts +18 -0
- package/templates/plugin-starter/index.html +13 -0
- package/templates/plugin-starter/package.json +19 -7
- package/templates/plugin-starter/postcss.config.js +3 -0
- package/templates/plugin-starter/scripts/test-e2e-manual.js +201 -0
- package/templates/plugin-starter/src/__tests__/cypress/component/ExampleRoute.cy.tsx +404 -0
- package/templates/plugin-starter/src/__tests__/cypress/component/PanelComponent.cy.tsx +287 -0
- package/templates/plugin-starter/src/__tests__/cypress/support/commands.ts +38 -0
- package/templates/plugin-starter/src/__tests__/cypress/support/component-index.html +11 -0
- package/templates/plugin-starter/src/__tests__/cypress/support/component.ts +33 -0
- package/templates/plugin-starter/src/__tests__/cypress/support/e2e.ts +11 -0
- package/templates/plugin-starter/src/__tests__/cypress/tsconfig.json +10 -0
- package/templates/plugin-starter/src/__tests__/e2e/README.md +47 -0
- package/templates/plugin-starter/src/__tests__/e2e/starter-plugin.ts +320 -0
- package/templates/plugin-starter/src/__tests__/integration.test.ts +138 -0
- package/templates/plugin-starter/src/__tests__/plugin.test.ts +182 -0
- package/templates/plugin-starter/src/__tests__/test-utils.ts +162 -0
- package/templates/plugin-starter/src/frontend/index.css +77 -0
- package/templates/plugin-starter/src/frontend/index.tsx +164 -0
- package/templates/plugin-starter/src/frontend/utils.ts +6 -0
- package/templates/plugin-starter/src/index.ts +31 -8
- package/templates/plugin-starter/src/tests.ts +6 -0
- package/templates/plugin-starter/tailwind.config.js +62 -0
- package/templates/plugin-starter/tsconfig.json +8 -8
- package/templates/plugin-starter/vite.config.ts +20 -0
- package/templates/project-starter/bunfig.toml +6 -0
- package/templates/project-starter/cypress.config.ts +31 -0
- package/templates/project-starter/index.html +13 -0
- package/templates/project-starter/package.json +37 -14
- package/templates/project-starter/postcss.config.js +3 -0
- package/templates/project-starter/scripts/test-all.sh +101 -0
- package/templates/project-starter/src/__tests__/actions.test.ts +155 -0
- package/templates/project-starter/src/__tests__/character.test.ts +86 -0
- package/templates/project-starter/src/__tests__/config.test.ts +193 -0
- package/templates/project-starter/src/__tests__/cypress/component/Accessibility.cy.tsx +271 -0
- package/templates/project-starter/src/__tests__/cypress/component/ApiIntegration.cy.tsx +220 -0
- package/templates/project-starter/src/__tests__/cypress/component/ExampleRoute.cy.tsx +146 -0
- package/templates/project-starter/src/__tests__/cypress/component/PanelComponent.cy.tsx +51 -0
- package/templates/project-starter/src/__tests__/cypress/e2e/agent-chat.cy.ts +235 -0
- package/templates/project-starter/src/__tests__/cypress/e2e/dashboard.cy.ts +146 -0
- package/templates/project-starter/src/__tests__/cypress/e2e/user-workflow.cy.ts +257 -0
- package/templates/project-starter/src/__tests__/cypress/support/commands.ts +44 -0
- package/templates/project-starter/src/__tests__/cypress/support/component-index.html +11 -0
- package/templates/project-starter/src/__tests__/cypress/support/component.ts +33 -0
- package/templates/project-starter/src/__tests__/cypress/support/e2e.ts +179 -0
- package/templates/project-starter/src/__tests__/e2e/index.ts +14 -0
- package/templates/project-starter/src/__tests__/e2e/natural-language.test.ts +246 -0
- package/templates/project-starter/src/__tests__/e2e/project.test.ts +155 -0
- package/templates/project-starter/src/__tests__/e2e/starter-plugin.test.ts +421 -0
- package/templates/project-starter/src/__tests__/env.test.ts +87 -0
- package/templates/project-starter/src/__tests__/error-handling.test.ts +177 -0
- package/templates/project-starter/src/__tests__/events.test.ts +144 -0
- package/templates/project-starter/src/__tests__/file-structure.test.ts +135 -0
- package/templates/project-starter/src/__tests__/integration.test.ts +209 -0
- package/templates/project-starter/src/__tests__/models.test.ts +152 -0
- package/templates/project-starter/src/__tests__/plugin.test.ts +393 -0
- package/templates/project-starter/src/__tests__/provider.test.ts +325 -0
- package/templates/project-starter/src/__tests__/routes.test.ts +79 -0
- package/templates/project-starter/src/__tests__/test-utils.ts +121 -0
- package/templates/project-starter/src/__tests__/utils/core-test-utils.ts +180 -0
- package/templates/project-starter/src/frontend/index.css +77 -0
- package/templates/project-starter/src/frontend/index.html +19 -0
- package/templates/project-starter/src/frontend/index.tsx +98 -0
- package/templates/project-starter/src/frontend/utils.ts +6 -0
- package/templates/project-starter/src/index.ts +9 -1
- package/templates/project-starter/tailwind.config.js +62 -0
- package/templates/project-starter/tsconfig.build.json +9 -2
- package/templates/project-starter/tsconfig.json +15 -6
- package/templates/project-starter/tsup.config.ts +1 -1
- package/templates/project-starter/vite.config.ts +39 -0
- package/templates/project-tee-starter/__tests__/actions.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/character.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/config.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/env.test.ts +2 -2
- package/templates/project-tee-starter/__tests__/error-handling.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/events.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/file-structure.test.ts +5 -5
- package/templates/project-tee-starter/__tests__/integration.test.ts +22 -26
- package/templates/project-tee-starter/__tests__/models.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/plugin.test.ts +6 -12
- package/templates/project-tee-starter/__tests__/provider.test.ts +6 -6
- package/templates/project-tee-starter/__tests__/routes.test.ts +1 -1
- package/templates/project-tee-starter/__tests__/test-utils.ts +15 -15
- package/templates/project-tee-starter/__tests__/utils/core-test-utils.ts +3 -3
- package/templates/project-tee-starter/bunfig.toml +6 -0
- package/templates/project-tee-starter/package.json +10 -12
- package/dist/assets/index-CZAd5zm2.js.br +0 -0
- package/dist/assets/index-CZAd5zm2.js.map +0 -1
- package/dist/assets/index-DyA-lndn.css +0 -1
- package/dist/assets/index-DyA-lndn.css.br +0 -0
- package/dist/chunk-CEE6RKN5.js +0 -2746
- package/dist/chunk-MA2ZXPG6.js +0 -260
- package/dist/chunk-TUAYJIF2.js +0 -3649
- package/dist/lib-NAGYZHVV.js +0 -9
- package/dist/plugin-creator-IC42XOHG.js +0 -29165
- package/templates/plugin-starter/e2e/starter-plugin.test.ts +0 -171
- package/templates/plugin-starter/images/README.md +0 -24
- package/templates/plugin-starter/vitest.config.ts +0 -16
- package/templates/project-starter/e2e/project.test.ts +0 -34
- package/templates/project-starter/e2e/starter-plugin.test.ts +0 -217
- package/templates/project-starter/vitest.config.ts +0 -16
- package/templates/project-tee-starter/vitest.config.ts +0 -19
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
esm_default,
|
|
7
|
+
ora,
|
|
8
|
+
sdk_default
|
|
9
|
+
} from "./chunk-LQ6XHF53.js";
|
|
10
|
+
import "./chunk-2GXSCVA2.js";
|
|
11
|
+
import {
|
|
12
|
+
runBunCommand
|
|
13
|
+
} from "./chunk-RIAWNDYI.js";
|
|
14
|
+
import "./chunk-WS4DWCDZ.js";
|
|
15
|
+
import "./chunk-567UPUC7.js";
|
|
16
|
+
|
|
17
|
+
// src/utils/plugin-creator.ts
|
|
18
|
+
import { logger } from "@elizaos/core";
|
|
19
|
+
import { execa } from "execa";
|
|
20
|
+
import * as fs from "fs-extra";
|
|
21
|
+
import inquirer from "inquirer";
|
|
22
|
+
import * as path from "path";
|
|
23
|
+
import * as os from "os";
|
|
24
|
+
var MAX_BUILD_ITERATIONS = 5;
|
|
25
|
+
var MAX_TEST_ITERATIONS = 5;
|
|
26
|
+
var MAX_REVISION_ITERATIONS = 3;
|
|
27
|
+
var CLAUDE_CODE_TIMEOUT = 15 * 60 * 1e3;
|
|
28
|
+
var MIN_DISK_SPACE_GB = 2;
|
|
29
|
+
var PluginCreator = class {
|
|
30
|
+
git;
|
|
31
|
+
pluginPath = null;
|
|
32
|
+
anthropic = null;
|
|
33
|
+
activeClaudeProcess = null;
|
|
34
|
+
options;
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
this.git = esm_default();
|
|
37
|
+
this.options = options;
|
|
38
|
+
this.registerCleanupHandlers();
|
|
39
|
+
}
|
|
40
|
+
registerCleanupHandlers() {
|
|
41
|
+
const cleanup = async () => {
|
|
42
|
+
logger.info("Cleaning up plugin creation process...");
|
|
43
|
+
if (this.activeClaudeProcess) {
|
|
44
|
+
try {
|
|
45
|
+
this.activeClaudeProcess.kill();
|
|
46
|
+
logger.info("Terminated active Claude Code process");
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.error("Failed to terminate Claude Code process:", error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
};
|
|
53
|
+
process.on("SIGINT", cleanup);
|
|
54
|
+
process.on("SIGTERM", cleanup);
|
|
55
|
+
process.on("uncaughtException", async (error) => {
|
|
56
|
+
logger.error("Uncaught exception:", error);
|
|
57
|
+
await cleanup();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async initializeAnthropic() {
|
|
61
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
62
|
+
if (!apiKey) {
|
|
63
|
+
logger.error("ANTHROPIC_API_KEY not found in environment.");
|
|
64
|
+
throw new Error("ANTHROPIC_API_KEY is required for plugin generation");
|
|
65
|
+
}
|
|
66
|
+
this.anthropic = new sdk_default({ apiKey });
|
|
67
|
+
}
|
|
68
|
+
async create(pluginSpec) {
|
|
69
|
+
const spinner = ora("Initializing plugin creator...").start();
|
|
70
|
+
try {
|
|
71
|
+
await this.initializeAnthropic();
|
|
72
|
+
spinner.info("Checking disk space...");
|
|
73
|
+
await this.checkDiskSpace();
|
|
74
|
+
try {
|
|
75
|
+
await execa("claude", ["--version"], { stdio: "pipe" });
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Claude Code is required for plugin generation. Install with: bun install -g @anthropic-ai/claude-code"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
spinner.info("Collecting plugin specification...");
|
|
82
|
+
const spec = pluginSpec || this.options.spec || await this.collectPluginSpecification();
|
|
83
|
+
spinner.succeed("Plugin specification collected");
|
|
84
|
+
spinner.info("Creating plugin from template...");
|
|
85
|
+
await this.createFromTemplate(spec.name);
|
|
86
|
+
spinner.succeed("Plugin structure created");
|
|
87
|
+
spinner.info("Generating detailed plugin specification...");
|
|
88
|
+
const detailedSpec = await this.generateDetailedSpecification(spec);
|
|
89
|
+
spinner.succeed("Detailed specification generated");
|
|
90
|
+
spinner.info("Creating plugin specification document...");
|
|
91
|
+
await this.createSpecificationDocument(spec, detailedSpec);
|
|
92
|
+
spinner.succeed("Specification document created");
|
|
93
|
+
spinner.info("Generating plugin code...");
|
|
94
|
+
const generationSuccess = await this.runGenerationWithValidation();
|
|
95
|
+
if (!generationSuccess) {
|
|
96
|
+
throw new Error("Plugin generation failed after maximum iterations");
|
|
97
|
+
}
|
|
98
|
+
spinner.succeed("Plugin code generated and validated");
|
|
99
|
+
const targetPath = await this.copyToCWD();
|
|
100
|
+
logger.info(`\u2705 Plugin successfully created!`);
|
|
101
|
+
logger.info(`\u{1F4C1} Plugin location: ${targetPath}`);
|
|
102
|
+
logger.info(`
|
|
103
|
+
\u{1F4CC} Next steps:`);
|
|
104
|
+
logger.info(`1. cd ${path.basename(targetPath)}`);
|
|
105
|
+
logger.info(`2. Review the generated code`);
|
|
106
|
+
logger.info(`3. Run tests: bun test`);
|
|
107
|
+
logger.info(`4. Add to your ElizaOS project
|
|
108
|
+
`);
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
pluginName: spec.name,
|
|
112
|
+
pluginPath: targetPath
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
spinner.fail("Plugin creation failed");
|
|
116
|
+
logger.error("Error creating plugin:", error);
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
pluginName: "",
|
|
120
|
+
pluginPath: "",
|
|
121
|
+
error
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async collectPluginSpecification() {
|
|
126
|
+
if (this.options.skipPrompts) {
|
|
127
|
+
throw new Error("Plugin specification required when skipping prompts");
|
|
128
|
+
}
|
|
129
|
+
const answers = await inquirer.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: "input",
|
|
132
|
+
name: "name",
|
|
133
|
+
message: 'Plugin name (without "plugin-" prefix):',
|
|
134
|
+
validate: (input) => {
|
|
135
|
+
if (!input || input.trim() === "") {
|
|
136
|
+
return "Plugin name is required";
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
},
|
|
140
|
+
filter: (input) => input.toLowerCase().replace(/\s+/g, "-")
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: "input",
|
|
144
|
+
name: "description",
|
|
145
|
+
message: "Plugin description:",
|
|
146
|
+
validate: (input) => input.length > 0 || "Description is required"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: "input",
|
|
150
|
+
name: "features",
|
|
151
|
+
message: "Main features (comma-separated):",
|
|
152
|
+
filter: (input) => input.split(",").map((f) => f.trim()).filter((f) => f)
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: "checkbox",
|
|
156
|
+
name: "components",
|
|
157
|
+
message: "Which components will this plugin include?",
|
|
158
|
+
choices: [
|
|
159
|
+
{ name: "Actions", value: "actions" },
|
|
160
|
+
{ name: "Providers", value: "providers" },
|
|
161
|
+
{ name: "Evaluators", value: "evaluators" },
|
|
162
|
+
{ name: "Services", value: "services" }
|
|
163
|
+
],
|
|
164
|
+
default: ["actions", "providers"]
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
const spec = {
|
|
168
|
+
name: answers.name,
|
|
169
|
+
description: answers.description,
|
|
170
|
+
features: answers.features
|
|
171
|
+
};
|
|
172
|
+
if (answers.components.includes("actions")) {
|
|
173
|
+
const actionAnswers = await inquirer.prompt([
|
|
174
|
+
{
|
|
175
|
+
type: "input",
|
|
176
|
+
name: "actions",
|
|
177
|
+
message: "Action names (comma-separated):",
|
|
178
|
+
filter: (input) => input.split(",").map((a) => a.trim()).filter((a) => a)
|
|
179
|
+
}
|
|
180
|
+
]);
|
|
181
|
+
spec.actions = actionAnswers.actions;
|
|
182
|
+
}
|
|
183
|
+
if (answers.components.includes("providers")) {
|
|
184
|
+
const providerAnswers = await inquirer.prompt([
|
|
185
|
+
{
|
|
186
|
+
type: "input",
|
|
187
|
+
name: "providers",
|
|
188
|
+
message: "Provider names (comma-separated):",
|
|
189
|
+
filter: (input) => input.split(",").map((p) => p.trim()).filter((p) => p)
|
|
190
|
+
}
|
|
191
|
+
]);
|
|
192
|
+
spec.providers = providerAnswers.providers;
|
|
193
|
+
}
|
|
194
|
+
if (answers.components.includes("evaluators")) {
|
|
195
|
+
const evaluatorAnswers = await inquirer.prompt([
|
|
196
|
+
{
|
|
197
|
+
type: "input",
|
|
198
|
+
name: "evaluators",
|
|
199
|
+
message: "Evaluator names (comma-separated):",
|
|
200
|
+
filter: (input) => input.split(",").map((e) => e.trim()).filter((e) => e)
|
|
201
|
+
}
|
|
202
|
+
]);
|
|
203
|
+
spec.evaluators = evaluatorAnswers.evaluators;
|
|
204
|
+
}
|
|
205
|
+
if (answers.components.includes("services")) {
|
|
206
|
+
const serviceAnswers = await inquirer.prompt([
|
|
207
|
+
{
|
|
208
|
+
type: "input",
|
|
209
|
+
name: "services",
|
|
210
|
+
message: "Service names (comma-separated):",
|
|
211
|
+
filter: (input) => input.split(",").map((s) => s.trim()).filter((s) => s)
|
|
212
|
+
}
|
|
213
|
+
]);
|
|
214
|
+
spec.services = serviceAnswers.services;
|
|
215
|
+
}
|
|
216
|
+
return spec;
|
|
217
|
+
}
|
|
218
|
+
async createFromTemplate(pluginName) {
|
|
219
|
+
const tempDir = path.join(os.tmpdir(), `plugin-${pluginName}-${Date.now()}`);
|
|
220
|
+
await fs.ensureDir(tempDir);
|
|
221
|
+
this.pluginPath = path.join(tempDir, `plugin-${pluginName}`);
|
|
222
|
+
try {
|
|
223
|
+
await execa(
|
|
224
|
+
"bunx",
|
|
225
|
+
["@elizaos/cli", "create", `plugin-${pluginName}`, "-t", "plugin-starter"],
|
|
226
|
+
{
|
|
227
|
+
cwd: tempDir,
|
|
228
|
+
stdio: "pipe"
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger.warn("Failed to use elizaos create, creating structure manually");
|
|
233
|
+
await this.createPluginStructureManually(pluginName);
|
|
234
|
+
}
|
|
235
|
+
this.git = esm_default(this.pluginPath);
|
|
236
|
+
await this.git.init();
|
|
237
|
+
await this.git.add(".");
|
|
238
|
+
await this.git.commit("Initial commit from plugin-starter template");
|
|
239
|
+
}
|
|
240
|
+
async createPluginStructureManually(pluginName) {
|
|
241
|
+
await fs.ensureDir(this.pluginPath);
|
|
242
|
+
const dirs = ["src", "src/actions", "src/providers", "src/evaluators", "src/services", "tests"];
|
|
243
|
+
for (const dir of dirs) {
|
|
244
|
+
await fs.ensureDir(path.join(this.pluginPath, dir));
|
|
245
|
+
}
|
|
246
|
+
const packageJson = {
|
|
247
|
+
name: `@elizaos/plugin-${pluginName}`,
|
|
248
|
+
version: "0.1.0",
|
|
249
|
+
description: `ElizaOS ${pluginName} plugin`,
|
|
250
|
+
main: "dist/index.js",
|
|
251
|
+
types: "dist/index.d.ts",
|
|
252
|
+
scripts: {
|
|
253
|
+
build: "tsup",
|
|
254
|
+
dev: "tsup --watch",
|
|
255
|
+
test: "bun test",
|
|
256
|
+
"test:watch": "bun test --watch"
|
|
257
|
+
},
|
|
258
|
+
dependencies: {
|
|
259
|
+
"@elizaos/core": "^1.0.0"
|
|
260
|
+
},
|
|
261
|
+
devDependencies: {
|
|
262
|
+
tsup: "^8.4.0",
|
|
263
|
+
typescript: "^5.3.0",
|
|
264
|
+
"@types/bun": "^1.0.0",
|
|
265
|
+
"@types/node": "^20.0.0"
|
|
266
|
+
},
|
|
267
|
+
peerDependencies: {
|
|
268
|
+
"@elizaos/core": "1.x"
|
|
269
|
+
},
|
|
270
|
+
files: ["dist", "README.md"]
|
|
271
|
+
};
|
|
272
|
+
await fs.writeJSON(path.join(this.pluginPath, "package.json"), packageJson, { spaces: 2 });
|
|
273
|
+
const tsconfig = {
|
|
274
|
+
extends: "../../tsconfig.json",
|
|
275
|
+
compilerOptions: {
|
|
276
|
+
outDir: "./dist",
|
|
277
|
+
rootDir: "./src"
|
|
278
|
+
},
|
|
279
|
+
include: ["src/**/*"],
|
|
280
|
+
exclude: ["node_modules", "dist"]
|
|
281
|
+
};
|
|
282
|
+
await fs.writeJSON(path.join(this.pluginPath, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
283
|
+
const tsupConfig = `import { defineConfig } from 'tsup';
|
|
284
|
+
|
|
285
|
+
export default defineConfig({
|
|
286
|
+
entry: ['src/index.ts'],
|
|
287
|
+
format: ['cjs', 'esm'],
|
|
288
|
+
dts: true,
|
|
289
|
+
sourcemap: true,
|
|
290
|
+
clean: true,
|
|
291
|
+
external: ['@elizaos/core'],
|
|
292
|
+
});
|
|
293
|
+
`;
|
|
294
|
+
await fs.writeFile(path.join(this.pluginPath, "tsup.config.ts"), tsupConfig);
|
|
295
|
+
const gitignore = `node_modules/
|
|
296
|
+
dist/
|
|
297
|
+
*.log
|
|
298
|
+
.env
|
|
299
|
+
.DS_Store
|
|
300
|
+
coverage/
|
|
301
|
+
`;
|
|
302
|
+
await fs.writeFile(path.join(this.pluginPath, ".gitignore"), gitignore);
|
|
303
|
+
const readme = `# @elizaos/plugin-${pluginName}
|
|
304
|
+
|
|
305
|
+
ElizaOS ${pluginName} plugin
|
|
306
|
+
|
|
307
|
+
## Installation
|
|
308
|
+
|
|
309
|
+
\`\`\`bash
|
|
310
|
+
bun install @elizaos/plugin-${pluginName}
|
|
311
|
+
\`\`\`
|
|
312
|
+
|
|
313
|
+
## Usage
|
|
314
|
+
|
|
315
|
+
\`\`\`typescript
|
|
316
|
+
import { plugin${pluginName.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("")} } from '@elizaos/plugin-${pluginName}';
|
|
317
|
+
|
|
318
|
+
// Add to your ElizaOS configuration
|
|
319
|
+
\`\`\`
|
|
320
|
+
`;
|
|
321
|
+
await fs.writeFile(path.join(this.pluginPath, "README.md"), readme);
|
|
322
|
+
const indexContent = `import { Plugin } from '@elizaos/core';
|
|
323
|
+
|
|
324
|
+
export const plugin${pluginName.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("")}: Plugin = {
|
|
325
|
+
name: 'plugin-${pluginName}',
|
|
326
|
+
version: '0.1.0',
|
|
327
|
+
actions: [],
|
|
328
|
+
providers: [],
|
|
329
|
+
evaluators: [],
|
|
330
|
+
services: [],
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export default plugin${pluginName.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("")};
|
|
334
|
+
`;
|
|
335
|
+
await fs.writeFile(path.join(this.pluginPath, "src/index.ts"), indexContent);
|
|
336
|
+
}
|
|
337
|
+
async generateDetailedSpecification(spec) {
|
|
338
|
+
const prompt = `You are creating a detailed technical specification for an ElizaOS plugin.
|
|
339
|
+
|
|
340
|
+
## Plugin Overview
|
|
341
|
+
Name: ${spec.name}
|
|
342
|
+
Description: ${spec.description}
|
|
343
|
+
Features: ${spec.features.join(", ")}
|
|
344
|
+
|
|
345
|
+
${spec.actions ? `Actions: ${spec.actions.join(", ")}` : ""}
|
|
346
|
+
${spec.providers ? `Providers: ${spec.providers.join(", ")}` : ""}
|
|
347
|
+
${spec.evaluators ? `Evaluators: ${spec.evaluators.join(", ")}` : ""}
|
|
348
|
+
${spec.services ? `Services: ${spec.services.join(", ")}` : ""}
|
|
349
|
+
|
|
350
|
+
## CRITICAL REQUIREMENTS
|
|
351
|
+
|
|
352
|
+
### 1. Database Compatibility (MANDATORY)
|
|
353
|
+
This plugin MUST work with both Pglite and PostgreSQL. The specification must include:
|
|
354
|
+
- Database-agnostic design patterns
|
|
355
|
+
- Use of runtime APIs only (no direct database access)
|
|
356
|
+
- Memory operations using runtime.createMemory(), runtime.searchMemories()
|
|
357
|
+
- Goal operations using runtime.createGoal(), runtime.updateGoal()
|
|
358
|
+
- Relationship operations using runtime.ensureConnection()
|
|
359
|
+
- NO database-specific code or SQL queries
|
|
360
|
+
- NO direct database adapter imports
|
|
361
|
+
|
|
362
|
+
### 2. Import Requirements (MANDATORY)
|
|
363
|
+
- ALL imports must come from @elizaos/core ONLY
|
|
364
|
+
- NO imports from @elizaos/plugin, @elizaos/types, @elizaos/logger (these don't exist)
|
|
365
|
+
- Use: import { Plugin, Action, AgentRuntime, logger, Memory, State } from '@elizaos/core'
|
|
366
|
+
|
|
367
|
+
## Task
|
|
368
|
+
Generate a detailed technical specification that includes:
|
|
369
|
+
|
|
370
|
+
1. **Architecture Overview**: How the plugin components work together (database-agnostic)
|
|
371
|
+
2. **Component Specifications**: Detailed specs for each action, provider, evaluator, and service
|
|
372
|
+
3. **Data Flow**: How data moves through the plugin using runtime APIs
|
|
373
|
+
4. **Integration Points**: How the plugin integrates with ElizaOS runtime
|
|
374
|
+
5. **Implementation Details**: Specific algorithms, APIs, or techniques to use (database-agnostic)
|
|
375
|
+
6. **Database Abstraction**: How to use runtime APIs for all data operations
|
|
376
|
+
7. **Testing Strategy**: What tests are needed (including database compatibility tests)
|
|
377
|
+
8. **Error Handling**: How errors should be handled
|
|
378
|
+
9. **Configuration**: Any configuration options needed
|
|
379
|
+
|
|
380
|
+
## Database Abstraction Examples:
|
|
381
|
+
\`\`\`typescript
|
|
382
|
+
// Memory operations - works with both Pglite and PostgreSQL
|
|
383
|
+
await runtime.createMemory({
|
|
384
|
+
entityId: message.entityId,
|
|
385
|
+
agentId: runtime.agentId,
|
|
386
|
+
content: { text: 'Important information' },
|
|
387
|
+
roomId: message.roomId,
|
|
388
|
+
embedding: await runtime.embed('Important information'),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Search memories - database-agnostic
|
|
392
|
+
const memories = await runtime.searchMemories({
|
|
393
|
+
text: query,
|
|
394
|
+
entityId: message.entityId,
|
|
395
|
+
count: 10,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Goal operations - database-agnostic
|
|
399
|
+
await runtime.createGoal({
|
|
400
|
+
entityId: message.entityId,
|
|
401
|
+
agentId: runtime.agentId,
|
|
402
|
+
name: 'Complete task',
|
|
403
|
+
status: 'IN_PROGRESS',
|
|
404
|
+
});
|
|
405
|
+
\`\`\`
|
|
406
|
+
|
|
407
|
+
Be extremely detailed and specific. This specification will be used to generate the actual code.
|
|
408
|
+
Remember: The plugin must work with BOTH Pglite and PostgreSQL without any code changes.`;
|
|
409
|
+
const message = await this.anthropic.messages.create({
|
|
410
|
+
model: "claude-opus-4-20250514",
|
|
411
|
+
max_tokens: 8192,
|
|
412
|
+
temperature: 0.3,
|
|
413
|
+
messages: [
|
|
414
|
+
{
|
|
415
|
+
role: "user",
|
|
416
|
+
content: prompt
|
|
417
|
+
}
|
|
418
|
+
]
|
|
419
|
+
});
|
|
420
|
+
return message.content.map((block) => block.type === "text" ? block.text : "").join("");
|
|
421
|
+
}
|
|
422
|
+
async createSpecificationDocument(spec, detailedSpec) {
|
|
423
|
+
const content = `# Plugin Specification: ${spec.name}
|
|
424
|
+
|
|
425
|
+
## Overview
|
|
426
|
+
${spec.description}
|
|
427
|
+
|
|
428
|
+
## Features
|
|
429
|
+
${spec.features.map((f) => `- ${f}`).join("\n")}
|
|
430
|
+
|
|
431
|
+
## Components
|
|
432
|
+
${spec.actions ? `### Actions
|
|
433
|
+
${spec.actions.map((a) => `- ${a}`).join("\n")}` : ""}
|
|
434
|
+
${spec.providers ? `### Providers
|
|
435
|
+
${spec.providers.map((p) => `- ${p}`).join("\n")}` : ""}
|
|
436
|
+
${spec.evaluators ? `### Evaluators
|
|
437
|
+
${spec.evaluators.map((e) => `- ${e}`).join("\n")}` : ""}
|
|
438
|
+
${spec.services ? `### Services
|
|
439
|
+
${spec.services.map((s) => `- ${s}`).join("\n")}` : ""}
|
|
440
|
+
|
|
441
|
+
## CRITICAL REQUIREMENTS
|
|
442
|
+
|
|
443
|
+
### Database Compatibility (MANDATORY)
|
|
444
|
+
This plugin MUST work with both Pglite and PostgreSQL without any code changes.
|
|
445
|
+
|
|
446
|
+
#### Database Abstraction Rules:
|
|
447
|
+
- \u2705 Use ONLY runtime.databaseAdapter for database operations
|
|
448
|
+
- \u2705 Use runtime.createMemory(), runtime.searchMemories(), runtime.createGoal()
|
|
449
|
+
- \u2705 Use runtime.ensureConnection() for relationships
|
|
450
|
+
- \u274C NEVER import database adapters directly (PgliteDatabaseAdapter, PgDatabaseAdapter)
|
|
451
|
+
- \u274C NEVER use database-specific SQL or queries
|
|
452
|
+
- \u274C NEVER make assumptions about database type
|
|
453
|
+
|
|
454
|
+
#### Example Database-Agnostic Code:
|
|
455
|
+
\`\`\`typescript
|
|
456
|
+
// \u2705 CORRECT - Memory operations
|
|
457
|
+
await runtime.createMemory({
|
|
458
|
+
entityId: message.entityId,
|
|
459
|
+
agentId: runtime.agentId,
|
|
460
|
+
content: { text: 'Information to store' },
|
|
461
|
+
roomId: message.roomId,
|
|
462
|
+
embedding: await runtime.embed('Information to store'),
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// \u2705 CORRECT - Search operations
|
|
466
|
+
const memories = await runtime.searchMemories({
|
|
467
|
+
text: searchQuery,
|
|
468
|
+
entityId: message.entityId,
|
|
469
|
+
count: 10,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// \u274C WRONG - Direct database imports
|
|
473
|
+
import { PgliteDatabaseAdapter } from '@elizaos/plugin-sql';
|
|
474
|
+
\`\`\`
|
|
475
|
+
|
|
476
|
+
### Import Requirements (MANDATORY)
|
|
477
|
+
ALL imports must come from @elizaos/core ONLY:
|
|
478
|
+
|
|
479
|
+
\`\`\`typescript
|
|
480
|
+
// \u2705 CORRECT - All from @elizaos/core
|
|
481
|
+
import {
|
|
482
|
+
Plugin,
|
|
483
|
+
Action,
|
|
484
|
+
AgentRuntime,
|
|
485
|
+
logger,
|
|
486
|
+
Memory,
|
|
487
|
+
State,
|
|
488
|
+
Content,
|
|
489
|
+
HandlerCallback,
|
|
490
|
+
Service,
|
|
491
|
+
} from '@elizaos/core';
|
|
492
|
+
|
|
493
|
+
// \u274C WRONG - These packages don't exist
|
|
494
|
+
import { logger } from '@elizaos/logger';
|
|
495
|
+
import { Action } from '@elizaos/types';
|
|
496
|
+
import { PgliteDatabaseAdapter } from '@elizaos/plugin-sql';
|
|
497
|
+
\`\`\`
|
|
498
|
+
|
|
499
|
+
## Detailed Technical Specification
|
|
500
|
+
|
|
501
|
+
${detailedSpec}
|
|
502
|
+
|
|
503
|
+
## Implementation Instructions
|
|
504
|
+
|
|
505
|
+
You are now going to implement this plugin following ElizaOS 1.0.0 best practices:
|
|
506
|
+
|
|
507
|
+
### 1. Core Implementation
|
|
508
|
+
- **Use TypeScript** for all code
|
|
509
|
+
- **Follow the ElizaOS plugin structure** exactly
|
|
510
|
+
- **Implement all components** specified above
|
|
511
|
+
- **Create comprehensive tests** for each component
|
|
512
|
+
- **Use proper error handling** throughout
|
|
513
|
+
- **Add detailed logging** using the ElizaOS logger
|
|
514
|
+
|
|
515
|
+
### 2. Database Compatibility
|
|
516
|
+
- **MANDATORY**: Plugin must work with both Pglite and PostgreSQL
|
|
517
|
+
- **Use ONLY runtime APIs** for all data operations
|
|
518
|
+
- **NO direct database imports** or database-specific code
|
|
519
|
+
- **Test with both databases** in test suite
|
|
520
|
+
|
|
521
|
+
### 3. Import Compliance
|
|
522
|
+
- **ALL imports from @elizaos/core ONLY**
|
|
523
|
+
- **NO imports from non-existent packages**
|
|
524
|
+
- **Follow import examples above exactly**
|
|
525
|
+
|
|
526
|
+
### 4. Component Requirements
|
|
527
|
+
- **Services**: Must extend the base Service class with lifecycle methods (initialize, start, stop)
|
|
528
|
+
- **Actions**: Must implement validate and handler functions
|
|
529
|
+
- **Providers**: Must return formatted context strings using runtime APIs
|
|
530
|
+
- **Evaluators**: Run after interactions, store data using runtime APIs
|
|
531
|
+
- **All components**: Must be properly exported in index.ts
|
|
532
|
+
|
|
533
|
+
### 5. Testing Requirements
|
|
534
|
+
- **Tests must use bun test** and cover all functionality
|
|
535
|
+
- **Database compatibility tests**:
|
|
536
|
+
\`\`\`typescript
|
|
537
|
+
describe('Database Compatibility', () => {
|
|
538
|
+
it('should work with Pglite', async () => {
|
|
539
|
+
process.env.PGLITE_DATA_DIR = './.test-db';
|
|
540
|
+
delete process.env.POSTGRES_URL;
|
|
541
|
+
// Test plugin functionality
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should work with PostgreSQL', async () => {
|
|
545
|
+
process.env.POSTGRES_URL = 'postgresql://test:test@localhost:5432/test';
|
|
546
|
+
delete process.env.PGLITE_DATA_DIR;
|
|
547
|
+
// Test plugin functionality
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
\`\`\`
|
|
551
|
+
|
|
552
|
+
### 6. Quality Requirements
|
|
553
|
+
- **NO stubs or incomplete code**
|
|
554
|
+
- **Production-ready implementation**
|
|
555
|
+
- **Proper error handling**
|
|
556
|
+
- **Clean, well-organized code**
|
|
557
|
+
|
|
558
|
+
## Production Readiness Checklist
|
|
559
|
+
|
|
560
|
+
Before considering implementation complete, verify:
|
|
561
|
+
|
|
562
|
+
- \u2705 All imports come from @elizaos/core only
|
|
563
|
+
- \u2705 No direct database adapter imports
|
|
564
|
+
- \u2705 Uses runtime APIs for all data operations
|
|
565
|
+
- \u2705 Works with both Pglite and PostgreSQL
|
|
566
|
+
- \u2705 Has comprehensive tests for both database types
|
|
567
|
+
- \u2705 No database-specific code or SQL
|
|
568
|
+
- \u2705 Proper error handling throughout
|
|
569
|
+
- \u2705 No stubs or incomplete code
|
|
570
|
+
- \u2705 Services extend base Service class
|
|
571
|
+
- \u2705 Actions have validation and handlers
|
|
572
|
+
- \u2705 All components properly exported
|
|
573
|
+
|
|
574
|
+
Work systematically through each component, implementing it completely before moving to the next.
|
|
575
|
+
Remember: Database compatibility is MANDATORY - the plugin MUST work with both Pglite and PostgreSQL.
|
|
576
|
+
`;
|
|
577
|
+
await fs.writeFile(path.join(this.pluginPath, "PLUGIN_SPEC.md"), content);
|
|
578
|
+
}
|
|
579
|
+
async runGenerationWithValidation() {
|
|
580
|
+
await this.runClaudeCode();
|
|
581
|
+
if (!await this.runBuildLoop()) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
if (!this.options.skipTests && !await this.runTestLoop()) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
if (!this.options.skipValidation && !await this.runProductionValidationLoop()) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
async runBuildLoop() {
|
|
593
|
+
let buildIteration = 0;
|
|
594
|
+
let buildSuccess = false;
|
|
595
|
+
while (buildIteration < MAX_BUILD_ITERATIONS && !buildSuccess) {
|
|
596
|
+
buildIteration++;
|
|
597
|
+
logger.info(`Build iteration ${buildIteration}/${MAX_BUILD_ITERATIONS}`);
|
|
598
|
+
if (buildIteration > 1) {
|
|
599
|
+
const buildErrors = await this.getBuildErrors();
|
|
600
|
+
await this.runClaudeCodeWithContext(buildErrors);
|
|
601
|
+
}
|
|
602
|
+
const buildResult = await this.runBuild();
|
|
603
|
+
buildSuccess = buildResult.success;
|
|
604
|
+
if (buildSuccess) {
|
|
605
|
+
logger.info("\u2705 Build successful!");
|
|
606
|
+
return true;
|
|
607
|
+
} else {
|
|
608
|
+
logger.warn(`Build failed. ${MAX_BUILD_ITERATIONS - buildIteration} attempts remaining.`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return buildSuccess;
|
|
612
|
+
}
|
|
613
|
+
async runTestLoop() {
|
|
614
|
+
let testIteration = 0;
|
|
615
|
+
let allTestsPass = false;
|
|
616
|
+
while (testIteration < MAX_TEST_ITERATIONS && !allTestsPass) {
|
|
617
|
+
testIteration++;
|
|
618
|
+
logger.info(`Test iteration ${testIteration}/${MAX_TEST_ITERATIONS}`);
|
|
619
|
+
if (testIteration > 1) {
|
|
620
|
+
const testErrors = await this.getTestErrors();
|
|
621
|
+
await this.runClaudeCodeWithContext(testErrors);
|
|
622
|
+
}
|
|
623
|
+
const testResult = await this.runTests();
|
|
624
|
+
allTestsPass = testResult.success;
|
|
625
|
+
if (allTestsPass) {
|
|
626
|
+
logger.info("\u2705 All tests passing!");
|
|
627
|
+
return true;
|
|
628
|
+
} else {
|
|
629
|
+
logger.warn(`Tests failed. ${MAX_TEST_ITERATIONS - testIteration} attempts remaining.`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return allTestsPass;
|
|
633
|
+
}
|
|
634
|
+
async runProductionValidationLoop() {
|
|
635
|
+
let revisionIteration = 0;
|
|
636
|
+
let productionReady = false;
|
|
637
|
+
while (revisionIteration < MAX_REVISION_ITERATIONS && !productionReady) {
|
|
638
|
+
revisionIteration++;
|
|
639
|
+
logger.info(
|
|
640
|
+
`Production validation iteration ${revisionIteration}/${MAX_REVISION_ITERATIONS}`
|
|
641
|
+
);
|
|
642
|
+
const validationResult = await this.validateProductionReadiness();
|
|
643
|
+
productionReady = validationResult.production_ready;
|
|
644
|
+
if (productionReady) {
|
|
645
|
+
logger.info("\u2705 Plugin validated as production ready!");
|
|
646
|
+
return true;
|
|
647
|
+
} else if (validationResult.revision_instructions) {
|
|
648
|
+
logger.warn("Plugin needs revisions. Applying changes...");
|
|
649
|
+
await this.runClaudeCodeWithContext(validationResult.revision_instructions);
|
|
650
|
+
if (!await this.runBuildLoop() || !this.options.skipTests && !await this.runTestLoop()) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return productionReady;
|
|
656
|
+
}
|
|
657
|
+
async runClaudeCode() {
|
|
658
|
+
const prompt = `Please read the PLUGIN_SPEC.md file in this repository and implement the complete plugin as specified. Create all components, tests, and ensure everything is production-ready with no stubs or incomplete code.`;
|
|
659
|
+
await this.runClaudeCodeWithPrompt(prompt);
|
|
660
|
+
}
|
|
661
|
+
async runClaudeCodeWithContext(context) {
|
|
662
|
+
const prompt = `Please read the PLUGIN_SPEC.md file and fix the following issues:
|
|
663
|
+
|
|
664
|
+
${context}
|
|
665
|
+
|
|
666
|
+
Make all necessary changes to fix the issues and ensure the plugin builds and all tests pass.`;
|
|
667
|
+
await this.runClaudeCodeWithPrompt(prompt);
|
|
668
|
+
}
|
|
669
|
+
async runClaudeCodeWithPrompt(prompt) {
|
|
670
|
+
process.chdir(this.pluginPath);
|
|
671
|
+
logger.info("\u{1F916} Starting Claude Code execution...");
|
|
672
|
+
logger.info(`\u{1F4C1} Working directory: ${this.pluginPath}`);
|
|
673
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
674
|
+
setTimeout(() => {
|
|
675
|
+
reject(
|
|
676
|
+
new Error(`Claude Code execution timed out after ${CLAUDE_CODE_TIMEOUT / 1e3} seconds`)
|
|
677
|
+
);
|
|
678
|
+
}, CLAUDE_CODE_TIMEOUT);
|
|
679
|
+
});
|
|
680
|
+
const executePromise = (async () => {
|
|
681
|
+
try {
|
|
682
|
+
const claudeArgs = [
|
|
683
|
+
"--print",
|
|
684
|
+
"--max-turns",
|
|
685
|
+
"30",
|
|
686
|
+
"--verbose",
|
|
687
|
+
"--model",
|
|
688
|
+
"opus",
|
|
689
|
+
"--dangerously-skip-permissions",
|
|
690
|
+
prompt
|
|
691
|
+
];
|
|
692
|
+
logger.info(`\u{1F680} Executing: claude ${claudeArgs.join(" ")}`);
|
|
693
|
+
this.activeClaudeProcess = execa("claude", claudeArgs, {
|
|
694
|
+
stdio: "inherit",
|
|
695
|
+
cwd: this.pluginPath
|
|
696
|
+
});
|
|
697
|
+
await this.activeClaudeProcess;
|
|
698
|
+
this.activeClaudeProcess = null;
|
|
699
|
+
logger.info("\u2705 Claude Code execution completed successfully");
|
|
700
|
+
} catch (error) {
|
|
701
|
+
this.activeClaudeProcess = null;
|
|
702
|
+
if (error.code === "ENOENT") {
|
|
703
|
+
throw new Error(
|
|
704
|
+
"Claude Code not found! Install with: bun install -g @anthropic-ai/claude-code"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
})();
|
|
710
|
+
try {
|
|
711
|
+
await Promise.race([executePromise, timeoutPromise]);
|
|
712
|
+
} catch (error) {
|
|
713
|
+
if (this.activeClaudeProcess) {
|
|
714
|
+
try {
|
|
715
|
+
this.activeClaudeProcess.kill();
|
|
716
|
+
this.activeClaudeProcess = null;
|
|
717
|
+
logger.warn("\u{1F6D1} Claude Code process terminated due to timeout");
|
|
718
|
+
} catch (killError) {
|
|
719
|
+
logger.error("Failed to kill timed-out process:", killError);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
logger.error("\u274C Claude Code execution failed:", error);
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async runBuild() {
|
|
727
|
+
try {
|
|
728
|
+
logger.info("Installing dependencies with bun...");
|
|
729
|
+
await runBunCommand(["install"], this.pluginPath);
|
|
730
|
+
logger.info("Running build with bun...");
|
|
731
|
+
await runBunCommand(["run", "build"], this.pluginPath);
|
|
732
|
+
return { success: true };
|
|
733
|
+
} catch (error) {
|
|
734
|
+
const errorOutput = (error.stdout || "") + "\n" + (error.stderr || "");
|
|
735
|
+
logger.error("Build failed:", errorOutput);
|
|
736
|
+
return { success: false, errors: errorOutput };
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
async getBuildErrors() {
|
|
740
|
+
const result = await this.runBuild();
|
|
741
|
+
return result.errors || "Build failed but no specific errors captured";
|
|
742
|
+
}
|
|
743
|
+
async runTests() {
|
|
744
|
+
try {
|
|
745
|
+
logger.info("Running tests with bun...");
|
|
746
|
+
await runBunCommand(["test"], this.pluginPath);
|
|
747
|
+
return { success: true };
|
|
748
|
+
} catch (error) {
|
|
749
|
+
const errorOutput = (error.stdout || "") + "\n" + (error.stderr || "");
|
|
750
|
+
logger.error("Tests failed:", errorOutput);
|
|
751
|
+
return { success: false, errors: errorOutput };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async getTestErrors() {
|
|
755
|
+
const result = await this.runTests();
|
|
756
|
+
return result.errors || "Tests failed but no specific errors captured";
|
|
757
|
+
}
|
|
758
|
+
async validateProductionReadiness() {
|
|
759
|
+
const allFiles = await this.getAllPluginFiles();
|
|
760
|
+
const prompt = `You are reviewing a newly generated ElizaOS plugin for production readiness.
|
|
761
|
+
|
|
762
|
+
## Plugin Files:
|
|
763
|
+
${allFiles}
|
|
764
|
+
|
|
765
|
+
## Evaluation Criteria:
|
|
766
|
+
|
|
767
|
+
### 1. Import Compliance (CRITICAL - MANDATORY)
|
|
768
|
+
- \u2705 ALL imports must come from @elizaos/core ONLY
|
|
769
|
+
- \u274C NO imports from @elizaos/plugin, @elizaos/types, @elizaos/logger, etc. (these don't exist)
|
|
770
|
+
- \u274C NO direct database adapter imports (PgliteDatabaseAdapter, PgDatabaseAdapter)
|
|
771
|
+
- \u2705 Correct import format: import { Plugin, Action, AgentRuntime, logger } from '@elizaos/core'
|
|
772
|
+
|
|
773
|
+
### 2. Database Compatibility (CRITICAL - MANDATORY)
|
|
774
|
+
- \u2705 Plugin must work with both Pglite and PostgreSQL without code changes
|
|
775
|
+
- \u2705 Uses ONLY runtime.databaseAdapter for database operations
|
|
776
|
+
- \u2705 Uses runtime.createMemory(), runtime.searchMemories(), runtime.createGoal()
|
|
777
|
+
- \u2705 Uses runtime.ensureConnection() for relationships
|
|
778
|
+
- \u274C NO database-specific SQL or queries
|
|
779
|
+
- \u274C NO assumptions about database type
|
|
780
|
+
- \u274C NO direct database adapter usage
|
|
781
|
+
- \u2705 Database compatibility tests for both Pglite and PostgreSQL
|
|
782
|
+
|
|
783
|
+
### 3. Component Implementation
|
|
784
|
+
- \u2705 All components are fully implemented (no stubs or TODOs)
|
|
785
|
+
- \u2705 Services extend base Service class with lifecycle methods (initialize, start, stop)
|
|
786
|
+
- \u2705 Actions have proper validation and handlers
|
|
787
|
+
- \u2705 Providers return formatted context using runtime APIs
|
|
788
|
+
- \u2705 Evaluators store data using runtime APIs
|
|
789
|
+
|
|
790
|
+
### 4. Testing Coverage
|
|
791
|
+
- \u2705 Comprehensive tests exist for all functionality
|
|
792
|
+
- \u2705 Tests use bun test framework
|
|
793
|
+
- \u2705 Database compatibility tests included
|
|
794
|
+
- \u2705 Tests cover main plugin functionality
|
|
795
|
+
- \u2705 Error handling tests included
|
|
796
|
+
|
|
797
|
+
### 5. Code Quality
|
|
798
|
+
- \u2705 Proper error handling throughout
|
|
799
|
+
- \u2705 Follows ElizaOS 1.0.0 patterns and best practices
|
|
800
|
+
- \u2705 Documentation is complete
|
|
801
|
+
- \u2705 Code is clean and well-organized
|
|
802
|
+
- \u2705 Plugin exports are correct
|
|
803
|
+
- \u2705 No stubs or incomplete code remains
|
|
804
|
+
|
|
805
|
+
## Response Format:
|
|
806
|
+
Respond with a JSON object:
|
|
807
|
+
{
|
|
808
|
+
"production_ready": boolean,
|
|
809
|
+
"revision_instructions": "Detailed instructions for what needs to be fixed (only if not production ready)"
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
## Validation Priority (ALL MUST PASS):
|
|
813
|
+
1. Import compliance is MANDATORY - any non-@elizaos/core imports = NOT production ready
|
|
814
|
+
2. Database compatibility is MANDATORY - must work with both Pglite and PostgreSQL
|
|
815
|
+
3. All components must be fully implemented
|
|
816
|
+
4. Comprehensive tests must exist
|
|
817
|
+
5. All other criteria must also pass
|
|
818
|
+
|
|
819
|
+
If ANY of the CRITICAL requirements fail, the plugin is NOT production ready.`;
|
|
820
|
+
const message = await this.anthropic.messages.create({
|
|
821
|
+
model: "claude-opus-4-20250514",
|
|
822
|
+
max_tokens: 8192,
|
|
823
|
+
temperature: 0,
|
|
824
|
+
messages: [
|
|
825
|
+
{
|
|
826
|
+
role: "user",
|
|
827
|
+
content: prompt
|
|
828
|
+
}
|
|
829
|
+
]
|
|
830
|
+
});
|
|
831
|
+
try {
|
|
832
|
+
const responseText = message.content.map((block) => block.type === "text" ? block.text : "").join("");
|
|
833
|
+
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
834
|
+
if (jsonMatch) {
|
|
835
|
+
return JSON.parse(jsonMatch[0]);
|
|
836
|
+
}
|
|
837
|
+
throw new Error("No JSON found in response");
|
|
838
|
+
} catch (error) {
|
|
839
|
+
logger.error("Failed to parse validation response:", error);
|
|
840
|
+
return {
|
|
841
|
+
production_ready: false,
|
|
842
|
+
revision_instructions: "Failed to parse validation response. Please review manually."
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
async getAllPluginFiles() {
|
|
847
|
+
let content = "";
|
|
848
|
+
const files = await fs.readdir(this.pluginPath, { recursive: true });
|
|
849
|
+
for (const file of files) {
|
|
850
|
+
if (typeof file !== "string") continue;
|
|
851
|
+
const filePath = path.join(this.pluginPath, file);
|
|
852
|
+
const stat2 = await fs.stat(filePath);
|
|
853
|
+
if (stat2.isFile() && !file.includes("node_modules") && !file.includes(".git")) {
|
|
854
|
+
const ext = path.extname(file);
|
|
855
|
+
if ([".ts", ".js", ".json", ".md"].includes(ext)) {
|
|
856
|
+
const fileContent = await fs.readFile(filePath, "utf-8");
|
|
857
|
+
content += `
|
|
858
|
+
### File: ${file}
|
|
859
|
+
\`\`\`${ext.slice(1)}
|
|
860
|
+
${fileContent}
|
|
861
|
+
\`\`\`
|
|
862
|
+
`;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return content;
|
|
867
|
+
}
|
|
868
|
+
async copyToCWD() {
|
|
869
|
+
const pluginName = path.basename(this.pluginPath);
|
|
870
|
+
const targetPath = path.join(process.cwd(), pluginName);
|
|
871
|
+
if (await fs.pathExists(targetPath)) {
|
|
872
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
873
|
+
const backupPath = `${targetPath}-backup-${timestamp}`;
|
|
874
|
+
logger.info(`Backing up existing ${pluginName} to ${path.basename(backupPath)}`);
|
|
875
|
+
await fs.move(targetPath, backupPath);
|
|
876
|
+
}
|
|
877
|
+
logger.info(`Copying plugin to ${targetPath}`);
|
|
878
|
+
await fs.copy(this.pluginPath, targetPath, {
|
|
879
|
+
filter: (src) => {
|
|
880
|
+
const relativePath = path.relative(this.pluginPath, src);
|
|
881
|
+
return !relativePath.includes(".git") && !relativePath.includes("node_modules");
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
return targetPath;
|
|
885
|
+
}
|
|
886
|
+
async checkDiskSpace() {
|
|
887
|
+
const diskSpace = await this.getAvailableDiskSpace();
|
|
888
|
+
if (diskSpace < MIN_DISK_SPACE_GB) {
|
|
889
|
+
throw new Error(
|
|
890
|
+
`Insufficient disk space. Need at least ${MIN_DISK_SPACE_GB}GB free, but only ${diskSpace.toFixed(2)}GB available.`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
async getAvailableDiskSpace() {
|
|
895
|
+
try {
|
|
896
|
+
const result = await execa("df", ["-k", os.tmpdir()]);
|
|
897
|
+
const lines = result.stdout.split("\n");
|
|
898
|
+
const dataLine = lines[1];
|
|
899
|
+
const parts = dataLine.split(/\s+/);
|
|
900
|
+
const availableKB = parseInt(parts[3]);
|
|
901
|
+
return availableKB / 1024 / 1024;
|
|
902
|
+
} catch (error) {
|
|
903
|
+
logger.warn("Could not check disk space, proceeding anyway");
|
|
904
|
+
return MIN_DISK_SPACE_GB + 1;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
export {
|
|
909
|
+
PluginCreator
|
|
910
|
+
};
|