mobile-ai-agents 1.0.23

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 (194) hide show
  1. package/AGENTS.md +228 -0
  2. package/CLAUDE.md +236 -0
  3. package/LICENSE +21 -0
  4. package/README.md +652 -0
  5. package/agents/android/android-crash-analyzer/README.md +134 -0
  6. package/agents/android/android-crash-analyzer/agent.md +206 -0
  7. package/agents/android/android-crash-analyzer/example-input.md +138 -0
  8. package/agents/android/android-crash-analyzer/example-output.md +225 -0
  9. package/agents/android/anr-investigation/README.md +78 -0
  10. package/agents/android/anr-investigation/agent.md +117 -0
  11. package/agents/android/anr-investigation/example-input.md +62 -0
  12. package/agents/android/anr-investigation/example-output.md +103 -0
  13. package/agents/android/axiom/agent.md +197 -0
  14. package/agents/android/code-reviewer/README.md +108 -0
  15. package/agents/android/code-reviewer/agent.md +210 -0
  16. package/agents/android/code-reviewer/example-input.md +116 -0
  17. package/agents/android/code-reviewer/example-output.md +115 -0
  18. package/agents/android/compose-navigation/README.md +76 -0
  19. package/agents/android/compose-navigation/agent.md +121 -0
  20. package/agents/android/compose-navigation/example-input.md +49 -0
  21. package/agents/android/compose-navigation/example-output.md +325 -0
  22. package/agents/android/compose-screen-builder/README.md +95 -0
  23. package/agents/android/compose-screen-builder/agent.md +170 -0
  24. package/agents/android/compose-screen-builder/example-input.md +40 -0
  25. package/agents/android/compose-screen-builder/example-output.md +568 -0
  26. package/agents/android/compose-ui-reviewer/README.md +73 -0
  27. package/agents/android/compose-ui-reviewer/agent.md +253 -0
  28. package/agents/android/compose-ui-reviewer/example-input.md +84 -0
  29. package/agents/android/compose-ui-reviewer/example-output.md +102 -0
  30. package/agents/android/crash-analyzer/README.md +53 -0
  31. package/agents/android/crash-analyzer/agent.md +295 -0
  32. package/agents/android/memory-leak-analyzer/README.md +79 -0
  33. package/agents/android/memory-leak-analyzer/agent.md +116 -0
  34. package/agents/android/memory-leak-analyzer/example-input.md +78 -0
  35. package/agents/android/memory-leak-analyzer/example-output.md +128 -0
  36. package/agents/cross-platform/accessibility-auditor/README.md +47 -0
  37. package/agents/cross-platform/accessibility-auditor/agent.md +268 -0
  38. package/agents/cross-platform/accessibility-auditor/example-input.md +84 -0
  39. package/agents/cross-platform/accessibility-auditor/example-output.md +132 -0
  40. package/agents/cross-platform/appforge/agent.md +303 -0
  41. package/agents/cross-platform/ci-cd-generator/README.md +65 -0
  42. package/agents/cross-platform/ci-cd-generator/agent.md +422 -0
  43. package/agents/cross-platform/ci-cd-generator/example-input.md +69 -0
  44. package/agents/cross-platform/ci-cd-generator/example-output.md +261 -0
  45. package/agents/cross-platform/crasher/agent.md +221 -0
  46. package/agents/cross-platform/figma/agent.md +247 -0
  47. package/agents/cross-platform/launchpad/agent.md +330 -0
  48. package/agents/cross-platform/mobile-harness/agent.md +422 -0
  49. package/agents/cross-platform/mobile-memory/agent.md +429 -0
  50. package/agents/cross-platform/perf/agent.md +261 -0
  51. package/agents/cross-platform/pipeline/agent.md +288 -0
  52. package/agents/cross-platform/release-notes-generator/README.md +63 -0
  53. package/agents/cross-platform/release-notes-generator/agent.md +218 -0
  54. package/agents/cross-platform/release-notes-generator/example-input.md +71 -0
  55. package/agents/cross-platform/release-notes-generator/example-output.md +96 -0
  56. package/agents/cross-platform/scribe/agent.md +180 -0
  57. package/agents/cross-platform/security-scanner/README.md +61 -0
  58. package/agents/cross-platform/security-scanner/agent.md +287 -0
  59. package/agents/cross-platform/security-scanner/example-input.md +77 -0
  60. package/agents/cross-platform/security-scanner/example-output.md +141 -0
  61. package/agents/cross-platform/sentinel/agent.md +217 -0
  62. package/agents/cross-platform/store-listing-writer/README.md +58 -0
  63. package/agents/cross-platform/store-listing-writer/agent.md +380 -0
  64. package/agents/cross-platform/store-listing-writer/example-input.md +67 -0
  65. package/agents/cross-platform/store-listing-writer/example-output.md +131 -0
  66. package/agents/flutter/bloc-feature-builder/README.md +101 -0
  67. package/agents/flutter/bloc-feature-builder/agent.md +225 -0
  68. package/agents/flutter/bloc-feature-builder/example-input.md +47 -0
  69. package/agents/flutter/bloc-feature-builder/example-output.md +838 -0
  70. package/agents/flutter/dart/agent.md +223 -0
  71. package/agents/flutter/widget-generator/README.md +66 -0
  72. package/agents/flutter/widget-generator/agent.md +264 -0
  73. package/agents/flutter/widget-generator/example-input.md +71 -0
  74. package/agents/flutter/widget-generator/example-output.md +163 -0
  75. package/agents/gaming/forge/agent.md +197 -0
  76. package/agents/gaming/unreal/agent.md +200 -0
  77. package/agents/ios/crash-analyzer/README.md +69 -0
  78. package/agents/ios/crash-analyzer/agent.md +300 -0
  79. package/agents/ios/crash-analyzer/example-input.md +83 -0
  80. package/agents/ios/crash-analyzer/example-output.md +126 -0
  81. package/agents/ios/swift/agent.md +206 -0
  82. package/agents/ios/swift-reviewer/README.md +68 -0
  83. package/agents/ios/swift-reviewer/agent.md +231 -0
  84. package/agents/ios/swift-reviewer/example-input.md +100 -0
  85. package/agents/ios/swift-reviewer/example-output.md +127 -0
  86. package/agents/react-native/bridge/agent.md +212 -0
  87. package/agents/react-native/performance-optimizer/README.md +81 -0
  88. package/agents/react-native/performance-optimizer/agent.md +284 -0
  89. package/agents/react-native/performance-optimizer/example-input.md +104 -0
  90. package/agents/react-native/performance-optimizer/example-output.md +132 -0
  91. package/agents/unity/shader-generator/README.md +77 -0
  92. package/agents/unity/shader-generator/agent.md +270 -0
  93. package/agents/unity/shader-generator/example-input.md +70 -0
  94. package/agents/unity/shader-generator/example-output.md +160 -0
  95. package/agents/unreal/blueprint-advisor/README.md +71 -0
  96. package/agents/unreal/blueprint-advisor/agent.md +311 -0
  97. package/agents/unreal/blueprint-advisor/example-input.md +73 -0
  98. package/agents/unreal/blueprint-advisor/example-output.md +217 -0
  99. package/cli/index.js +1091 -0
  100. package/docs/agent-guide.md +138 -0
  101. package/docs/chatgpt-integration.md +219 -0
  102. package/docs/core-loops.md +143 -0
  103. package/docs/cursor-integration.md +143 -0
  104. package/docs/getting-started.md +132 -0
  105. package/docs/index.html +711 -0
  106. package/docs/installation.md +271 -0
  107. package/docs/mobile-harness.md +160 -0
  108. package/docs/mobile-mcp.md +172 -0
  109. package/docs/mobile-memory.md +349 -0
  110. package/docs/release-process.md +85 -0
  111. package/docs/releases/release-v1.0.0.md +165 -0
  112. package/docs/roadmap/github-issues.md +239 -0
  113. package/docs/skill-categories.md +125 -0
  114. package/docs/vscode-copilot-integration.md +104 -0
  115. package/docs/wiki.md +615 -0
  116. package/install.sh +507 -0
  117. package/mobile-karpathy.md +79 -0
  118. package/package.json +57 -0
  119. package/skills/android/README.md +31 -0
  120. package/skills/android/android-tdd.md +89 -0
  121. package/skills/android/anr-investigation.md +251 -0
  122. package/skills/android/code-review.md +98 -0
  123. package/skills/android/compose-migration.md +48 -0
  124. package/skills/android/compose-review.md +78 -0
  125. package/skills/android/kotlin-modernize.md +51 -0
  126. package/skills/android/memory-leak-investigation.md +256 -0
  127. package/skills/android/proguard-rules.md +48 -0
  128. package/skills/cross-platform/accessibility-audit.md +63 -0
  129. package/skills/cross-platform/api-versioning.md +59 -0
  130. package/skills/cross-platform/clean-code-audit.md +239 -0
  131. package/skills/cross-platform/crash-triage.md +53 -0
  132. package/skills/cross-platform/deeplink-debug.md +57 -0
  133. package/skills/cross-platform/feature-slice.md +48 -0
  134. package/skills/cross-platform/grill-mobile.md +64 -0
  135. package/skills/cross-platform/mobile-mcp-qa.md +193 -0
  136. package/skills/cross-platform/mobile-memory-graph.md +179 -0
  137. package/skills/cross-platform/mobile-memory-save.md +210 -0
  138. package/skills/cross-platform/mobile-memory-search.md +154 -0
  139. package/skills/cross-platform/perf-audit.md +61 -0
  140. package/skills/cross-platform/prd-verification.md +324 -0
  141. package/skills/cross-platform/release-prep.md +64 -0
  142. package/skills/cross-platform/security-audit.md +281 -0
  143. package/skills/cross-platform/store-listing.md +75 -0
  144. package/skills/flutter/README.md +32 -0
  145. package/skills/flutter/dart-modernize.md +47 -0
  146. package/skills/flutter/flutter-review.md +59 -0
  147. package/skills/flutter/flutter-tdd.md +73 -0
  148. package/skills/flutter/widget-extract.md +41 -0
  149. package/skills/flutter/widget-gen.md +131 -0
  150. package/skills/gaming/blueprint-to-cpp.md +51 -0
  151. package/skills/gaming/game-perf.md +54 -0
  152. package/skills/gaming/shader-gen.md +44 -0
  153. package/skills/gaming/unity-tdd.md +85 -0
  154. package/skills/ios/README.md +45 -0
  155. package/skills/ios/data-persistence.md +151 -0
  156. package/skills/ios/ios-tdd.md +90 -0
  157. package/skills/ios/networking.md +139 -0
  158. package/skills/ios/performance.md +122 -0
  159. package/skills/ios/swift-concurrency.md +57 -0
  160. package/skills/ios/swift-review.md +124 -0
  161. package/skills/ios/swiftui-review.md +49 -0
  162. package/skills/ios/swiftui-state.md +122 -0
  163. package/skills/ios/unit-testing.md +144 -0
  164. package/skills/ios/xcode-warnings.md +37 -0
  165. package/skills/react-native/README.md +42 -0
  166. package/skills/react-native/bridge-audit.md +104 -0
  167. package/skills/react-native/expo-optimize.md +55 -0
  168. package/skills/react-native/new-arch-migrate.md +53 -0
  169. package/skills/react-native/performance.md +124 -0
  170. package/skills/react-native/rn-review.md +53 -0
  171. package/skills/react-native/rn-tdd.md +76 -0
  172. package/skills/shared/README.md +46 -0
  173. package/skills/shared/accessibility-audit.md +132 -0
  174. package/skills/shared/crash-analysis.md +110 -0
  175. package/skills/shared/security-scan.md +149 -0
  176. package/skills/unity/README.md +31 -0
  177. package/skills/unity/shader-review.md +110 -0
  178. package/templates/agent-template.md +88 -0
  179. package/templates/prompt-template.md +50 -0
  180. package/templates/skill-template.md +64 -0
  181. package/templates/workflow-template.md +48 -0
  182. package/workflows/app-launch.md +66 -0
  183. package/workflows/appforge-workflow.md +232 -0
  184. package/workflows/ci-setup.md +77 -0
  185. package/workflows/crash-to-fix.md +71 -0
  186. package/workflows/feature-ship.md +61 -0
  187. package/workflows/game-level.md +72 -0
  188. package/workflows/mobile-harness.md +319 -0
  189. package/workflows/mobile-mcp-qa.md +228 -0
  190. package/workflows/mobile-memory-workflow.md +243 -0
  191. package/workflows/new-project-setup.md +93 -0
  192. package/workflows/new-screen.md +68 -0
  193. package/workflows/perf-sprint.md +78 -0
  194. package/workflows/release-cycle.md +71 -0
package/cli/index.js ADDED
@@ -0,0 +1,1091 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const https = require('https');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ const REPO = 'salmanashraf/mobile-agency';
10
+ const BRANCH = 'main';
11
+ const RAW = `https://raw.githubusercontent.com/${REPO}/${BRANCH}`;
12
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
13
+
14
+ const CLAUDE_AGENTS_DIR = path.join(os.homedir(), '.claude', 'agents');
15
+ const CLAUDE_COMMANDS_DIR = path.join(os.homedir(), '.claude', 'commands');
16
+
17
+ // ─── Manifest ────────────────────────────────────────────────────────────────
18
+
19
+ const AGENTS = {
20
+ // Android
21
+ 'anr-investigation': { file: 'agents/android/anr-investigation/agent.md', platform: 'android' },
22
+ 'android-crash-analyzer': { file: 'agents/android/android-crash-analyzer/agent.md', platform: 'android' },
23
+ 'axiom': { file: 'agents/android/axiom/agent.md', platform: 'android' },
24
+ 'code-reviewer': { file: 'agents/android/code-reviewer/agent.md', platform: 'android' },
25
+ 'compose-navigation': { file: 'agents/android/compose-navigation/agent.md', platform: 'android' },
26
+ 'compose-screen-builder': { file: 'agents/android/compose-screen-builder/agent.md', platform: 'android' },
27
+ 'compose-ui-reviewer': { file: 'agents/android/compose-ui-reviewer/agent.md', platform: 'android' },
28
+ 'crash-analyzer-android': { file: 'agents/android/crash-analyzer/agent.md', platform: 'android' },
29
+ 'memory-leak-analyzer': { file: 'agents/android/memory-leak-analyzer/agent.md', platform: 'android' },
30
+ // iOS
31
+ 'crash-analyzer-ios': { file: 'agents/ios/crash-analyzer/agent.md', platform: 'ios' },
32
+ 'swift-reviewer': { file: 'agents/ios/swift-reviewer/agent.md', platform: 'ios' },
33
+ 'swift': { file: 'agents/ios/swift/agent.md', platform: 'ios' },
34
+ // Flutter
35
+ 'bloc-feature-builder': { file: 'agents/flutter/bloc-feature-builder/agent.md', platform: 'flutter' },
36
+ 'dart': { file: 'agents/flutter/dart/agent.md', platform: 'flutter' },
37
+ 'widget-generator': { file: 'agents/flutter/widget-generator/agent.md', platform: 'flutter' },
38
+ // React Native
39
+ 'bridge': { file: 'agents/react-native/bridge/agent.md', platform: 'rn' },
40
+ 'performance-optimizer': { file: 'agents/react-native/performance-optimizer/agent.md', platform: 'rn' },
41
+ // Gaming
42
+ 'forge': { file: 'agents/gaming/forge/agent.md', platform: 'gaming' },
43
+ 'unreal': { file: 'agents/gaming/unreal/agent.md', platform: 'gaming' },
44
+ 'shader-generator': { file: 'agents/unity/shader-generator/agent.md', platform: 'gaming' },
45
+ 'blueprint-advisor': { file: 'agents/unreal/blueprint-advisor/agent.md', platform: 'gaming' },
46
+ // Cross-platform
47
+ 'accessibility-auditor': { file: 'agents/cross-platform/accessibility-auditor/agent.md', platform: 'cross' },
48
+ 'appforge': { file: 'agents/cross-platform/appforge/agent.md', platform: 'cross' },
49
+ 'ci-cd-generator': { file: 'agents/cross-platform/ci-cd-generator/agent.md', platform: 'cross' },
50
+ 'crasher': { file: 'agents/cross-platform/crasher/agent.md', platform: 'cross' },
51
+ 'figma': { file: 'agents/cross-platform/figma/agent.md', platform: 'cross' },
52
+ 'launchpad': { file: 'agents/cross-platform/launchpad/agent.md', platform: 'cross' },
53
+ 'mobile-harness': { file: 'agents/cross-platform/mobile-harness/agent.md', platform: 'cross' },
54
+ 'mobile-memory': { file: 'agents/cross-platform/mobile-memory/agent.md', platform: 'cross' },
55
+ 'perf': { file: 'agents/cross-platform/perf/agent.md', platform: 'cross' },
56
+ 'pipeline': { file: 'agents/cross-platform/pipeline/agent.md', platform: 'cross' },
57
+ 'release-notes-generator': { file: 'agents/cross-platform/release-notes-generator/agent.md', platform: 'cross' },
58
+ 'scribe': { file: 'agents/cross-platform/scribe/agent.md', platform: 'cross' },
59
+ 'security-scanner': { file: 'agents/cross-platform/security-scanner/agent.md', platform: 'cross' },
60
+ 'sentinel': { file: 'agents/cross-platform/sentinel/agent.md', platform: 'cross' },
61
+ 'store-listing-writer': { file: 'agents/cross-platform/store-listing-writer/agent.md', platform: 'cross' },
62
+ };
63
+
64
+ const SKILLS = {
65
+ // Android
66
+ 'anr-investigation': { file: 'skills/android/anr-investigation.md', platform: 'android' },
67
+ 'android-tdd': { file: 'skills/android/android-tdd.md', platform: 'android' },
68
+ 'code-review': { file: 'skills/android/code-review.md', platform: 'android' },
69
+ 'compose-migration': { file: 'skills/android/compose-migration.md', platform: 'android' },
70
+ 'compose-review': { file: 'skills/android/compose-review.md', platform: 'android' },
71
+ 'kotlin-modernize': { file: 'skills/android/kotlin-modernize.md', platform: 'android' },
72
+ 'memory-leak-investigation': { file: 'skills/android/memory-leak-investigation.md', platform: 'android' },
73
+ 'proguard-rules': { file: 'skills/android/proguard-rules.md', platform: 'android' },
74
+ // iOS
75
+ 'data-persistence': { file: 'skills/ios/data-persistence.md', platform: 'ios' },
76
+ 'ios-tdd': { file: 'skills/ios/ios-tdd.md', platform: 'ios' },
77
+ 'networking': { file: 'skills/ios/networking.md', platform: 'ios' },
78
+ 'ios-performance': { file: 'skills/ios/performance.md', platform: 'ios' },
79
+ 'swift-concurrency': { file: 'skills/ios/swift-concurrency.md', platform: 'ios' },
80
+ 'swift-review': { file: 'skills/ios/swift-review.md', platform: 'ios' },
81
+ 'swiftui-review': { file: 'skills/ios/swiftui-review.md', platform: 'ios' },
82
+ 'swiftui-state': { file: 'skills/ios/swiftui-state.md', platform: 'ios' },
83
+ 'unit-testing': { file: 'skills/ios/unit-testing.md', platform: 'ios' },
84
+ 'xcode-warnings': { file: 'skills/ios/xcode-warnings.md', platform: 'ios' },
85
+ // Flutter
86
+ 'dart-modernize': { file: 'skills/flutter/dart-modernize.md', platform: 'flutter' },
87
+ 'flutter-review': { file: 'skills/flutter/flutter-review.md', platform: 'flutter' },
88
+ 'flutter-tdd': { file: 'skills/flutter/flutter-tdd.md', platform: 'flutter' },
89
+ 'widget-extract': { file: 'skills/flutter/widget-extract.md', platform: 'flutter' },
90
+ 'widget-gen': { file: 'skills/flutter/widget-gen.md', platform: 'flutter' },
91
+ // React Native
92
+ 'bridge-audit': { file: 'skills/react-native/bridge-audit.md', platform: 'rn' },
93
+ 'expo-optimize': { file: 'skills/react-native/expo-optimize.md', platform: 'rn' },
94
+ 'new-arch-migrate': { file: 'skills/react-native/new-arch-migrate.md', platform: 'rn' },
95
+ 'rn-performance': { file: 'skills/react-native/performance.md', platform: 'rn' },
96
+ 'rn-review': { file: 'skills/react-native/rn-review.md', platform: 'rn' },
97
+ 'rn-tdd': { file: 'skills/react-native/rn-tdd.md', platform: 'rn' },
98
+ // Gaming
99
+ 'blueprint-to-cpp': { file: 'skills/gaming/blueprint-to-cpp.md', platform: 'gaming' },
100
+ 'game-perf': { file: 'skills/gaming/game-perf.md', platform: 'gaming' },
101
+ 'shader-gen': { file: 'skills/gaming/shader-gen.md', platform: 'gaming' },
102
+ 'unity-tdd': { file: 'skills/gaming/unity-tdd.md', platform: 'gaming' },
103
+ 'shader-review': { file: 'skills/unity/shader-review.md', platform: 'gaming' },
104
+ // Cross-platform
105
+ 'accessibility-audit':{ file: 'skills/cross-platform/accessibility-audit.md', platform: 'cross' },
106
+ 'api-versioning': { file: 'skills/cross-platform/api-versioning.md', platform: 'cross' },
107
+ 'clean-code-audit': { file: 'skills/cross-platform/clean-code-audit.md', platform: 'cross' },
108
+ 'crash-triage': { file: 'skills/cross-platform/crash-triage.md', platform: 'cross' },
109
+ 'deeplink-debug': { file: 'skills/cross-platform/deeplink-debug.md', platform: 'cross' },
110
+ 'feature-slice': { file: 'skills/cross-platform/feature-slice.md', platform: 'cross' },
111
+ 'grill-mobile': { file: 'skills/cross-platform/grill-mobile.md', platform: 'cross' },
112
+ 'mobile-mcp-qa': { file: 'skills/cross-platform/mobile-mcp-qa.md', platform: 'cross' },
113
+ 'mobile-memory-graph': { file: 'skills/cross-platform/mobile-memory-graph.md', platform: 'cross' },
114
+ 'mobile-memory-search': { file: 'skills/cross-platform/mobile-memory-search.md', platform: 'cross' },
115
+ 'mobile-memory-save': { file: 'skills/cross-platform/mobile-memory-save.md', platform: 'cross' },
116
+ 'perf-audit': { file: 'skills/cross-platform/perf-audit.md', platform: 'cross' },
117
+ 'prd-verification': { file: 'skills/cross-platform/prd-verification.md', platform: 'cross' },
118
+ 'release-prep': { file: 'skills/cross-platform/release-prep.md', platform: 'cross' },
119
+ 'security-audit': { file: 'skills/cross-platform/security-audit.md', platform: 'cross' },
120
+ 'store-listing': { file: 'skills/cross-platform/store-listing.md', platform: 'cross' },
121
+ // Shared
122
+ 'crash-analysis': { file: 'skills/shared/crash-analysis.md', platform: 'cross' },
123
+ 'security-scan': { file: 'skills/shared/security-scan.md', platform: 'cross' },
124
+ };
125
+
126
+ const WORKFLOWS = {
127
+ 'feature-ship': { file: 'workflows/feature-ship.md' },
128
+ 'release-cycle': { file: 'workflows/release-cycle.md' },
129
+ 'game-level': { file: 'workflows/game-level.md' },
130
+ 'crash-to-fix': { file: 'workflows/crash-to-fix.md' },
131
+ 'ci-setup': { file: 'workflows/ci-setup.md' },
132
+ 'new-screen': { file: 'workflows/new-screen.md' },
133
+ 'new-project-setup': { file: 'workflows/new-project-setup.md' },
134
+ 'app-launch': { file: 'workflows/app-launch.md' },
135
+ 'perf-sprint': { file: 'workflows/perf-sprint.md' },
136
+ 'mobile-memory-workflow': { file: 'workflows/mobile-memory-workflow.md' },
137
+ 'appforge-workflow': { file: 'workflows/appforge-workflow.md' },
138
+ 'mobile-mcp-qa': { file: 'workflows/mobile-mcp-qa.md' },
139
+ 'mobile-harness': { file: 'workflows/mobile-harness.md' },
140
+ };
141
+
142
+ const PLATFORM_AGENTS = {
143
+ android: [
144
+ 'anr-investigation', 'android-crash-analyzer', 'axiom', 'code-reviewer', 'compose-navigation',
145
+ 'compose-screen-builder', 'compose-ui-reviewer', 'crash-analyzer-android',
146
+ 'memory-leak-analyzer',
147
+ // cross-platform bundled for android
148
+ 'appforge', 'crasher', 'sentinel', 'mobile-harness', 'mobile-memory', 'perf',
149
+ 'scribe', 'pipeline', 'figma', 'launchpad',
150
+ 'accessibility-auditor', 'ci-cd-generator', 'release-notes-generator',
151
+ 'security-scanner', 'store-listing-writer',
152
+ ],
153
+ ios: [
154
+ 'crash-analyzer-ios', 'swift-reviewer', 'swift',
155
+ // cross-platform bundled for ios
156
+ 'appforge', 'crasher', 'sentinel', 'mobile-harness', 'mobile-memory', 'perf',
157
+ 'scribe', 'pipeline', 'figma', 'launchpad',
158
+ 'accessibility-auditor', 'ci-cd-generator', 'release-notes-generator',
159
+ 'security-scanner', 'store-listing-writer',
160
+ ],
161
+ flutter: [
162
+ 'bloc-feature-builder', 'dart', 'widget-generator',
163
+ // cross-platform bundled for flutter
164
+ 'appforge', 'crasher', 'sentinel', 'mobile-harness', 'mobile-memory', 'perf',
165
+ 'scribe', 'pipeline', 'figma', 'launchpad',
166
+ 'accessibility-auditor', 'ci-cd-generator', 'release-notes-generator',
167
+ 'security-scanner', 'store-listing-writer',
168
+ ],
169
+ rn: [
170
+ 'bridge', 'performance-optimizer',
171
+ // cross-platform bundled for rn
172
+ 'appforge', 'crasher', 'sentinel', 'mobile-harness', 'mobile-memory', 'perf',
173
+ 'scribe', 'pipeline', 'figma', 'launchpad',
174
+ 'accessibility-auditor', 'ci-cd-generator', 'release-notes-generator',
175
+ 'security-scanner', 'store-listing-writer',
176
+ ],
177
+ gaming: [
178
+ 'forge', 'unreal', 'shader-generator', 'blueprint-advisor',
179
+ // cross-platform bundled for gaming
180
+ 'appforge', 'mobile-harness', 'mobile-memory', 'perf',
181
+ ],
182
+ cross: [
183
+ 'accessibility-auditor', 'appforge', 'ci-cd-generator', 'crasher', 'figma',
184
+ 'launchpad', 'mobile-harness', 'mobile-memory',
185
+ 'perf', 'pipeline', 'release-notes-generator', 'scribe', 'security-scanner',
186
+ 'sentinel', 'store-listing-writer',
187
+ ],
188
+ };
189
+
190
+ const PLATFORM_SKILLS = {
191
+ android: [
192
+ 'anr-investigation', 'android-tdd', 'code-review', 'compose-migration', 'compose-review',
193
+ 'kotlin-modernize', 'memory-leak-investigation', 'proguard-rules',
194
+ // cross-platform bundled for android
195
+ 'accessibility-audit', 'api-versioning', 'clean-code-audit', 'crash-analysis', 'crash-triage',
196
+ 'deeplink-debug', 'feature-slice', 'grill-mobile', 'mobile-mcp-qa',
197
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'perf-audit', 'prd-verification',
198
+ 'release-prep', 'security-audit', 'security-scan', 'store-listing',
199
+ ],
200
+ ios: [
201
+ 'data-persistence', 'ios-tdd', 'networking', 'ios-performance',
202
+ 'swift-concurrency', 'swift-review', 'swiftui-review', 'swiftui-state',
203
+ 'unit-testing', 'xcode-warnings',
204
+ // cross-platform bundled for ios
205
+ 'accessibility-audit', 'api-versioning', 'clean-code-audit', 'crash-analysis', 'crash-triage',
206
+ 'deeplink-debug', 'feature-slice', 'grill-mobile', 'mobile-mcp-qa',
207
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'perf-audit', 'prd-verification',
208
+ 'release-prep', 'security-audit', 'security-scan', 'store-listing',
209
+ ],
210
+ flutter: [
211
+ 'dart-modernize', 'flutter-review', 'flutter-tdd', 'widget-extract', 'widget-gen',
212
+ // cross-platform bundled for flutter
213
+ 'accessibility-audit', 'api-versioning', 'clean-code-audit', 'crash-analysis', 'crash-triage',
214
+ 'deeplink-debug', 'feature-slice', 'grill-mobile', 'mobile-mcp-qa',
215
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'perf-audit', 'prd-verification',
216
+ 'release-prep', 'security-audit', 'security-scan', 'store-listing',
217
+ ],
218
+ rn: [
219
+ 'bridge-audit', 'expo-optimize', 'new-arch-migrate', 'rn-performance',
220
+ 'rn-review', 'rn-tdd',
221
+ // cross-platform bundled for rn
222
+ 'accessibility-audit', 'api-versioning', 'clean-code-audit', 'crash-analysis', 'crash-triage',
223
+ 'deeplink-debug', 'feature-slice', 'grill-mobile', 'mobile-mcp-qa',
224
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'perf-audit', 'prd-verification',
225
+ 'release-prep', 'security-audit', 'security-scan', 'store-listing',
226
+ ],
227
+ gaming: [
228
+ 'blueprint-to-cpp', 'clean-code-audit', 'game-perf', 'mobile-mcp-qa',
229
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'prd-verification', 'security-audit', 'shader-gen',
230
+ 'unity-tdd', 'shader-review',
231
+ ],
232
+ cross: [
233
+ 'accessibility-audit', 'api-versioning', 'clean-code-audit', 'crash-analysis', 'crash-triage',
234
+ 'deeplink-debug', 'feature-slice', 'grill-mobile', 'mobile-mcp-qa',
235
+ 'mobile-memory-graph', 'mobile-memory-search', 'mobile-memory-save', 'perf-audit', 'prd-verification',
236
+ 'release-prep', 'security-audit', 'security-scan', 'store-listing',
237
+ ],
238
+ };
239
+
240
+ // Workflows are always installed regardless of platform selection
241
+ const ALL_WORKFLOWS = Object.keys(WORKFLOWS);
242
+
243
+ const PRIMARY_MEMORY_DIR = '.mobile-ai-agents';
244
+ const LEGACY_MEMORY_DIR = '.mobile-agency';
245
+ const MEMORY_DIR = fs.existsSync(path.join(process.cwd(), LEGACY_MEMORY_DIR)) &&
246
+ !fs.existsSync(path.join(process.cwd(), PRIMARY_MEMORY_DIR))
247
+ ? LEGACY_MEMORY_DIR
248
+ : PRIMARY_MEMORY_DIR;
249
+ const MEMORY_SUBDIR = path.join(MEMORY_DIR, 'memory');
250
+ const MEMORY_EVENTS_FILE = path.join(MEMORY_SUBDIR, 'events.jsonl');
251
+ const MEMORY_INDEX_FILE = path.join(MEMORY_SUBDIR, 'index.md');
252
+ const MEMORY_CONFIG_FILE = path.join(MEMORY_SUBDIR, 'config.json');
253
+ const MOBILE_MEMORY_FILE = 'MOBILE_MEMORY.md';
254
+
255
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
256
+
257
+ function log(msg) { process.stdout.write(` ${msg}\n`); }
258
+ function ok(msg) { process.stdout.write(` ✓ ${msg}\n`); }
259
+ function err(msg) { process.stderr.write(` ✗ ${msg}\n`); }
260
+ function bold(s) { return `\x1b[1m${s}\x1b[0m`; }
261
+ function dim(s) { return `\x1b[2m${s}\x1b[0m`; }
262
+ function green(s) { return `\x1b[32m${s}\x1b[0m`; }
263
+ function yellow(s) { return `\x1b[33m${s}\x1b[0m`; }
264
+
265
+ function nowIso() {
266
+ return new Date().toISOString();
267
+ }
268
+
269
+ function slugify(s) {
270
+ return String(s || 'memory')
271
+ .toLowerCase()
272
+ .replace(/[^a-z0-9]+/g, '-')
273
+ .replace(/^-+|-+$/g, '')
274
+ .slice(0, 80) || 'memory';
275
+ }
276
+
277
+ function parseOptions(args) {
278
+ const out = { _: [] };
279
+ for (let i = 0; i < args.length; i++) {
280
+ const arg = args[i];
281
+ if (!arg.startsWith('--')) {
282
+ out._.push(arg);
283
+ continue;
284
+ }
285
+ const key = arg.slice(2);
286
+ const next = args[i + 1];
287
+ if (!next || next.startsWith('--')) {
288
+ out[key] = true;
289
+ } else {
290
+ out[key] = next;
291
+ i++;
292
+ }
293
+ }
294
+ return out;
295
+ }
296
+
297
+ function readStdinIfPiped() {
298
+ if (process.stdin.isTTY) return '';
299
+ try {
300
+ return fs.readFileSync(0, 'utf8').trim();
301
+ } catch {
302
+ return '';
303
+ }
304
+ }
305
+
306
+ function readJsonl(file) {
307
+ if (!fs.existsSync(file)) return [];
308
+ return fs.readFileSync(file, 'utf8')
309
+ .split(/\r?\n/)
310
+ .filter(Boolean)
311
+ .map((line) => {
312
+ try { return JSON.parse(line); }
313
+ catch { return null; }
314
+ })
315
+ .filter(Boolean);
316
+ }
317
+
318
+ function appendJsonl(file, value) {
319
+ ensureDir(path.dirname(file));
320
+ fs.appendFileSync(file, `${JSON.stringify(value)}\n`);
321
+ }
322
+
323
+ function detectProjectName() {
324
+ try {
325
+ const pkgPath = path.join(process.cwd(), 'package.json');
326
+ if (fs.existsSync(pkgPath)) {
327
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
328
+ if (pkg.name) return pkg.name;
329
+ }
330
+ } catch {}
331
+ return path.basename(process.cwd());
332
+ }
333
+
334
+ function detectPlatform() {
335
+ const cwd = process.cwd();
336
+ if (fs.existsSync(path.join(cwd, 'pubspec.yaml'))) return 'Flutter';
337
+ if (fs.existsSync(path.join(cwd, 'Package.swift'))) return 'iOS / Swift';
338
+ if (fs.existsSync(path.join(cwd, 'android')) && fs.existsSync(path.join(cwd, 'ios'))) return 'React Native / Flutter';
339
+ if (fs.existsSync(path.join(cwd, 'build.gradle')) || fs.existsSync(path.join(cwd, 'settings.gradle')) || fs.existsSync(path.join(cwd, 'settings.gradle.kts'))) return 'Android';
340
+ if (fs.existsSync(path.join(cwd, 'project.pbxproj')) || fs.readdirSync(cwd).some(f => f.endsWith('.xcodeproj'))) return 'iOS';
341
+ if (fs.existsSync(path.join(cwd, 'Assets')) && fs.existsSync(path.join(cwd, 'ProjectSettings'))) return 'Unity';
342
+ if (fs.readdirSync(cwd).some(f => f.endsWith('.uproject'))) return 'Unreal';
343
+ return 'Unknown';
344
+ }
345
+
346
+ function ensureMemory() {
347
+ ensureDir(path.join(process.cwd(), MEMORY_SUBDIR));
348
+ if (!fs.existsSync(path.join(process.cwd(), MEMORY_CONFIG_FILE))) {
349
+ const config = {
350
+ project: detectProjectName(),
351
+ platform: detectPlatform(),
352
+ createdAt: nowIso(),
353
+ version: 1,
354
+ privacy: {
355
+ privateTag: '<private>...</private>',
356
+ note: 'Do not capture secrets, customer data, private logs, tokens, or credentials.'
357
+ }
358
+ };
359
+ fs.writeFileSync(path.join(process.cwd(), MEMORY_CONFIG_FILE), `${JSON.stringify(config, null, 2)}\n`);
360
+ }
361
+ if (!fs.existsSync(path.join(process.cwd(), MEMORY_EVENTS_FILE))) {
362
+ fs.writeFileSync(path.join(process.cwd(), MEMORY_EVENTS_FILE), '');
363
+ }
364
+ if (!fs.existsSync(path.join(process.cwd(), MEMORY_INDEX_FILE))) {
365
+ fs.writeFileSync(path.join(process.cwd(), MEMORY_INDEX_FILE), '# Mobile AI Agents Memory Index\n\nNo memories captured yet.\n');
366
+ }
367
+ }
368
+
369
+ function loadMemoryConfig() {
370
+ ensureMemory();
371
+ return JSON.parse(fs.readFileSync(path.join(process.cwd(), MEMORY_CONFIG_FILE), 'utf8'));
372
+ }
373
+
374
+ function stripPrivateTags(text) {
375
+ return String(text || '').replace(/<private>[\s\S]*?<\/private>/gi, '[private omitted]');
376
+ }
377
+
378
+ function memoryEventFromOptions(opts) {
379
+ const piped = readStdinIfPiped();
380
+ let text = opts.text || piped || opts._.join(' ');
381
+ if (opts.file) {
382
+ const filePath = path.resolve(process.cwd(), opts.file);
383
+ text = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : text;
384
+ }
385
+ text = stripPrivateTags(text).trim();
386
+ if (!text) {
387
+ throw new Error('Memory capture needs --text, --file, positional text, or piped stdin.');
388
+ }
389
+ const title = opts.title || text.split(/\r?\n/)[0].slice(0, 90);
390
+ return {
391
+ id: `${Date.now()}-${slugify(title)}`,
392
+ createdAt: nowIso(),
393
+ type: opts.type || 'note',
394
+ title,
395
+ tags: opts.tags ? String(opts.tags).split(',').map(s => s.trim()).filter(Boolean) : [],
396
+ files: opts.files ? String(opts.files).split(',').map(s => s.trim()).filter(Boolean) : [],
397
+ text,
398
+ };
399
+ }
400
+
401
+ function updateMemoryIndex(events) {
402
+ const recent = events.slice(-50).reverse();
403
+ const lines = [
404
+ '# Mobile AI Agents Memory Index',
405
+ '',
406
+ `Updated: ${nowIso()}`,
407
+ `Events: ${events.length}`,
408
+ '',
409
+ '| Time | Type | Title | Tags |',
410
+ '|---|---|---|---|',
411
+ ];
412
+ for (const e of recent) {
413
+ lines.push(`| ${e.createdAt || ''} | ${e.type || ''} | ${String(e.title || '').replace(/\|/g, '/')} | ${(e.tags || []).join(', ')} |`);
414
+ }
415
+ lines.push('');
416
+ fs.writeFileSync(path.join(process.cwd(), MEMORY_INDEX_FILE), lines.join('\n'));
417
+ }
418
+
419
+ function formatMemoryEvent(e, includeText = false) {
420
+ const tags = e.tags && e.tags.length ? ` [${e.tags.join(', ')}]` : '';
421
+ const files = e.files && e.files.length ? ` files: ${e.files.join(', ')}` : '';
422
+ const head = `${e.createdAt} · ${e.type} · ${e.title}${tags}${files}`;
423
+ if (!includeText) return head;
424
+ return `${head}\n${e.text}`;
425
+ }
426
+
427
+ function buildMobileMemoryFromEvents(config, events) {
428
+ const recent = events.slice(-20);
429
+ const decisions = events.filter(e => e.type === 'decision').slice(-10);
430
+ const findings = events.filter(e => ['finding', 'audit', 'qa', 'bug'].includes(e.type)).slice(-10);
431
+ const progress = events.filter(e => ['progress', 'stage', 'task', 'checkpoint'].includes(e.type)).slice(-10);
432
+ const next = [...events].reverse().find(e => e.type === 'next-action') || [...events].reverse()[0];
433
+
434
+ const decisionRows = decisions.length
435
+ ? decisions.map(e => `| ${e.title.replace(/\|/g, '/')} | ${e.text.replace(/\r?\n/g, ' ').replace(/\|/g, '/').slice(0, 120)} | Not captured |`).join('\n')
436
+ : '| None captured | - | - |';
437
+
438
+ const findingRows = findings.length
439
+ ? findings.map(e => `| ${e.type.toUpperCase()} | ${e.title.replace(/\|/g, '/')} | ${e.text.replace(/\r?\n/g, ' ').replace(/\|/g, '/').slice(0, 120)} | Review |`).join('\n')
440
+ : '| None | None captured | - | - |';
441
+
442
+ const done = progress.length
443
+ ? progress.map(e => `${e.type}: ${e.title}`).join('; ')
444
+ : 'No progress events captured yet.';
445
+
446
+ return `---\n# Mobile Memory\n**Project:** ${config.project || detectProjectName()}\n**Platform:** ${config.platform || detectPlatform()}\n**Stack:** Unknown\n**Architecture:** Unknown\n**Saved:** ${nowIso()}\n**Compatible:** Claude Code · Cursor · Windsurf · ChatGPT · Gemini · Copilot\n**Token reduction:** Generated from ${events.length} memory events\n\n---\n\n## ⚡ INSTANT RESUME\n${config.project || detectProjectName()} has ${events.length} captured Mobile AI Agents memory events. Recent work: ${recent.slice(-3).map(e => e.title).join('; ') || 'No events yet'}. Continue from NEXT ACTION below and read exact files before editing.\n\n---\n\n## 🗺️ Knowledge Graph\n\n### Nodes\n| Node | Type | Layer | Health |\n|---|---|---|---|\n| Memory Index | CONTEXT | Project | OK |\n\n### Key Edges\n| From | Edge | To | Note |\n|---|---|---|---|\n| Memory Index | SUMMARIZES | Session Events | Generated by mobile-ai-agents memory checkpoint |\n\n### God Nodes\n| Node | Connections | Platform Risk | Recommendation |\n|---|---|---|---|\n| Unknown | 0 | Unknown | Run /mobile-memory-graph with code files |\n\n### Architecture Violations\n- None captured. Run platform reviewer and /mobile-memory-graph for code-level risks.\n\n---\n\n## 🏥 Health Report\n### 🚨 CRITICAL\n- None captured.\n\n### ⚠️ WARNING\n- Unknown architecture health until code graph is generated.\n\n### 🏦 Tech Debt\n- Convert important memory events into richer graph nodes when the feature stabilizes.\n\n---\n\n## 🎯 Session Context\n\n### Current Task\n${next ? next.text : 'No active task captured.'}\n\n### Decisions Made\n| Decision | Reason | Rejected |\n|---|---|---|\n${decisionRows}\n\n### Progress\n✅ Done: ${done}\n🔄 In Progress: ${next ? next.title : 'Nothing captured'}\n⏭️ NEXT ACTION: ${next ? next.text.replace(/\r?\n/g, ' ').slice(0, 240) : 'Capture a next action with mobile-ai-agents memory capture --type next-action --text \"...\".'}\n🚧 Blocked: Nothing captured\n\n### Open Questions\n- None captured.\n\n---\n\n## 🤖 Agent State\n| Agent | Last Action | Finding | Pending |\n|---|---|---|---|\n${findingRows}\n\n---\n\n## 📄 Code State\nSee ${MEMORY_EVENTS_FILE} for raw event history. Capture mid-edit files with mobile-ai-agents memory capture --type code-state --file <path>.\n\n---\n\n## 🔄 Resume Instructions\n\n**Claude Code:**\nStart new session → paste INSTANT RESUME → paste full MOBILE_MEMORY.md → say "Continue"\n\n**Cursor/Windsurf:**\nSave as MOBILE_MEMORY.md in project root → next prompt: "Read MOBILE_MEMORY.md and continue"\n\n**ChatGPT/Gemini:**\nPaste full file as first message → "Resume from NEXT ACTION"\n\n**Same tool, new session:**\nPaste full file → /mobile-memory restore\n---\n`;
447
+ }
448
+
449
+ function fetch(url) {
450
+ return new Promise((resolve, reject) => {
451
+ https.get(url, (res) => {
452
+ if (res.statusCode === 301 || res.statusCode === 302) {
453
+ return fetch(res.headers.location).then(resolve).catch(reject);
454
+ }
455
+ if (res.statusCode !== 200) {
456
+ return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
457
+ }
458
+ const chunks = [];
459
+ res.on('data', c => chunks.push(c));
460
+ res.on('end', () => resolve(Buffer.concat(chunks).toString()));
461
+ res.on('error', reject);
462
+ }).on('error', reject);
463
+ });
464
+ }
465
+
466
+ function ensureDir(dir) {
467
+ fs.mkdirSync(dir, { recursive: true });
468
+ }
469
+
470
+ async function downloadFile(remotePath, destPaths) {
471
+ const content = await fetchRemote(remotePath);
472
+ for (const destPath of destPaths) {
473
+ ensureDir(path.dirname(destPath));
474
+ fs.writeFileSync(destPath, content);
475
+ }
476
+ return content;
477
+ }
478
+
479
+ async function fetchRemote(remotePath) {
480
+ const localPath = path.join(PACKAGE_ROOT, remotePath);
481
+ if (fs.existsSync(localPath)) {
482
+ return fs.readFileSync(localPath, 'utf8');
483
+ }
484
+ return fetch(`${RAW}/${remotePath}`);
485
+ }
486
+
487
+ // Collect all agent/skill/workflow names and their remote paths for a platform set
488
+ function collectItems(platforms) {
489
+ const agentNames = new Set();
490
+ const skillNames = new Set();
491
+ for (const p of platforms) {
492
+ (PLATFORM_AGENTS[p] || []).forEach(a => agentNames.add(a));
493
+ (PLATFORM_SKILLS[p] || []).forEach(s => skillNames.add(s));
494
+ }
495
+ return { agentNames: [...agentNames], skillNames: [...skillNames] };
496
+ }
497
+
498
+ // ─── Install targets ─────────────────────────────────────────────────────────
499
+
500
+ async function installForClaude(platforms) {
501
+ log('Installing for Claude Code...');
502
+ ensureDir(CLAUDE_AGENTS_DIR);
503
+ ensureDir(CLAUDE_COMMANDS_DIR);
504
+
505
+ const { agentNames, skillNames } = collectItems(platforms);
506
+
507
+ // Agents → ~/.claude/agents/<name>.md AND ~/.claude/commands/<name>.md
508
+ process.stdout.write(`\n Agents (${agentNames.length})\n`);
509
+ await Promise.all(agentNames.map(async (name) => {
510
+ const meta = AGENTS[name];
511
+ if (!meta) return;
512
+ const agentDest = path.join(CLAUDE_AGENTS_DIR, `${name}.md`);
513
+ const commandDest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
514
+ await downloadFile(meta.file, [agentDest, commandDest]);
515
+ ok(name);
516
+ }));
517
+
518
+ // Skills → ~/.claude/commands/<slug>.md
519
+ process.stdout.write(`\n Skills (${skillNames.length})\n`);
520
+ await Promise.all(skillNames.map(async (name) => {
521
+ const meta = SKILLS[name];
522
+ if (!meta) return;
523
+ const dest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
524
+ await downloadFile(meta.file, [dest]);
525
+ ok(name);
526
+ }));
527
+
528
+ // Workflows → ~/.claude/commands/<name>.md (always)
529
+ process.stdout.write(`\n Workflows (${ALL_WORKFLOWS.length})\n`);
530
+ await Promise.all(ALL_WORKFLOWS.map(async (name) => {
531
+ const meta = WORKFLOWS[name];
532
+ if (!meta) return;
533
+ const dest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
534
+ await downloadFile(meta.file, [dest]);
535
+ ok(name);
536
+ }));
537
+
538
+ console.log('');
539
+ log(`Agents → ${dim(CLAUDE_AGENTS_DIR)}`);
540
+ log(`Commands → ${dim(CLAUDE_COMMANDS_DIR)}`);
541
+ log(` (agents + skills + workflows as slash commands)`);
542
+ }
543
+
544
+ async function installForCursor(platforms) {
545
+ log('Installing for Cursor (.cursor/rules/)...');
546
+ const rulesDir = path.join(process.cwd(), '.cursor', 'rules');
547
+ ensureDir(rulesDir);
548
+
549
+ const { agentNames, skillNames } = collectItems(platforms);
550
+ const total = agentNames.length + skillNames.length + ALL_WORKFLOWS.length;
551
+ process.stdout.write(`\n Rules (${total})\n`);
552
+
553
+ // Agents
554
+ await Promise.all(agentNames.map(async (name) => {
555
+ const meta = AGENTS[name];
556
+ if (!meta) return;
557
+ const dest = path.join(rulesDir, `${name}.mdc`);
558
+ await downloadFile(meta.file, [dest]);
559
+ ok(name);
560
+ }));
561
+
562
+ // Skills
563
+ await Promise.all(skillNames.map(async (name) => {
564
+ const meta = SKILLS[name];
565
+ if (!meta) return;
566
+ const dest = path.join(rulesDir, `${name}.mdc`);
567
+ await downloadFile(meta.file, [dest]);
568
+ ok(name);
569
+ }));
570
+
571
+ // Workflows
572
+ await Promise.all(ALL_WORKFLOWS.map(async (name) => {
573
+ const meta = WORKFLOWS[name];
574
+ if (!meta) return;
575
+ const dest = path.join(rulesDir, `${name}.mdc`);
576
+ await downloadFile(meta.file, [dest]);
577
+ ok(name);
578
+ }));
579
+
580
+ console.log('');
581
+ log(`Rules → ${dim(rulesDir)}`);
582
+ }
583
+
584
+ async function installForWindsurf(platforms) {
585
+ log('Installing for Windsurf (.windsurfrules)...');
586
+
587
+ const { agentNames, skillNames } = collectItems(platforms);
588
+ const total = agentNames.length + skillNames.length + ALL_WORKFLOWS.length;
589
+ process.stdout.write(`\n Fetching ${total} files...\n`);
590
+
591
+ const parts = [];
592
+
593
+ const agentContents = await Promise.all(
594
+ agentNames.map(name => {
595
+ const meta = AGENTS[name];
596
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
597
+ })
598
+ );
599
+ agentContents.forEach((c, i) => { if (c) { parts.push(`# Agent: ${agentNames[i]}\n\n${c}`); ok(agentNames[i]); } });
600
+
601
+ const skillContents = await Promise.all(
602
+ skillNames.map(name => {
603
+ const meta = SKILLS[name];
604
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
605
+ })
606
+ );
607
+ skillContents.forEach((c, i) => { if (c) { parts.push(`# Skill: ${skillNames[i]}\n\n${c}`); ok(skillNames[i]); } });
608
+
609
+ const workflowContents = await Promise.all(
610
+ ALL_WORKFLOWS.map(name => {
611
+ const meta = WORKFLOWS[name];
612
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
613
+ })
614
+ );
615
+ workflowContents.forEach((c, i) => { if (c) { parts.push(`# Workflow: ${ALL_WORKFLOWS[i]}\n\n${c}`); ok(ALL_WORKFLOWS[i]); } });
616
+
617
+ const dest = path.join(process.cwd(), '.windsurfrules');
618
+ fs.writeFileSync(dest, parts.join('\n\n---\n\n'));
619
+
620
+ console.log('');
621
+ log(`.windsurfrules → ${dim(dest)}`);
622
+ }
623
+
624
+ async function installForCopilot(platforms) {
625
+ log('Installing for GitHub Copilot (.github/copilot-instructions.md)...');
626
+ const githubDir = path.join(process.cwd(), '.github');
627
+ ensureDir(githubDir);
628
+
629
+ const { agentNames, skillNames } = collectItems(platforms);
630
+ const total = agentNames.length + skillNames.length;
631
+ process.stdout.write(`\n Fetching ${total} files...\n`);
632
+
633
+ const parts = [
634
+ '# Mobile AI Agents — AI Dev Team Instructions',
635
+ '',
636
+ `> Auto-generated by mobile-ai-agents CLI from github.com/${REPO}`,
637
+ '',
638
+ ];
639
+
640
+ parts.push('## Agents\n');
641
+ const agentContents = await Promise.all(
642
+ agentNames.map(name => {
643
+ const meta = AGENTS[name];
644
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
645
+ })
646
+ );
647
+ agentContents.forEach((c, i) => {
648
+ if (c) { parts.push(`### ${agentNames[i]}\n\n${c}`); ok(agentNames[i]); }
649
+ });
650
+
651
+ parts.push('\n## Skills\n');
652
+ const skillContents = await Promise.all(
653
+ skillNames.map(name => {
654
+ const meta = SKILLS[name];
655
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
656
+ })
657
+ );
658
+ skillContents.forEach((c, i) => {
659
+ if (c) { parts.push(`### ${skillNames[i]}\n\n${c}`); ok(skillNames[i]); }
660
+ });
661
+
662
+ const dest = path.join(githubDir, 'copilot-instructions.md');
663
+ fs.writeFileSync(dest, parts.join('\n\n'));
664
+
665
+ console.log('');
666
+ log(`Copilot instructions → ${dim(dest)}`);
667
+ }
668
+
669
+ async function installForCodex(platforms) {
670
+ log('Installing for Codex (AGENTS.md)...');
671
+
672
+ const { agentNames, skillNames } = collectItems(platforms);
673
+ const total = agentNames.length + skillNames.length + ALL_WORKFLOWS.length;
674
+ process.stdout.write(`\n Fetching ${total} files...\n`);
675
+
676
+ const parts = [
677
+ '# Mobile AI Agents — AGENTS.md',
678
+ '',
679
+ `> Auto-generated by mobile-ai-agents CLI from github.com/${REPO}`,
680
+ '',
681
+ ];
682
+
683
+ parts.push('## Agents\n');
684
+ const agentContents = await Promise.all(
685
+ agentNames.map(name => {
686
+ const meta = AGENTS[name];
687
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
688
+ })
689
+ );
690
+ agentContents.forEach((c, i) => {
691
+ if (c) { parts.push(`### ${agentNames[i]}\n\n${c}`); ok(agentNames[i]); }
692
+ });
693
+
694
+ parts.push('\n## Skills\n');
695
+ const skillContents = await Promise.all(
696
+ skillNames.map(name => {
697
+ const meta = SKILLS[name];
698
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
699
+ })
700
+ );
701
+ skillContents.forEach((c, i) => {
702
+ if (c) { parts.push(`### ${skillNames[i]}\n\n${c}`); ok(skillNames[i]); }
703
+ });
704
+
705
+ parts.push('\n## Workflows\n');
706
+ const workflowContents = await Promise.all(
707
+ ALL_WORKFLOWS.map(name => {
708
+ const meta = WORKFLOWS[name];
709
+ return meta ? fetchRemote(meta.file) : Promise.resolve(null);
710
+ })
711
+ );
712
+ workflowContents.forEach((c, i) => {
713
+ if (c) { parts.push(`### ${ALL_WORKFLOWS[i]}\n\n${c}`); ok(ALL_WORKFLOWS[i]); }
714
+ });
715
+
716
+ const dest = path.join(process.cwd(), 'AGENTS.md');
717
+ fs.writeFileSync(dest, parts.join('\n\n'));
718
+
719
+ console.log('');
720
+ log(`AGENTS.md → ${dim(dest)}`);
721
+ }
722
+
723
+ // ─── Commands ────────────────────────────────────────────────────────────────
724
+
725
+ async function cmdInstall(args) {
726
+ let platform = 'all';
727
+ let tool = 'claude';
728
+
729
+ for (let i = 0; i < args.length; i++) {
730
+ if (args[i] === '--platform' || args[i] === '-p') platform = args[++i];
731
+ if (args[i] === '--tool' || args[i] === '-t') tool = args[++i];
732
+ }
733
+
734
+ const platforms = platform === 'all'
735
+ ? ['android', 'ios', 'flutter', 'rn', 'gaming', 'cross']
736
+ : [platform];
737
+
738
+ console.log('');
739
+ console.log(bold('Mobile AI Agents'));
740
+ console.log(dim(` github.com/${REPO}`));
741
+ console.log('');
742
+ console.log(` Platform : ${bold(platform)}`);
743
+ console.log(` Tool : ${bold(tool)}`);
744
+ console.log('');
745
+
746
+ if (tool === 'claude' || tool === 'all') await installForClaude(platforms);
747
+ if (tool === 'cursor' || tool === 'all') await installForCursor(platforms);
748
+ if (tool === 'windsurf' || tool === 'all') await installForWindsurf(platforms);
749
+ if (tool === 'copilot' || tool === 'all') await installForCopilot(platforms);
750
+ if (tool === 'codex' || tool === 'all') await installForCodex(platforms);
751
+
752
+ console.log('');
753
+ console.log(green(bold(' Done. Happy shipping.')));
754
+ console.log('');
755
+ }
756
+
757
+ async function cmdAdd(args) {
758
+ const type = args[0]; // 'agent' | 'skill' | 'workflow'
759
+ const name = args[1];
760
+
761
+ if (!type || !name) {
762
+ console.error('\nUsage: npx mobile-ai-agents add agent <name>');
763
+ console.error(' npx mobile-ai-agents add skill <name>');
764
+ console.error(' npx mobile-ai-agents add workflow <name>\n');
765
+ process.exit(1);
766
+ }
767
+
768
+ console.log('');
769
+
770
+ if (type === 'agent') {
771
+ const meta = AGENTS[name];
772
+ if (!meta) {
773
+ err(`Unknown agent: ${name}`);
774
+ console.log(`\n Available agents: ${Object.keys(AGENTS).join(', ')}\n`);
775
+ process.exit(1);
776
+ }
777
+ ensureDir(CLAUDE_AGENTS_DIR);
778
+ ensureDir(CLAUDE_COMMANDS_DIR);
779
+ const agentDest = path.join(CLAUDE_AGENTS_DIR, `${name}.md`);
780
+ const commandDest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
781
+ log(`Fetching ${bold(name)} agent...`);
782
+ await downloadFile(meta.file, [agentDest, commandDest]);
783
+ ok(`${name} → ${dim(agentDest)}`);
784
+ ok(`${name} → ${dim(commandDest)}`);
785
+ } else if (type === 'skill') {
786
+ const meta = SKILLS[name];
787
+ if (!meta) {
788
+ err(`Unknown skill: ${name}`);
789
+ console.log(`\n Available skills: ${Object.keys(SKILLS).join(', ')}\n`);
790
+ process.exit(1);
791
+ }
792
+ ensureDir(CLAUDE_COMMANDS_DIR);
793
+ const dest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
794
+ log(`Fetching ${bold(name)} skill...`);
795
+ await downloadFile(meta.file, [dest]);
796
+ ok(`${name} → ${dim(dest)}`);
797
+ } else if (type === 'workflow') {
798
+ const meta = WORKFLOWS[name];
799
+ if (!meta) {
800
+ err(`Unknown workflow: ${name}`);
801
+ console.log(`\n Available workflows: ${Object.keys(WORKFLOWS).join(', ')}\n`);
802
+ process.exit(1);
803
+ }
804
+ ensureDir(CLAUDE_COMMANDS_DIR);
805
+ const dest = path.join(CLAUDE_COMMANDS_DIR, `${name}.md`);
806
+ log(`Fetching ${bold(name)} workflow...`);
807
+ await downloadFile(meta.file, [dest]);
808
+ ok(`${name} → ${dim(dest)}`);
809
+ } else {
810
+ err(`Unknown type: ${type}. Use 'agent', 'skill', or 'workflow'.`);
811
+ process.exit(1);
812
+ }
813
+
814
+ console.log('');
815
+ }
816
+
817
+ function cmdMemory(args) {
818
+ const sub = args[0];
819
+ const opts = parseOptions(args.slice(1));
820
+
821
+ if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
822
+ console.log('');
823
+ console.log(bold(' npx mobile-ai-agents memory <command> [options]'));
824
+ console.log('');
825
+ console.log(' Commands:');
826
+ console.log(` ${bold('init')} Create .mobile-ai-agents/memory store`);
827
+ console.log(` ${bold('capture')} Append a memory event`);
828
+ console.log(` ${bold('status')} Show memory store status`);
829
+ console.log(` ${bold('search')} <query> Search captured memory`);
830
+ console.log(` ${bold('timeline')} Show recent memory events`);
831
+ console.log(` ${bold('inject')} Print compact context for a new AI session`);
832
+ console.log(` ${bold('checkpoint')} Generate MOBILE_MEMORY.md from memory events`);
833
+ console.log('');
834
+ console.log(' Capture options:');
835
+ console.log(' --type note | decision | progress | finding | qa | stage | next-action | code-state');
836
+ console.log(' --title Short title');
837
+ console.log(' --text Memory text');
838
+ console.log(' --file Read text from a file');
839
+ console.log(' --tags Comma-separated tags');
840
+ console.log(' --files Comma-separated related files');
841
+ console.log('');
842
+ console.log(' Examples:');
843
+ console.log(dim(' npx mobile-ai-agents memory init'));
844
+ console.log(dim(' npx mobile-ai-agents memory capture --type decision --title "Use Room" --text "Persist habits locally with Room."'));
845
+ console.log(dim(' git diff | npx mobile-ai-agents memory capture --type code-state --title "Current diff"'));
846
+ console.log(dim(' npx mobile-ai-agents memory search persistence'));
847
+ console.log(dim(' npx mobile-ai-agents memory checkpoint'));
848
+ console.log('');
849
+ return;
850
+ }
851
+
852
+ if (sub === 'init') {
853
+ ensureMemory();
854
+ const gitignorePath = path.join(process.cwd(), MEMORY_DIR, '.gitignore');
855
+ if (!fs.existsSync(gitignorePath)) {
856
+ fs.writeFileSync(gitignorePath, [
857
+ '# Mobile AI Agents local memory',
858
+ '# Keep raw event history local by default. Commit MOBILE_MEMORY.md when a handoff is useful.',
859
+ 'memory/events.jsonl',
860
+ ''
861
+ ].join('\n'));
862
+ }
863
+ console.log('');
864
+ ok(`${MEMORY_SUBDIR}/`);
865
+ ok(MEMORY_CONFIG_FILE);
866
+ ok(MEMORY_INDEX_FILE);
867
+ if (MEMORY_DIR === LEGACY_MEMORY_DIR) {
868
+ log(`Using existing legacy memory store at ${bold(LEGACY_MEMORY_DIR)}/`);
869
+ }
870
+ log(`Capture with ${bold('npx mobile-ai-agents memory capture --type decision --text "..."')}`);
871
+ console.log('');
872
+ return;
873
+ }
874
+
875
+ ensureMemory();
876
+ const config = loadMemoryConfig();
877
+ const eventsPath = path.join(process.cwd(), MEMORY_EVENTS_FILE);
878
+ const events = readJsonl(eventsPath);
879
+
880
+ if (sub === 'capture') {
881
+ const event = memoryEventFromOptions(opts);
882
+ appendJsonl(eventsPath, event);
883
+ const nextEvents = events.concat(event);
884
+ updateMemoryIndex(nextEvents);
885
+ console.log('');
886
+ ok(`Captured ${event.type}: ${event.title}`);
887
+ log(`Events: ${nextEvents.length}`);
888
+ console.log('');
889
+ return;
890
+ }
891
+
892
+ if (sub === 'status') {
893
+ console.log('');
894
+ console.log(bold('Mobile AI Agents Memory'));
895
+ console.log(` Project : ${bold(config.project || detectProjectName())}`);
896
+ console.log(` Platform: ${bold(config.platform || detectPlatform())}`);
897
+ console.log(` Events : ${bold(String(events.length))}`);
898
+ console.log(` Store : ${dim(path.join(process.cwd(), MEMORY_SUBDIR))}`);
899
+ if (events.length) {
900
+ console.log('');
901
+ console.log(bold('Recent'));
902
+ events.slice(-5).reverse().forEach(e => console.log(` - ${formatMemoryEvent(e)}`));
903
+ }
904
+ console.log('');
905
+ return;
906
+ }
907
+
908
+ if (sub === 'search') {
909
+ const query = opts._.join(' ').toLowerCase();
910
+ if (!query) throw new Error('memory search needs a query.');
911
+ const results = events.filter(e => JSON.stringify(e).toLowerCase().includes(query)).slice(-20).reverse();
912
+ console.log('');
913
+ console.log(bold(`Memory search: ${query}`));
914
+ if (!results.length) {
915
+ log('No matching memory events.');
916
+ } else {
917
+ results.forEach(e => {
918
+ console.log(`\n${bold(e.id)}`);
919
+ console.log(formatMemoryEvent(e, true));
920
+ });
921
+ }
922
+ console.log('');
923
+ return;
924
+ }
925
+
926
+ if (sub === 'timeline') {
927
+ const limit = Number(opts.limit || 20);
928
+ console.log('');
929
+ console.log(bold(`Memory timeline (${Math.min(limit, events.length)} of ${events.length})`));
930
+ events.slice(-limit).forEach(e => console.log(` - ${formatMemoryEvent(e)}`));
931
+ console.log('');
932
+ return;
933
+ }
934
+
935
+ if (sub === 'inject') {
936
+ const limit = Number(opts.limit || 8);
937
+ const recent = events.slice(-limit);
938
+ console.log(`# Mobile AI Agents Memory Context\n`);
939
+ console.log(`Project: ${config.project || detectProjectName()}`);
940
+ console.log(`Platform: ${config.platform || detectPlatform()}`);
941
+ console.log(`Events: ${events.length}\n`);
942
+ console.log('## Recent Memory');
943
+ if (!recent.length) {
944
+ console.log('- No memory events captured yet.');
945
+ } else {
946
+ recent.forEach(e => console.log(`- ${formatMemoryEvent(e)} — ${String(e.text || '').replace(/\r?\n/g, ' ').slice(0, 180)}`));
947
+ }
948
+ const next = [...events].reverse().find(e => e.type === 'next-action');
949
+ console.log('\n## Next Action');
950
+ console.log(next ? `- ${next.text}` : '- Not captured yet.');
951
+ console.log('\nUse this as context, then read exact files before editing.');
952
+ return;
953
+ }
954
+
955
+ if (sub === 'checkpoint') {
956
+ const out = buildMobileMemoryFromEvents(config, events);
957
+ fs.writeFileSync(path.join(process.cwd(), MOBILE_MEMORY_FILE), out);
958
+ updateMemoryIndex(events);
959
+ console.log('');
960
+ ok(`${MOBILE_MEMORY_FILE} generated from ${events.length} memory events`);
961
+ log(`Review before committing. Raw events stay local in ${MEMORY_EVENTS_FILE}.`);
962
+ console.log('');
963
+ return;
964
+ }
965
+
966
+ throw new Error(`Unknown memory command: ${sub}`);
967
+ }
968
+
969
+ function cmdList() {
970
+ const agentCount = Object.keys(AGENTS).length;
971
+ const skillCount = Object.keys(SKILLS).length;
972
+ const workflowCount = Object.keys(WORKFLOWS).length;
973
+
974
+ console.log('');
975
+ console.log(bold(`Mobile AI Agents — Agents (${agentCount}), Skills (${skillCount}), Workflows (${workflowCount})`));
976
+ console.log(dim(` github.com/${REPO}`));
977
+ console.log('');
978
+
979
+ console.log(bold(` AGENTS (${agentCount})`));
980
+ const agentsByPlatform = {};
981
+ for (const [name, meta] of Object.entries(AGENTS)) {
982
+ if (!agentsByPlatform[meta.platform]) agentsByPlatform[meta.platform] = [];
983
+ agentsByPlatform[meta.platform].push(name);
984
+ }
985
+ for (const [platform, names] of Object.entries(agentsByPlatform)) {
986
+ console.log(` ${yellow(platform.padEnd(10))} ${names.join(', ')}`);
987
+ }
988
+
989
+ console.log('');
990
+ console.log(bold(` SKILLS (${skillCount})`));
991
+ const skillsByPlatform = {};
992
+ for (const [name, meta] of Object.entries(SKILLS)) {
993
+ if (!skillsByPlatform[meta.platform]) skillsByPlatform[meta.platform] = [];
994
+ skillsByPlatform[meta.platform].push(name);
995
+ }
996
+ for (const [platform, names] of Object.entries(skillsByPlatform)) {
997
+ console.log(` ${yellow(platform.padEnd(10))} ${names.join(', ')}`);
998
+ }
999
+
1000
+ console.log('');
1001
+ console.log(bold(` WORKFLOWS (${workflowCount})`));
1002
+ console.log(` ${yellow('all'.padEnd(10))} ${Object.keys(WORKFLOWS).join(', ')}`);
1003
+
1004
+ console.log('');
1005
+ console.log(bold(' INSTALL'));
1006
+ console.log(dim(' npx mobile-ai-agents install # everything (claude)'));
1007
+ console.log(dim(' npx mobile-ai-agents install --platform android # android only'));
1008
+ console.log(dim(' npx mobile-ai-agents install --tool cursor # cursor only'));
1009
+ console.log(dim(' npx mobile-ai-agents install --tool windsurf # windsurf only'));
1010
+ console.log(dim(' npx mobile-ai-agents install --tool copilot # github copilot'));
1011
+ console.log(dim(' npx mobile-ai-agents install --tool codex # openai codex (AGENTS.md)'));
1012
+ console.log(dim(' npx mobile-ai-agents install --tool all # every tool'));
1013
+ console.log(dim(' npx mobile-ai-agents add agent crasher # one agent'));
1014
+ console.log(dim(' npx mobile-ai-agents add skill grill-mobile # one skill'));
1015
+ console.log(dim(' npx mobile-ai-agents add workflow feature-ship # one workflow'));
1016
+ console.log(dim(' npx mobile-ai-agents memory init # local Mobile Memory'));
1017
+ console.log('');
1018
+ }
1019
+
1020
+ function cmdHelp() {
1021
+ console.log('');
1022
+ console.log(bold(' npx mobile-ai-agents <command> [options]'));
1023
+ console.log('');
1024
+ console.log(' Commands:');
1025
+ console.log(` ${bold('install')} Install agents, skills, and workflows`);
1026
+ console.log(` ${bold('add')} agent|skill|workflow <name> Install a single item`);
1027
+ console.log(` ${bold('memory')} init|capture|search|... Local Mobile Memory store`);
1028
+ console.log(` ${bold('list')} List all available agents, skills, and workflows`);
1029
+ console.log(` ${bold('help')} Show this help`);
1030
+ console.log('');
1031
+ console.log(' Install options:');
1032
+ console.log(' --platform android | ios | flutter | rn | gaming | cross | all (default: all)');
1033
+ console.log(' --tool claude | cursor | windsurf | copilot | codex | all (default: claude)');
1034
+ console.log('');
1035
+ console.log(' What gets installed per tool:');
1036
+ console.log('');
1037
+ console.log(' claude Agents → ~/.claude/agents/<name>.md');
1038
+ console.log(' Agents → ~/.claude/commands/<name>.md (slash commands)');
1039
+ console.log(' Skills → ~/.claude/commands/<slug>.md (slash commands)');
1040
+ console.log(' Workflows→ ~/.claude/commands/<name>.md (slash commands, always)');
1041
+ console.log('');
1042
+ console.log(' cursor Each agent/skill/workflow → .cursor/rules/<name>.mdc');
1043
+ console.log('');
1044
+ console.log(' windsurf All files concatenated → .windsurfrules');
1045
+ console.log('');
1046
+ console.log(' copilot All agents + skills → .github/copilot-instructions.md');
1047
+ console.log('');
1048
+ console.log(' codex All agents/skills/workflows → AGENTS.md');
1049
+ console.log('');
1050
+ console.log(' Examples:');
1051
+ console.log(dim(' npx mobile-ai-agents install'));
1052
+ console.log(dim(' npx mobile-ai-agents install --platform android'));
1053
+ console.log(dim(' npx mobile-ai-agents install --platform ios --tool cursor'));
1054
+ console.log(dim(' npx mobile-ai-agents install --tool all'));
1055
+ console.log(dim(' npx mobile-ai-agents add agent crasher'));
1056
+ console.log(dim(' npx mobile-ai-agents add skill grill-mobile'));
1057
+ console.log(dim(' npx mobile-ai-agents add workflow feature-ship'));
1058
+ console.log(dim(' npx mobile-ai-agents memory init'));
1059
+ console.log(dim(' npx mobile-ai-agents memory capture --type decision --text "Use Room for offline persistence"'));
1060
+ console.log('');
1061
+ console.log(dim(` github.com/${REPO}`));
1062
+ console.log('');
1063
+ }
1064
+
1065
+ // ─── Main ────────────────────────────────────────────────────────────────────
1066
+
1067
+ async function main() {
1068
+ const [,, cmd, ...args] = process.argv;
1069
+
1070
+ try {
1071
+ switch (cmd) {
1072
+ case 'install': await cmdInstall(args); break;
1073
+ case 'add': await cmdAdd(args); break;
1074
+ case 'memory': cmdMemory(args); break;
1075
+ case 'list': cmdList(); break;
1076
+ case 'help':
1077
+ case '--help':
1078
+ case '-h':
1079
+ case undefined: cmdHelp(); break;
1080
+ default:
1081
+ console.error(`\n Unknown command: ${cmd}\n`);
1082
+ cmdHelp();
1083
+ process.exit(1);
1084
+ }
1085
+ } catch (e) {
1086
+ console.error(`\n Error: ${e.message}\n`);
1087
+ process.exit(1);
1088
+ }
1089
+ }
1090
+
1091
+ main();