aman-intelligence 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -0
  3. package/dist/bin/aman.d.ts +2 -0
  4. package/dist/bin/aman.js +165 -0
  5. package/dist/cli/global-install.d.ts +7 -0
  6. package/dist/cli/global-install.js +36 -0
  7. package/dist/cli/help-text.d.ts +1 -0
  8. package/dist/cli/help-text.js +62 -0
  9. package/dist/cli/version.d.ts +1 -0
  10. package/dist/cli/version.js +6 -0
  11. package/dist/commands/backup.d.ts +11 -0
  12. package/dist/commands/backup.js +262 -0
  13. package/dist/commands/browse.d.ts +11 -0
  14. package/dist/commands/browse.js +641 -0
  15. package/dist/commands/cache.d.ts +1 -0
  16. package/dist/commands/cache.js +38 -0
  17. package/dist/commands/config.d.ts +4 -0
  18. package/dist/commands/config.js +146 -0
  19. package/dist/commands/dashboard.d.ts +1 -0
  20. package/dist/commands/dashboard.js +1004 -0
  21. package/dist/commands/doctor.d.ts +4 -0
  22. package/dist/commands/doctor.js +54 -0
  23. package/dist/commands/export.d.ts +1 -0
  24. package/dist/commands/export.js +137 -0
  25. package/dist/commands/help.d.ts +1 -0
  26. package/dist/commands/help.js +47 -0
  27. package/dist/commands/import-wizard.d.ts +7 -0
  28. package/dist/commands/import-wizard.js +374 -0
  29. package/dist/commands/import.d.ts +9 -0
  30. package/dist/commands/import.js +351 -0
  31. package/dist/commands/info.d.ts +1 -0
  32. package/dist/commands/info.js +174 -0
  33. package/dist/commands/init.d.ts +20 -0
  34. package/dist/commands/init.js +146 -0
  35. package/dist/commands/install.d.ts +10 -0
  36. package/dist/commands/install.js +342 -0
  37. package/dist/commands/pack.d.ts +23 -0
  38. package/dist/commands/pack.js +331 -0
  39. package/dist/commands/registry.d.ts +6 -0
  40. package/dist/commands/registry.js +218 -0
  41. package/dist/commands/remove.d.ts +1 -0
  42. package/dist/commands/remove.js +76 -0
  43. package/dist/commands/search.d.ts +7 -0
  44. package/dist/commands/search.js +295 -0
  45. package/dist/commands/stack.d.ts +18 -0
  46. package/dist/commands/stack.js +327 -0
  47. package/dist/commands/sync.d.ts +9 -0
  48. package/dist/commands/sync.js +428 -0
  49. package/dist/commands/update.d.ts +1 -0
  50. package/dist/commands/update.js +97 -0
  51. package/dist/config/features.d.ts +2 -0
  52. package/dist/config/features.js +2 -0
  53. package/dist/config/index.d.ts +13 -0
  54. package/dist/config/index.js +80 -0
  55. package/dist/config/paths.d.ts +23 -0
  56. package/dist/config/paths.js +45 -0
  57. package/dist/import/adapters.d.ts +14 -0
  58. package/dist/import/adapters.js +580 -0
  59. package/dist/import/discovery.service.d.ts +8 -0
  60. package/dist/import/discovery.service.js +26 -0
  61. package/dist/import/import.service.d.ts +7 -0
  62. package/dist/import/import.service.js +259 -0
  63. package/dist/import/types.d.ts +71 -0
  64. package/dist/import/types.js +1 -0
  65. package/dist/import/utils.d.ts +36 -0
  66. package/dist/import/utils.js +428 -0
  67. package/dist/marketplace/cache.d.ts +18 -0
  68. package/dist/marketplace/cache.js +141 -0
  69. package/dist/marketplace/github-search.d.ts +17 -0
  70. package/dist/marketplace/github-search.js +268 -0
  71. package/dist/marketplace/install-from-candidate.d.ts +6 -0
  72. package/dist/marketplace/install-from-candidate.js +14 -0
  73. package/dist/marketplace/install.d.ts +15 -0
  74. package/dist/marketplace/install.js +54 -0
  75. package/dist/marketplace/metadata-validator.d.ts +8 -0
  76. package/dist/marketplace/metadata-validator.js +79 -0
  77. package/dist/marketplace/types.d.ts +34 -0
  78. package/dist/marketplace/types.js +1 -0
  79. package/dist/providers/local.provider.d.ts +9 -0
  80. package/dist/providers/local.provider.js +51 -0
  81. package/dist/providers/provider.interface.d.ts +7 -0
  82. package/dist/providers/provider.interface.js +1 -0
  83. package/dist/providers/registry.provider.d.ts +2 -0
  84. package/dist/providers/registry.provider.js +42 -0
  85. package/dist/providers/skills-sh.provider.d.ts +11 -0
  86. package/dist/providers/skills-sh.provider.js +56 -0
  87. package/dist/registry/adapter.interface.d.ts +16 -0
  88. package/dist/registry/adapter.interface.js +1 -0
  89. package/dist/registry/errors.d.ts +5 -0
  90. package/dist/registry/errors.js +8 -0
  91. package/dist/registry/filesystem-registry.adapter.d.ts +25 -0
  92. package/dist/registry/filesystem-registry.adapter.js +288 -0
  93. package/dist/registry/github-registry.adapter.d.ts +11 -0
  94. package/dist/registry/github-registry.adapter.js +32 -0
  95. package/dist/registry/index.d.ts +8 -0
  96. package/dist/registry/index.js +8 -0
  97. package/dist/registry/local-registry.adapter.d.ts +6 -0
  98. package/dist/registry/local-registry.adapter.js +9 -0
  99. package/dist/registry/registry.service.d.ts +44 -0
  100. package/dist/registry/registry.service.js +163 -0
  101. package/dist/registry/slug-utils.d.ts +12 -0
  102. package/dist/registry/slug-utils.js +51 -0
  103. package/dist/registry/types.d.ts +160 -0
  104. package/dist/registry/types.js +1 -0
  105. package/dist/services/asset.service.d.ts +12 -0
  106. package/dist/services/asset.service.js +142 -0
  107. package/dist/services/backup.service.d.ts +8 -0
  108. package/dist/services/backup.service.js +169 -0
  109. package/dist/services/classification.service.d.ts +31 -0
  110. package/dist/services/classification.service.js +271 -0
  111. package/dist/services/config.service.d.ts +9 -0
  112. package/dist/services/config.service.js +20 -0
  113. package/dist/services/doctor.service.d.ts +5 -0
  114. package/dist/services/doctor.service.js +186 -0
  115. package/dist/services/environment.service.d.ts +42 -0
  116. package/dist/services/environment.service.js +227 -0
  117. package/dist/services/github.service.d.ts +7 -0
  118. package/dist/services/github.service.js +42 -0
  119. package/dist/services/lock.service.d.ts +12 -0
  120. package/dist/services/lock.service.js +71 -0
  121. package/dist/services/marketplace.service.d.ts +40 -0
  122. package/dist/services/marketplace.service.js +225 -0
  123. package/dist/services/pack.service.d.ts +9 -0
  124. package/dist/services/pack.service.js +193 -0
  125. package/dist/services/stack.service.d.ts +9 -0
  126. package/dist/services/stack.service.js +94 -0
  127. package/dist/storage/asset-layout.d.ts +46 -0
  128. package/dist/storage/asset-layout.js +277 -0
  129. package/dist/storage/filesystem.d.ts +12 -0
  130. package/dist/storage/filesystem.js +113 -0
  131. package/dist/storage/scan-by-type.d.ts +2 -0
  132. package/dist/storage/scan-by-type.js +8 -0
  133. package/dist/storage/scanner.d.ts +11 -0
  134. package/dist/storage/scanner.js +188 -0
  135. package/dist/types/asset-metadata.d.ts +84 -0
  136. package/dist/types/asset-metadata.js +104 -0
  137. package/dist/types/index.d.ts +212 -0
  138. package/dist/types/index.js +1 -0
  139. package/dist/ui/animations/ErrorIndicator.d.ts +5 -0
  140. package/dist/ui/animations/ErrorIndicator.js +6 -0
  141. package/dist/ui/animations/GithubIndicator.d.ts +6 -0
  142. package/dist/ui/animations/GithubIndicator.js +9 -0
  143. package/dist/ui/animations/ProgressBar.d.ts +5 -0
  144. package/dist/ui/animations/ProgressBar.js +15 -0
  145. package/dist/ui/animations/Spinner.d.ts +5 -0
  146. package/dist/ui/animations/Spinner.js +21 -0
  147. package/dist/ui/animations/SuccessIndicator.d.ts +5 -0
  148. package/dist/ui/animations/SuccessIndicator.js +6 -0
  149. package/dist/ui/animations/SyncActivity.d.ts +5 -0
  150. package/dist/ui/animations/SyncActivity.js +21 -0
  151. package/dist/ui/animations/TransitionScreen.d.ts +7 -0
  152. package/dist/ui/animations/TransitionScreen.js +25 -0
  153. package/dist/ui/animations/useAnimationMode.d.ts +1 -0
  154. package/dist/ui/animations/useAnimationMode.js +16 -0
  155. package/dist/ui/assetDisplay.d.ts +19 -0
  156. package/dist/ui/assetDisplay.js +59 -0
  157. package/dist/ui/components/Confirm.d.ts +8 -0
  158. package/dist/ui/components/Confirm.js +14 -0
  159. package/dist/ui/components/CustomSelect.d.ts +19 -0
  160. package/dist/ui/components/CustomSelect.js +13 -0
  161. package/dist/ui/components/Header.d.ts +6 -0
  162. package/dist/ui/components/Header.js +9 -0
  163. package/dist/ui/components/HealthReport.d.ts +7 -0
  164. package/dist/ui/components/HealthReport.js +13 -0
  165. package/dist/ui/components/MarketplaceInstallConfirm.d.ts +19 -0
  166. package/dist/ui/components/MarketplaceInstallConfirm.js +23 -0
  167. package/dist/ui/components/Narrator.d.ts +9 -0
  168. package/dist/ui/components/Narrator.js +26 -0
  169. package/dist/ui/components/ScopePrompt.d.ts +8 -0
  170. package/dist/ui/components/ScopePrompt.js +23 -0
  171. package/dist/ui/components/TooSmallScreen.d.ts +8 -0
  172. package/dist/ui/components/TooSmallScreen.js +6 -0
  173. package/dist/ui/date.d.ts +2 -0
  174. package/dist/ui/date.js +33 -0
  175. package/dist/ui/layout.d.ts +23 -0
  176. package/dist/ui/layout.js +44 -0
  177. package/dist/ui/list-item.d.ts +12 -0
  178. package/dist/ui/list-item.js +1 -0
  179. package/dist/ui/marketplaceDisplay.d.ts +10 -0
  180. package/dist/ui/marketplaceDisplay.js +36 -0
  181. package/dist/ui/theme.d.ts +42 -0
  182. package/dist/ui/theme.js +47 -0
  183. package/dist/utils/asset-list-fields.d.ts +11 -0
  184. package/dist/utils/asset-list-fields.js +28 -0
  185. package/dist/utils/error-message.d.ts +2 -0
  186. package/dist/utils/error-message.js +6 -0
  187. package/dist/utils/integrity.d.ts +9 -0
  188. package/dist/utils/integrity.js +23 -0
  189. package/dist/utils/lock-migrate.d.ts +25 -0
  190. package/dist/utils/lock-migrate.js +93 -0
  191. package/dist/utils/mcp-local.d.ts +15 -0
  192. package/dist/utils/mcp-local.js +129 -0
  193. package/dist/utils/slug.d.ts +6 -0
  194. package/dist/utils/slug.js +13 -0
  195. package/dist/utils/stack-normalize.d.ts +3 -0
  196. package/dist/utils/stack-normalize.js +43 -0
  197. package/package.json +77 -0
@@ -0,0 +1,227 @@
1
+ import { execFileSync, spawnSync } from 'child_process';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { GLOBAL_CONFIG_DIR, GLOBAL_DIR, LOCAL_DIR } from '../config/paths.js';
5
+ import { configService } from './config.service.js';
6
+ import { copyDir, ensureDir, exists, writeJson } from '../storage/filesystem.js';
7
+ const ENVIRONMENT_DIRS = ['skills', 'prompts', 'mcps', 'stacks'];
8
+ function expandHome(inputPath) {
9
+ if (inputPath === '~')
10
+ return os.homedir();
11
+ if (inputPath.startsWith(`~${path.sep}`) || inputPath.startsWith('~/')) {
12
+ return path.join(os.homedir(), inputPath.slice(2));
13
+ }
14
+ return inputPath;
15
+ }
16
+ function repoSlugToDirName(repository) {
17
+ return repository.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'aman-environment';
18
+ }
19
+ export class EnvironmentService {
20
+ getActiveEnvironmentDir() {
21
+ return configService.get('environmentPath') || GLOBAL_DIR;
22
+ }
23
+ getStorage() {
24
+ return configService.get('storage') || { type: 'local' };
25
+ }
26
+ getProjectEnvironmentDir() {
27
+ return path.resolve(process.cwd(), LOCAL_DIR);
28
+ }
29
+ isEnvironmentInitialized(scope = 'global') {
30
+ const base = scope === 'project' ? this.getProjectEnvironmentDir() : this.getActiveEnvironmentDir();
31
+ return exists(path.join(base, 'aman.json'));
32
+ }
33
+ resolveStoragePath(storagePath) {
34
+ return path.resolve(expandHome(storagePath || GLOBAL_DIR));
35
+ }
36
+ isGithubCliAvailable() {
37
+ try {
38
+ this.addKnownGithubCliPaths();
39
+ execFileSync('gh', ['--version'], { stdio: 'ignore' });
40
+ return true;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ installGithubCli() {
47
+ if (this.isGithubCliAvailable())
48
+ return;
49
+ const attempts = this.githubCliInstallAttempts();
50
+ if (attempts.length === 0) {
51
+ throw new Error('Automatic GitHub CLI install is not supported on this operating system yet.');
52
+ }
53
+ for (const attempt of attempts) {
54
+ const result = spawnSync(attempt.command, attempt.args, { stdio: 'pipe' });
55
+ if (result.status === 0 && this.isGithubCliAvailable()) {
56
+ return;
57
+ }
58
+ }
59
+ throw new Error('Could not install GitHub CLI automatically.');
60
+ }
61
+ async ensureEnvironment(baseDir, storage) {
62
+ await ensureDir(baseDir);
63
+ for (const dir of ENVIRONMENT_DIRS) {
64
+ await ensureDir(path.join(baseDir, dir));
65
+ }
66
+ const manifestPath = path.join(baseDir, 'aman.json');
67
+ if (!exists(manifestPath)) {
68
+ const manifest = {
69
+ name: path.basename(baseDir) || 'aman-environment',
70
+ version: 1,
71
+ createdAt: new Date().toISOString(),
72
+ storage,
73
+ };
74
+ await writeJson(manifestPath, manifest);
75
+ }
76
+ }
77
+ async ensureProjectEnvironment() {
78
+ const projectDir = this.getProjectEnvironmentDir();
79
+ await this.ensureEnvironment(projectDir, { type: 'local' });
80
+ return projectDir;
81
+ }
82
+ async ensureActiveEnvironment() {
83
+ const environmentPath = this.getActiveEnvironmentDir();
84
+ await this.ensureEnvironment(environmentPath, this.getStorage());
85
+ return environmentPath;
86
+ }
87
+ async initLocal(options = {}) {
88
+ const environmentPath = this.resolveStoragePath(options.storagePath);
89
+ await this.ensureEnvironment(environmentPath, { type: 'local' });
90
+ await ensureDir(GLOBAL_CONFIG_DIR);
91
+ configService.set('environmentPath', environmentPath);
92
+ configService.set('storage', { type: 'local' });
93
+ return environmentPath;
94
+ }
95
+ async initGithub(options) {
96
+ this.ensureGhAvailable();
97
+ this.ensureGhAuthenticated();
98
+ const repoDir = path.join(GLOBAL_DIR, 'repositories', repoSlugToDirName(options.repository));
99
+ await ensureDir(path.dirname(repoDir));
100
+ if (options.mode === 'existing') {
101
+ await this.cloneRepository(options.repository, repoDir);
102
+ await this.ensureEnvironment(repoDir, { type: 'github', repository: options.repository });
103
+ this.gitCommitAndPush(repoDir, 'Standardize Aman environment');
104
+ }
105
+ else {
106
+ await this.ensureEnvironment(repoDir, { type: 'github', repository: options.repository });
107
+ this.gitInit(repoDir);
108
+ this.gitCommit(repoDir, 'Initial Aman environment');
109
+ this.createAndPushRepository(options.repository, repoDir);
110
+ }
111
+ await ensureDir(GLOBAL_CONFIG_DIR);
112
+ configService.set('environmentPath', repoDir);
113
+ configService.set('storage', { type: 'github', repository: options.repository });
114
+ return repoDir;
115
+ }
116
+ async importStandardized(sourceDir, targetDir) {
117
+ await this.ensureEnvironment(targetDir, { type: 'local' });
118
+ for (const dir of ENVIRONMENT_DIRS) {
119
+ const src = path.join(sourceDir, dir);
120
+ if (exists(src)) {
121
+ await copyDir(src, path.join(targetDir, dir));
122
+ }
123
+ }
124
+ }
125
+ ensureGhAvailable() {
126
+ if (!this.isGithubCliAvailable()) {
127
+ throw new Error('GitHub CLI not found.');
128
+ }
129
+ }
130
+ addKnownGithubCliPaths() {
131
+ if (process.platform !== 'win32')
132
+ return;
133
+ const pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === 'path') || 'Path';
134
+ const currentPath = process.env[pathKey] || '';
135
+ const candidates = [
136
+ process.env.ProgramFiles ? path.join(process.env.ProgramFiles, 'GitHub CLI') : undefined,
137
+ process.env['ProgramFiles(x86)'] ? path.join(process.env['ProgramFiles(x86)'], 'GitHub CLI') : undefined,
138
+ process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'GitHub CLI') : undefined,
139
+ ].filter((candidate) => Boolean(candidate));
140
+ for (const candidate of candidates) {
141
+ if (!exists(path.join(candidate, 'gh.exe')))
142
+ continue;
143
+ if (currentPath.toLowerCase().split(';').includes(candidate.toLowerCase()))
144
+ continue;
145
+ process.env[pathKey] = `${candidate};${currentPath}`;
146
+ return;
147
+ }
148
+ }
149
+ githubCliInstallAttempts() {
150
+ if (process.platform === 'win32') {
151
+ return [
152
+ {
153
+ command: 'winget',
154
+ args: [
155
+ 'install',
156
+ '--id',
157
+ 'GitHub.cli',
158
+ '-e',
159
+ '--source',
160
+ 'winget',
161
+ '--accept-package-agreements',
162
+ '--accept-source-agreements',
163
+ ],
164
+ },
165
+ { command: 'choco', args: ['install', 'gh', '-y'] },
166
+ { command: 'scoop', args: ['install', 'gh'] },
167
+ ];
168
+ }
169
+ if (process.platform === 'darwin') {
170
+ return [{ command: 'brew', args: ['install', 'gh'] }];
171
+ }
172
+ if (process.platform === 'linux') {
173
+ return [
174
+ { command: 'sh', args: ['-lc', 'command -v apt-get >/dev/null && sudo apt-get update && sudo apt-get install -y gh'] },
175
+ { command: 'sh', args: ['-lc', 'command -v dnf >/dev/null && sudo dnf install -y gh'] },
176
+ { command: 'sh', args: ['-lc', 'command -v yum >/dev/null && sudo yum install -y gh'] },
177
+ { command: 'sh', args: ['-lc', 'command -v pacman >/dev/null && sudo pacman -S --noconfirm github-cli'] },
178
+ ];
179
+ }
180
+ return [];
181
+ }
182
+ ensureGhAuthenticated() {
183
+ const status = spawnSync('gh', ['auth', 'status'], { stdio: 'ignore' });
184
+ if (status.status === 0)
185
+ return;
186
+ const login = spawnSync('gh', ['auth', 'login'], { stdio: 'inherit' });
187
+ if (login.status !== 0) {
188
+ throw new Error('GitHub authentication was not completed.');
189
+ }
190
+ }
191
+ cloneRepository(repository, repoDir) {
192
+ if (exists(repoDir))
193
+ return;
194
+ const result = spawnSync('gh', ['repo', 'clone', repository, repoDir], { stdio: 'pipe' });
195
+ if (result.status !== 0) {
196
+ const errDetail = result.stderr?.toString().trim();
197
+ throw new Error(`Could not clone ${repository}. Check the repository name and your GitHub access.${errDetail ? ` Details: ${errDetail}` : ''}`);
198
+ }
199
+ }
200
+ gitInit(repoDir) {
201
+ if (exists(path.join(repoDir, '.git')))
202
+ return;
203
+ execFileSync('git', ['-C', repoDir, 'init'], { stdio: 'ignore' });
204
+ }
205
+ gitCommit(repoDir, message) {
206
+ execFileSync('git', ['-C', repoDir, 'add', '.'], { stdio: 'ignore' });
207
+ execFileSync('git', ['-C', repoDir, 'commit', '-m', message], { stdio: 'ignore' });
208
+ }
209
+ gitCommitAndPush(repoDir, message) {
210
+ try {
211
+ execFileSync('git', ['-C', repoDir, 'add', '.'], { stdio: 'ignore' });
212
+ execFileSync('git', ['-C', repoDir, 'diff', '--cached', '--quiet'], { stdio: 'ignore' });
213
+ }
214
+ catch {
215
+ this.gitCommit(repoDir, message);
216
+ execFileSync('git', ['-C', repoDir, 'push'], { stdio: 'ignore' });
217
+ }
218
+ }
219
+ createAndPushRepository(repository, repoDir) {
220
+ const result = spawnSync('gh', ['repo', 'create', repository, '--private', '--source', repoDir, '--remote', 'origin', '--push'], { stdio: 'pipe' });
221
+ if (result.status !== 0) {
222
+ const errDetail = result.stderr?.toString().trim();
223
+ throw new Error(`Could not create GitHub repository ${repository}.${errDetail ? ` Details: ${errDetail}` : ''}`);
224
+ }
225
+ }
226
+ }
227
+ export const environmentService = new EnvironmentService();
@@ -0,0 +1,7 @@
1
+ export declare class GithubService {
2
+ /**
3
+ * Imports a repository from GitHub or a local folder to a temporary directory.
4
+ */
5
+ import(source: string): Promise<string>;
6
+ }
7
+ export declare const githubService: GithubService;
@@ -0,0 +1,42 @@
1
+ import { spawnSync } from 'child_process';
2
+ import { exists, ensureDir, removeDir } from '../storage/filesystem.js';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ export class GithubService {
6
+ /**
7
+ * Imports a repository from GitHub or a local folder to a temporary directory.
8
+ */
9
+ async import(source) {
10
+ const tempDir = path.join(os.tmpdir(), `aman-import-${Date.now()}`);
11
+ await ensureDir(tempDir);
12
+ let cloneUrl = source;
13
+ // Handle local folder
14
+ if (source.startsWith('.') || path.isAbsolute(source) || exists(source)) {
15
+ const srcPath = path.resolve(process.cwd(), source);
16
+ if (!exists(srcPath)) {
17
+ throw new Error(`Local path does not exist: ${srcPath}`);
18
+ }
19
+ const { copyDir } = await import('../storage/filesystem.js');
20
+ await copyDir(srcPath, tempDir);
21
+ return tempDir;
22
+ }
23
+ // Handle user/repo format
24
+ if (!source.startsWith('http') && source.includes('/')) {
25
+ cloneUrl = `https://github.com/${source}.git`;
26
+ }
27
+ try {
28
+ const result = spawnSync('git', ['clone', '--depth', '1', cloneUrl, tempDir], { stdio: 'ignore' });
29
+ if (result.status !== 0) {
30
+ throw new Error('clone failed');
31
+ }
32
+ // Remove .git directory so it's not detected as part of the assets
33
+ await removeDir(path.join(tempDir, '.git'));
34
+ return tempDir;
35
+ }
36
+ catch {
37
+ await removeDir(tempDir);
38
+ throw new Error(`Failed to clone ${cloneUrl}. Is git installed? Does the repo exist?`);
39
+ }
40
+ }
41
+ }
42
+ export const githubService = new GithubService();
@@ -0,0 +1,12 @@
1
+ import { Lockfile, LockEntry, AssetType, Scope } from '../types/index.js';
2
+ export declare class LockService {
3
+ private getLockfilePath;
4
+ private scopeRoot;
5
+ read(scope: Scope): Promise<Lockfile>;
6
+ private quarantineCorruptFile;
7
+ write(scope: Scope, lockfile: Lockfile): Promise<void>;
8
+ getEntries(lockfile: Lockfile): LockEntry[];
9
+ addEntry(scope: Scope, entry: LockEntry): Promise<void>;
10
+ removeEntry(scope: Scope, localName: string, type: AssetType): Promise<void>;
11
+ }
12
+ export declare const lockService: LockService;
@@ -0,0 +1,71 @@
1
+ import path from 'path';
2
+ import { LOCAL_LOCKFILE } from '../config/paths.js';
3
+ import { exists, readJson, writeJson } from '../storage/filesystem.js';
4
+ import { promises as fs } from 'fs';
5
+ import { environmentService } from './environment.service.js';
6
+ import { isLegacyLockfile, normalizeLockfile } from '../utils/lock-migrate.js';
7
+ import { migrateScopeLayout } from '../storage/asset-layout.js';
8
+ import { LOCAL_DIR } from '../config/paths.js';
9
+ export class LockService {
10
+ async getLockfilePath(scope) {
11
+ return scope === 'global' ? path.join(environmentService.getActiveEnvironmentDir(), 'aman.lock') : LOCAL_LOCKFILE;
12
+ }
13
+ scopeRoot(scope) {
14
+ return scope === 'global' ? environmentService.getActiveEnvironmentDir() : LOCAL_DIR;
15
+ }
16
+ async read(scope) {
17
+ const lockPath = await this.getLockfilePath(scope);
18
+ await migrateScopeLayout(this.scopeRoot(scope));
19
+ let raw = await readJson(lockPath);
20
+ if (raw === null && exists(lockPath)) {
21
+ await this.quarantineCorruptFile(lockPath, 'lock');
22
+ raw = null;
23
+ }
24
+ const lockfile = normalizeLockfile(raw, scope);
25
+ if (raw && isLegacyLockfile(raw)) {
26
+ await writeJson(lockPath, lockfile);
27
+ }
28
+ return lockfile;
29
+ }
30
+ async quarantineCorruptFile(filePath, label) {
31
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
32
+ const backupPath = `${filePath}.corrupt-${stamp}`;
33
+ try {
34
+ await fs.rename(filePath, backupPath);
35
+ console.warn(` Warning: Corrupt ${label} file quarantined to ${backupPath}`);
36
+ }
37
+ catch {
38
+ // If rename fails, leave file in place; normalizeLockfile still returns empty
39
+ }
40
+ }
41
+ async write(scope, lockfile) {
42
+ await migrateScopeLayout(this.scopeRoot(scope));
43
+ const lockPath = await this.getLockfilePath(scope);
44
+ await writeJson(lockPath, {
45
+ ...lockfile,
46
+ schemaVersion: 1,
47
+ scope,
48
+ generatedAt: new Date().toISOString(),
49
+ });
50
+ }
51
+ getEntries(lockfile) {
52
+ return lockfile.assets;
53
+ }
54
+ async addEntry(scope, entry) {
55
+ const lockfile = await this.read(scope);
56
+ const existingIndex = lockfile.assets.findIndex((e) => e.type === entry.type && e.localName === entry.localName);
57
+ if (existingIndex >= 0) {
58
+ lockfile.assets[existingIndex] = { ...entry, scope };
59
+ }
60
+ else {
61
+ lockfile.assets.push({ ...entry, scope });
62
+ }
63
+ await this.write(scope, lockfile);
64
+ }
65
+ async removeEntry(scope, localName, type) {
66
+ const lockfile = await this.read(scope);
67
+ lockfile.assets = lockfile.assets.filter((e) => !(e.type === type && e.localName === localName));
68
+ await this.write(scope, lockfile);
69
+ }
70
+ }
71
+ export const lockService = new LockService();
@@ -0,0 +1,40 @@
1
+ import { AssetType, ProviderResult } from '../types/index.js';
2
+ import { MarketplaceAsset, MarketplaceSearchResult } from '../marketplace/types.js';
3
+ export interface InstallCandidate {
4
+ name: string;
5
+ type: AssetType;
6
+ source: string;
7
+ sources?: string[];
8
+ sourcePath: string;
9
+ description?: string;
10
+ tags?: string[];
11
+ category?: string;
12
+ installs?: number;
13
+ rating?: number;
14
+ updated?: string;
15
+ version?: string;
16
+ organization?: string;
17
+ installed?: boolean;
18
+ slug?: string;
19
+ checksum?: string;
20
+ verified?: boolean;
21
+ githubSource?: string;
22
+ marketplaceAsset?: MarketplaceAsset;
23
+ }
24
+ export declare class MarketplaceService {
25
+ private providers;
26
+ listProviders(): string[];
27
+ search(query: string, type?: AssetType): Promise<ProviderResult[]>;
28
+ searchGitHubMarketplace(_query: string, _type?: AssetType): Promise<MarketplaceSearchResult>;
29
+ loadGitHubMarketplaceCatalog(options?: {
30
+ query?: string;
31
+ typeFilter?: 'all' | AssetType;
32
+ }): Promise<MarketplaceSearchResult>;
33
+ private marketplaceAssetToResult;
34
+ findInstallCandidate(name: string): Promise<InstallCandidate | null>;
35
+ private mergeResults;
36
+ private normalizeName;
37
+ private uniqueSources;
38
+ private uniqueTags;
39
+ }
40
+ export declare const marketplaceService: MarketplaceService;
@@ -0,0 +1,225 @@
1
+ import { skillsShProvider } from '../providers/skills-sh.provider.js';
2
+ import { localProvider } from '../providers/local.provider.js';
3
+ import { registryProvider } from '../providers/registry.provider.js';
4
+ import { ASSET_TAB_ORDER } from '../ui/assetDisplay.js';
5
+ import { findMarketplaceAsset, indexMarketplaceAssets, loadMarketplaceIndex, searchGitHubMarketplace, } from '../marketplace/github-search.js';
6
+ import { MARKETPLACE_ENABLED } from '../config/features.js';
7
+ const EMPTY_MARKETPLACE_RESULT = {
8
+ assets: [],
9
+ fromCache: false,
10
+ cacheAgeMinutes: null,
11
+ rateLimited: false,
12
+ offline: false,
13
+ };
14
+ export class MarketplaceService {
15
+ providers = [registryProvider, skillsShProvider, localProvider];
16
+ listProviders() {
17
+ return this.providers.map((provider) => provider.name);
18
+ }
19
+ async search(query, type) {
20
+ const localResults = [];
21
+ for (const provider of this.providers) {
22
+ if (!(await provider.available()))
23
+ continue;
24
+ const results = await provider.search(query, type);
25
+ for (const r of results) {
26
+ localResults.push({ ...r, section: 'local' });
27
+ }
28
+ }
29
+ const mergedLocal = this.mergeResults(localResults);
30
+ if (!MARKETPLACE_ENABLED) {
31
+ return mergedLocal;
32
+ }
33
+ const gh = await searchGitHubMarketplace({
34
+ query,
35
+ typeFilter: type ?? 'all',
36
+ });
37
+ indexMarketplaceAssets(gh.assets);
38
+ let marketplace = gh.assets.map((a) => this.marketplaceAssetToResult(a));
39
+ if (type) {
40
+ marketplace = marketplace.filter((r) => r.type === type);
41
+ }
42
+ return [...mergedLocal, ...marketplace];
43
+ }
44
+ async searchGitHubMarketplace(_query, _type) {
45
+ if (!MARKETPLACE_ENABLED)
46
+ return { ...EMPTY_MARKETPLACE_RESULT };
47
+ return searchGitHubMarketplace({ query: _query, typeFilter: _type ?? 'all' });
48
+ }
49
+ async loadGitHubMarketplaceCatalog(options) {
50
+ if (!MARKETPLACE_ENABLED)
51
+ return { ...EMPTY_MARKETPLACE_RESULT };
52
+ return loadMarketplaceIndex({
53
+ query: options?.query ?? '',
54
+ typeFilter: options?.typeFilter ?? 'all',
55
+ });
56
+ }
57
+ marketplaceAssetToResult(asset) {
58
+ return {
59
+ type: asset.type,
60
+ name: asset.localName,
61
+ slug: asset.slug,
62
+ source: 'Marketplace',
63
+ sources: ['marketplace'],
64
+ description: asset.description,
65
+ tags: asset.tags,
66
+ version: asset.version,
67
+ organization: asset.author,
68
+ stars: asset.stars,
69
+ verified: asset.verified,
70
+ section: 'marketplace',
71
+ checksum: asset.checksum,
72
+ githubSource: asset.source,
73
+ confidence: 0.95,
74
+ installed: false,
75
+ };
76
+ }
77
+ async findInstallCandidate(name) {
78
+ const trimmed = name.trim();
79
+ if (MARKETPLACE_ENABLED && trimmed.startsWith('@')) {
80
+ await loadMarketplaceIndex({ query: '' });
81
+ const gh = findMarketplaceAsset(trimmed);
82
+ if (gh) {
83
+ return {
84
+ name: gh.localName,
85
+ type: gh.type,
86
+ source: 'marketplace',
87
+ sources: ['marketplace'],
88
+ sourcePath: '',
89
+ description: gh.description,
90
+ tags: gh.tags,
91
+ version: gh.version,
92
+ organization: gh.author,
93
+ slug: gh.slug,
94
+ checksum: gh.checksum,
95
+ verified: gh.verified,
96
+ githubSource: gh.source,
97
+ marketplaceAsset: gh,
98
+ };
99
+ }
100
+ }
101
+ const types = ASSET_TAB_ORDER;
102
+ const requestedName = this.normalizeName(name);
103
+ for (const type of types) {
104
+ const mergedMatches = await this.search(name, type);
105
+ const exact = mergedMatches.find((item) => this.normalizeName(item.name) === requestedName);
106
+ if (!exact)
107
+ continue;
108
+ for (const provider of this.providers) {
109
+ if (!(await provider.available()))
110
+ continue;
111
+ const providerMatches = await provider.search(exact.name, type);
112
+ const providerExact = providerMatches.find((item) => this.normalizeName(item.name) === this.normalizeName(exact.name));
113
+ if (!providerExact)
114
+ continue;
115
+ if (exact.section === 'marketplace') {
116
+ await loadMarketplaceIndex({ query: '' });
117
+ const gh = findMarketplaceAsset(exact.slug ?? exact.name) ??
118
+ findMarketplaceAsset(exact.name);
119
+ if (gh) {
120
+ return {
121
+ name: gh.localName,
122
+ type: gh.type,
123
+ source: 'marketplace',
124
+ sources: ['marketplace'],
125
+ sourcePath: '',
126
+ description: gh.description,
127
+ tags: gh.tags,
128
+ version: gh.version,
129
+ organization: gh.author,
130
+ slug: gh.slug,
131
+ checksum: gh.checksum,
132
+ verified: gh.verified,
133
+ githubSource: gh.source,
134
+ marketplaceAsset: gh,
135
+ };
136
+ }
137
+ }
138
+ return {
139
+ name: exact.name,
140
+ type: exact.type,
141
+ source: provider.name,
142
+ sources: exact.sources,
143
+ sourcePath: await provider.fetch(exact.name, exact.type),
144
+ description: exact.description,
145
+ tags: exact.tags,
146
+ category: exact.category,
147
+ installs: exact.installs,
148
+ rating: exact.rating,
149
+ updated: exact.updated,
150
+ version: exact.version,
151
+ organization: exact.organization,
152
+ installed: exact.installed,
153
+ slug: exact.slug,
154
+ checksum: exact.checksum,
155
+ verified: exact.verified,
156
+ githubSource: exact.githubSource,
157
+ };
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ mergeResults(results) {
163
+ const merged = new Map();
164
+ for (const result of results) {
165
+ const key = `${result.type}:${this.normalizeName(result.name)}`;
166
+ const existing = merged.get(key);
167
+ if (!existing) {
168
+ const sources = this.uniqueSources(result.sources || [result.source]);
169
+ merged.set(key, {
170
+ ...result,
171
+ sources,
172
+ source: sources.join(' + '),
173
+ tags: this.uniqueTags(result.tags),
174
+ installed: Boolean(result.installed || sources.includes('installed')),
175
+ });
176
+ continue;
177
+ }
178
+ const sources = this.uniqueSources([
179
+ ...(existing.sources || [existing.source]),
180
+ ...(result.sources || [result.source]),
181
+ ]);
182
+ merged.set(key, {
183
+ ...existing,
184
+ source: sources.join(' + '),
185
+ sources,
186
+ description: existing.description || result.description,
187
+ tags: this.uniqueTags([...(existing.tags || []), ...(result.tags || [])]),
188
+ category: existing.category || result.category,
189
+ installs: existing.installs ?? result.installs,
190
+ rating: existing.rating ?? result.rating,
191
+ updated: existing.updated || result.updated,
192
+ version: existing.version || result.version,
193
+ organization: existing.organization || result.organization,
194
+ installed: Boolean(existing.installed || result.installed || sources.includes('installed')),
195
+ confidence: Math.max(existing.confidence, result.confidence),
196
+ });
197
+ }
198
+ return Array.from(merged.values()).sort((a, b) => a.name.localeCompare(b.name));
199
+ }
200
+ normalizeName(value) {
201
+ return value.trim().toLowerCase().replace(/[\s_]+/g, '-');
202
+ }
203
+ uniqueSources(sources) {
204
+ const priority = ['installed', 'registry', 'skills.sh', 'bundled', 'local'];
205
+ const unique = Array.from(new Set(sources.filter(Boolean)));
206
+ return unique.sort((a, b) => {
207
+ const aIndex = priority.indexOf(a);
208
+ const bIndex = priority.indexOf(b);
209
+ if (aIndex === -1 && bIndex === -1)
210
+ return a.localeCompare(b);
211
+ if (aIndex === -1)
212
+ return 1;
213
+ if (bIndex === -1)
214
+ return -1;
215
+ return aIndex - bIndex;
216
+ });
217
+ }
218
+ uniqueTags(tags) {
219
+ if (!tags)
220
+ return undefined;
221
+ const unique = Array.from(new Set(tags.filter((tag) => tag.trim().length > 0)));
222
+ return unique.length > 0 ? unique : undefined;
223
+ }
224
+ }
225
+ export const marketplaceService = new MarketplaceService();
@@ -0,0 +1,9 @@
1
+ import { Pack, Scope } from '../types/index.js';
2
+ export declare class PackService {
3
+ create(name: string, packData: Pack, outputPath: string): Promise<void>;
4
+ install(packPath: string, scope: Scope, onProgress?: (progress: number) => void): Promise<void>;
5
+ inspect(packPath: string): Promise<Pack>;
6
+ private walk;
7
+ private extractPack;
8
+ }
9
+ export declare const packService: PackService;