@dyyz1993/create-agent 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/package.json +1 -1
  2. package/src/commands/create.ts +31 -31
  3. package/src/commands/workspace.ts +112 -105
  4. package/src/lib/copy.ts +24 -1
  5. package/templates/agent/electron/main.js +46 -0
  6. package/templates/agent/electron/preload.js +5 -0
  7. package/templates/agent/electron-builder.json +40 -0
  8. package/templates/agent/eslint.config.mjs +17 -2
  9. package/templates/agent/package.json +69 -3
  10. package/templates/agent/src/__tests__/hybrid-mode.test.ts +126 -0
  11. package/templates/agent/src/__tests__/server-config-security.test.ts +55 -0
  12. package/templates/agent/src/bun/index.ts +96 -41
  13. package/templates/agent/src/bun/three.d.ts +1 -0
  14. package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +118 -0
  15. package/templates/agent/src/gateway/http-routes.ts +1 -1
  16. package/templates/agent/src/gateway/ws-handler.ts +84 -56
  17. package/templates/agent/src/mainview/App.tsx +29 -26
  18. package/templates/agent/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  19. package/templates/agent/src/mainview/__tests__/setup.ts +32 -29
  20. package/templates/agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
  21. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +88 -88
  22. package/templates/agent/src/mainview/components/common/ErrorBoundary.tsx +1 -58
  23. package/templates/agent/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  24. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +1 -0
  25. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +232 -210
  26. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +105 -81
  27. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +427 -378
  28. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +149 -122
  29. package/templates/agent/src/mainview/hooks/use-input-history.ts +70 -61
  30. package/templates/agent/src/mainview/lib/api-client.ts +179 -172
  31. package/templates/agent/src/mainview/main.tsx +4 -10
  32. package/templates/agent/src/mainview/stores/use-feed-store.ts +107 -107
  33. package/templates/agent/src/mainview/utils/drop-handler.ts +114 -115
  34. package/templates/agent/src/server-config.ts +34 -32
  35. package/templates/agent/src/server.ts +42 -54
  36. package/templates/agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  37. package/templates/agent/src/shared/handlers/__tests__/handler-isolation.test.ts +244 -0
  38. package/templates/agent/src/shared/handlers/bash.ts +65 -65
  39. package/templates/agent/src/shared/handlers/chat.ts +189 -155
  40. package/templates/agent/src/shared/handlers/debug.ts +12 -0
  41. package/templates/agent/src/shared/handlers/feed.ts +31 -32
  42. package/templates/agent/src/shared/handlers/git.ts +286 -243
  43. package/templates/agent/src/shared/handlers/index.ts +1 -0
  44. package/templates/agent/src/shared/handlers/rules.ts +31 -31
  45. package/templates/agent/src/shared/handlers/todo.ts +31 -31
  46. package/templates/agent/src/shared/http-routes.ts +250 -0
  47. package/templates/agent/src/shared/lib/__tests__/web-server.test.ts +190 -0
  48. package/templates/agent/src/shared/lib/bash-security.ts +43 -43
  49. package/templates/agent/src/shared/lib/logger.ts +117 -95
  50. package/templates/agent/src/shared/lib/web-server.ts +128 -0
  51. package/templates/agent/src/shared/modules/debug.ts +8 -0
  52. package/templates/agent/src/shared/rpc-schema.ts +16 -3
  53. package/templates/agent/test-upload.txt +0 -0
  54. package/templates/agent/tsconfig.ipc.json +14 -0
  55. package/templates/agent/tsconfig.json +13 -3
  56. package/templates/chat/eslint.config.mjs +15 -2
  57. package/templates/chat/package.json +13 -2
  58. package/templates/chat/src/__tests__/hybrid-mode.test.ts +125 -0
  59. package/templates/chat/src/__tests__/server-config-security.test.ts +55 -0
  60. package/templates/chat/src/__tests__/server-config.test.ts +46 -44
  61. package/templates/chat/src/bun/index.ts +94 -44
  62. package/templates/chat/src/bun/three.d.ts +1 -0
  63. package/templates/chat/src/gateway/http-routes.ts +1 -1
  64. package/templates/chat/src/gateway/ws-handler.ts +84 -56
  65. package/templates/chat/src/mainview/App.tsx +6 -0
  66. package/templates/chat/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  67. package/templates/chat/src/mainview/__tests__/theme-variables.test.ts +36 -0
  68. package/templates/chat/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  69. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +1 -0
  70. package/templates/chat/src/mainview/hooks/use-input-history.ts +70 -61
  71. package/templates/chat/src/mainview/lib/api-client.ts +180 -156
  72. package/templates/chat/src/mainview/lib/rpc-cache.ts +84 -0
  73. package/templates/chat/src/mainview/main.tsx +10 -6
  74. package/templates/chat/src/server-config.ts +33 -31
  75. package/templates/chat/src/server.ts +42 -60
  76. package/templates/chat/src/shared/handlers/chat.ts +196 -162
  77. package/templates/chat/src/shared/handlers/debug.ts +12 -0
  78. package/templates/chat/src/shared/handlers/index.ts +1 -0
  79. package/templates/chat/src/shared/http-routes.ts +250 -0
  80. package/templates/chat/src/shared/lib/__tests__/logger.test.ts +92 -0
  81. package/templates/chat/src/shared/lib/logger.ts +108 -95
  82. package/templates/chat/src/shared/lib/web-server.ts +128 -0
  83. package/templates/chat/src/shared/modules/debug.ts +8 -0
  84. package/templates/chat/src/shared/rpc-schema.ts +3 -2
  85. package/templates/chat/test-upload.txt +0 -0
  86. package/templates/chat/tsconfig.ipc.json +14 -0
  87. package/templates/chat/tsconfig.json +12 -2
  88. package/templates/general/eslint.config.mjs +15 -2
  89. package/templates/general/package.json +13 -2
  90. package/templates/general/src/__tests__/hybrid-mode.test.ts +125 -0
  91. package/templates/general/src/__tests__/server-config-security.test.ts +55 -0
  92. package/templates/general/src/__tests__/server-config.test.ts +46 -44
  93. package/templates/general/src/bun/index.ts +94 -44
  94. package/templates/general/src/bun/three.d.ts +1 -0
  95. package/templates/general/src/gateway/http-routes.ts +1 -1
  96. package/templates/general/src/gateway/ws-handler.ts +84 -56
  97. package/templates/general/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  98. package/templates/general/src/mainview/__tests__/theme-variables.test.ts +36 -0
  99. package/templates/general/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  100. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +1 -0
  101. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +241 -219
  102. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +101 -81
  103. package/templates/general/src/mainview/components/layout/AppLayout.tsx +6 -0
  104. package/templates/general/src/mainview/components/search/SearchPanel.tsx +429 -378
  105. package/templates/general/src/mainview/hooks/use-input-history.ts +70 -61
  106. package/templates/general/src/mainview/lib/api-client.ts +180 -156
  107. package/templates/general/src/mainview/lib/rpc-cache.ts +84 -0
  108. package/templates/general/src/mainview/main.tsx +10 -6
  109. package/templates/general/src/mainview/stores/use-feed-store.ts +107 -107
  110. package/templates/general/src/mainview/utils/drop-handler.ts +114 -115
  111. package/templates/general/src/server-config.ts +33 -31
  112. package/templates/general/src/server.ts +42 -60
  113. package/templates/general/src/shared/handlers/__tests__/chat.test.ts +165 -160
  114. package/templates/general/src/shared/handlers/chat.ts +189 -155
  115. package/templates/general/src/shared/handlers/debug.ts +12 -0
  116. package/templates/general/src/shared/handlers/feed.ts +38 -40
  117. package/templates/general/src/shared/handlers/git.ts +286 -243
  118. package/templates/general/src/shared/handlers/index.ts +1 -0
  119. package/templates/general/src/shared/http-routes.ts +250 -0
  120. package/templates/general/src/shared/lib/__tests__/logger.test.ts +92 -0
  121. package/templates/general/src/shared/lib/logger.ts +117 -95
  122. package/templates/general/src/shared/lib/web-server.ts +128 -0
  123. package/templates/general/src/shared/modules/debug.ts +8 -0
  124. package/templates/general/src/shared/rpc-schema.ts +12 -2
  125. package/templates/general/test-upload.txt +0 -0
  126. package/templates/general/tsconfig.ipc.json +14 -0
  127. package/templates/general/tsconfig.json +12 -2
  128. package/templates/shared/__tests__/cors-security.test.ts +165 -0
  129. package/templates/shared/__tests__/file-path-security.test.ts +260 -0
  130. package/templates/shared/__tests__/http-routes.test.ts +174 -0
  131. package/templates/shared/__tests__/logger.test.ts +186 -0
  132. package/templates/shared/components/ErrorBoundary.tsx +59 -0
  133. package/templates/shared/env.d.ts +11 -0
  134. package/templates/shared/http-routes.ts +250 -0
  135. package/templates/shared/logger.ts +129 -0
  136. package/templates/shared/test-upload.txt +0 -0
  137. package/templates/shared/vite-base.config.ts +92 -0
  138. package/templates/shared/vitest-base.config.ts +42 -0
  139. package/templates/agent/eslint-plugin-rpc/index.js +0 -50
  140. package/templates/agent/eslint-plugin-rpc/package.json +0 -6
  141. package/templates/agent/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  142. package/templates/agent/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  143. package/templates/agent/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  144. package/templates/agent/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  145. package/templates/agent/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  146. package/templates/agent/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  147. package/templates/agent/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
  148. package/templates/chat/eslint-plugin-rpc/index.js +0 -50
  149. package/templates/chat/eslint-plugin-rpc/package.json +0 -6
  150. package/templates/chat/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  151. package/templates/chat/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  152. package/templates/chat/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  153. package/templates/chat/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  154. package/templates/chat/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  155. package/templates/chat/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  156. package/templates/chat/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
  157. package/templates/general/eslint-plugin-rpc/index.js +0 -50
  158. package/templates/general/eslint-plugin-rpc/package.json +0 -6
  159. package/templates/general/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  160. package/templates/general/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  161. package/templates/general/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  162. package/templates/general/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  163. package/templates/general/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  164. package/templates/general/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  165. package/templates/general/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyyz1993/create-agent",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Create Agent project scaffolding CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,27 +1,27 @@
1
- import { resolve } from 'path';
2
- import { resolveTemplateDir, getRootDir } from '../lib/templates.js';
3
- import { copyTemplate } from '../lib/copy.js';
1
+ import { resolve } from "path";
2
+ import { resolveTemplateDir, getRootDir } from "../lib/templates.js";
3
+ import { copyTemplate } from "../lib/copy.js";
4
4
 
5
5
  export async function runCreate(args: string[]): Promise<void> {
6
- let templateType = 'general';
7
- let customDir: string | undefined;
6
+ let templateType = "general";
7
+ let customDir: string | undefined;
8
8
 
9
- const positional: string[] = [];
10
- for (let i = 0; i < args.length; i++) {
11
- const arg = args[i];
12
- if (arg === '--type' && args[i + 1]) {
13
- templateType = args[++i];
14
- } else if (arg === '--dir' && args[i + 1]) {
15
- customDir = args[++i];
16
- } else if (!arg.startsWith('--')) {
17
- positional.push(arg);
18
- }
19
- }
9
+ const positional: string[] = [];
10
+ for (let i = 0; i < args.length; i++) {
11
+ const arg = args[i]!;
12
+ if (arg === "--type" && args[i + 1]) {
13
+ templateType = args[++i]!;
14
+ } else if (arg === "--dir" && args[i + 1]) {
15
+ customDir = args[++i]!;
16
+ } else if (!arg.startsWith("--")) {
17
+ positional.push(arg);
18
+ }
19
+ }
20
20
 
21
- const projectName = positional[0];
22
- const targetArg = customDir || positional[1];
23
- if (!projectName) {
24
- console.log(`
21
+ const projectName = positional[0];
22
+ const targetArg = customDir || positional[1];
23
+ if (!projectName) {
24
+ console.log(`
25
25
  Usage: create-agent create <name> [--type <type>] [--dir <path>]
26
26
 
27
27
  Options:
@@ -33,17 +33,17 @@ Examples:
33
33
  create-agent create my-app --type chat
34
34
  create-agent create my-app --dir ~/projects/my-app
35
35
  `);
36
- process.exit(1);
37
- }
36
+ process.exit(1);
37
+ }
38
38
 
39
- const sanitized = projectName.replace(/[^a-zA-Z0-9-_]/g, '-');
40
- const targetDir = targetArg ? resolve(targetArg) : resolve(process.cwd(), sanitized);
41
- const rootDir = getRootDir();
42
- const templateDir = resolveTemplateDir(rootDir, templateType);
39
+ const sanitized = projectName.replace(/[^a-zA-Z0-9-_]/g, "-");
40
+ const targetDir = targetArg ? resolve(targetArg) : resolve(process.cwd(), sanitized);
41
+ const rootDir = getRootDir();
42
+ const templateDir = resolveTemplateDir(rootDir, templateType);
43
43
 
44
- await copyTemplate({
45
- projectName: sanitized,
46
- templateDir,
47
- targetDir,
48
- });
44
+ await copyTemplate({
45
+ projectName: sanitized,
46
+ templateDir,
47
+ targetDir,
48
+ });
49
49
  }
@@ -1,45 +1,50 @@
1
- import { resolve } from 'path';
2
- import { execSync } from 'child_process';
3
- import { existsSync, readFileSync, mkdirSync } from 'fs';
4
- import { createServer } from 'net';
5
- import type { PortEntry } from '../lib/types.js';
1
+ import { resolve } from "path";
2
+ import { execSync } from "child_process";
3
+ import { existsSync, readFileSync, mkdirSync } from "fs";
4
+ import { createServer } from "net";
5
+ import type { PortEntry } from "../lib/types.js";
6
6
 
7
- const PORT_REGISTRY = resolve(process.env.HOME || '~', '.pi-agent', 'ports.json');
7
+ const PORT_REGISTRY = resolve(process.env.HOME || "~", ".pi-agent", "ports.json");
8
8
 
9
9
  function readRegistry(): Record<string, PortEntry> {
10
- try {
11
- return JSON.parse(readFileSync(PORT_REGISTRY, 'utf-8'));
12
- } catch {
13
- return {};
14
- }
10
+ try {
11
+ return JSON.parse(readFileSync(PORT_REGISTRY, "utf-8"));
12
+ } catch {
13
+ return {};
14
+ }
15
15
  }
16
16
 
17
17
  function isPortFree(port: number): Promise<boolean> {
18
- return new Promise((resolve) => {
19
- const server = createServer();
20
- server.once('error', () => resolve(false));
21
- server.once('listening', () => { server.close(); resolve(true); });
22
- server.listen(port);
23
- });
18
+ return new Promise((resolve) => {
19
+ const server = createServer();
20
+ server.once("error", () => resolve(false));
21
+ server.once("listening", () => {
22
+ server.close();
23
+ resolve(true);
24
+ });
25
+ server.listen(port);
26
+ });
24
27
  }
25
28
 
26
- async function findFreePorts(entries: Record<string, PortEntry>): Promise<{ backend: number; vite: number }> {
27
- const usedPorts = new Set<number>();
28
- for (const entry of Object.values(entries)) {
29
- usedPorts.add(entry.port);
30
- }
31
- let backend = 3100;
32
- while (usedPorts.has(backend) || !(await isPortFree(backend))) backend++;
33
- let vite = 5173;
34
- while (usedPorts.has(vite) || !(await isPortFree(vite))) vite++;
35
- return { backend, vite };
29
+ async function findFreePorts(
30
+ entries: Record<string, PortEntry>
31
+ ): Promise<{ backend: number; vite: number }> {
32
+ const usedPorts = new Set<number>();
33
+ for (const entry of Object.values(entries)) {
34
+ usedPorts.add(entry.port);
35
+ }
36
+ let backend = 3100;
37
+ while (usedPorts.has(backend) || !(await isPortFree(backend))) backend++;
38
+ let vite = 5173;
39
+ while (usedPorts.has(vite) || !(await isPortFree(vite))) vite++;
40
+ return { backend, vite };
36
41
  }
37
42
 
38
43
  export async function runWorkspace(args: string[]): Promise<void> {
39
- const subCommand = args[0];
44
+ const subCommand = args[0];
40
45
 
41
- if (!subCommand || subCommand === '--help' || subCommand === '-h') {
42
- console.log(`
46
+ if (!subCommand || subCommand === "--help" || subCommand === "-h") {
47
+ console.log(`
43
48
  Usage: create-agent workspace add <name> [--base <branch>]
44
49
 
45
50
  Creates an isolated git worktree in .workspace/<name> with its own branch and ports.
@@ -51,79 +56,81 @@ Examples:
51
56
  create-agent workspace add feature-chat-ui
52
57
  create-agent workspace add fix-login --base develop
53
58
  `);
54
- return;
55
- }
56
-
57
- if (subCommand !== 'add') {
58
- console.error(`Unknown workspace subcommand: "${subCommand}"`);
59
- console.log('Usage: create-agent workspace add <name>');
60
- return;
61
- }
62
-
63
- let name = '';
64
- let baseBranch = '';
65
- const rest = args.slice(1);
66
- for (let i = 0; i < rest.length; i++) {
67
- if (rest[i] === '--base' && rest[i + 1]) {
68
- baseBranch = rest[++i];
69
- } else if (!rest[i].startsWith('--')) {
70
- name = rest[i];
71
- }
72
- }
73
-
74
- if (!name) {
75
- console.error('Error: workspace name is required');
76
- process.exit(1);
77
- }
78
-
79
- const projectRoot = process.cwd();
80
- const workspaceDir = resolve(projectRoot, '.workspace', name);
81
-
82
- if (existsSync(workspaceDir)) {
83
- console.error(`Error: workspace "${name}" already exists at ${workspaceDir}`);
84
- process.exit(1);
85
- }
86
-
87
- const branchName = `workspace/${name}`;
88
- const baseArg = baseBranch ? baseBranch : 'HEAD';
89
-
90
- console.log(`Creating workspace: ${name}`);
91
- console.log(` Branch: ${branchName}`);
92
- console.log(` Directory: .workspace/${name}`);
93
-
94
- mkdirSync(resolve(projectRoot, '.workspace'), { recursive: true });
95
-
96
- try {
97
- execSync(`git worktree add "${workspaceDir}" -b ${branchName} ${baseArg}`, {
98
- cwd: projectRoot,
99
- stdio: 'pipe',
100
- });
101
- } catch (err: unknown) {
102
- const message = err instanceof Error ? err.message : String(err);
103
- console.error(`Failed to create worktree: ${message}`);
104
- process.exit(1);
105
- }
106
-
107
- const registry = readRegistry();
108
- const ports = await findFreePorts(registry);
109
-
110
- console.log(` Backend port: ${ports.backend}`);
111
- console.log(` Vite port: ${ports.vite}`);
112
- console.log('');
113
- console.log('Installing dependencies...');
114
- try {
115
- execSync('bun install', { cwd: workspaceDir, stdio: 'pipe' });
116
- } catch {
117
- console.log('(bun install skipped)');
118
- }
119
-
120
- console.log('');
121
- console.log('Workspace created successfully!');
122
- console.log('');
123
- console.log('To start developing:');
124
- console.log(` cd .workspace/${name}`);
125
- console.log(` PORT=${ports.backend} VITE_PORT=${ports.vite} bun run dev:web`);
126
- console.log('');
127
- console.log('Or start from project root:');
128
- console.log(` PORT=${ports.backend} VITE_PORT=${ports.vite} bun run dev:web`);
59
+ return;
60
+ }
61
+
62
+ if (subCommand !== "add") {
63
+ console.error(`Unknown workspace subcommand: "${subCommand}"`);
64
+ console.log("Usage: create-agent workspace add <name>");
65
+ return;
66
+ }
67
+
68
+ let name = "";
69
+ let baseBranch = "";
70
+ const rest = args.slice(1);
71
+ for (let i = 0; i < rest.length; i++) {
72
+ const arg = rest[i];
73
+ if (arg === undefined) continue;
74
+ if (arg === "--base" && rest[i + 1]) {
75
+ baseBranch = rest[++i] as string;
76
+ } else if (!arg.startsWith("--")) {
77
+ name = arg;
78
+ }
79
+ }
80
+
81
+ if (!name) {
82
+ console.error("Error: workspace name is required");
83
+ process.exit(1);
84
+ }
85
+
86
+ const projectRoot = process.cwd();
87
+ const workspaceDir = resolve(projectRoot, ".workspace", name);
88
+
89
+ if (existsSync(workspaceDir)) {
90
+ console.error(`Error: workspace "${name}" already exists at ${workspaceDir}`);
91
+ process.exit(1);
92
+ }
93
+
94
+ const branchName = `workspace/${name}`;
95
+ const baseArg = baseBranch ? baseBranch : "HEAD";
96
+
97
+ console.log(`Creating workspace: ${name}`);
98
+ console.log(` Branch: ${branchName}`);
99
+ console.log(` Directory: .workspace/${name}`);
100
+
101
+ mkdirSync(resolve(projectRoot, ".workspace"), { recursive: true });
102
+
103
+ try {
104
+ execSync(`git worktree add "${workspaceDir}" -b ${branchName} ${baseArg}`, {
105
+ cwd: projectRoot,
106
+ stdio: "pipe",
107
+ });
108
+ } catch (err: unknown) {
109
+ const message = err instanceof Error ? err.message : String(err);
110
+ console.error(`Failed to create worktree: ${message}`);
111
+ process.exit(1);
112
+ }
113
+
114
+ const registry = readRegistry();
115
+ const ports = await findFreePorts(registry);
116
+
117
+ console.log(` Backend port: ${ports.backend}`);
118
+ console.log(` Vite port: ${ports.vite}`);
119
+ console.log("");
120
+ console.log("Installing dependencies...");
121
+ try {
122
+ execSync("bun install", { cwd: workspaceDir, stdio: "pipe" });
123
+ } catch {
124
+ console.log("(bun install skipped)");
125
+ }
126
+
127
+ console.log("");
128
+ console.log("Workspace created successfully!");
129
+ console.log("");
130
+ console.log("To start developing:");
131
+ console.log(` cd .workspace/${name}`);
132
+ console.log(` PORT=${ports.backend} VITE_PORT=${ports.vite} bun run dev:web`);
133
+ console.log("");
134
+ console.log("Or start from project root:");
135
+ console.log(` PORT=${ports.backend} VITE_PORT=${ports.vite} bun run dev:web`);
129
136
  }
package/src/lib/copy.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from "fs";
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, unlinkSync } from "fs";
2
2
  import { join, resolve } from "path";
3
3
  import { execSync } from "child_process";
4
4
 
@@ -73,6 +73,11 @@ function copySharedModules(monorepoRoot: string, targetDir: string, projectName:
73
73
  const destSharedDir = join(targetDir, "shared");
74
74
  copyAndReplace(sharedDir, destSharedDir, projectName);
75
75
 
76
+ for (const f of ["http-routes.ts", "logger.ts"]) {
77
+ const p = join(destSharedDir, f);
78
+ if (existsSync(p)) unlinkSync(p);
79
+ }
80
+
76
81
  const tsconfigPath = join(targetDir, "tsconfig.json");
77
82
  if (existsSync(tsconfigPath)) {
78
83
  let tsconfig = readFileSync(tsconfigPath, "utf-8");
@@ -173,6 +178,7 @@ function updatePackageJson(targetDir: string, _projectName: string): void {
173
178
  let inRpcSection = false;
174
179
  for (let i = 0; i < lines.length; i++) {
175
180
  const line = lines[i];
181
+ if (line === undefined) continue;
176
182
 
177
183
  if (line.includes("import rpcPlugin") && line.includes("@dyyz1993/eslint-plugin-rpc")) {
178
184
  continue;
@@ -224,6 +230,23 @@ export async function copyTemplate(options: CopyOptions): Promise<void> {
224
230
  if (isMonorepo) {
225
231
  copyTraeRules(localRoot, targetDir, projectName);
226
232
  copySharedModules(localRoot, targetDir, projectName);
233
+ } else {
234
+ const publishedShared = resolve(import.meta.dir, "..", "..", "templates", "shared");
235
+ if (existsSync(publishedShared)) {
236
+ copyAndReplace(publishedShared, join(targetDir, "shared"), projectName);
237
+ for (const f of ["http-routes.ts", "logger.ts"]) {
238
+ const p = join(targetDir, "shared", f);
239
+ if (existsSync(p)) unlinkSync(p);
240
+ }
241
+ const tsconfigPath = join(targetDir, "tsconfig.json");
242
+ if (existsSync(tsconfigPath)) {
243
+ let tsconfig = readFileSync(tsconfigPath, "utf-8");
244
+ tsconfig = tsconfig.replace(/"\.\.\/shared\/\*"/g, '"./shared/*"');
245
+ tsconfig = tsconfig.replace(/"\.\.\/shared"/g, '"./shared"');
246
+ writeFileSync(tsconfigPath, tsconfig);
247
+ }
248
+ cleanSharedViteConfig(join(targetDir, "shared"));
249
+ }
227
250
  }
228
251
  cleanViteConfig(targetDir);
229
252
  updatePackageJson(targetDir, projectName);
@@ -0,0 +1,46 @@
1
+ const { app, BrowserWindow, ipcMain } = require('electron');
2
+ const path = require('path');
3
+
4
+ let mainWindow;
5
+
6
+ function createWindow() {
7
+ mainWindow = new BrowserWindow({
8
+ width: 1200,
9
+ height: 800,
10
+ webPreferences: {
11
+ preload: path.join(__dirname, 'preload.js'),
12
+ nodeIntegration: false,
13
+ contextIsolation: true,
14
+ },
15
+ });
16
+
17
+ if (process.env.NODE_ENV === 'development') {
18
+ mainWindow.loadURL('http://localhost:5173');
19
+ } else {
20
+ mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
21
+ }
22
+
23
+ mainWindow.on('closed', () => {
24
+ mainWindow = null;
25
+ });
26
+ }
27
+
28
+ app.whenReady().then(() => {
29
+ createWindow();
30
+ });
31
+
32
+ app.on('window-all-closed', () => {
33
+ if (process.platform !== 'darwin') {
34
+ app.quit();
35
+ }
36
+ });
37
+
38
+ app.on('activate', () => {
39
+ if (BrowserWindow.getAllWindows().length === 0) {
40
+ createWindow();
41
+ }
42
+ });
43
+
44
+ ipcMain.handle('ping', async () => {
45
+ return 'pong';
46
+ });
@@ -0,0 +1,5 @@
1
+ const { contextBridge, ipcRenderer } = require('electron');
2
+
3
+ contextBridge.exposeInMainWorld('electronAPI', {
4
+ ping: () => ipcRenderer.invoke('ping'),
5
+ });
@@ -0,0 +1,40 @@
1
+ {
2
+ "appId": "com.piagent.app",
3
+ "productName": "Pi Agent",
4
+ "copyright": "Copyright © 2024 dyyz1993",
5
+ "directories": {
6
+ "output": "dist-electron",
7
+ "buildResources": "build"
8
+ },
9
+ "files": ["dist/**/*", "electron/**/*", "package.json"],
10
+ "mac": {
11
+ "category": "public.app-category.developer-tools",
12
+ "hardenedRuntime": true,
13
+ "gatekeeperAssess": false,
14
+ "entitlements": ["com.apple.security.cs.allow-jit"],
15
+ "target": [
16
+ {
17
+ "target": "dmg",
18
+ "arch": ["x64", "arm64"]
19
+ }
20
+ ]
21
+ },
22
+ "win": {
23
+ "target": [
24
+ {
25
+ "target": "nsis",
26
+ "arch": ["x64"]
27
+ }
28
+ ],
29
+ "artifactName": "${productName}-${version}-${arch}.${ext}"
30
+ },
31
+ "linux": {
32
+ "target": ["AppImage", "deb"],
33
+ "category": "Development"
34
+ },
35
+ "publish": {
36
+ "provider": "github",
37
+ "owner": "dyyz1993",
38
+ "repo": "pi-agent-app"
39
+ }
40
+ }
@@ -1,6 +1,9 @@
1
1
  import js from '@eslint/js';
2
2
  import tseslint from 'typescript-eslint';
3
- import rpcPlugin from './eslint-plugin-rpc/index.js';
3
+ import rpcPlugin from '@dyyz1993/eslint-plugin-rpc';
4
+ import reactHooks from 'eslint-plugin-react-hooks';
5
+ import reactRefresh from 'eslint-plugin-react-refresh';
6
+ import jsxA11y from 'eslint-plugin-jsx-a11y';
4
7
 
5
8
  export default tseslint.config(
6
9
  js.configs.recommended,
@@ -10,7 +13,8 @@ export default tseslint.config(
10
13
  'node_modules/**',
11
14
  'build/**',
12
15
  'dist/**',
13
- 'eslint-plugin-rpc/**',
16
+ 'dist-electron/**',
17
+ 'electron/**',
14
18
  'postcss.config.js',
15
19
  'tailwind.config.js',
16
20
  ],
@@ -19,8 +23,19 @@ export default tseslint.config(
19
23
  files: ['src/**/*.{ts,tsx}'],
20
24
  plugins: {
21
25
  rpc: rpcPlugin,
26
+ 'react-hooks': reactHooks,
27
+ 'react-refresh': reactRefresh,
28
+ 'jsx-a11y': jsxA11y,
22
29
  },
23
30
  rules: {
31
+ ...reactHooks.configs.recommended.rules,
32
+ 'react-hooks/rules-of-hooks': 'warn',
33
+ 'react-hooks/exhaustive-deps': 'warn',
34
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
35
+ ...jsxA11y.configs.recommended.rules,
36
+ 'jsx-a11y/click-events-have-key-events': 'warn',
37
+ 'jsx-a11y/no-static-element-interactions': 'warn',
38
+ 'jsx-a11y/no-autofocus': 'warn',
24
39
  // TS 严格规则
25
40
  '@typescript-eslint/no-explicit-any': 'error',
26
41
  '@typescript-eslint/no-empty-object-type': ['error', { allowObjectTypes: 'always', allowInterfaces: 'with-single-extends' }],
@@ -2,6 +2,54 @@
2
2
  "name": "pi-agent-app",
3
3
  "version": "1.0.0",
4
4
  "description": "Coding agent template with Bash, Rules, and Todo management",
5
+ "author": {
6
+ "name": "xuyingzhou",
7
+ "email": "xuyingzhou@tuzhanai.com"
8
+ },
9
+ "homepage": "https://github.com/dyyz1993/pi-agent-template",
10
+ "main": "electron/main.js",
11
+ "build": {
12
+ "appId": "com.piagent.app",
13
+ "productName": "Pi Agent",
14
+ "directories": {
15
+ "output": "dist-electron"
16
+ },
17
+ "files": [
18
+ "dist/**/*",
19
+ "electron/**/*",
20
+ "package.json"
21
+ ],
22
+ "mac": {
23
+ "category": "public.app-category.developer-tools",
24
+ "target": [
25
+ {
26
+ "target": "dmg",
27
+ "arch": [
28
+ "x64",
29
+ "arm64"
30
+ ]
31
+ }
32
+ ]
33
+ },
34
+ "win": {
35
+ "target": [
36
+ {
37
+ "target": "nsis",
38
+ "arch": [
39
+ "x64"
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ "linux": {
45
+ "maintainer": "xuyingzhou <xuyingzhou@tuzhanai.com>",
46
+ "target": [
47
+ "AppImage",
48
+ "deb"
49
+ ],
50
+ "category": "Development"
51
+ }
52
+ },
5
53
  "scripts": {
6
54
  "start": "vite build && electrobun dev",
7
55
  "dev": "electrobun dev --watch",
@@ -9,6 +57,14 @@
9
57
  "dev:server": "bun src/server.ts",
10
58
  "build": "vite build",
11
59
  "build:canary": "vite build && electrobun build --env=canary",
60
+ "build:mac": "vite build && electrobun build --env=canary",
61
+ "build:win": "vite build && electrobun build --env=canary",
62
+ "build:linux": "vite build && electrobun build --env=canary",
63
+ "electron:dev": "vite build && electron .",
64
+ "electron:build": "vite build && electron-builder --mac --win --linux",
65
+ "electron:build:mac": "vite build && electron-builder --mac",
66
+ "electron:build:win": "vite build && electron-builder --win",
67
+ "electron:build:linux": "vite build && electron-builder --linux",
12
68
  "lint": "eslint .",
13
69
  "lint:fix": "eslint . --fix",
14
70
  "test": "vitest run",
@@ -19,7 +75,7 @@
19
75
  "dependencies": {
20
76
  "@dyyz1993/rpc-core": "^1.2.0",
21
77
  "@tanstack/react-virtual": "^3.13.24",
22
- "electrobun": "1.13.1",
78
+ "electrobun": "1.18.1",
23
79
  "lucide-react": "^1.9.0",
24
80
  "prism-react-renderer": "^2.4.1",
25
81
  "react": "^18.3.1",
@@ -33,7 +89,8 @@
33
89
  "zustand": "^5.0.12"
34
90
  },
35
91
  "devDependencies": {
36
- "@eslint/js": "^9.0.0",
92
+ "@dyyz1993/eslint-plugin-rpc": "workspace:*",
93
+ "@eslint/js": "^9.0.0",
37
94
  "@testing-library/jest-dom": "^6.9.1",
38
95
  "@testing-library/react": "^16.3.2",
39
96
  "@testing-library/user-event": "^14.6.1",
@@ -44,7 +101,12 @@
44
101
  "@vitejs/plugin-react": "^4.3.4",
45
102
  "autoprefixer": "^10.4.20",
46
103
  "concurrently": "^9.1.0",
104
+ "electron": "^42.1.0",
105
+ "electron-builder": "^26.8.1",
47
106
  "eslint": "^9.0.0",
107
+ "eslint-plugin-jsx-a11y": "^6.10.0",
108
+ "eslint-plugin-react-hooks": "^5.2.0",
109
+ "eslint-plugin-react-refresh": "^0.4.20",
48
110
  "happy-dom": "^20.9.0",
49
111
  "husky": "^9.1.0",
50
112
  "jsdom": "^29.1.1",
@@ -53,6 +115,10 @@
53
115
  "typescript": "^5.7.2",
54
116
  "typescript-eslint": "^8.0.0",
55
117
  "vite": "^6.0.3",
56
- "vitest": "^4.1.5"
118
+ "vitest": "^4.1.5",
119
+ "ws": "^8.18.0"
120
+ },
121
+ "overrides": {
122
+ "minimatch": "^10.0.1"
57
123
  }
58
124
  }