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,641 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import { render, Box, Text, useApp, useInput } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import { assetService } from '../services/asset.service.js';
6
+ import { marketplaceService } from '../services/marketplace.service.js';
7
+ import { assetListDescription, assetListOriginalName, } from '../utils/asset-list-fields.js';
8
+ import { Header } from '../ui/components/Header.js';
9
+ import { Narrator } from '../ui/components/Narrator.js';
10
+ import { ScopePrompt } from '../ui/components/ScopePrompt.js';
11
+ import { theme } from '../ui/theme.js';
12
+ import { useResponsiveLayout, MIN_COLUMNS, MIN_ROWS, PLAIN_OUTPUT_MAX_COLUMNS } from '../ui/layout.js';
13
+ import { verifiedBadge, shortDescription } from '../ui/marketplaceDisplay.js';
14
+ import { installFromCandidate } from '../marketplace/install-from-candidate.js';
15
+ import { MarketplaceInstallConfirm } from '../ui/components/MarketplaceInstallConfirm.js';
16
+ import { MARKETPLACE_ENABLED } from '../config/features.js';
17
+ import { TooSmallScreen } from '../ui/components/TooSmallScreen.js';
18
+ import { TransitionScreen } from '../ui/animations/TransitionScreen.js';
19
+ import { titleize } from '../ui/marketplaceDisplay.js';
20
+ import { ASSET_TYPE_PLURAL, ASSET_TAB_ORDER, assetTypeBadge } from '../ui/assetDisplay.js';
21
+ import { CustomSelectInput } from '../ui/components/CustomSelect.js';
22
+ const viewTabs = MARKETPLACE_ENABLED ? ['Local', 'Marketplace'] : ['Local'];
23
+ const MARKETPLACE_PAGE_SIZE = 20;
24
+ const subFilters = ['All', 'Project', 'Global'];
25
+ function scopeLabel(scope) {
26
+ return scope === 'project' ? 'project' : 'global';
27
+ }
28
+ function assetTypeIndex(type) {
29
+ return ASSET_TAB_ORDER.indexOf(type);
30
+ }
31
+ export const BrowseApp = ({ onBack, initialAssetType = 'mcp', initialView = MARKETPLACE_ENABLED ? 'marketplace' : 'local', }) => {
32
+ const { exit } = useApp();
33
+ const handleExit = () => {
34
+ if (onBack)
35
+ onBack();
36
+ else
37
+ exit();
38
+ };
39
+ const { rows, isTooSmall, isCompact, canShowDescriptions, physicalColumns, physicalRows } = useResponsiveLayout();
40
+ const pageSize = Math.max(1, rows - (canShowDescriptions ? 16 : 11));
41
+ const [activeAssetType, setActiveAssetType] = useState(initialAssetType);
42
+ const [activeView, setActiveView] = useState(initialView === 'installed'
43
+ ? 'Local'
44
+ : initialView === 'marketplace' && MARKETPLACE_ENABLED
45
+ ? 'Marketplace'
46
+ : 'Local');
47
+ const [marketplacePage, setMarketplacePage] = useState(0);
48
+ const [marketplaceSort, setMarketplaceSort] = useState('stars');
49
+ const [marketplaceNotice, setMarketplaceNotice] = useState();
50
+ const [detailAsset, setDetailAsset] = useState(null);
51
+ const [localDetailItem, setLocalDetailItem] = useState(null);
52
+ const [installCandidate, setInstallCandidate] = useState(null);
53
+ const [activeFilter, setActiveFilter] = useState(0);
54
+ const [items, setItems] = useState([]);
55
+ const [cursor, setCursor] = useState(0);
56
+ const [selected, setSelected] = useState(new Set());
57
+ const [refreshCount, setRefreshCount] = useState(0);
58
+ const [mode, setMode] = useState('loading');
59
+ const [mochiState, setMochiState] = useState('searching');
60
+ const [message, setMessage] = useState('Loading...');
61
+ const [searchQuery, setSearchQuery] = useState('');
62
+ const [focusedSection, setFocusedSection] = useState('search');
63
+ const isMarketplace = MARKETPLACE_ENABLED && activeView === 'Marketplace';
64
+ function truncateDescription(text) {
65
+ if (!text)
66
+ return undefined;
67
+ if (physicalColumns <= 70) {
68
+ const max = 40;
69
+ return text.length <= max ? text : `${text.slice(0, max - 3)}...`;
70
+ }
71
+ return shortDescription(text);
72
+ }
73
+ async function loadMarketplaceItems(query) {
74
+ const catalog = await marketplaceService.loadGitHubMarketplaceCatalog({
75
+ query,
76
+ typeFilter: activeAssetType,
77
+ });
78
+ if (catalog.message) {
79
+ setMarketplaceNotice(catalog.fromCache && catalog.cacheAgeMinutes !== null
80
+ ? `${catalog.message} Results from ${catalog.cacheAgeMinutes} minutes ago.`
81
+ : catalog.message);
82
+ }
83
+ else if (catalog.fromCache && catalog.cacheAgeMinutes !== null) {
84
+ setMarketplaceNotice(`Results from ${catalog.cacheAgeMinutes} minutes ago.`);
85
+ }
86
+ else {
87
+ setMarketplaceNotice(undefined);
88
+ }
89
+ const globalAssets = await assetService.list(activeAssetType, 'global');
90
+ let projectAssets = [];
91
+ try {
92
+ projectAssets = await assetService.list(activeAssetType, 'project');
93
+ }
94
+ catch {
95
+ // no project env
96
+ }
97
+ const installedNames = new Set([
98
+ ...globalAssets.map((s) => s.name),
99
+ ...projectAssets.map((s) => s.name),
100
+ ]);
101
+ const sorted = [...catalog.assets].sort((a, b) => {
102
+ if (marketplaceSort === 'stars')
103
+ return b.stars - a.stars || a.name.localeCompare(b.name);
104
+ if (marketplaceSort === 'recent') {
105
+ const ta = a.updatedAt ? Date.parse(a.updatedAt) : 0;
106
+ const tb = b.updatedAt ? Date.parse(b.updatedAt) : 0;
107
+ return tb - ta || b.stars - a.stars;
108
+ }
109
+ return a.name.localeCompare(b.name);
110
+ });
111
+ return sorted.map((asset) => ({
112
+ label: `${titleize(asset.localName)}${verifiedBadge(asset.verified)}`,
113
+ value: asset.slug,
114
+ description: truncateDescription(asset.description),
115
+ metadata: [
116
+ asset.type,
117
+ asset.author,
118
+ physicalColumns > 70 ? `★${asset.stars}` : '',
119
+ `v${asset.version}`,
120
+ ].filter(Boolean),
121
+ status: installedNames.has(asset.localName) ? 'installed' : undefined,
122
+ marketplaceAsset: asset,
123
+ }));
124
+ }
125
+ async function loadInstalledItems() {
126
+ const globalAssets = await assetService.list(activeAssetType, 'global');
127
+ let projectAssets = [];
128
+ try {
129
+ projectAssets = await assetService.list(activeAssetType, 'project');
130
+ }
131
+ catch {
132
+ // fine
133
+ }
134
+ const merged = new Map();
135
+ const seenBaseNames = new Set();
136
+ const getBaseName = (name, originalName) => (originalName || name)
137
+ .replace(/[-_\s]+\d+$/, '')
138
+ .toLowerCase()
139
+ .replace(/[-_\s]+/g, ' ')
140
+ .trim();
141
+ for (const item of globalAssets) {
142
+ const baseName = getBaseName(item.name, assetListOriginalName(item));
143
+ if (seenBaseNames.has(baseName))
144
+ continue;
145
+ seenBaseNames.add(baseName);
146
+ merged.set(item.name, {
147
+ label: titleize(baseName),
148
+ value: item.name,
149
+ description: shortDescription(assetListDescription(item)),
150
+ badge: 'global',
151
+ metadata: ['global'],
152
+ status: item.source === 'installed' ? 'installed' : undefined,
153
+ rawAsset: item,
154
+ scope: 'global',
155
+ });
156
+ }
157
+ for (const item of projectAssets) {
158
+ const baseName = getBaseName(item.name, assetListOriginalName(item));
159
+ if (seenBaseNames.has(baseName)) {
160
+ let foundKey = '';
161
+ for (const [k] of merged.entries()) {
162
+ const globalItem = globalAssets.find((s) => s.name === k);
163
+ const kBase = getBaseName(k, globalItem ? assetListOriginalName(globalItem) : undefined);
164
+ if (kBase === baseName) {
165
+ foundKey = k;
166
+ break;
167
+ }
168
+ }
169
+ if (foundKey) {
170
+ merged.set(foundKey, {
171
+ ...merged.get(foundKey),
172
+ badge: 'project',
173
+ metadata: ['project'],
174
+ status: 'installed',
175
+ rawAsset: item,
176
+ scope: 'project',
177
+ });
178
+ }
179
+ continue;
180
+ }
181
+ seenBaseNames.add(baseName);
182
+ merged.set(item.name, {
183
+ label: titleize(baseName),
184
+ value: item.name,
185
+ description: shortDescription(assetListDescription(item)),
186
+ badge: 'project',
187
+ metadata: ['project'],
188
+ status: 'installed',
189
+ rawAsset: item,
190
+ scope: 'project',
191
+ });
192
+ }
193
+ return Array.from(merged.values());
194
+ }
195
+ useEffect(() => {
196
+ let active = true;
197
+ async function loadItems() {
198
+ setMode('loading');
199
+ setCursor(0);
200
+ setMochiState('searching');
201
+ setMessage(isMarketplace ? 'Loading marketplace...' : 'Loading your assets...');
202
+ try {
203
+ const loaded = isMarketplace
204
+ ? await loadMarketplaceItems(searchQuery)
205
+ : await loadInstalledItems();
206
+ if (!active)
207
+ return;
208
+ setItems(loaded);
209
+ }
210
+ catch {
211
+ if (!active)
212
+ return;
213
+ setItems([]);
214
+ }
215
+ setMode('browsing');
216
+ }
217
+ loadItems();
218
+ return () => {
219
+ active = false;
220
+ };
221
+ }, [activeAssetType, activeView, refreshCount]);
222
+ const paginatedItems = useMemo(() => {
223
+ if (!isMarketplace)
224
+ return items;
225
+ const start = marketplacePage * MARKETPLACE_PAGE_SIZE;
226
+ return items.slice(start, start + MARKETPLACE_PAGE_SIZE);
227
+ }, [items, isMarketplace, marketplacePage]);
228
+ const filteredItems = useMemo(() => {
229
+ let result = isMarketplace ? paginatedItems : items;
230
+ if (!isMarketplace && activeFilter > 0) {
231
+ const filterScope = subFilters[activeFilter].toLowerCase();
232
+ result = result.filter((item) => item.badge === filterScope);
233
+ }
234
+ if (searchQuery.trim()) {
235
+ const q = searchQuery.toLowerCase();
236
+ result = result.filter((item) => item.label.toLowerCase().includes(q) ||
237
+ (item.description && item.description.toLowerCase().includes(q)));
238
+ }
239
+ return result;
240
+ }, [items, paginatedItems, isMarketplace, activeFilter, searchQuery]);
241
+ const visibleItems = useMemo(() => {
242
+ const total = filteredItems.length;
243
+ if (total === 0)
244
+ return [];
245
+ const half = Math.floor(pageSize / 2);
246
+ const start = Math.max(0, Math.min(cursor - half, Math.max(0, total - pageSize)));
247
+ const end = Math.min(start + pageSize, total);
248
+ return filteredItems.slice(start, end).map((item, idx) => ({
249
+ item,
250
+ index: start + idx,
251
+ }));
252
+ }, [cursor, filteredItems, pageSize]);
253
+ useInput((input, key) => {
254
+ if (input === 'q') {
255
+ handleExit();
256
+ return;
257
+ }
258
+ if (key.escape) {
259
+ if (selected.size > 0) {
260
+ setSelected(new Set());
261
+ return;
262
+ }
263
+ if (focusedSection === 'list') {
264
+ setFocusedSection('search');
265
+ }
266
+ else {
267
+ handleExit();
268
+ }
269
+ return;
270
+ }
271
+ if (mode !== 'browsing')
272
+ return;
273
+ if (focusedSection === 'search') {
274
+ if (key.downArrow && filteredItems.length > 0) {
275
+ setFocusedSection('list');
276
+ setCursor(0);
277
+ return;
278
+ }
279
+ if (key.leftArrow || key.rightArrow) {
280
+ const typeIdx = assetTypeIndex(activeAssetType);
281
+ const nextIdx = key.leftArrow
282
+ ? (typeIdx - 1 + ASSET_TAB_ORDER.length) % ASSET_TAB_ORDER.length
283
+ : (typeIdx + 1) % ASSET_TAB_ORDER.length;
284
+ setActiveAssetType(ASSET_TAB_ORDER[nextIdx]);
285
+ setCursor(0);
286
+ setSearchQuery('');
287
+ return;
288
+ }
289
+ if (key.tab) {
290
+ setActiveView((prev) => (prev === 'Local' ? 'Marketplace' : 'Local'));
291
+ setMarketplacePage(0);
292
+ setActiveFilter(0);
293
+ setCursor(0);
294
+ setSearchQuery('');
295
+ return;
296
+ }
297
+ if (key.return) {
298
+ if (isMarketplace) {
299
+ setMode('loading');
300
+ loadMarketplaceItems(searchQuery).then((loaded) => {
301
+ setItems(loaded);
302
+ setMode('browsing');
303
+ setFocusedSection('list');
304
+ });
305
+ }
306
+ else if (filteredItems.length > 0) {
307
+ setFocusedSection('list');
308
+ setCursor(0);
309
+ }
310
+ return;
311
+ }
312
+ return;
313
+ }
314
+ if (focusedSection === 'list') {
315
+ if (key.upArrow) {
316
+ if (cursor === 0)
317
+ setFocusedSection('search');
318
+ else
319
+ setCursor((prev) => Math.max(0, prev - 1));
320
+ return;
321
+ }
322
+ if (key.downArrow) {
323
+ setCursor((prev) => Math.min(filteredItems.length - 1, prev + 1));
324
+ return;
325
+ }
326
+ if (key.leftArrow || key.rightArrow) {
327
+ const typeIdx = assetTypeIndex(activeAssetType);
328
+ const nextIdx = key.leftArrow
329
+ ? (typeIdx - 1 + ASSET_TAB_ORDER.length) % ASSET_TAB_ORDER.length
330
+ : (typeIdx + 1) % ASSET_TAB_ORDER.length;
331
+ setActiveAssetType(ASSET_TAB_ORDER[nextIdx]);
332
+ setActiveFilter(0);
333
+ setCursor(0);
334
+ setSearchQuery('');
335
+ setFocusedSection('search');
336
+ return;
337
+ }
338
+ if (key.tab) {
339
+ if (!isMarketplace) {
340
+ setActiveFilter((prev) => (prev + 1) % subFilters.length);
341
+ }
342
+ else {
343
+ setActiveView('Local');
344
+ }
345
+ setCursor(0);
346
+ return;
347
+ }
348
+ if (input === ' ') {
349
+ toggleCurrent();
350
+ return;
351
+ }
352
+ if (input === 'i' && isMarketplace && selected.size > 0) {
353
+ setMode('scope');
354
+ return;
355
+ }
356
+ if (input === 'x' && !isMarketplace && selected.size > 0) {
357
+ setMode('loading');
358
+ setMochiState('searching');
359
+ setMessage(`Removing ${selected.size} selected assets...`);
360
+ async function runBulkRemove() {
361
+ const names = Array.from(selected);
362
+ let removedCount = 0;
363
+ let failedCount = 0;
364
+ for (const name of names) {
365
+ const item = items.find((i) => i.value === name);
366
+ const scope = item?.scope ?? 'global';
367
+ try {
368
+ await assetService.remove(name, activeAssetType, scope);
369
+ removedCount++;
370
+ }
371
+ catch {
372
+ failedCount++;
373
+ }
374
+ }
375
+ setSelected(new Set());
376
+ if (failedCount > 0) {
377
+ setMochiState('error');
378
+ setMessage(`Removed ${removedCount}, failed ${failedCount}`);
379
+ }
380
+ else {
381
+ setMochiState('success');
382
+ setMessage(`Successfully removed ${removedCount} assets.`);
383
+ }
384
+ setMode('done');
385
+ setTimeout(() => {
386
+ setRefreshCount((c) => c + 1);
387
+ }, 1500);
388
+ }
389
+ void runBulkRemove();
390
+ return;
391
+ }
392
+ if (key.return && filteredItems.length > 0) {
393
+ const item = filteredItems[cursor];
394
+ if (isMarketplace) {
395
+ if (item?.marketplaceAsset) {
396
+ setDetailAsset(item.marketplaceAsset);
397
+ setMode('detail');
398
+ }
399
+ }
400
+ else {
401
+ if (item) {
402
+ setLocalDetailItem(item);
403
+ setMode('local-detail');
404
+ }
405
+ }
406
+ }
407
+ if (input === 'n' && isMarketplace) {
408
+ setMarketplacePage((p) => p + 1);
409
+ setCursor(0);
410
+ }
411
+ if (input === 'p' && isMarketplace) {
412
+ setMarketplacePage((p) => Math.max(0, p - 1));
413
+ setCursor(0);
414
+ }
415
+ }
416
+ });
417
+ function toggleCurrent() {
418
+ const item = filteredItems[cursor];
419
+ if (!item)
420
+ return;
421
+ setSelected((prev) => {
422
+ const next = new Set(prev);
423
+ if (next.has(item.value))
424
+ next.delete(item.value);
425
+ else
426
+ next.add(item.value);
427
+ return next;
428
+ });
429
+ }
430
+ async function installSelected(scope) {
431
+ const names = Array.from(selected);
432
+ setMode('installing');
433
+ setMochiState('installing');
434
+ let installedCount = 0;
435
+ let failedCount = 0;
436
+ const installedNames = [];
437
+ for (let i = 0; i < names.length; i++) {
438
+ const name = names[i];
439
+ setMessage(`Installing ${i + 1}/${names.length}: ${titleize(name)}...`);
440
+ try {
441
+ const candidate = await marketplaceService.findInstallCandidate(name);
442
+ if (!candidate) {
443
+ await assetService.install(name, activeAssetType, scope, undefined, 'bundled');
444
+ }
445
+ else {
446
+ await installFromCandidate(candidate, scope);
447
+ }
448
+ installedCount++;
449
+ installedNames.push(name);
450
+ }
451
+ catch {
452
+ failedCount++;
453
+ }
454
+ }
455
+ const typeLabel = ASSET_TYPE_PLURAL[activeAssetType].toLowerCase();
456
+ if (failedCount > 0) {
457
+ setMochiState('error');
458
+ setMessage(`Installed ${installedCount}, failed ${failedCount}`);
459
+ setMode('done');
460
+ setTimeout(() => {
461
+ setSelected(new Set());
462
+ setRefreshCount((c) => c + 1);
463
+ }, 1800);
464
+ return;
465
+ }
466
+ setMochiState('success');
467
+ setMessage(installedCount === 1
468
+ ? `Installed ${titleize(installedNames[0])} → ${scopeLabel(scope)}`
469
+ : `Installed ${installedCount} ${typeLabel} → ${scopeLabel(scope)}`);
470
+ setMode('done');
471
+ setTimeout(() => {
472
+ setSelected(new Set());
473
+ setRefreshCount((c) => c + 1);
474
+ }, 1100);
475
+ }
476
+ if (isTooSmall) {
477
+ return (_jsx(TooSmallScreen, { columns: physicalColumns, rows: physicalRows, minColumns: MIN_COLUMNS, minRows: MIN_ROWS }));
478
+ }
479
+ if (mode === 'loading' || mode === 'installing' || mode === 'done') {
480
+ const showTransition = mode === 'loading' && isMarketplace;
481
+ const content = (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsx(Narrator, { state: mochiState, message: message })] }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "Please wait..." }) })] }));
482
+ if (showTransition) {
483
+ return _jsx(TransitionScreen, { message: message, children: content });
484
+ }
485
+ return content;
486
+ }
487
+ if (mode === 'local-detail' && localDetailItem) {
488
+ const raw = localDetailItem.rawAsset;
489
+ const scope = localDetailItem.scope ?? 'global';
490
+ const provenance = raw?.provenance;
491
+ const localActions = [
492
+ { label: 'Remove Asset', value: 'remove' },
493
+ { label: 'Back', value: 'back' },
494
+ ];
495
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsxs(Text, { bold: true, children: [assetTypeBadge(activeAssetType), " ", localDetailItem.value] }), _jsxs(Text, { color: theme.dim, children: ["Scope: ", scope] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: theme.secondary, children: raw?.description || 'No description provided.' }), raw?.installedAt && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.dim, children: ["Installed: ", new Date(raw.installedAt).toLocaleString()] }) })), provenance && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: theme.accent, children: "Provenance:" }), _jsxs(Text, { color: theme.dim, children: [" \u2022 Tool: ", provenance.tool] }), _jsxs(Text, { color: theme.dim, children: [" \u2022 Path: ", provenance.sourcePath] }), provenance.importedAt && (_jsxs(Text, { color: theme.dim, children: [" \u2022 Time: ", new Date(provenance.importedAt).toLocaleString()] }))] }))] }), _jsx(Box, { marginTop: 1, children: _jsx(CustomSelectInput, { items: localActions, onSelect: async (item) => {
496
+ if (item.value === 'back') {
497
+ setMode('browsing');
498
+ return;
499
+ }
500
+ if (item.value === 'remove') {
501
+ setMode('loading');
502
+ setMochiState('searching');
503
+ setMessage(`Removing ${localDetailItem.value}...`);
504
+ try {
505
+ await assetService.remove(localDetailItem.value, activeAssetType, scope);
506
+ setMochiState('success');
507
+ setMessage(`Removed ${localDetailItem.value} successfully.`);
508
+ }
509
+ catch (err) {
510
+ setMochiState('error');
511
+ setMessage(`Failed to remove: ${err.message}`);
512
+ }
513
+ setMode('done');
514
+ setTimeout(() => {
515
+ setRefreshCount((c) => c + 1);
516
+ }, 1500);
517
+ }
518
+ } }) })] }) }));
519
+ }
520
+ if (mode === 'detail' && detailAsset) {
521
+ const detailActions = [
522
+ { label: 'Install', value: 'install' },
523
+ { label: 'Back', value: 'back' },
524
+ ];
525
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsxs(Text, { bold: true, children: [assetTypeBadge(detailAsset.type), " ", detailAsset.slug, verifiedBadge(detailAsset.verified)] }), _jsx(Text, { color: theme.secondary, children: detailAsset.description }), _jsxs(Text, { color: theme.dim, children: ["Source: ", detailAsset.source] }), _jsxs(Text, { color: theme.dim, children: ["Repository: github.com/", detailAsset.owner, "/", detailAsset.repo] }), _jsxs(Text, { color: theme.dim, children: ["Checksum: ", detailAsset.checksum] }), detailAsset.updatedAt && (_jsxs(Text, { color: theme.dim, children: ["Updated: ", detailAsset.updatedAt] })), _jsx(Box, { marginTop: 1, children: _jsx(CustomSelectInput, { items: detailActions, onSelect: async (item) => {
526
+ if (item.value === 'back') {
527
+ setMode('browsing');
528
+ return;
529
+ }
530
+ const candidate = await marketplaceService.findInstallCandidate(detailAsset.slug);
531
+ if (candidate) {
532
+ setInstallCandidate(candidate);
533
+ setMode('confirm');
534
+ }
535
+ } }) })] }) }));
536
+ }
537
+ if (mode === 'confirm' && installCandidate) {
538
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsx(MarketplaceInstallConfirm, { details: {
539
+ name: installCandidate.name,
540
+ type: installCandidate.type,
541
+ slug: installCandidate.slug,
542
+ author: installCandidate.organization,
543
+ version: installCandidate.version,
544
+ source: installCandidate.githubSource ?? installCandidate.source,
545
+ checksum: installCandidate.checksum,
546
+ verified: installCandidate.verified,
547
+ }, onConfirm: async (scope) => {
548
+ setMode('installing');
549
+ try {
550
+ await installFromCandidate(installCandidate, scope);
551
+ setMochiState('success');
552
+ setMessage(`Installed → ${scopeLabel(scope)}`);
553
+ }
554
+ catch (err) {
555
+ setMochiState('error');
556
+ setMessage(err instanceof Error ? err.message : String(err));
557
+ }
558
+ setMode('done');
559
+ setTimeout(() => {
560
+ setRefreshCount((c) => c + 1);
561
+ }, 1200);
562
+ }, onCancel: () => setMode('detail') }) }));
563
+ }
564
+ if (mode === 'scope') {
565
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: selected.size }), _jsxs(Text, { color: theme.dim, children: [' ', ASSET_TYPE_PLURAL[activeAssetType].toLowerCase(), " selected"] })] }), _jsx(Box, { marginTop: 1, children: _jsx(ScopePrompt, { onSelect: (scope) => installSelected(scope) }) })] }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "Select installation scope \u00B7 esc cancel" }) })] }));
566
+ }
567
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsx(Text, { bold: true, color: theme.accent, children: "Browse Assets" }), marketplaceNotice && isMarketplace && (_jsx(Text, { color: theme.warning, wrap: "truncate", children: marketplaceNotice })), _jsx(Box, { marginBottom: 1, marginTop: 1, children: ASSET_TAB_ORDER.map((type) => {
568
+ const isActive = type === activeAssetType;
569
+ return (_jsx(Box, { marginRight: 3, children: _jsxs(Text, { color: isActive ? theme.primary : theme.dim, bold: isActive, underline: isActive, children: [isActive ? '❯ ' : ' ', ASSET_TYPE_PLURAL[type]] }) }, type));
570
+ }) }), _jsxs(Box, { marginBottom: 1, children: [viewTabs.map((tab) => {
571
+ const isActive = tab === activeView;
572
+ return (_jsx(Box, { marginRight: 3, children: _jsx(Text, { color: isActive ? theme.text : theme.dim, bold: isActive, children: tab }) }, tab));
573
+ }), !isMarketplace && (_jsx(Box, { marginLeft: 2, children: subFilters.map((filter, idx) => (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: idx === activeFilter ? theme.text : theme.dim, bold: idx === activeFilter, children: filter }) }, filter))) }))] }), _jsxs(Box, { marginBottom: 1, flexDirection: "row", children: [_jsx(Text, { color: focusedSection === 'search' ? theme.primary : theme.dim, children: focusedSection === 'search' ? ' › ' : ' ' }), _jsx(TextInput, { value: searchQuery, onChange: (val) => {
574
+ setSearchQuery(val);
575
+ setCursor(0);
576
+ }, focus: focusedSection === 'search', placeholder: isMarketplace
577
+ ? `Search ${ASSET_TYPE_PLURAL[activeAssetType].toLowerCase()} marketplace...`
578
+ : `Filter registry and installed ${ASSET_TYPE_PLURAL[activeAssetType].toLowerCase()}...` })] }), _jsx(Box, { flexDirection: "column", height: pageSize, children: filteredItems.length === 0 ? (_jsx(Text, { color: theme.dim, children: isMarketplace
579
+ ? `No matching ${ASSET_TYPE_PLURAL[activeAssetType].toLowerCase()} in marketplace.`
580
+ : `No registry or installed ${ASSET_TYPE_PLURAL[activeAssetType].toLowerCase()} matching your query.` })) : (visibleItems.map(({ item, index }) => {
581
+ const isCurrent = index === cursor && focusedSection === 'list';
582
+ const isSelected = selected.has(item.value);
583
+ const isInstalled = item.status === 'installed';
584
+ const indicatorText = isSelected ? '[✓] ' : isInstalled && isMarketplace ? '[●] ' : '[ ] ';
585
+ const indicatorColor = isSelected
586
+ ? theme.primary
587
+ : isInstalled && isMarketplace
588
+ ? theme.success
589
+ : theme.dim;
590
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: isCurrent ? theme.primary : theme.dim, children: isCurrent ? ' › ' : ' ' }), _jsx(Text, { color: indicatorColor, bold: isSelected, children: indicatorText }), _jsx(Text, { color: isCurrent ? theme.text : isSelected ? theme.primary : theme.secondary, bold: isCurrent || isSelected, children: item.label }), item.badge && _jsxs(Text, { color: theme.dim, children: [" [", item.badge, "]"] }), isInstalled && isMarketplace && !isSelected && _jsx(Text, { color: theme.dim, children: " (installed)" }), isSelected && _jsx(Text, { color: theme.primary, bold: true, children: " (selected)" })] }, item.value));
591
+ })) })] }), _jsxs(Box, { flexDirection: "column", children: [selected.size > 0 ? (_jsx(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, borderStyle: "round", borderColor: isMarketplace ? theme.primary : theme.error, height: isCompact ? 0 : 5, justifyContent: "center", children: _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", alignItems: "center", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: isMarketplace ? theme.primary : theme.error, children: isMarketplace ? '⚡ BULK INSTALL' : '⚡ BULK UNINSTALL' }), _jsxs(Text, { color: theme.text, children: [' ', "| ", selected.size, " ", ASSET_TYPE_PLURAL[activeAssetType].toLowerCase(), " selected"] })] }), _jsxs(Box, { children: [isMarketplace ? (_jsx(Text, { bold: true, color: theme.primary, children: "Press [i] to Install All" })) : (_jsx(Text, { bold: true, color: theme.error, children: "Press [x] to Remove All" })), _jsx(Text, { color: theme.dim, children: " \u00B7 [esc] to clear" })] })] }) })) : (canShowDescriptions && (_jsx(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, borderStyle: "round", borderColor: theme.borderMuted, height: isCompact ? 0 : 5, justifyContent: "center", children: _jsx(Text, { color: theme.text, children: filteredItems[cursor]?.description || '' }) }))), _jsx(Box, { marginTop: isCompact ? 1 : 0, children: _jsxs(Text, { color: theme.dim, children: [isCompact
592
+ ? `←→ type tab view ↑↓ navigate esc back${selected.size > 0 ? (isMarketplace ? ' [i] install' : ' [x] remove') : ''}`
593
+ : `←→ asset type tab ${isMarketplace ? 'view' : 'filter'} ↑↓ navigate ${isMarketplace ? 'space select enter install' : 'space select enter details'} esc back q quit`, selected.size > 0 ? ` · ${selected.size} selected` : '', selected.size > 0 && !isCompact ? (isMarketplace ? (_jsx(Text, { color: theme.primary, children: " \u00B7 press [i] to install selected" })) : (_jsx(Text, { color: theme.error, children: " \u00B7 press [x] to remove selected" }))) : ''] }) })] })] }));
594
+ };
595
+ export async function browseCommand(args = [], options) {
596
+ const wantsList = Boolean(options?.list || options?.l);
597
+ const wantsJson = Boolean(options?.json);
598
+ const columns = process.stdout.columns ?? 80;
599
+ if (columns <= PLAIN_OUTPUT_MAX_COLUMNS) {
600
+ console.error('\n Terminal too narrow for browse. Use: aman search <query>\n');
601
+ process.exit(1);
602
+ }
603
+ if (!process.stdin.isTTY) {
604
+ if (wantsList || wantsJson) {
605
+ const types = ASSET_TAB_ORDER;
606
+ const payload = { installed: {}, catalog: {} };
607
+ for (const type of types) {
608
+ const installedGlobal = await assetService.list(type, 'global');
609
+ const installedProject = await assetService.list(type, 'project');
610
+ const catalog = await marketplaceService.search('', type);
611
+ payload.installed[type] = { global: installedGlobal, project: installedProject };
612
+ payload.catalog[type] = catalog;
613
+ }
614
+ if (wantsJson) {
615
+ console.log(JSON.stringify(payload, null, 2));
616
+ return;
617
+ }
618
+ console.log('\n Browse Assets:\n');
619
+ for (const type of types) {
620
+ console.log(` ${ASSET_TYPE_PLURAL[type]}:`);
621
+ const ig = await assetService.list(type, 'global');
622
+ const ip = await assetService.list(type, 'project');
623
+ ig.forEach((s) => console.log(` - [installed:global] ${s.name}`));
624
+ ip.forEach((s) => console.log(` - [installed:project] ${s.name}`));
625
+ const catalog = await marketplaceService.search('', type);
626
+ catalog.slice(0, 5).forEach((s) => {
627
+ const label = s.section === 'marketplace' ? 'marketplace' : 'registry';
628
+ console.log(` - [${label}] ${s.name}`);
629
+ });
630
+ }
631
+ return;
632
+ }
633
+ console.log('\n \x1b[31;1mInteractive mode unavailable.\x1b[0m');
634
+ console.log(' Use:');
635
+ console.log(' \x1b[33maman browse --list\x1b[0m');
636
+ console.log(' \x1b[33maman browse --json\x1b[0m\n');
637
+ process.exit(1);
638
+ }
639
+ const { waitUntilExit } = render(_jsx(BrowseApp, {}));
640
+ await waitUntilExit();
641
+ }
@@ -0,0 +1 @@
1
+ export declare function cacheCommand(args: string[]): Promise<void>;