@dyyz1993/create-agent 1.9.0 → 2.0.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/package.json +23 -23
- package/src/lib/copy.ts +236 -243
- package/templates/agent/eslint-plugin-rpc/index.js +50 -0
- package/templates/agent/eslint-plugin-rpc/package.json +6 -0
- package/templates/agent/eslint-plugin-rpc/rules/module-file-naming.js +99 -0
- package/templates/agent/eslint-plugin-rpc/rules/no-bare-method.js +78 -0
- package/templates/agent/eslint-plugin-rpc/rules/no-direct-register.js +65 -0
- package/templates/agent/eslint-plugin-rpc/rules/no-hardcoded-strings.js +69 -0
- package/templates/agent/eslint-plugin-rpc/rules/require-api-client.js +58 -0
- package/templates/agent/eslint-plugin-rpc/rules/require-typed-register.js +78 -0
- package/templates/agent/eslint-plugin-rpc/rules/schema-merge-only.js +55 -0
- package/templates/agent/eslint.config.mjs +2 -1
- package/templates/agent/package.json +1 -2
- package/templates/agent/src/mainview/components/layout/AppLayout.tsx +3 -1
- package/templates/chat/eslint-plugin-rpc/index.js +50 -0
- package/templates/chat/eslint-plugin-rpc/package.json +6 -0
- package/templates/chat/eslint-plugin-rpc/rules/module-file-naming.js +99 -0
- package/templates/chat/eslint-plugin-rpc/rules/no-bare-method.js +78 -0
- package/templates/chat/eslint-plugin-rpc/rules/no-direct-register.js +65 -0
- package/templates/chat/eslint-plugin-rpc/rules/no-hardcoded-strings.js +69 -0
- package/templates/chat/eslint-plugin-rpc/rules/require-api-client.js +58 -0
- package/templates/chat/eslint-plugin-rpc/rules/require-typed-register.js +78 -0
- package/templates/chat/eslint-plugin-rpc/rules/schema-merge-only.js +55 -0
- package/templates/chat/eslint.config.mjs +2 -1
- package/templates/chat/package.json +1 -2
- package/templates/general/eslint-plugin-rpc/index.js +50 -0
- package/templates/general/eslint-plugin-rpc/package.json +6 -0
- package/templates/general/eslint-plugin-rpc/rules/module-file-naming.js +99 -0
- package/templates/general/eslint-plugin-rpc/rules/no-bare-method.js +78 -0
- package/templates/general/eslint-plugin-rpc/rules/no-direct-register.js +65 -0
- package/templates/general/eslint-plugin-rpc/rules/no-hardcoded-strings.js +69 -0
- package/templates/general/eslint-plugin-rpc/rules/require-api-client.js +58 -0
- package/templates/general/eslint-plugin-rpc/rules/require-typed-register.js +78 -0
- package/templates/general/eslint-plugin-rpc/rules/schema-merge-only.js +55 -0
- package/templates/general/eslint.config.mjs +2 -1
- package/templates/general/package.json +1 -2
- package/templates/agent/bun.lock +0 -1279
- package/templates/agent/package-lock.json +0 -3670
- package/templates/chat/bun.lock +0 -1203
- package/templates/chat/package-lock.json +0 -3670
- package/templates/general/bun.lock +0 -1266
- package/templates/general/package-lock.json +0 -3670
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
"name": "@dyyz1993/create-agent",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Create Agent project scaffolding CLI",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/dyyz1993/pi-agent-template"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"create-agent": "./src/cli.ts"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src/",
|
|
14
|
+
"templates/"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"sync-templates": "rm -rf templates/general templates/chat templates/agent && rsync -a --exclude='node_modules' --exclude='dist' --exclude='build' --exclude='.server-port' --exclude='logs' --exclude='bun.lock' ../../templates/general/ templates/general/ && rsync -a --exclude='node_modules' --exclude='dist' --exclude='build' --exclude='.server-port' --exclude='logs' --exclude='bun.lock' ../../templates/chat/ templates/chat/ && rsync -a --exclude='node_modules' --exclude='dist' --exclude='build' --exclude='.server-port' --exclude='logs' --exclude='bun.lock' ../../templates/agent/ templates/agent/ && rsync -a ../../packages/eslint-plugin-rpc/ templates/agent/eslint-plugin-rpc/ && rsync -a ../../packages/eslint-plugin-rpc/ templates/chat/eslint-plugin-rpc/ && rsync -a ../../packages/eslint-plugin-rpc/ templates/general/eslint-plugin-rpc/ && node scripts/post-sync-templates.mjs",
|
|
18
|
+
"test": "bun test src/__tests__/",
|
|
19
|
+
"prepublishOnly": "test -d templates/general/src && test -d templates/chat/src && test -d templates/agent/src"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"bun": ">=1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT"
|
|
25
25
|
}
|
package/src/lib/copy.ts
CHANGED
|
@@ -1,285 +1,278 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from
|
|
2
|
-
import { join, resolve } from
|
|
3
|
-
import { execSync } from
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from "fs";
|
|
2
|
+
import { join, resolve } from "path";
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
4
|
|
|
5
|
-
const SKIP_DIRS = new Set([
|
|
6
|
-
const SKIP_FILES = new Set([
|
|
5
|
+
const SKIP_DIRS = new Set(["node_modules", "build", "dist", ".git", ".husky", ".trae"]);
|
|
6
|
+
const SKIP_FILES = new Set(["bun.lock", "rpc-browser.js"]);
|
|
7
7
|
|
|
8
8
|
export interface CopyOptions {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
projectName: string;
|
|
10
|
+
templateDir: string;
|
|
11
|
+
targetDir: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function deriveNames(projectName: string) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const pascalName = projectName.replace(/(^|-)([a-z])/g, (_, _sep, c) => c.toUpperCase());
|
|
16
|
+
const identifier = `com.${projectName.replace(/-/g, "")}.app`;
|
|
17
|
+
const shortId = `com.${projectName.replace(/-/g, "")}`;
|
|
18
|
+
return { pascalName, identifier, shortId };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function copyAndReplace(srcDir: string, destDir: string, projectName: string): void {
|
|
22
|
-
|
|
22
|
+
const { pascalName, identifier, shortId } = deriveNames(projectName);
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
mkdirSync(destDir, { recursive: true });
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const entries = readdirSync(srcDir, { withFileTypes: true });
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const srcPath = join(srcDir, entry.name);
|
|
30
|
+
const destPath = join(destDir, entry.name);
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
34
|
+
copyAndReplace(srcPath, destPath, projectName);
|
|
35
|
+
} else {
|
|
36
|
+
if (SKIP_FILES.has(entry.name)) continue;
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
let content = readFileSync(srcPath, "utf-8");
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
content = content.replace(/pi-agent-template/g, projectName);
|
|
41
|
+
content = content.replace(/Pi Agent Template/g, pascalName);
|
|
42
|
+
content = content.replace(/Pi Agent/g, pascalName);
|
|
43
|
+
content = content.replace(/com\.piagent\.template/g, identifier);
|
|
44
|
+
content = content.replace(/com\.piagent/g, shortId);
|
|
45
|
+
content = content.replace(/@pi-agent\//g, `@${projectName}/`);
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
writeFileSync(destPath, content);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function copyTraeRules(monorepoRoot: string, targetDir: string, projectName: string): void {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
const traeRulesDir = resolve(monorepoRoot, ".trae", "rules");
|
|
54
|
+
if (!existsSync(traeRulesDir)) return;
|
|
55
|
+
|
|
56
|
+
const destRulesDir = join(targetDir, ".trae", "rules");
|
|
57
|
+
mkdirSync(destRulesDir, { recursive: true });
|
|
58
|
+
|
|
59
|
+
const files = readdirSync(traeRulesDir).filter((f) => f !== "memory.md");
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
const src = join(traeRulesDir, file);
|
|
62
|
+
const dest = join(destRulesDir, file);
|
|
63
|
+
let content = readFileSync(src, "utf-8");
|
|
64
|
+
content = content.replace(/pi-agent-template/g, projectName);
|
|
65
|
+
writeFileSync(dest, content);
|
|
66
|
+
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function copySharedModules(monorepoRoot: string, targetDir: string, projectName: string): void {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const sharedDir = resolve(monorepoRoot, "templates", "shared");
|
|
71
|
+
if (!existsSync(sharedDir)) return;
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const destSharedDir = join(targetDir, "shared");
|
|
74
|
+
copyAndReplace(sharedDir, destSharedDir, projectName);
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
const tsconfigPath = join(targetDir, "tsconfig.json");
|
|
77
|
+
if (existsSync(tsconfigPath)) {
|
|
78
|
+
let tsconfig = readFileSync(tsconfigPath, "utf-8");
|
|
79
|
+
tsconfig = tsconfig.replace(/"\.\.\/shared\/\*"/g, '"./shared/*"');
|
|
80
|
+
tsconfig = tsconfig.replace(/"\.\.\/shared"/g, '"./shared"');
|
|
81
|
+
writeFileSync(tsconfigPath, tsconfig);
|
|
82
|
+
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
cleanSharedViteConfig(destSharedDir);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function cleanViteConfig(targetDir: string): void {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
const viteConfigPath = join(targetDir, "vite.config.ts");
|
|
89
|
+
const vitestConfigPath = join(targetDir, "vitest.config.ts");
|
|
90
|
+
|
|
91
|
+
for (const configPath of [viteConfigPath, vitestConfigPath]) {
|
|
92
|
+
if (!existsSync(configPath)) continue;
|
|
93
|
+
|
|
94
|
+
let config = readFileSync(configPath, "utf-8");
|
|
95
|
+
|
|
96
|
+
config = config.replace(/,\n\s+resolve:\s*\{\n\s+alias:\s*\{[^}]*\}[,\s]*\}[,;]?\n/g, "");
|
|
97
|
+
|
|
98
|
+
if (!config.includes('from "path"') && config.includes("resolve(")) {
|
|
99
|
+
config = 'import { resolve } from "path";\n' + config;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
config = config.replace(
|
|
103
|
+
/from ["']\.\.\/shared\/vite-base\.config["']/g,
|
|
104
|
+
'from "./shared/vite-base.config"'
|
|
105
|
+
);
|
|
106
|
+
config = config.replace(
|
|
107
|
+
/from ["']\.\.\/shared\/vitest-base\.config["']/g,
|
|
108
|
+
'from "./shared/vitest-base.config"'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
writeFileSync(configPath, config);
|
|
112
|
+
}
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
function cleanSharedViteConfig(sharedDir: string): void {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
config = config.replace(
|
|
131
|
-
/export function create(Vite|Vitest)Config\(dirname: string\)/g,
|
|
132
|
-
"export function create$1Config()"
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
writeFileSync(configPath, config);
|
|
136
|
-
}
|
|
116
|
+
const viteBaseConfigPath = join(sharedDir, "vite-base.config.ts");
|
|
117
|
+
const vitestBaseConfigPath = join(sharedDir, "vitest-base.config.ts");
|
|
118
|
+
|
|
119
|
+
for (const configPath of [viteBaseConfigPath, vitestBaseConfigPath]) {
|
|
120
|
+
if (!existsSync(configPath)) continue;
|
|
121
|
+
|
|
122
|
+
let config = readFileSync(configPath, "utf-8");
|
|
123
|
+
config = config.replace(
|
|
124
|
+
/,?\s*\n?\s*"@dyyz1993\/rpc-core":\s*resolve\(\s*\n?\s*dirname,\s*\n?\s*"\.\.",\s*\n?\s*"\.\.",\s*\n?\s*"packages",\s*\n?\s*"rpc-core",\s*\n?\s*"src",\s*\n?\s*"index\.ts"\s*\n?\s*\),?\s*\n?/g,
|
|
125
|
+
""
|
|
126
|
+
);
|
|
127
|
+
config = config.replace(
|
|
128
|
+
/resolve\(\s*\n?\s*dirname,\s*\n?\s*"\.\.",\s*\n?\s*"shared"\s*\n?\s*\)/g,
|
|
129
|
+
'resolve(dirname, "shared")'
|
|
130
|
+
);
|
|
131
|
+
writeFileSync(configPath, config);
|
|
132
|
+
}
|
|
137
133
|
}
|
|
138
134
|
|
|
139
135
|
function resolvePackageVersion(packageName: string): string {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
136
|
+
try {
|
|
137
|
+
return execSync(`npm view ${packageName} version`, { encoding: "utf-8" }).trim();
|
|
138
|
+
} catch {
|
|
139
|
+
return "1.0.0";
|
|
140
|
+
}
|
|
145
141
|
}
|
|
146
142
|
|
|
147
|
-
const WORKSPACE_PACKAGES = [
|
|
148
|
-
'@dyyz1993/rpc-core',
|
|
149
|
-
'@dyyz1993/eslint-plugin-rpc',
|
|
150
|
-
];
|
|
143
|
+
const WORKSPACE_PACKAGES = ["@dyyz1993/rpc-core", "@dyyz1993/eslint-plugin-rpc"];
|
|
151
144
|
|
|
152
145
|
function updatePackageJson(targetDir: string, _projectName: string): void {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
146
|
+
const rootPkgPath = join(targetDir, "package.json");
|
|
147
|
+
if (!existsSync(rootPkgPath)) return;
|
|
148
|
+
|
|
149
|
+
const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8"));
|
|
150
|
+
|
|
151
|
+
delete rootPkg.workspaces;
|
|
152
|
+
|
|
153
|
+
for (const depKey of ["dependencies", "devDependencies"] as const) {
|
|
154
|
+
if (!rootPkg[depKey]) continue;
|
|
155
|
+
for (const pkgName of WORKSPACE_PACKAGES) {
|
|
156
|
+
if (!rootPkg[depKey][pkgName]) continue;
|
|
157
|
+
if (pkgName === "@dyyz1993/rpc-core") {
|
|
158
|
+
const version = resolvePackageVersion(pkgName);
|
|
159
|
+
rootPkg[depKey][pkgName] = `^${version}`;
|
|
160
|
+
} else {
|
|
161
|
+
delete rootPkg[depKey][pkgName];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, "\t") + "\n");
|
|
167
|
+
|
|
168
|
+
const eslintConfigPath = join(targetDir, "eslint.config.mjs");
|
|
169
|
+
if (existsSync(eslintConfigPath)) {
|
|
170
|
+
const lines = readFileSync(eslintConfigPath, "utf-8").split("\n");
|
|
171
|
+
const filteredLines: string[] = [];
|
|
172
|
+
|
|
173
|
+
let inRpcSection = false;
|
|
174
|
+
for (let i = 0; i < lines.length; i++) {
|
|
175
|
+
const line = lines[i];
|
|
176
|
+
|
|
177
|
+
if (line.includes("import rpcPlugin") && line.includes("@dyyz1993/eslint-plugin-rpc")) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (line.includes("RPC") && line.includes("规范规则")) {
|
|
182
|
+
inRpcSection = true;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (inRpcSection) {
|
|
187
|
+
if (line.trim().startsWith("'rpc/") || line.trim().startsWith('"rpc/')) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
inRpcSection = false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (line.includes("rpc: rpcPlugin")) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
filteredLines.push(line);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let content = filteredLines.join("\n");
|
|
201
|
+
content = content.replace(/\n\s+plugins:\s*\{\s*\},?\s*\n/g, "\n");
|
|
202
|
+
|
|
203
|
+
writeFileSync(eslintConfigPath, content);
|
|
204
|
+
}
|
|
212
205
|
}
|
|
213
206
|
|
|
214
207
|
export async function copyTemplate(options: CopyOptions): Promise<void> {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
208
|
+
const { projectName, templateDir, targetDir } = options;
|
|
209
|
+
const localRoot = resolve(import.meta.dir, "..", "..", "..", "..");
|
|
210
|
+
const isMonorepo = existsSync(resolve(localRoot, "templates", "general"));
|
|
211
|
+
|
|
212
|
+
if (existsSync(targetDir)) {
|
|
213
|
+
throw new Error(`Directory "${targetDir}" already exists.`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { identifier } = deriveNames(projectName);
|
|
217
|
+
|
|
218
|
+
console.log(`Creating project: ${projectName}`);
|
|
219
|
+
console.log(`Target directory: ${targetDir}`);
|
|
220
|
+
console.log(`App identifier: ${identifier}`);
|
|
221
|
+
console.log("");
|
|
222
|
+
|
|
223
|
+
copyAndReplace(templateDir, targetDir, projectName);
|
|
224
|
+
if (isMonorepo) {
|
|
225
|
+
copyTraeRules(localRoot, targetDir, projectName);
|
|
226
|
+
copySharedModules(localRoot, targetDir, projectName);
|
|
227
|
+
}
|
|
228
|
+
cleanViteConfig(targetDir);
|
|
229
|
+
updatePackageJson(targetDir, projectName);
|
|
230
|
+
|
|
231
|
+
console.log("Initializing git...");
|
|
232
|
+
execSync("git init", { cwd: targetDir, stdio: "pipe" });
|
|
233
|
+
|
|
234
|
+
console.log("Installing dependencies...");
|
|
235
|
+
execSync("bun install", { cwd: targetDir, stdio: "inherit" });
|
|
236
|
+
|
|
237
|
+
console.log("Building browser bundle...");
|
|
238
|
+
try {
|
|
239
|
+
execSync("bun run build:browser", { cwd: targetDir, stdio: "pipe" });
|
|
240
|
+
} catch {
|
|
241
|
+
console.log("(build:browser skipped - script not found)");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const huskyDir = join(targetDir, ".husky");
|
|
245
|
+
mkdirSync(huskyDir, { recursive: true });
|
|
246
|
+
writeFileSync(
|
|
247
|
+
join(huskyDir, "pre-commit"),
|
|
248
|
+
[
|
|
249
|
+
"#!/bin/sh",
|
|
250
|
+
"bun run lint",
|
|
251
|
+
'ERRORS=$(bunx tsc --noEmit 2>&1 | grep "error TS" | grep -v "node_modules" || true)',
|
|
252
|
+
'if [ -n "$ERRORS" ]; then',
|
|
253
|
+
' echo "$ERRORS"',
|
|
254
|
+
" exit 1",
|
|
255
|
+
"fi",
|
|
256
|
+
].join("\n") + "\n"
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
execSync("git add -A", { cwd: targetDir, stdio: "pipe" });
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
execSync(`git commit --no-verify -m "feat: init ${projectName} from pi-agent-template"`, {
|
|
263
|
+
cwd: targetDir,
|
|
264
|
+
stdio: "pipe",
|
|
265
|
+
});
|
|
266
|
+
} catch {
|
|
267
|
+
console.log("(git commit skipped - no files to commit)");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log("");
|
|
271
|
+
console.log("Project created successfully!");
|
|
272
|
+
console.log("");
|
|
273
|
+
console.log("Next steps:");
|
|
274
|
+
console.log(` cd ${targetDir}`);
|
|
275
|
+
console.log(" bun run dev # Start desktop app (Electrobun)");
|
|
276
|
+
console.log(" bun run dev:web # Start web mode (Vite + Gateway)");
|
|
277
|
+
console.log("");
|
|
285
278
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eslint-plugin-rpc — RPC 模块化规范 ESLint 插件(单注册架构)
|
|
3
|
+
*
|
|
4
|
+
* 规则列表:
|
|
5
|
+
* - rpc/no-bare-method : 方法名必须使用 module.action 格式
|
|
6
|
+
* - rpc/no-direct-register : server.register() 只允许在 handlers/ 目录内使用
|
|
7
|
+
* - rpc/schema-merge-only : rpc-schema.ts 禁止直接定义方法
|
|
8
|
+
* - rpc/module-file-naming : 模块文件命名、导出、方法前缀强制规范
|
|
9
|
+
* - rpc/require-typed-register : 入口文件必须导入 registerAllHandlers
|
|
10
|
+
* - rpc/require-api-client : 前端必须通过 apiClient 调用 RPC
|
|
11
|
+
*/
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
const noBareMethod = require("./rules/no-bare-method");
|
|
15
|
+
const noDirectRegister = require("./rules/no-direct-register");
|
|
16
|
+
const schemaMergeOnly = require("./rules/schema-merge-only");
|
|
17
|
+
const moduleFileNaming = require("./rules/module-file-naming");
|
|
18
|
+
const requireTypedRegister = require("./rules/require-typed-register");
|
|
19
|
+
const requireApiClient = require("./rules/require-api-client");
|
|
20
|
+
const noHardcodedStrings = require("./rules/no-hardcoded-strings");
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
meta: {
|
|
24
|
+
name: "eslint-plugin-rpc",
|
|
25
|
+
version: "1.0.0",
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
"no-bare-method": noBareMethod,
|
|
29
|
+
"no-direct-register": noDirectRegister,
|
|
30
|
+
"schema-merge-only": schemaMergeOnly,
|
|
31
|
+
"module-file-naming": moduleFileNaming,
|
|
32
|
+
"require-typed-register": requireTypedRegister,
|
|
33
|
+
"require-api-client": requireApiClient,
|
|
34
|
+
"no-hardcoded-strings": noHardcodedStrings,
|
|
35
|
+
},
|
|
36
|
+
configs: {
|
|
37
|
+
recommended: {
|
|
38
|
+
plugins: ["rpc"],
|
|
39
|
+
rules: {
|
|
40
|
+
"rpc/no-bare-method": "error",
|
|
41
|
+
"rpc/no-direct-register": "error",
|
|
42
|
+
"rpc/schema-merge-only": "error",
|
|
43
|
+
"rpc/module-file-naming": "error",
|
|
44
|
+
"rpc/require-typed-register": "error",
|
|
45
|
+
"rpc/require-api-client": "error",
|
|
46
|
+
"rpc/no-hardcoded-strings": "warn",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|