foliko 1.0.83 → 1.0.84

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 (167) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +355 -6
  39. package/.agent/data/plugins-state.json +185 -146
  40. package/.agent/data/puppeteer-sessions/undefined.json +6 -0
  41. package/.agent/mcp_config.json +0 -1
  42. package/.agent/mcp_config_updated.json +12 -0
  43. package/.agent/plugins/puppeteer-plugin/README.md +147 -0
  44. package/.agent/plugins/puppeteer-plugin/index.js +1320 -0
  45. package/.agent/plugins/puppeteer-plugin/package.json +9 -0
  46. package/.agent/plugins.json +5 -14
  47. package/.agent/rules/GEMINI.md +273 -0
  48. package/.agent/rules/allow-rule.md +77 -0
  49. package/.agent/rules/log-rule.md +83 -0
  50. package/.agent/rules/security-rule.md +93 -0
  51. package/.agent/scripts/auto_preview.py +148 -0
  52. package/.agent/scripts/checklist.py +217 -0
  53. package/.agent/scripts/session_manager.py +120 -0
  54. package/.agent/scripts/verify_all.py +327 -0
  55. package/.agent/skills/api-patterns/SKILL.md +81 -0
  56. package/.agent/skills/api-patterns/api-style.md +42 -0
  57. package/.agent/skills/api-patterns/auth.md +24 -0
  58. package/.agent/skills/api-patterns/documentation.md +26 -0
  59. package/.agent/skills/api-patterns/graphql.md +41 -0
  60. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  61. package/.agent/skills/api-patterns/response.md +37 -0
  62. package/.agent/skills/api-patterns/rest.md +40 -0
  63. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  64. package/.agent/skills/api-patterns/security-testing.md +122 -0
  65. package/.agent/skills/api-patterns/trpc.md +41 -0
  66. package/.agent/skills/api-patterns/versioning.md +22 -0
  67. package/.agent/skills/app-builder/SKILL.md +75 -0
  68. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  69. package/.agent/skills/app-builder/feature-building.md +53 -0
  70. package/.agent/skills/app-builder/project-detection.md +34 -0
  71. package/.agent/skills/app-builder/scaffolding.md +118 -0
  72. package/.agent/skills/app-builder/tech-stack.md +40 -0
  73. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  74. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  75. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  76. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  77. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  78. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  79. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  80. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  81. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  82. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  83. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  84. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  85. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  86. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  87. package/.agent/skills/architecture/SKILL.md +55 -0
  88. package/.agent/skills/architecture/context-discovery.md +43 -0
  89. package/.agent/skills/architecture/examples.md +94 -0
  90. package/.agent/skills/architecture/pattern-selection.md +68 -0
  91. package/.agent/skills/architecture/patterns-reference.md +50 -0
  92. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  93. package/.agent/skills/clean-code/SKILL.md +201 -0
  94. package/.agent/skills/doc.md +177 -0
  95. package/.agent/skills/frontend-design/SKILL.md +418 -0
  96. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  97. package/.agent/skills/frontend-design/color-system.md +311 -0
  98. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  99. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  100. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  101. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  102. package/.agent/skills/frontend-design/typography-system.md +345 -0
  103. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  104. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  105. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  106. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  107. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  108. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  109. package/.agent/workflows/brainstorm.md +113 -0
  110. package/.agent/workflows/create.md +59 -0
  111. package/.agent/workflows/debug.md +103 -0
  112. package/.agent/workflows/deploy.md +176 -0
  113. package/.agent/workflows/enhance.md +63 -0
  114. package/.agent/workflows/orchestrate.md +237 -0
  115. package/.agent/workflows/plan.md +89 -0
  116. package/.agent/workflows/preview.md +81 -0
  117. package/.agent/workflows/simple-test.md +42 -0
  118. package/.agent/workflows/status.md +86 -0
  119. package/.agent/workflows/structured-orchestrate.md +180 -0
  120. package/.agent/workflows/test.md +144 -0
  121. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  122. package/.claude/settings.local.json +178 -171
  123. package/.env.example +56 -56
  124. package/cli/src/commands/plugin.js +482 -0
  125. package/cli/src/index.js +7 -0
  126. package/cli/src/utils/plugin-config.js +50 -0
  127. package/package.json +1 -1
  128. package/plugins/audit-plugin.js +2 -0
  129. package/plugins/extension-executor-plugin.js +30 -0
  130. package/plugins/plugin-manager-plugin.js +402 -0
  131. package/skills/find-skills/AGENTS.md +162 -162
  132. package/skills/find-skills/SKILL.md +133 -133
  133. package/skills/foliko-dev/SKILL.md +563 -563
  134. package/skills/plugin-guide/SKILL.md +139 -0
  135. package/skills/python-plugin-dev/SKILL.md +238 -238
  136. package/src/core/agent-chat.js +115 -46
  137. package/src/core/framework.js +42 -1
  138. package/src/executors/mcp-executor.js +33 -0
  139. package/src/utils/index.js +153 -0
  140. package/xhs_auth.json +268 -0
  141. package/.agent/agents/code-assistant.json +0 -14
  142. package/.agent/agents/email-assistant.json +0 -14
  143. package/.agent/agents/file-assistant.json +0 -15
  144. package/.agent/agents/system-assistant.json +0 -15
  145. package/.agent/agents/web-assistant.json +0 -12
  146. package/.agent/data/ambient/goals.json +0 -50
  147. package/.agent/data/ambient/memories.json +0 -7
  148. package/.agent/data/scheduler/tasks.json +0 -1
  149. package/.agent/package.json +0 -8
  150. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  151. package/.agent/plugins/system-info/index.js +0 -387
  152. package/.agent/plugins/system-info/package.json +0 -4
  153. package/.agent/plugins/system-info/test.js +0 -40
  154. package/.agent/plugins/test_plugin.py +0 -304
  155. package/.agent/python-scripts/test_sample.py +0 -24
  156. package/.agent/skills/sysinfo/SKILL.md +0 -38
  157. package/.agent/skills/sysinfo/system-info.sh +0 -130
  158. package/.agent/skills/workflow/SKILL.md +0 -324
  159. package/.agent/workflows/email-digest.json +0 -50
  160. package/.agent/workflows/file-backup.json +0 -21
  161. package/.agent/workflows/get-ip-notify.json +0 -32
  162. package/.agent/workflows/news-aggregator.json +0 -93
  163. package/.agent/workflows/news-dashboard-v2.json +0 -94
  164. package/.agent/workflows/notification-batch.json +0 -32
  165. package/reports/system-health-report-20260401.md +0 -79
  166. package/test/tool-registry-validation.test.js +0 -218
  167. package/test_report.md +0 -70
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Plugin 命令实现
3
+ * 支持插件的上传(publish)和下载(install)
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { execSync } = require('child_process');
9
+ const https = require('https');
10
+ const http = require('http');
11
+ const { DEFAULT_REPO, shouldIgnore } = require('../utils/plugin-config');
12
+
13
+ /**
14
+ * 递归复制目录(带过滤)
15
+ */
16
+ function copyDirRecursive(src, dest, ignorePatterns = []) {
17
+ if (!fs.existsSync(src)) return;
18
+
19
+ fs.mkdirSync(dest, { recursive: true });
20
+
21
+ const entries = fs.readdirSync(src, { withFileTypes: true });
22
+
23
+ for (const entry of entries) {
24
+ const srcPath = path.join(src, entry.name);
25
+ const destPath = path.join(dest, entry.name);
26
+
27
+ // 检查是否忽略
28
+ if (shouldIgnore(entry.name)) {
29
+ console.log(` Ignoring: ${entry.name}`);
30
+ continue;
31
+ }
32
+
33
+ if (entry.isDirectory()) {
34
+ copyDirRecursive(srcPath, destPath, ignorePatterns);
35
+ } else {
36
+ fs.copyFileSync(srcPath, destPath);
37
+ }
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 解析 Git URL 获取信息
43
+ */
44
+ function parseGitUrl(url) {
45
+ // 支持多种格式
46
+ const patterns = [
47
+ /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
48
+ /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/,
49
+ ];
50
+
51
+ for (const pattern of patterns) {
52
+ const match = url.match(pattern);
53
+ if (match) {
54
+ return { owner: match[1], repo: match[2] };
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * 执行 Git 命令
62
+ */
63
+ function gitCommand(args, cwd) {
64
+ try {
65
+ return execSync(`git ${args}`, { cwd, encoding: 'utf-8', stdio: 'pipe' });
66
+ } catch (err) {
67
+ return err.stdout || err.stderr || '';
68
+ }
69
+ }
70
+
71
+ /**
72
+ * 下载文件
73
+ */
74
+ function downloadFile(url, dest) {
75
+ return new Promise((resolve, reject) => {
76
+ const file = fs.createWriteStream(dest);
77
+ const protocol = url.startsWith('https') ? https : http;
78
+
79
+ protocol
80
+ .get(url, (response) => {
81
+ if (response.statusCode === 301 || response.statusCode === 302) {
82
+ // 处理重定向
83
+ file.close();
84
+ downloadFile(response.headers.location, dest).then(resolve).catch(reject);
85
+ return;
86
+ }
87
+
88
+ if (response.statusCode !== 200) {
89
+ file.close();
90
+ reject(new Error(`HTTP ${response.statusCode}`));
91
+ return;
92
+ }
93
+
94
+ response.pipe(file);
95
+ file.on('finish', () => {
96
+ file.close();
97
+ resolve();
98
+ });
99
+ })
100
+ .on('error', (err) => {
101
+ file.close();
102
+ fs.unlink(dest, () => {});
103
+ reject(err);
104
+ });
105
+ });
106
+ }
107
+
108
+ /**
109
+ * 发布插件到 Git
110
+ */
111
+ async function publishCommand(pluginName, options) {
112
+ // 支持 .agent/plugins 和 plugins 两个目录
113
+ const agentPluginsDir = path.resolve(process.cwd(), '.agent', 'plugins');
114
+ const localPluginsDir = path.resolve(process.cwd(), 'plugins');
115
+ const pluginsDir = fs.existsSync(agentPluginsDir) ? agentPluginsDir : localPluginsDir;
116
+ const pluginPath = path.join(pluginsDir, `${pluginName}.js`);
117
+ const pluginSourceDir = path.join(pluginsDir, pluginName);
118
+
119
+ // 检查插件是否存在(可以是 .js 文件或目录)
120
+ if (!fs.existsSync(pluginPath) && !fs.existsSync(pluginSourceDir)) {
121
+ console.error(`Plugin not found: ${pluginName}`);
122
+ console.error(`Searched in: ${pluginsDir}`);
123
+ console.error('Available plugins:');
124
+ if (fs.existsSync(pluginsDir)) {
125
+ fs.readdirSync(pluginsDir).forEach((f) => {
126
+ if (f.endsWith('.js')) {
127
+ console.error(` - ${f.replace('.js', '')} (file)`);
128
+ } else if (fs.statSync(path.join(pluginsDir, f)).isDirectory()) {
129
+ console.error(` - ${f} (directory)`);
130
+ }
131
+ });
132
+ }
133
+ if (fs.existsSync(localPluginsDir) && localPluginsDir !== pluginsDir) {
134
+ fs.readdirSync(localPluginsDir).forEach((f) => {
135
+ if (fs.statSync(path.join(localPluginsDir, f)).isDirectory()) {
136
+ console.error(` - ${f} (directory)`);
137
+ } else if (f.endsWith('.js')) {
138
+ console.error(` - ${f.replace('.js', '')} (file)`);
139
+ }
140
+ });
141
+ }
142
+ process.exit(1);
143
+ }
144
+
145
+ const repo = options.repo || DEFAULT_REPO;
146
+ const tmpDir = path.join(require('os').tmpdir(), `foliko-plugin-publish-${Date.now()}`);
147
+
148
+ try {
149
+ console.log(`Publishing plugin "${pluginName}" to ${repo}...`);
150
+
151
+ // 1. 克隆仓库(如果不存在)或拉取更新
152
+ fs.mkdirSync(tmpDir, { recursive: true });
153
+
154
+ const repoInfo = parseGitUrl(repo);
155
+ if (!repoInfo) {
156
+ console.error('Invalid repository URL');
157
+ process.exit(1);
158
+ }
159
+
160
+ // 尝试克隆
161
+ let isNewRepo = false;
162
+ try {
163
+ console.log('Cloning repository...');
164
+ gitCommand(`clone ${repo} "${tmpDir}" --depth 1`, process.cwd());
165
+ } catch (err) {
166
+ // 仓库可能为空,尝试初始化
167
+ console.log('Initializing new repository...');
168
+ fs.mkdirSync(tmpDir, { recursive: true });
169
+ gitCommand('init', tmpDir);
170
+ gitCommand(`remote add origin ${repo}`, tmpDir);
171
+ isNewRepo = true;
172
+ }
173
+
174
+ // 2. 创建插件目录
175
+ const pluginDir = path.join(tmpDir, pluginName);
176
+ fs.mkdirSync(pluginDir, { recursive: true });
177
+
178
+ // 3. 复制插件(整个目录或单个文件)
179
+ let pluginContent = null;
180
+ if (fs.existsSync(pluginSourceDir) && fs.statSync(pluginSourceDir).isDirectory()) {
181
+ // 插件在子目录中
182
+ console.log('Copying plugin directory...');
183
+ copyDirRecursive(pluginSourceDir, pluginDir);
184
+
185
+ // 尝试从主 js 文件读取描述
186
+ const mainJsPath = path.join(pluginSourceDir, `${pluginName}.js`);
187
+ if (fs.existsSync(mainJsPath)) {
188
+ pluginContent = fs.readFileSync(mainJsPath, 'utf-8');
189
+ }
190
+ } else {
191
+ // 单个插件文件
192
+ console.log('Copying plugin file...');
193
+ pluginContent = fs.readFileSync(pluginPath, 'utf-8');
194
+ const targetPath = path.join(pluginDir, `${pluginName}.js`);
195
+ fs.writeFileSync(targetPath, pluginContent);
196
+
197
+ // 复制配置文件(如果存在)
198
+ const configPath = path.join(pluginsDir, `${pluginName}.json`);
199
+ if (fs.existsSync(configPath)) {
200
+ fs.copyFileSync(configPath, path.join(pluginDir, `${pluginName}.json`));
201
+ }
202
+ }
203
+
204
+ // 4. 创建 README.md(如果没有)
205
+ const readmePath = path.join(pluginDir, 'README.md');
206
+ if (!fs.existsSync(readmePath)) {
207
+ // 尝试从插件文件中提取描述
208
+ const descMatch =
209
+ pluginContent?.match(/\*\*Description\*\*:\s*(.+)/) ||
210
+ pluginContent?.match(/description[:\s]+(.+)/i);
211
+ const desc = descMatch ? descMatch[1] : `Foliko plugin: ${pluginName}`;
212
+ fs.writeFileSync(readmePath, `# ${pluginName}\n\n${desc}\n`);
213
+ }
214
+
215
+ // 6. Git 提交和推送
216
+ gitCommand('add .', tmpDir);
217
+
218
+ const status = gitCommand('status --porcelain', tmpDir);
219
+ if (!status.trim()) {
220
+ console.log('No changes to commit.');
221
+ return;
222
+ }
223
+
224
+ gitCommand(`commit -m "Add/update plugin: ${pluginName}"`, tmpDir);
225
+ gitCommand(`push ${isNewRepo ? '-u' : ''} origin main`, tmpDir);
226
+
227
+ console.log(`\nPlugin "${pluginName}" published successfully!`);
228
+ console.log(`Repository: ${repo}`);
229
+ console.log(`Path: ${pluginName}/${pluginName}.js`);
230
+ } catch (err) {
231
+ console.error('Publish failed:', err.message);
232
+ process.exit(1);
233
+ } finally {
234
+ // 清理临时目录
235
+ fs.rmSync(tmpDir, { recursive: true, force: true });
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 从 Git 安装插件
241
+ */
242
+ async function installCommand(pluginName, options) {
243
+ const repo = options.repo || DEFAULT_REPO;
244
+ const tmpDir = path.join(require('os').tmpdir(), `foliko-plugin-install-${Date.now()}`);
245
+ const localPluginsDir = path.resolve(process.cwd(), 'plugins');
246
+
247
+ try {
248
+ console.log(`Installing plugin "${pluginName}" from ${repo}...`);
249
+
250
+ // 1. 克隆仓库
251
+ console.log('Fetching plugin repository...');
252
+ fs.mkdirSync(tmpDir, { recursive: true });
253
+ gitCommand(`clone ${repo} "${tmpDir}" --depth 1`, process.cwd());
254
+
255
+ // 2. 检查插件目录/文件
256
+ const pluginDir = path.join(tmpDir, pluginName);
257
+ let sourcePath;
258
+
259
+ if (fs.existsSync(pluginDir) && fs.statSync(pluginDir).isDirectory()) {
260
+ // 插件在子目录中
261
+ sourcePath = path.join(pluginDir, `${pluginName}.js`);
262
+ } else {
263
+ // 插件在仓库根目录
264
+ sourcePath = path.join(tmpDir, `${pluginName}.js`);
265
+ }
266
+
267
+ if (!fs.existsSync(sourcePath)) {
268
+ // 列出可用插件
269
+ console.error(`Plugin "${pluginName}" not found in repository.`);
270
+ console.error('\nAvailable plugins:');
271
+
272
+ const repos = fs.readdirSync(tmpDir).filter((f) => {
273
+ const fullPath = path.join(tmpDir, f);
274
+ return fs.statSync(fullPath).isDirectory();
275
+ });
276
+
277
+ if (repos.length === 0) {
278
+ // 检查根目录的 js 文件
279
+ fs.readdirSync(tmpDir)
280
+ .filter((f) => f.endsWith('.js') && !f.startsWith('.'))
281
+ .forEach((f) => console.error(` - ${f.replace('.js', '')}`));
282
+ } else {
283
+ repos.forEach((r) => console.error(` - ${r}`));
284
+ }
285
+
286
+ process.exit(1);
287
+ }
288
+
289
+ // 3. 确保本地 plugins 目录存在
290
+ if (!fs.existsSync(localPluginsDir)) {
291
+ fs.mkdirSync(localPluginsDir, { recursive: true });
292
+ }
293
+
294
+ // 4. 复制插件文件
295
+ const targetPath = path.join(localPluginsDir, `${pluginName}.js`);
296
+ fs.copyFileSync(sourcePath, targetPath);
297
+ console.log(`Plugin copied to: ${targetPath}`);
298
+
299
+ // 5. 复制配置文件(如果存在)
300
+ const configSource = path.join(path.dirname(sourcePath), `${pluginName}.json`);
301
+ if (fs.existsSync(configSource)) {
302
+ const configTarget = path.join(localPluginsDir, `${pluginName}.json`);
303
+ fs.copyFileSync(configSource, configTarget);
304
+ console.log(`Config copied to: ${configTarget}`);
305
+ }
306
+
307
+ console.log(`\nPlugin "${pluginName}" installed successfully!`);
308
+ } catch (err) {
309
+ console.error('Install failed:', err.message);
310
+ process.exit(1);
311
+ } finally {
312
+ // 清理临时目录
313
+ fs.rmSync(tmpDir, { recursive: true, force: true });
314
+ }
315
+ }
316
+
317
+ /**
318
+ * 列出远程仓库中的插件
319
+ */
320
+ async function listCommand(options) {
321
+ const repo = options.repo || DEFAULT_REPO;
322
+
323
+ try {
324
+ console.log(`Fetching plugins from ${repo}...`);
325
+
326
+ const repoInfo = parseGitUrl(repo);
327
+ if (!repoInfo) {
328
+ console.error('Invalid repository URL');
329
+ process.exit(1);
330
+ }
331
+
332
+ // 使用 GitHub API 获取仓库内容
333
+ const apiUrl = `https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/contents`;
334
+
335
+ const response = await fetch(apiUrl);
336
+
337
+ if (!response.ok) {
338
+ if (response.status === 404) {
339
+ console.error('Repository not found or is empty.');
340
+ } else {
341
+ console.error(`GitHub API error: ${response.status}`);
342
+ }
343
+ process.exit(1);
344
+ }
345
+
346
+ const contents = await response.json();
347
+
348
+ if (!Array.isArray(contents) || contents.length === 0) {
349
+ console.log('No plugins found in repository.');
350
+ return;
351
+ }
352
+
353
+ console.log(`\nPlugins in ${repo}:\n`);
354
+ console.log('Name Type Description');
355
+ console.log('---------------- -------- ------------------------------------------');
356
+
357
+ for (const item of contents) {
358
+ if (item.type === 'dir') {
359
+ // 尝试读取 README
360
+ let desc = '-';
361
+ try {
362
+ const readmeUrl = `https://raw.githubusercontent.com/${repoInfo.owner}/${repoInfo.repo}/main/${item.name}/README.md`;
363
+ const readmeResp = await fetch(readmeUrl);
364
+ if (readmeResp.ok) {
365
+ const readmeText = await readmeResp.text();
366
+ const lines = readmeText.split('\n').filter((l) => l.trim());
367
+ if (lines.length > 1) {
368
+ desc = lines.slice(1).join(' ').slice(0, 40);
369
+ }
370
+ }
371
+ } catch (e) {}
372
+
373
+ console.log(`${item.name.padEnd(16)} plugin ${desc}`);
374
+ } else if (item.type === 'file' && item.name.endsWith('.js')) {
375
+ const name = item.name.replace('.js', '');
376
+ console.log(`${name.padEnd(16)} plugin (root file)`);
377
+ }
378
+ }
379
+ } catch (err) {
380
+ console.error('List failed:', err.message);
381
+ process.exit(1);
382
+ }
383
+ }
384
+
385
+ /**
386
+ * 插件命令主入口
387
+ */
388
+ async function pluginCommand(args) {
389
+ let subcommand = args[0];
390
+
391
+ // 处理帮助选项
392
+ if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
393
+ printHelp();
394
+ return;
395
+ }
396
+
397
+ if (!subcommand) {
398
+ printHelp();
399
+ return;
400
+ }
401
+
402
+ const options = {
403
+ repo: process.env.FOLIKO_PLUGIN_REPO || DEFAULT_REPO,
404
+ };
405
+
406
+ // 解析全局选项
407
+ const remainingArgs = [];
408
+ for (let i = 1; i < args.length; i++) {
409
+ if (args[i] === '--repo' && args[i + 1]) {
410
+ options.repo = args[i + 1];
411
+ i++;
412
+ } else {
413
+ remainingArgs.push(args[i]);
414
+ }
415
+ }
416
+
417
+ switch (subcommand) {
418
+ case 'publish':
419
+ case 'push': {
420
+ const pluginName = remainingArgs[0];
421
+ if (!pluginName) {
422
+ console.error('Plugin name required: foliko plugin publish <name>');
423
+ process.exit(1);
424
+ }
425
+ await publishCommand(pluginName, options);
426
+ break;
427
+ }
428
+
429
+ case 'install':
430
+ case 'add': {
431
+ const pluginName = remainingArgs[0];
432
+ if (!pluginName) {
433
+ console.error('Plugin name required: foliko plugin install <name>');
434
+ process.exit(1);
435
+ }
436
+ await installCommand(pluginName, options);
437
+ break;
438
+ }
439
+
440
+ case 'list':
441
+ case 'ls': {
442
+ await listCommand(options);
443
+ break;
444
+ }
445
+
446
+ case 'help':
447
+ printHelp();
448
+ break;
449
+
450
+ default:
451
+ console.error(`Unknown subcommand: ${subcommand}`);
452
+ printHelp();
453
+ process.exit(1);
454
+ }
455
+ }
456
+
457
+ function printHelp() {
458
+ console.log(`
459
+ Foliko Plugin Manager - Publish and install plugins to/from Git
460
+
461
+ Usage: foliko plugin <command> [options]
462
+
463
+ Commands:
464
+ list, ls List available plugins from repository
465
+ publish <name> Publish a plugin to the repository
466
+ install <name> Install a plugin from the repository
467
+
468
+ Options:
469
+ --repo <url> Specify repository URL (default: ${DEFAULT_REPO})
470
+
471
+ Environment Variables:
472
+ FOLIKO_PLUGIN_REPO Default repository URL
473
+
474
+ Examples:
475
+ foliko plugin list
476
+ foliko plugin publish my-plugin
477
+ foliko plugin install my-plugin
478
+ foliko plugin install other-plugin --repo https://github.com/user/other-plugins.git
479
+ `);
480
+ }
481
+
482
+ module.exports = { pluginCommand };
package/cli/src/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { chatCommand } = require('./commands/chat');
6
6
  const { listCommand } = require('./commands/list');
7
+ const { pluginCommand } = require('./commands/plugin');
7
8
  const fs = require('fs');
8
9
  const path = require('path');
9
10
  /**
@@ -24,6 +25,11 @@ async function cli() {
24
25
  await listCommand();
25
26
  break;
26
27
 
28
+ case 'plugin':
29
+ case 'plugins':
30
+ await pluginCommand(args.slice(1));
31
+ break;
32
+
27
33
  case 'help':
28
34
  case '--help':
29
35
  case '-h':
@@ -56,6 +62,7 @@ Usage: foliko <command> [options]
56
62
  Commands:
57
63
  chat 启动持续对话聊天
58
64
  list 列出所有子Agent配置
65
+ plugin 插件管理 (publish/install/list)
59
66
  help 显示帮助信息
60
67
  version 显示版本号
61
68
 
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 插件管理器公共配置
3
+ */
4
+
5
+ // 默认插件仓库
6
+ const DEFAULT_REPO = 'https://github.com/chnak/foliko-plugins.git';
7
+
8
+ // 发布时忽略的文件和目录
9
+ const IGNORE_PATTERNS = [
10
+ 'node_modules',
11
+ '.git',
12
+ '.env',
13
+ '.DS_Store',
14
+ 'Thumbs.db',
15
+ '*.log',
16
+ '*.lock',
17
+ '*.bak',
18
+ '.claude',
19
+ '.agent',
20
+ 'examples',
21
+ 'dist',
22
+ 'build',
23
+ 'coverage',
24
+ 'tests',
25
+ '__tests__',
26
+ '*.test.js',
27
+ '*.spec.js',
28
+ 'package-lock.json',
29
+ 'yarn.lock',
30
+ 'pnpm-lock.yaml',
31
+ ];
32
+
33
+ /**
34
+ * 检查文件/目录是否应该被忽略
35
+ */
36
+ function shouldIgnore(name) {
37
+ return IGNORE_PATTERNS.some((pattern) => {
38
+ if (pattern.startsWith('*.')) {
39
+ const ext = pattern.slice(1);
40
+ return name.endsWith(ext);
41
+ }
42
+ return name === pattern;
43
+ });
44
+ }
45
+
46
+ module.exports = {
47
+ DEFAULT_REPO,
48
+ IGNORE_PATTERNS,
49
+ shouldIgnore,
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.0.83",
3
+ "version": "1.0.84",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -30,7 +30,9 @@ class AuditPlugin extends Plugin {
30
30
  this._framework = framework
31
31
 
32
32
  // 监听框架事件
33
+ // tool:call 和 tool-call 两种格式都监听(兼容不同来源)
33
34
  framework.on('tool:call', (data) => this._log('tool_call', data))
35
+ framework.on('tool-call', (data) => this._log('tool_call', data))
34
36
  framework.on('tool:result', (data) => this._log('tool_result', data))
35
37
  framework.on('tool:error', (data) => this._log('tool_error', data))
36
38
  framework.on('agent:message', (data) => this._log('agent_message', data))
@@ -102,11 +102,41 @@ class ExtensionExecutorPlugin extends Plugin {
102
102
  }
103
103
 
104
104
  try {
105
+ // 触发扩展工具开始事件
106
+ framework.emit('tool:call', {
107
+ name: `${plugin}:${tool}`,
108
+ args: toolArgs,
109
+ source: 'extension'
110
+ });
111
+ framework.emit('tool-call', {
112
+ name: `${plugin}:${tool}`,
113
+ args: toolArgs,
114
+ source: 'extension'
115
+ });
116
+
105
117
  // 统一 execute 签名为 (args, framework)
106
118
  const result = await toolDef.execute(toolArgs, framework);
119
+
120
+ // 触发扩展工具完成事件
121
+ framework.emit('tool:result', {
122
+ name: `${plugin}:${tool}`,
123
+ args: toolArgs,
124
+ result,
125
+ source: 'extension'
126
+ });
127
+
107
128
  return { success: true, result };
108
129
  } catch (err) {
109
130
  log.error(` Tool '${tool}' failed:`, err.message);
131
+
132
+ // 触发扩展工具错误事件
133
+ framework.emit('tool:error', {
134
+ name: `${plugin}:${tool}`,
135
+ args: toolArgs,
136
+ error: err.message,
137
+ source: 'extension'
138
+ });
139
+
110
140
  return { success: false, error: err.message };
111
141
  }
112
142
  },