@elliotding/ai-agent-mcp 0.1.26 → 0.1.28

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 (186) hide show
  1. package/README.md +330 -30
  2. package/dist/api/cached-client.d.ts +48 -0
  3. package/dist/api/cached-client.d.ts.map +1 -0
  4. package/dist/api/cached-client.js +126 -0
  5. package/dist/api/cached-client.js.map +1 -0
  6. package/dist/api/client.d.ts +295 -0
  7. package/dist/api/client.d.ts.map +1 -0
  8. package/dist/api/client.js +385 -0
  9. package/dist/api/client.js.map +1 -0
  10. package/dist/auth/index.d.ts +8 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +26 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/middleware.d.ts +36 -0
  15. package/dist/auth/middleware.d.ts.map +1 -0
  16. package/dist/auth/middleware.js +194 -0
  17. package/dist/auth/middleware.js.map +1 -0
  18. package/dist/auth/permissions.d.ts +60 -0
  19. package/dist/auth/permissions.d.ts.map +1 -0
  20. package/dist/auth/permissions.js +262 -0
  21. package/dist/auth/permissions.js.map +1 -0
  22. package/dist/auth/token-validator.d.ts +52 -0
  23. package/dist/auth/token-validator.d.ts.map +1 -0
  24. package/dist/auth/token-validator.js +215 -0
  25. package/dist/auth/token-validator.js.map +1 -0
  26. package/dist/cache/cache-manager.d.ts +49 -0
  27. package/dist/cache/cache-manager.d.ts.map +1 -0
  28. package/dist/cache/cache-manager.js +191 -0
  29. package/dist/cache/cache-manager.js.map +1 -0
  30. package/dist/cache/index.d.ts +6 -0
  31. package/dist/cache/index.d.ts.map +1 -0
  32. package/dist/cache/index.js +12 -0
  33. package/dist/cache/index.js.map +1 -0
  34. package/dist/cache/redis-client.d.ts +45 -0
  35. package/dist/cache/redis-client.d.ts.map +1 -0
  36. package/dist/cache/redis-client.js +210 -0
  37. package/dist/cache/redis-client.js.map +1 -0
  38. package/dist/config/constants.d.ts +28 -0
  39. package/dist/config/constants.d.ts.map +1 -0
  40. package/dist/config/constants.js +31 -0
  41. package/dist/config/constants.js.map +1 -0
  42. package/dist/config/index.d.ts +71 -0
  43. package/dist/config/index.d.ts.map +1 -0
  44. package/dist/config/index.js +190 -0
  45. package/dist/config/index.js.map +1 -0
  46. package/dist/filesystem/manager.d.ts +45 -0
  47. package/dist/filesystem/manager.d.ts.map +1 -0
  48. package/dist/filesystem/manager.js +246 -0
  49. package/dist/filesystem/manager.js.map +1 -0
  50. package/dist/git/multi-source-manager.d.ts +105 -0
  51. package/dist/git/multi-source-manager.d.ts.map +1 -0
  52. package/dist/git/multi-source-manager.js +677 -0
  53. package/dist/git/multi-source-manager.js.map +1 -0
  54. package/dist/git/operations.d.ts +27 -0
  55. package/dist/git/operations.d.ts.map +1 -0
  56. package/dist/git/operations.js +83 -0
  57. package/dist/git/operations.js.map +1 -0
  58. package/dist/index.d.ts +6 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +122 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/monitoring/health.d.ts +35 -0
  63. package/dist/monitoring/health.d.ts.map +1 -0
  64. package/dist/monitoring/health.js +105 -0
  65. package/dist/monitoring/health.js.map +1 -0
  66. package/dist/prompts/cache.d.ts +69 -0
  67. package/dist/prompts/cache.d.ts.map +1 -0
  68. package/dist/prompts/cache.js +163 -0
  69. package/dist/prompts/cache.js.map +1 -0
  70. package/dist/prompts/generator.d.ts +49 -0
  71. package/dist/prompts/generator.d.ts.map +1 -0
  72. package/dist/prompts/generator.js +160 -0
  73. package/dist/prompts/generator.js.map +1 -0
  74. package/dist/prompts/index.d.ts +13 -0
  75. package/dist/prompts/index.d.ts.map +1 -0
  76. package/dist/prompts/index.js +24 -0
  77. package/dist/prompts/index.js.map +1 -0
  78. package/dist/prompts/manager.d.ts +213 -0
  79. package/dist/prompts/manager.d.ts.map +1 -0
  80. package/dist/prompts/manager.js +587 -0
  81. package/dist/prompts/manager.js.map +1 -0
  82. package/dist/resources/index.d.ts +6 -0
  83. package/dist/resources/index.d.ts.map +1 -0
  84. package/dist/resources/index.js +10 -0
  85. package/dist/resources/index.js.map +1 -0
  86. package/dist/resources/loader.d.ts +88 -0
  87. package/dist/resources/loader.d.ts.map +1 -0
  88. package/dist/resources/loader.js +492 -0
  89. package/dist/resources/loader.js.map +1 -0
  90. package/dist/server/http.d.ts +57 -0
  91. package/dist/server/http.d.ts.map +1 -0
  92. package/dist/server/http.js +435 -0
  93. package/dist/server/http.js.map +1 -0
  94. package/dist/server.d.ts +13 -0
  95. package/dist/server.d.ts.map +1 -0
  96. package/dist/server.js +201 -0
  97. package/dist/server.js.map +1 -0
  98. package/dist/session/manager.d.ts +91 -0
  99. package/dist/session/manager.d.ts.map +1 -0
  100. package/dist/session/manager.js +251 -0
  101. package/dist/session/manager.js.map +1 -0
  102. package/dist/telemetry/index.d.ts +3 -0
  103. package/dist/telemetry/index.d.ts.map +1 -0
  104. package/dist/telemetry/index.js +7 -0
  105. package/dist/telemetry/index.js.map +1 -0
  106. package/dist/telemetry/manager.d.ts +151 -0
  107. package/dist/telemetry/manager.d.ts.map +1 -0
  108. package/dist/telemetry/manager.js +367 -0
  109. package/dist/telemetry/manager.js.map +1 -0
  110. package/dist/tools/index.d.ts +13 -0
  111. package/dist/tools/index.d.ts.map +1 -0
  112. package/dist/tools/index.js +29 -0
  113. package/dist/tools/index.js.map +1 -0
  114. package/dist/tools/manage-subscription.d.ts +47 -0
  115. package/dist/tools/manage-subscription.d.ts.map +1 -0
  116. package/dist/tools/manage-subscription.js +317 -0
  117. package/dist/tools/manage-subscription.js.map +1 -0
  118. package/dist/tools/registry.d.ts +40 -0
  119. package/dist/tools/registry.d.ts.map +1 -0
  120. package/dist/tools/registry.js +85 -0
  121. package/dist/tools/registry.js.map +1 -0
  122. package/dist/tools/resolve-prompt-content.d.ts +35 -0
  123. package/dist/tools/resolve-prompt-content.d.ts.map +1 -0
  124. package/dist/tools/resolve-prompt-content.js +99 -0
  125. package/dist/tools/resolve-prompt-content.js.map +1 -0
  126. package/dist/tools/search-resources.d.ts +35 -0
  127. package/dist/tools/search-resources.d.ts.map +1 -0
  128. package/dist/tools/search-resources.js +159 -0
  129. package/dist/tools/search-resources.js.map +1 -0
  130. package/dist/tools/sync-resources.d.ts +54 -0
  131. package/dist/tools/sync-resources.d.ts.map +1 -0
  132. package/dist/tools/sync-resources.js +793 -0
  133. package/dist/tools/sync-resources.js.map +1 -0
  134. package/dist/tools/track-usage.d.ts +63 -0
  135. package/dist/tools/track-usage.d.ts.map +1 -0
  136. package/dist/tools/track-usage.js +90 -0
  137. package/dist/tools/track-usage.js.map +1 -0
  138. package/dist/tools/uninstall-resource.d.ts +30 -0
  139. package/dist/tools/uninstall-resource.d.ts.map +1 -0
  140. package/dist/tools/uninstall-resource.js +186 -0
  141. package/dist/tools/uninstall-resource.js.map +1 -0
  142. package/dist/tools/upload-resource.d.ts +81 -0
  143. package/dist/tools/upload-resource.d.ts.map +1 -0
  144. package/dist/tools/upload-resource.js +393 -0
  145. package/dist/tools/upload-resource.js.map +1 -0
  146. package/dist/transport/sse.d.ts +29 -0
  147. package/dist/transport/sse.d.ts.map +1 -0
  148. package/dist/transport/sse.js +271 -0
  149. package/dist/transport/sse.js.map +1 -0
  150. package/dist/types/errors.d.ts +60 -0
  151. package/dist/types/errors.d.ts.map +1 -0
  152. package/dist/types/errors.js +112 -0
  153. package/dist/types/errors.js.map +1 -0
  154. package/dist/types/index.d.ts +7 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +23 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/types/mcp.d.ts +50 -0
  159. package/dist/types/mcp.d.ts.map +1 -0
  160. package/dist/types/mcp.js +6 -0
  161. package/dist/types/mcp.js.map +1 -0
  162. package/dist/types/resources.d.ts +109 -0
  163. package/dist/types/resources.d.ts.map +1 -0
  164. package/dist/types/resources.js +7 -0
  165. package/dist/types/resources.js.map +1 -0
  166. package/dist/types/tools.d.ts +274 -0
  167. package/dist/types/tools.d.ts.map +1 -0
  168. package/dist/types/tools.js +6 -0
  169. package/dist/types/tools.js.map +1 -0
  170. package/dist/utils/cursor-paths.d.ts +84 -0
  171. package/dist/utils/cursor-paths.d.ts.map +1 -0
  172. package/dist/utils/cursor-paths.js +166 -0
  173. package/dist/utils/cursor-paths.js.map +1 -0
  174. package/dist/utils/log-cleaner.d.ts +18 -0
  175. package/dist/utils/log-cleaner.d.ts.map +1 -0
  176. package/dist/utils/log-cleaner.js +112 -0
  177. package/dist/utils/log-cleaner.js.map +1 -0
  178. package/dist/utils/logger.d.ts +59 -0
  179. package/dist/utils/logger.d.ts.map +1 -0
  180. package/dist/utils/logger.js +292 -0
  181. package/dist/utils/logger.js.map +1 -0
  182. package/dist/utils/validation.d.ts +58 -0
  183. package/dist/utils/validation.d.ts.map +1 -0
  184. package/dist/utils/validation.js +214 -0
  185. package/dist/utils/validation.js.map +1 -0
  186. package/package.json +1 -1
package/README.md CHANGED
@@ -9,9 +9,11 @@ CSP AI Agent is an MCP server that enables seamless synchronization of AI resour
9
9
  ## Key Features
10
10
 
11
11
  - **AI Resource Management**: Subscribe, sync, search, and upload AI resources
12
+ - **Hybrid Sync Strategy (v2.0)**: Complex skills download scripts locally while maintaining remote telemetry
12
13
  - **Multi-source Git Support**: Aggregate resources from multiple Git repositories with priority-based conflict resolution
14
+ - **Incremental Update**: String content equality comparison to skip unchanged files (reduces bandwidth)
13
15
  - **Intelligent Caching**: Skip redundant downloads and file writes using content-based comparison
14
- - **MCP Prompt Mode**: Commands and Skills are registered as MCP Prompts (no local file writes)
16
+ - **MCP Prompt Mode**: Commands and Skills are registered as MCP Prompts (no local file writes for simple resources)
15
17
  - **Solid Prompt Fallback**: Newly subscribed Commands and Skills can be resolved immediately through `resolve_prompt_content`
16
18
  - **Auto-configuration**: MCP servers are automatically registered in `~/.cursor/mcp.json`
17
19
  - **Telemetry & Analytics**: Track resource usage and sync health
@@ -72,24 +74,58 @@ csp-ai-agent-mcp --transport stdio
72
74
 
73
75
  #### 1. `sync_resources`
74
76
 
75
- Synchronize subscribed AI resources.
77
+ Synchronize subscribed AI resources with hybrid sync strategy (v2.0).
78
+
79
+ **Hybrid Sync Strategy:**
80
+ - **Simple resources** (single markdown file): Registered as MCP Prompt only
81
+ - **Complex skills** (with scripts): MCP Prompt + local script files in `~/.cursor/skills/<name>/`
82
+ - **Incremental mode**: Skips unchanged files using string content equality comparison (client-side)
76
83
 
77
84
  ```typescript
78
85
  // In Cursor AI Agent context
79
86
  const mcpJson = JSON.parse(fs.readFileSync('~/.cursor/mcp.json', 'utf8'));
80
87
  const configured = Object.keys(mcpJson.mcpServers || {});
81
88
 
82
- await callMcpTool('sync_resources', {
89
+ const result = await callMcpTool('sync_resources', {
83
90
  mode: 'incremental', // or 'full', 'check'
84
91
  configured_mcp_servers: configured, // Optimization: skip already configured MCPs
85
92
  });
93
+
94
+ // Execute local actions (for complex skills, rules, and MCPs)
95
+ if (result.local_actions_required) {
96
+ for (const action of result.local_actions_required) {
97
+ if (action.action === 'write_file') {
98
+ const localPath = expandPath(action.path);
99
+
100
+ // Skip if content unchanged
101
+ if (fs.existsSync(localPath)) {
102
+ const local = fs.readFileSync(localPath, 'utf8');
103
+ if (local === action.content) continue;
104
+ }
105
+
106
+ // Write file
107
+ fs.mkdirSync(path.dirname(localPath), { recursive: true });
108
+ fs.writeFileSync(localPath, action.content, action.encoding || 'utf8');
109
+
110
+ // Set permissions (Unix only)
111
+ if (action.mode && process.platform !== 'win32') {
112
+ fs.chmodSync(localPath, parseInt(action.mode, 8));
113
+ }
114
+ }
115
+ }
116
+ }
86
117
  ```
87
118
 
88
119
  **Modes:**
89
- - `incremental` (default): Update only changed resources
120
+ - `incremental` (default): Update only changed resources, skip unchanged files
90
121
  - `full`: Download all resources (file recovery mode)
91
122
  - `check`: Status check only (no downloads)
92
123
 
124
+ **Result Fields:**
125
+ - `summary.skipped`: Number of resources skipped (already up-to-date)
126
+ - `skipped_resources`: Details of skipped resources with reasons
127
+ - `local_actions_required`: File operations for AI to execute locally
128
+
93
129
  **Optimization:** Pass `configured_mcp_servers` to skip downloading MCP resources that are already in `~/.cursor/mcp.json`. This can save **70-90% of API calls and network traffic** in typical scenarios.
94
130
 
95
131
  #### 2. `manage_subscription`
@@ -145,15 +181,37 @@ await callMcpTool('upload_resource', {
145
181
 
146
182
  #### 5. `uninstall_resource`
147
183
 
148
- Remove a resource from local installation.
184
+ Remove a resource from local installation (v2.0: includes local script cleanup).
149
185
 
150
186
  ```typescript
151
- await callMcpTool('uninstall_resource', {
152
- resource_id_or_name: 'my-skill',
153
- remove_from_account: false, // Keep subscription, only remove local files
187
+ // Uninstall a skill with local scripts
188
+ const result = await callMcpTool('uninstall_resource', {
189
+ resource_id_or_name: 'zoom-build',
190
+ remove_from_account: true, // Also cancel subscription
154
191
  });
192
+
193
+ // Execute local cleanup (AI must perform on user's machine)
194
+ if (result.local_actions_required) {
195
+ for (const action of result.local_actions_required) {
196
+ if (action.action === 'delete_file') {
197
+ const localPath = expandPath(action.path);
198
+ if (fs.existsSync(localPath)) {
199
+ fs.rmSync(localPath, {
200
+ recursive: action.recursive || false,
201
+ force: true
202
+ });
203
+ console.log(`Deleted: ${action.path}`);
204
+ }
205
+ }
206
+ }
207
+ }
155
208
  ```
156
209
 
210
+ **Behavior:**
211
+ - **Command/Skill**: Unregisters MCP Prompt + queues local directory deletion (`~/.cursor/skills/<name>/`)
212
+ - **Rule/MCP**: Returns `local_actions_required` for file/config removal
213
+ - **`remove_from_account: true`**: Also cancels server-side subscription (otherwise will re-sync on next sync)
214
+
157
215
  #### 6. `track_usage`
158
216
 
159
217
  Record resource invocation telemetry.
@@ -262,21 +320,117 @@ The server reads `AI-Resources/ai-resources-config.json` to discover available G
262
320
 
263
321
  ## Architecture
264
322
 
265
- ### Resource Delivery Strategy
323
+ ### Hybrid Sync Strategy (v2.1 - Client-Side Metadata Scanning)
324
+
325
+ **Zero Server Dependency!** MCP Server scans local Git repositories to detect complex skills, eliminating the need for server-side metadata API.
326
+
327
+ **Architecture:**
328
+
329
+ ```
330
+ ┌─────────────────────────────────────────────────────────────┐
331
+ │ MCP Server (user-csp-ai-agent) │
332
+ │ │
333
+ │ ┌───────────────────────────────────────────────────────┐ │
334
+ │ │ Git Working Directory (AI-Resources/) │ │
335
+ │ │ ├── csp/ai-resources/skills/zoom-build/ │ │
336
+ │ │ │ ├── SKILL.md │ │
337
+ │ │ │ ├── scripts/build-cli │ │
338
+ │ │ │ ├── scripts/build-trigger │ │
339
+ │ │ │ └── teams/client-android.json │ │
340
+ │ │ └── csp/ai-resources/skills/hang-log-analyzer/ │ │
341
+ │ │ └── SKILL.md │ │
342
+ │ └───────────────────────────────────────────────────────┘ │
343
+ │ ↓ │
344
+ │ ┌───────────────────────────────────────────────────────┐ │
345
+ │ │ scanResourceMetadata(resourceName, type) │ │
346
+ │ │ - Recursively reads all files in directory │ │
347
+ │ │ - Detects scripts/, teams/, references/ paths │ │
348
+ │ │ - Infers file permissions (0755 for scripts) │ │
349
+ │ │ - Returns { has_scripts, script_files[] } │ │
350
+ │ └───────────────────────────────────────────────────────┘ │
351
+ │ ↓ │
352
+ │ ┌─────────────┐ ┌────────────────────────────┐ │
353
+ │ │ MCP Prompt │ │ Local Script Files │ │
354
+ │ │ Registration│ + │ (for complex skills only) │ │
355
+ │ │ (Telemetry) │ │ → local_actions_required[] │ │
356
+ │ └─────────────┘ └────────────────────────────┘ │
357
+ └─────────────────────────────────────────────────────────────┘
358
+ ```
359
+
360
+ **Two-Layer Resource Delivery:**
361
+
362
+ 1. **Remote Layer (MCP Prompt)** - For telemetry tracking
363
+ - All Skills/Commands registered as MCP Prompts
364
+ - AI invokes via `/skill/name` → MCP Server records usage
365
+ - Returns `SKILL.md` content to AI
366
+
367
+ 2. **Local Layer (Script Files)** - For complex skills only
368
+ - Skills with `has_scripts=true` download to `~/.cursor/skills/<name>/`
369
+ - Includes executable scripts, configuration files, and references
370
+ - AI can execute local scripts referenced in `SKILL.md`
371
+
372
+ **Resource Classification:**
373
+
374
+ ```
375
+ ┌─────────────────┬──────────────────┬─────────────────────┬────────────────┐
376
+ │ Resource Type │ MCP Prompt │ Local Files │ Decision │
377
+ ├─────────────────┼──────────────────┼─────────────────────┼────────────────┤
378
+ │ Simple Command │ ✅ Registered │ ❌ Not downloaded │ Single .md │
379
+ │ Simple Skill │ ✅ Registered │ ❌ Not downloaded │ Only SKILL.md │
380
+ │ Complex Skill │ ✅ Registered │ ✅ Downloaded │ has_scripts=T │
381
+ │ Rule │ ❌ Not applicable │ ✅ Downloaded │ Engine needs │
382
+ │ MCP │ ❌ Not applicable │ ✅ Downloaded │ Engine needs │
383
+ └─────────────────┴──────────────────┴─────────────────────┴────────────────┘
384
+ ```
385
+
386
+ **Example: Complex Skill (zoom-build)**
387
+
388
+ ```
389
+ Server-side (MCP Server):
390
+ .prompt-cache/skill-6dea7a2c8cf83e5d227ee39035411730.md
391
+ (AI fetches via prompts/get, telemetry recorded)
392
+
393
+ User-side (Cursor machine):
394
+ ~/.cursor/skills/zoom-build/
395
+ ├── SKILL.md
396
+ ├── scripts/
397
+ │ ├── build-cli ← mode 755 (executable)
398
+ │ ├── build-trigger ← mode 755
399
+ │ └── build-poll
400
+ └── teams/
401
+ ├── client-android.json
402
+ └── client-ios.json
403
+
404
+ Invocation flow:
405
+ /skill/zoom-build
406
+ → MCP Server: prompts/get → tracks telemetry ✅
407
+ → AI gets SKILL.md: "Run ~/.cursor/skills/zoom-build/scripts/build-cli"
408
+ → AI executes local script ✅
409
+ → Script returns build URL
410
+ ```
411
+
412
+ ### Resource Delivery Strategy (Legacy Reference)
413
+
414
+ **Previous Versions:**
415
+ - v1.0-1.4: All resources downloaded to local files
416
+ - v1.5-1.7: Pure MCP Prompt mode (telemetry enabled, but complex skills broken)
417
+ - v2.0+: Hybrid approach (best of both worlds)
266
418
 
267
419
  ```
268
420
  ┌─────────────────┬──────────────────┬─────────────────────┐
269
421
  │ Resource Type │ Storage │ Delivery Method │
270
422
  ├─────────────────┼──────────────────┼─────────────────────┤
271
423
  │ Command │ MCP Prompt │ In-memory cache │
272
- │ Skill │ MCP Prompt │ In-memory cache │
424
+ │ Skill (simple) │ MCP Prompt │ In-memory cache │
425
+ │ Skill (complex) │ MCP Prompt + │ Cache + local files │
426
+ │ │ Local scripts │ │
273
427
  │ Rule │ ~/.cursor/rules/ │ write_file action │
274
428
  │ MCP (local) │ ~/.cursor/mcp-*/ │ write_file actions │
275
429
  │ MCP (remote) │ ~/.cursor/mcp.* │ merge_mcp_json only │
276
430
  └─────────────────┴──────────────────┴─────────────────────┘
277
431
  ```
278
432
 
279
- ### Sync Flow
433
+ ### Sync Flow (v2.0 Hybrid)
280
434
 
281
435
  ```
282
436
  User: "csp 同步资源"
@@ -287,32 +441,166 @@ AI Agent: Call sync_resources(mode: 'incremental', configured_mcp_servers: [...]
287
441
 
288
442
  MCP Server:
289
443
  1. Fetch subscription list from CSP API
290
- 2. For each subscription:
291
- - Skip if MCP already in configured_mcp_servers (optimization)
292
- - Download resource files (API or Git fallback)
293
- - Generate local_actions_required
294
- 3. Return result + actions
444
+ 2. Git pull resource repositories
445
+ 3. For each subscription:
446
+ a. Register MCP Prompt (all Skills/Commands)
447
+ b. Scan local Git via scanResourceMetadata() (not API call)
448
+ c. If has_scripts=true:
449
+ - Generate write_file actions for ALL script files
450
+ - Set mode="0755" for executables
451
+ d. Track skipped resources
452
+ 4. Return result + local_actions_required
295
453
 
296
- AI Agent: Execute local_actions_required
297
- - write_file: Compare content, skip if identical
298
- - merge_mcp_json: Check skip_if_exists, preserve user env
454
+ AI Agent (Cursor): Execute local_actions_required
455
+ - write_file:
456
+ 1. Read existing file (if exists)
457
+ 2. Check content equality (localContent === action.content)
458
+ 3. SKIP write if identical (already up-to-date)
459
+ 4. Write file + create parent dirs
460
+ 5. Set permissions (chmod on Unix)
461
+ - merge_mcp_json: Smart merge preserving user env
299
462
 
300
463
  User: Resources synced ✅
464
+ - Summary: synced=5, skipped=3 (already up-to-date)
465
+ - Complex skills now have local scripts available
301
466
  ```
302
467
 
303
468
  ## Performance Optimizations
304
469
 
305
- ### 1. Content-based File Comparison (v0.1.23)
470
+ ### 1. Client-Side Metadata Scanning (v2.1 - 2026-03-27)
471
+
472
+ **Breakthrough:** Eliminated dependency on server-side metadata API by scanning local Git repositories.
473
+
474
+ **Architecture:**
475
+
476
+ ```
477
+ Before (v2.0):
478
+ sync_resources → REST API /resources/{id}/metadata → has_scripts + script_files
479
+ ↑ (requires server team coordination)
480
+
481
+ After (v2.1):
482
+ sync_resources → multiSourceGitManager.scanResourceMetadata() → has_scripts + script_files
483
+ ↑ (scans AI-Resources/ local filesystem)
484
+ ```
485
+
486
+ **Implementation:**
487
+
488
+ ```typescript
489
+ // New method in multi-source-manager.ts
490
+ const metadata = await multiSourceGitManager.scanResourceMetadata(
491
+ 'zoom-build',
492
+ 'skill'
493
+ );
494
+ // Returns:
495
+ // {
496
+ // has_scripts: true,
497
+ // script_files: [
498
+ // { relative_path: 'scripts/build-cli', content: '...', mode: '0755' },
499
+ // { relative_path: 'teams/client-android.json', content: '...', mode: '0644' }
500
+ // ]
501
+ // }
502
+ ```
503
+
504
+ **Benefits:**
505
+ - ✅ Zero server-side code changes needed
506
+ - ✅ No REST API dependency
507
+ - ✅ Real-time Git repository reflection
508
+ - ✅ Instant deployment (no backend coordination)
509
+ - ✅ Local filesystem speed (< 50ms for typical skill)
510
+
511
+ ### 2. Incremental Sync with SKILL.md Content Check (v2.1.1)
512
+
513
+ **Problem:** Re-downloading unchanged multi-file skills wastes bandwidth and time.
514
+
515
+ **Solution:** Skill-level content comparison (SKILL.md only).
516
+
517
+ ```typescript
518
+ // Client-side logic when executing write_file actions
519
+ for (const action of local_actions_required) {
520
+ if (action.action === 'write_file') {
521
+ const localPath = expandPath(action.path);
522
+
523
+ // For SKILL.md: compare content to decide whether to skip entire skill
524
+ if (action.path.endsWith('SKILL.md')) {
525
+ const existingContent = fs.existsSync(localPath)
526
+ ? fs.readFileSync(localPath, 'utf8')
527
+ : null;
528
+
529
+ if (existingContent === action.content) {
530
+ // SKILL.md unchanged → skip entire skill
531
+ skipSkill(skillName);
532
+ continue;
533
+ }
534
+ }
535
+
536
+ // SKILL.md changed → re-download all script files
537
+ writeFile(localPath, action.content, action.mode);
538
+ }
539
+ }
540
+ ```
541
+
542
+ **Why SKILL.md-only?**
543
+ - ✅ Atomic update: either skip all or download all
544
+ - ✅ Detects file additions/deletions (version bump in SKILL.md)
545
+ - ✅ Simpler logic (single content check vs multiple file I/O)
546
+ - ✅ SKILL.md is the version manifest (any script change should update version)
547
+
548
+ **Benefits:**
549
+ - ✅ 67% less I/O for unchanged skills (1 file read vs 3+)
550
+ - ✅ Prevents orphaned files (no partial updates)
551
+ - ✅ Guarantees consistency (all-or-nothing sync)
552
+
553
+ **Metrics (zoom-build skill, 3 files, 50KB):**
554
+ - First sync: 50KB downloaded, ~200ms
555
+ - Second sync (SKILL.md unchanged): 0 bytes downloaded, ~50ms ✅
556
+ - Third sync (SKILL.md changed): 50KB downloaded, ~200ms (re-downloads all)
557
+
558
+ ### 3. Content-based File Comparison (v2.2.0)
559
+
560
+ **Before:** Used SHA-256 hash calculation (introduced extra CPU overhead)
561
+ **After:** Direct string equality check (`existingContent === action.content`)
562
+
563
+ **Benefits:**
564
+ - ✅ Zero hash calculation overhead (no `crypto` calls)
565
+ - ✅ 100% reliable (string equality is unambiguous)
566
+ - ✅ Simpler implementation (removed `file-hash.ts` utility)
567
+ - ✅ Client-side execution (no MCP Server filesystem access)
568
+
569
+ ### 4. Cross-Resource Invocation Guidance (v2.3.0)
306
570
 
307
- **Before:** Used SHA-256 hash comparison (error-prone, platform-dependent)
308
- **After:** Direct string equality check (`existing === action.content`)
571
+ **Problem:** When a Command/Skill/Rule references another independent resource, AI Agent might read local files directly, bypassing telemetry tracking.
572
+
573
+ **Solution:** Auto-inject guidance prefix when returning content via `resolve_prompt_content`.
574
+
575
+ **Implementation:**
576
+ ```typescript
577
+ // PromptManager.resolvePromptContentForInvocation()
578
+ const guidancePrefix = buildCrossResourceGuidance(resourceType);
579
+ return {
580
+ content: guidancePrefix + strippedContent
581
+ };
582
+ ```
583
+
584
+ **Example returned content:**
585
+ ```markdown
586
+ <!-- CROSS-RESOURCE INVOCATION GUIDANCE (auto-generated by MCP Server) -->
587
+ > **Important**: If this command references OTHER independent Commands or Skills:
588
+ > - ALWAYS invoke them via resolve_prompt_content
589
+ > - NEVER read local files directly for cross-resource calls
590
+ > - This ensures every independent resource invocation is tracked in telemetry.
591
+ > ...
592
+ <!-- END GUIDANCE -->
593
+
594
+ [Actual Command/Skill content]
595
+ ```
309
596
 
310
597
  **Benefits:**
311
- - ✅ Infinitely faster (0ms vs 6ms per file)
312
- - ✅ 100% reliable (no platform issues)
313
- - ✅ Simpler implementation (6 lines vs 15+ lines)
598
+ - ✅ Transparent to resource creators (no need to modify Command/Skill content)
599
+ - ✅ Clear guidance for AI Agent (no guessing required)
600
+ - ✅ Accurate telemetry (cross-resource calls always go through MCP Server)
601
+ - ✅ Distinguishes cross-resource calls vs internal skill tools
314
602
 
315
- ### 2. MCP Skip Optimization (v0.1.23)
603
+ ### 5. MCP Skip Optimization (v0.1.23)
316
604
 
317
605
  **Before:** Always downloaded all MCP resources, generated all write_file actions
318
606
  **After:** Skip downloading MCPs that are already in `configured_mcp_servers`
@@ -449,17 +737,29 @@ GET /sse
449
737
 
450
738
  ## Version History
451
739
 
740
+ ### v0.1.24 (2026-03-27)
741
+
742
+ **New Features:**
743
+ - Added cross-resource invocation guidance mechanism in `resolve_prompt_content`
744
+ - Auto-inject guidance prefix to instruct AI Agent how to handle references to other Commands/Skills
745
+ - Clear distinction between cross-resource calls (use `resolve_prompt_content`) vs internal skill tools (use local files)
746
+
747
+ **Code Quality:**
748
+ - Added `is_skill_manifest` marker to `WriteFileAction` for atomic skill-level updates
749
+ - Enhanced `PromptManager.buildCrossResourceGuidance()` to generate resource-specific guidance
750
+ - Improved telemetry accuracy by ensuring cross-resource calls always go through MCP Server
751
+
452
752
  ### v0.1.23 (2026-03-27)
453
753
 
454
754
  **Performance Optimizations:**
455
- - Removed hash-based file comparison, use direct content equality
755
+ - Removed hash-based file comparison, use direct string content equality check
456
756
  - Added `configured_mcp_servers` parameter to skip downloading already-configured MCPs
457
757
  - Typical sync time reduced by 80-85% (8-12s → 1-2s)
458
- - Removed `crypto` dependency from sync operations
758
+ - Removed `crypto` dependency and `file-hash.ts` utility from sync operations
459
759
 
460
760
  **Bug Fixes:**
461
- - Fixed hash calculation mismatch between MCP Server and AI Agent (BUG-2026-03-27-001)
462
- - Eliminated platform-dependent `cat | sha256sum` issues
761
+ - Fixed content comparison logic to use string equality (eliminates platform-dependent issues)
762
+ - Eliminated hash calculation overhead (BUG-2026-03-27-001)
463
763
 
464
764
  ### v0.1.22 and earlier
465
765
 
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Cached API Client
3
+ * Wraps API Client with caching layer
4
+ */
5
+ import { AxiosRequestConfig } from 'axios';
6
+ declare class CachedAPIClient {
7
+ private cache;
8
+ private readonly cacheNamespace;
9
+ constructor();
10
+ /**
11
+ * Initialize cache connection
12
+ */
13
+ connect(): Promise<void>;
14
+ /**
15
+ * Generate cache key from URL and config
16
+ */
17
+ private generateCacheKey;
18
+ /**
19
+ * GET request with caching
20
+ */
21
+ get<T>(url: string, options?: {
22
+ config?: AxiosRequestConfig;
23
+ skipCache?: boolean;
24
+ }): Promise<T>;
25
+ /**
26
+ * POST request (no caching for mutations)
27
+ */
28
+ post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T>;
29
+ /**
30
+ * PUT request (no caching for mutations)
31
+ */
32
+ put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T>;
33
+ /**
34
+ * DELETE request (no caching for mutations)
35
+ */
36
+ delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
37
+ /**
38
+ * Clear all API cache
39
+ */
40
+ clearCache(): Promise<void>;
41
+ /**
42
+ * Get cache statistics
43
+ */
44
+ getCacheStats(): import("../cache").CacheStats | null;
45
+ }
46
+ export declare const cachedAPIClient: CachedAPIClient;
47
+ export {};
48
+ //# sourceMappingURL=cached-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cached-client.d.ts","sourceRoot":"","sources":["../../src/api/cached-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE3C,cAAM,eAAe;IACnB,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;;IAWxC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBrG;;OAEG;IACG,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAcnF;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAalF;;OAEG;IACG,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAarE;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACH,aAAa;CAMd;AAGD,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * Cached API Client
4
+ * Wraps API Client with caching layer
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.cachedAPIClient = void 0;
8
+ const cache_1 = require("../cache");
9
+ const client_1 = require("./client");
10
+ const config_1 = require("../config");
11
+ const logger_1 = require("../utils/logger");
12
+ class CachedAPIClient {
13
+ cache = null;
14
+ cacheNamespace = 'api';
15
+ constructor() {
16
+ if (config_1.config.cache.enabled) {
17
+ this.cache = cache_1.CacheManager.getInstance({ namespace: this.cacheNamespace });
18
+ logger_1.logger.info('Cached API client initialized with caching enabled');
19
+ }
20
+ else {
21
+ logger_1.logger.info('Cached API client initialized without caching');
22
+ }
23
+ }
24
+ /**
25
+ * Initialize cache connection
26
+ */
27
+ async connect() {
28
+ if (this.cache) {
29
+ await this.cache.connect();
30
+ }
31
+ }
32
+ /**
33
+ * Generate cache key from URL and config
34
+ */
35
+ generateCacheKey(method, url, data) {
36
+ const parts = [method.toUpperCase(), url];
37
+ if (data) {
38
+ parts.push(JSON.stringify(data));
39
+ }
40
+ return parts.join(':');
41
+ }
42
+ /**
43
+ * GET request with caching
44
+ */
45
+ async get(url, options) {
46
+ const cacheKey = this.generateCacheKey('GET', url);
47
+ // Try cache first (if enabled and not skipped)
48
+ if (this.cache && !options?.skipCache) {
49
+ const cached = await this.cache.get(cacheKey);
50
+ if (cached !== null) {
51
+ logger_1.logger.debug({ url, cacheKey }, 'API cache hit');
52
+ return cached;
53
+ }
54
+ logger_1.logger.debug({ url, cacheKey }, 'API cache miss');
55
+ }
56
+ // Fetch from API
57
+ const result = await client_1.apiClient.get(url, options?.config);
58
+ // Store in cache (if enabled)
59
+ if (this.cache && !options?.skipCache) {
60
+ await this.cache.set(cacheKey, result);
61
+ logger_1.logger.debug({ url, cacheKey }, 'API response cached');
62
+ }
63
+ return result;
64
+ }
65
+ /**
66
+ * POST request (no caching for mutations)
67
+ */
68
+ async post(url, data, config) {
69
+ const result = await client_1.apiClient.post(url, data, config);
70
+ // Invalidate related cache entries
71
+ if (this.cache) {
72
+ // Clear GET cache for the same URL
73
+ const getCacheKey = this.generateCacheKey('GET', url);
74
+ await this.cache.del(getCacheKey);
75
+ logger_1.logger.debug({ url }, 'Cache invalidated after POST');
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * PUT request (no caching for mutations)
81
+ */
82
+ async put(url, data, config) {
83
+ const result = await client_1.apiClient.put(url, data, config);
84
+ // Invalidate related cache entries
85
+ if (this.cache) {
86
+ const getCacheKey = this.generateCacheKey('GET', url);
87
+ await this.cache.del(getCacheKey);
88
+ logger_1.logger.debug({ url }, 'Cache invalidated after PUT');
89
+ }
90
+ return result;
91
+ }
92
+ /**
93
+ * DELETE request (no caching for mutations)
94
+ */
95
+ async delete(url, config) {
96
+ const result = await client_1.apiClient.delete(url, config);
97
+ // Invalidate related cache entries
98
+ if (this.cache) {
99
+ const getCacheKey = this.generateCacheKey('GET', url);
100
+ await this.cache.del(getCacheKey);
101
+ logger_1.logger.debug({ url }, 'Cache invalidated after DELETE');
102
+ }
103
+ return result;
104
+ }
105
+ /**
106
+ * Clear all API cache
107
+ */
108
+ async clearCache() {
109
+ if (this.cache) {
110
+ await this.cache.clear();
111
+ logger_1.logger.info('API cache cleared');
112
+ }
113
+ }
114
+ /**
115
+ * Get cache statistics
116
+ */
117
+ getCacheStats() {
118
+ if (this.cache) {
119
+ return this.cache.getStats();
120
+ }
121
+ return null;
122
+ }
123
+ }
124
+ // Singleton instance
125
+ exports.cachedAPIClient = new CachedAPIClient();
126
+ //# sourceMappingURL=cached-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cached-client.js","sourceRoot":"","sources":["../../src/api/cached-client.ts"],"names":[],"mappings":"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"}