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,295 @@
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 { CustomSelectInput } from '../ui/components/CustomSelect.js';
5
+ import TextInput from 'ink-text-input';
6
+ import { marketplaceService } from '../services/marketplace.service.js';
7
+ import { assetService } from '../services/asset.service.js';
8
+ import { Header } from '../ui/components/Header.js';
9
+ import { Narrator } from '../ui/components/Narrator.js';
10
+ import { theme } from '../ui/theme.js';
11
+ import { useResponsiveLayout, MIN_COLUMNS, MIN_ROWS } from '../ui/layout.js';
12
+ import { TooSmallScreen } from '../ui/components/TooSmallScreen.js';
13
+ import { metadataParts, titleize, verifiedBadge } from '../ui/marketplaceDisplay.js';
14
+ import { installFromCandidate } from '../marketplace/install-from-candidate.js';
15
+ import { MarketplaceInstallConfirm } from '../ui/components/MarketplaceInstallConfirm.js';
16
+ import { PLAIN_OUTPUT_MAX_COLUMNS } from '../ui/layout.js';
17
+ import { MARKETPLACE_ENABLED } from '../config/features.js';
18
+ import { ASSET_TAB_ORDER, ASSET_TYPE_PLURAL, assetTypeBadge, groupResultsByType, matchesAssetSearch, } from '../ui/assetDisplay.js';
19
+ import { TransitionScreen } from '../ui/animations/TransitionScreen.js';
20
+ export const SearchApp = ({ onBack, initialQuery = '' }) => {
21
+ const { exit } = useApp();
22
+ const handleExit = () => {
23
+ if (onBack)
24
+ onBack();
25
+ else
26
+ exit();
27
+ };
28
+ const { rows, isTooSmall, isCompact, canShowDescriptions, physicalColumns, physicalRows } = useResponsiveLayout();
29
+ const pageSize = Math.max(1, rows - (canShowDescriptions ? 14 : 9));
30
+ const [query, setQuery] = useState(initialQuery);
31
+ const [results, setResults] = useState([]);
32
+ const [flatResults, setFlatResults] = useState([]);
33
+ const [cursor, setCursor] = useState(0);
34
+ const [mode, setMode] = useState(initialQuery ? 'results' : 'input');
35
+ const [mochiState, setMochiState] = useState('searching');
36
+ const [message, setMessage] = useState('Searching...');
37
+ const [loading, setLoading] = useState(Boolean(initialQuery));
38
+ const [filterQuery, setFilterQuery] = useState('');
39
+ const [filterActive, setFilterActive] = useState(false);
40
+ const [marketplaceNotice, setMarketplaceNotice] = useState();
41
+ const [installCandidate, setInstallCandidate] = useState(null);
42
+ async function performSearch(searchQuery) {
43
+ if (!searchQuery.trim())
44
+ return;
45
+ setLoading(true);
46
+ setCursor(0);
47
+ setMochiState('searching');
48
+ setMessage('Searching assets...');
49
+ try {
50
+ if (MARKETPLACE_ENABLED) {
51
+ const ghMeta = await marketplaceService.searchGitHubMarketplace(searchQuery);
52
+ if (ghMeta.message) {
53
+ setMarketplaceNotice(ghMeta.fromCache && ghMeta.cacheAgeMinutes !== null
54
+ ? `${ghMeta.message} Results from ${ghMeta.cacheAgeMinutes} minutes ago.`
55
+ : ghMeta.message);
56
+ }
57
+ else if (ghMeta.fromCache && ghMeta.cacheAgeMinutes !== null) {
58
+ setMarketplaceNotice(`Marketplace results from ${ghMeta.cacheAgeMinutes} minutes ago.`);
59
+ }
60
+ else {
61
+ setMarketplaceNotice(undefined);
62
+ }
63
+ }
64
+ const found = await marketplaceService.search(searchQuery);
65
+ const installedByType = {
66
+ skill: new Set(),
67
+ prompt: new Set(),
68
+ mcp: new Set(),
69
+ };
70
+ for (const type of ASSET_TAB_ORDER) {
71
+ const global = await assetService.list(type, 'global');
72
+ const project = await assetService.list(type, 'project');
73
+ for (const item of [...global, ...project]) {
74
+ installedByType[type].add(item.name);
75
+ }
76
+ }
77
+ const filtered = found.filter((r) => matchesAssetSearch(r, searchQuery));
78
+ for (const r of filtered) {
79
+ if (installedByType[r.type].has(r.name)) {
80
+ r.installed = true;
81
+ }
82
+ }
83
+ const grouped = groupResultsByType(filtered);
84
+ const flat = [];
85
+ for (const type of ASSET_TAB_ORDER) {
86
+ flat.push(...grouped[type]);
87
+ }
88
+ setResults(filtered);
89
+ setFlatResults(flat);
90
+ setMode('results');
91
+ }
92
+ catch {
93
+ setResults([]);
94
+ setFlatResults([]);
95
+ setMode('results');
96
+ }
97
+ setLoading(false);
98
+ }
99
+ useEffect(() => {
100
+ if (initialQuery) {
101
+ performSearch(initialQuery);
102
+ }
103
+ }, [initialQuery]);
104
+ const displayResults = useMemo(() => {
105
+ const q = filterQuery.trim().toLowerCase();
106
+ if (!q)
107
+ return flatResults;
108
+ return flatResults.filter((r) => {
109
+ const hay = [r.name, r.slug, r.description, r.organization, ...(r.tags ?? [])]
110
+ .filter(Boolean)
111
+ .join(' ')
112
+ .toLowerCase();
113
+ return hay.includes(q);
114
+ });
115
+ }, [flatResults, filterQuery]);
116
+ const visibleItems = useMemo(() => {
117
+ const total = displayResults.length;
118
+ if (total === 0)
119
+ return [];
120
+ const half = Math.floor(pageSize / 2);
121
+ const start = Math.max(0, Math.min(cursor - half, Math.max(0, total - pageSize)));
122
+ return displayResults.slice(start, start + pageSize).map((item, idx) => ({
123
+ item,
124
+ index: start + idx,
125
+ }));
126
+ }, [cursor, displayResults, pageSize]);
127
+ const focusedItem = displayResults[cursor];
128
+ useInput((input, key) => {
129
+ if (input === 'q') {
130
+ handleExit();
131
+ return;
132
+ }
133
+ if (mode === 'input') {
134
+ if (key.escape)
135
+ handleExit();
136
+ return;
137
+ }
138
+ if (mode === 'results') {
139
+ if (input === '/') {
140
+ setFilterActive(true);
141
+ setFilterQuery('');
142
+ setCursor(0);
143
+ return;
144
+ }
145
+ if (filterActive && input.length === 1 && !key.ctrl && !key.meta) {
146
+ setFilterQuery((prev) => prev + input);
147
+ setCursor(0);
148
+ return;
149
+ }
150
+ if (key.escape || key.backspace || key.delete) {
151
+ if (filterActive || filterQuery) {
152
+ setFilterQuery('');
153
+ setFilterActive(false);
154
+ setCursor(0);
155
+ }
156
+ else {
157
+ setMode('input');
158
+ }
159
+ return;
160
+ }
161
+ if (key.upArrow || input === 'k') {
162
+ setCursor((p) => Math.max(0, p - 1));
163
+ return;
164
+ }
165
+ if (key.downArrow || input === 'j') {
166
+ setCursor((p) => Math.min(displayResults.length - 1, p + 1));
167
+ return;
168
+ }
169
+ if (key.return && focusedItem) {
170
+ setMode('detail');
171
+ return;
172
+ }
173
+ }
174
+ if (mode === 'detail') {
175
+ if (key.escape || input === 'b') {
176
+ setMode('results');
177
+ return;
178
+ }
179
+ }
180
+ });
181
+ async function prepareInstall() {
182
+ if (!focusedItem)
183
+ return;
184
+ const candidate = await marketplaceService.findInstallCandidate(focusedItem.slug ?? focusedItem.name);
185
+ if (!candidate) {
186
+ setMochiState('error');
187
+ setMessage('Asset not found');
188
+ setMode('done');
189
+ return;
190
+ }
191
+ setInstallCandidate(candidate);
192
+ setMode('confirm');
193
+ }
194
+ async function doInstall(scope) {
195
+ if (!installCandidate)
196
+ return;
197
+ setMode('installing');
198
+ setMochiState('installing');
199
+ setMessage(`Installing ${titleize(installCandidate.name)}...`);
200
+ try {
201
+ const result = await installFromCandidate(installCandidate, scope);
202
+ setMochiState('success');
203
+ setMessage(`Installed ${assetTypeBadge(installCandidate.type)} ${titleize(installCandidate.name)} → ${result.path}`);
204
+ }
205
+ catch (err) {
206
+ setMochiState('error');
207
+ setMessage(`Error: ${err instanceof Error ? err.message : String(err)}`);
208
+ }
209
+ setMode('done');
210
+ setTimeout(() => handleExit(), 1200);
211
+ }
212
+ if (isTooSmall) {
213
+ return (_jsx(TooSmallScreen, { columns: physicalColumns, rows: physicalRows, minColumns: MIN_COLUMNS, minRows: MIN_ROWS }));
214
+ }
215
+ if (mode === 'input') {
216
+ 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: "Search Assets" }), _jsx(Text, { color: theme.dim, children: "Search skills, prompts, and MCPs by name, description, or tags" }), _jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsx(Text, { color: theme.primary, children: "\u276F " }), _jsx(TextInput, { value: query, onChange: setQuery, onSubmit: (val) => performSearch(val), placeholder: "Type search query... (e.g. react, github)" })] })] }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "press enter to search \u00B7 esc go back" }) })] }));
217
+ }
218
+ if (loading) {
219
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), _jsx(Narrator, { state: "searching", message: "Searching..." })] }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "Please wait..." }) })] }));
220
+ }
221
+ if (mode === 'installing' || mode === 'done') {
222
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsx(Box, { flexDirection: "column", children: _jsx(Narrator, { state: mochiState, message: message }) }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "Please wait..." }) })] }));
223
+ }
224
+ if (mode === 'confirm' && installCandidate) {
225
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: _jsx(MarketplaceInstallConfirm, { details: {
226
+ name: installCandidate.name,
227
+ type: installCandidate.type,
228
+ slug: installCandidate.slug,
229
+ author: installCandidate.organization,
230
+ version: installCandidate.version,
231
+ source: installCandidate.githubSource ?? installCandidate.source,
232
+ checksum: installCandidate.checksum,
233
+ verified: installCandidate.verified,
234
+ }, onConfirm: (s) => doInstall(s), onCancel: () => setMode('detail') }) }));
235
+ }
236
+ if (mode === 'detail' && focusedItem) {
237
+ const detailActions = [
238
+ { label: 'Install', value: 'install' },
239
+ { label: 'Back', value: 'back' },
240
+ ];
241
+ return (_jsxs(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(focusedItem.type), " ", titleize(focusedItem.name)] }), focusedItem.description && (_jsx(Text, { color: theme.secondary, children: focusedItem.description })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [metadataParts(focusedItem).map((part, i) => (_jsx(Text, { color: theme.dim, children: part }, i))), focusedItem.tags && focusedItem.tags.length > 0 && (_jsxs(Text, { color: theme.dim, children: ["Tags: ", focusedItem.tags.join(', ')] })), focusedItem.installed && _jsx(Text, { color: theme.success, children: "installed" })] }), _jsx(Box, { marginTop: 1, children: _jsx(CustomSelectInput, { items: detailActions, onSelect: (item) => {
242
+ if (item.value === 'install')
243
+ void prepareInstall();
244
+ if (item.value === 'back')
245
+ setMode('results');
246
+ } }) })] }), _jsx(Box, { children: _jsx(Text, { color: theme.dim, children: "press enter to select \u00B7 esc go back" }) })] }));
247
+ }
248
+ const localResults = results.filter((r) => r.section !== 'marketplace');
249
+ const marketplaceResults = results.filter((r) => r.section === 'marketplace');
250
+ const grouped = groupResultsByType(displayResults);
251
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rows, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { compact: true }), marketplaceNotice && (_jsx(Text, { color: theme.warning, wrap: "truncate", children: marketplaceNotice })), _jsxs(Text, { color: theme.dim, children: [displayResults.length, " result", displayResults.length !== 1 ? 's' : '', " \u00B7 Local", ' ', localResults.length, MARKETPLACE_ENABLED ? ` · Marketplace ${marketplaceResults.length}` : ''] }), !filterActive && filterQuery === '' && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: theme.dim, children: "/ filter \u00B7 j/k navigate \u00B7 enter details \u00B7 q quit" }) })), (filterActive || filterQuery !== '') && (_jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: theme.primary, children: "/ " }), _jsx(TextInput, { value: filterQuery, onChange: (v) => {
252
+ setFilterQuery(v);
253
+ setCursor(0);
254
+ }, placeholder: "filter..." })] })), _jsx(Text, { children: ' ' }), _jsx(Box, { flexDirection: "column", height: pageSize, children: visibleItems.length === 0 ? (_jsx(Text, { color: theme.dim, children: "No results found." })) : (visibleItems.map(({ item, index }) => {
255
+ const isCurrent = index === cursor;
256
+ const isInstalled = item.installed;
257
+ const showSectionHeader = index === 0 || displayResults[index - 1]?.section !== item.section;
258
+ const showTypeHeader = showSectionHeader ||
259
+ displayResults[index - 1]?.type !== item.type;
260
+ return (_jsxs(Box, { flexDirection: "column", children: [showSectionHeader && (_jsx(Text, { color: theme.accent, bold: true, children: item.section === 'marketplace' ? 'Marketplace' : 'Local' })), showTypeHeader && (_jsx(Text, { color: theme.secondary, children: ASSET_TYPE_PLURAL[item.type] })), _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: isCurrent ? theme.primary : theme.dim, children: isCurrent ? ' › ' : ' ' }), _jsx(Text, { color: isCurrent ? theme.text : theme.secondary, children: isInstalled ? '● ' : '○ ' }), _jsxs(Text, { color: theme.dim, children: [assetTypeBadge(item.type), " "] }), _jsxs(Text, { color: isCurrent ? theme.text : theme.secondary, bold: isCurrent, children: [item.slug ?? titleize(item.name), verifiedBadge(item.verified)] }), item.stars !== undefined && !isCompact && (_jsxs(Text, { color: theme.dim, children: [" \u2605", item.stars] })), isInstalled && _jsx(Text, { color: theme.dim, children: " installed" })] })] }, `${item.section}:${item.type}:${item.name}`));
261
+ })) })] }), _jsxs(Box, { flexDirection: "column", children: [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: displayResults[cursor]?.description || '' }) })), _jsx(Box, { marginTop: isCompact ? 1 : 0, children: _jsx(Text, { color: theme.dim, children: isCompact
262
+ ? '↑↓ navigate enter details esc search again'
263
+ : '↑↓ navigate enter details esc search again q quit' }) })] })] }));
264
+ };
265
+ export async function searchCommand(args, options = {}) {
266
+ const query = args.join(' ');
267
+ const forcePlain = options.noTty === true;
268
+ const columns = process.stdout.columns ?? 80;
269
+ const plain = forcePlain || !process.stdin.isTTY || !process.stdout.isTTY || columns <= PLAIN_OUTPUT_MAX_COLUMNS;
270
+ if (plain) {
271
+ if (MARKETPLACE_ENABLED) {
272
+ const ghMeta = await marketplaceService.searchGitHubMarketplace(query);
273
+ if (ghMeta.message) {
274
+ console.error(ghMeta.message);
275
+ }
276
+ }
277
+ const results = (await marketplaceService.search(query)).filter((r) => matchesAssetSearch(r, query));
278
+ for (const r of results) {
279
+ const label = r.section === 'marketplace' ? 'marketplace' : 'registry';
280
+ const badge = r.verified ? ' ✓' : '';
281
+ console.log(`${r.type} ${r.slug ?? r.name} ${r.version ?? '-'} ${r.description ?? ''}${badge} [${label}]`);
282
+ }
283
+ if (results.length === 0)
284
+ console.log('No results found.');
285
+ return;
286
+ }
287
+ if (query.trim()) {
288
+ const { waitUntilExit } = render(_jsx(TransitionScreen, { message: `Searching assets for "${query}"...`, children: _jsx(SearchApp, { initialQuery: query }) }));
289
+ await waitUntilExit();
290
+ }
291
+ else {
292
+ const { waitUntilExit } = render(_jsx(SearchApp, { initialQuery: "" }));
293
+ await waitUntilExit();
294
+ }
295
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { Scope } from '../types/index.js';
3
+ interface StackCreateAppProps {
4
+ name: string;
5
+ scope: Scope;
6
+ }
7
+ export declare const StackCreateApp: React.FC<StackCreateAppProps & {
8
+ onBack?: () => void;
9
+ }>;
10
+ interface StackActivateAppProps {
11
+ name: string;
12
+ scope: Scope;
13
+ }
14
+ export declare const StackActivateApp: React.FC<StackActivateAppProps & {
15
+ onBack?: () => void;
16
+ }>;
17
+ export declare function stackCommand(args: string[], options: any): Promise<void>;
18
+ export {};
@@ -0,0 +1,327 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState, useRef } from 'react';
3
+ import { render, Box, Text, useApp, useInput } from 'ink';
4
+ import { stackService } from '../services/stack.service.js';
5
+ import { assetService } from '../services/asset.service.js';
6
+ import { Narrator } from '../ui/components/Narrator.js';
7
+ import { Header } from '../ui/components/Header.js';
8
+ import { theme } from '../ui/theme.js';
9
+ import { titleize } from '../ui/marketplaceDisplay.js';
10
+ import { scanAll } from '../storage/scanner.js';
11
+ import { environmentService } from '../services/environment.service.js';
12
+ import { exists } from '../storage/filesystem.js';
13
+ import { ASSET_TAB_ORDER, ASSET_TYPE_PLURAL, assetCompositeKey, parseAssetCompositeKey, } from '../ui/assetDisplay.js';
14
+ const PAGE_SIZE = 8;
15
+ async function installStackMembers(stack, stackName) {
16
+ let installed = 0;
17
+ let skills = 0;
18
+ let prompts = 0;
19
+ let mcps = 0;
20
+ const members = [
21
+ ...stack.skills.map((name) => ({ name, type: 'skill' })),
22
+ ...stack.prompts.map((name) => ({ name, type: 'prompt' })),
23
+ ...stack.mcps.map((name) => ({ name, type: 'mcp' })),
24
+ ];
25
+ for (const { name, type } of members) {
26
+ try {
27
+ await assetService.install(name, type, 'project', undefined, `stack:${stackName}`);
28
+ installed++;
29
+ if (type === 'skill')
30
+ skills++;
31
+ if (type === 'prompt')
32
+ prompts++;
33
+ if (type === 'mcp')
34
+ mcps++;
35
+ }
36
+ catch {
37
+ // Already installed or not found
38
+ }
39
+ }
40
+ return { installed, skills, prompts, mcps };
41
+ }
42
+ function activationSummary(counts) {
43
+ const parts = [];
44
+ if (counts.skills > 0)
45
+ parts.push(`${counts.skills} skill${counts.skills !== 1 ? 's' : ''}`);
46
+ if (counts.prompts > 0)
47
+ parts.push(`${counts.prompts} prompt${counts.prompts !== 1 ? 's' : ''}`);
48
+ if (counts.mcps > 0)
49
+ parts.push(`${counts.mcps} MCP${counts.mcps !== 1 ? 's' : ''}`);
50
+ return parts.length > 0 ? parts.join(', ') : '0 assets';
51
+ }
52
+ export const StackCreateApp = ({ name, scope, onBack }) => {
53
+ const { exit } = useApp();
54
+ const handleExit = () => {
55
+ if (onBack)
56
+ onBack();
57
+ else
58
+ exit();
59
+ };
60
+ const [category, setCategory] = useState(ASSET_TAB_ORDER[0]);
61
+ const [allItems, setAllItems] = useState([]);
62
+ const [cursor, setCursor] = useState(0);
63
+ const [selected, setSelected] = useState(new Set());
64
+ const [done, setDone] = useState(false);
65
+ const [state, setState] = useState('idle');
66
+ const [message, setMessage] = useState('');
67
+ const [loading, setLoading] = useState(true);
68
+ useEffect(() => {
69
+ async function load() {
70
+ const globalDir = environmentService.getActiveEnvironmentDir();
71
+ const projectDir = environmentService.getProjectEnvironmentDir();
72
+ const globalData = await scanAll(globalDir, 'global');
73
+ let projectData = {
74
+ skills: [],
75
+ prompts: [],
76
+ mcps: [],
77
+ };
78
+ if (exists(projectDir)) {
79
+ projectData = await scanAll(projectDir, 'project');
80
+ }
81
+ function mergeAssets(items, type) {
82
+ const merged = new Map();
83
+ const seen = new Set();
84
+ for (const item of items) {
85
+ const baseName = (item.originalName || item.name)
86
+ .replace(/[-_\s]+\d+$/, '')
87
+ .toLowerCase()
88
+ .replace(/[-_\s]+/g, ' ')
89
+ .trim();
90
+ if (seen.has(baseName))
91
+ continue;
92
+ seen.add(baseName);
93
+ merged.set(item.name, { name: item.name, label: titleize(baseName), type });
94
+ }
95
+ return Array.from(merged.values());
96
+ }
97
+ setAllItems([
98
+ ...mergeAssets([...globalData.skills, ...projectData.skills], 'skill'),
99
+ ...mergeAssets([...globalData.prompts, ...projectData.prompts], 'prompt'),
100
+ ...mergeAssets([...globalData.mcps, ...projectData.mcps], 'mcp'),
101
+ ]);
102
+ setLoading(false);
103
+ }
104
+ load();
105
+ }, []);
106
+ const items = useMemo(() => allItems.filter((i) => i.type === category), [allItems, category]);
107
+ const visibleItems = useMemo(() => {
108
+ if (items.length === 0)
109
+ return [];
110
+ const half = Math.floor(PAGE_SIZE / 2);
111
+ const start = Math.max(0, Math.min(cursor - half, Math.max(0, items.length - PAGE_SIZE)));
112
+ return items.slice(start, start + PAGE_SIZE).map((item, idx) => ({
113
+ item,
114
+ index: start + idx,
115
+ }));
116
+ }, [cursor, items]);
117
+ useInput((input, key) => {
118
+ if (input === 'q' || key.escape) {
119
+ handleExit();
120
+ return;
121
+ }
122
+ if (done)
123
+ return;
124
+ if (key.leftArrow || key.rightArrow) {
125
+ setCategory((prev) => {
126
+ const idx = ASSET_TAB_ORDER.indexOf(prev);
127
+ const next = key.leftArrow
128
+ ? (idx - 1 + ASSET_TAB_ORDER.length) % ASSET_TAB_ORDER.length
129
+ : (idx + 1) % ASSET_TAB_ORDER.length;
130
+ return ASSET_TAB_ORDER[next];
131
+ });
132
+ setCursor(0);
133
+ return;
134
+ }
135
+ if (key.upArrow) {
136
+ setCursor((p) => Math.max(0, p - 1));
137
+ return;
138
+ }
139
+ if (key.downArrow) {
140
+ setCursor((p) => Math.min(items.length - 1, p + 1));
141
+ return;
142
+ }
143
+ if (input === ' ') {
144
+ const item = items[cursor];
145
+ if (!item)
146
+ return;
147
+ const keyId = assetCompositeKey(item.type, item.name);
148
+ setSelected((prev) => {
149
+ const next = new Set(prev);
150
+ if (next.has(keyId))
151
+ next.delete(keyId);
152
+ else
153
+ next.add(keyId);
154
+ return next;
155
+ });
156
+ return;
157
+ }
158
+ if (key.return && selected.size > 0) {
159
+ createStack();
160
+ }
161
+ });
162
+ async function createStack() {
163
+ setDone(true);
164
+ setState('installing');
165
+ setMessage(`Creating stack: ${name}...`);
166
+ try {
167
+ const skills = [];
168
+ const prompts = [];
169
+ const mcps = [];
170
+ for (const keyId of selected) {
171
+ const parsed = parseAssetCompositeKey(keyId);
172
+ if (!parsed)
173
+ continue;
174
+ if (parsed.type === 'skill')
175
+ skills.push(parsed.localName);
176
+ if (parsed.type === 'prompt')
177
+ prompts.push(parsed.localName);
178
+ if (parsed.type === 'mcp')
179
+ mcps.push(parsed.localName);
180
+ }
181
+ await stackService.create(scope, name, skills, prompts, mcps, `Stack: ${name}`);
182
+ setState('success');
183
+ setMessage(`Created stack: ${name} (${skills.length} skills, ${prompts.length} prompts, ${mcps.length} MCPs)`);
184
+ }
185
+ catch (err) {
186
+ setState('error');
187
+ setMessage(`Error: ${err.message}`);
188
+ }
189
+ setTimeout(() => handleExit(), 1000);
190
+ }
191
+ if (loading) {
192
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Header, { compact: true }), _jsx(Narrator, { state: "searching", message: "Loading assets..." })] }));
193
+ }
194
+ if (done) {
195
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsx(Narrator, { state: state, message: message }) }));
196
+ }
197
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Header, { compact: true }), _jsxs(Text, { children: ["Select assets for ", _jsx(Text, { bold: true, children: name }), " stack"] }), _jsx(Box, { marginY: 1, children: ASSET_TAB_ORDER.map((t) => (_jsx(Box, { marginRight: 2, children: _jsxs(Text, { color: category === t ? theme.primary : theme.dim, bold: category === t, children: [category === t ? '❯ ' : ' ', ASSET_TYPE_PLURAL[t]] }) }, t))) }), items.length === 0 ? (_jsxs(Text, { color: theme.dim, children: ["No installed ", ASSET_TYPE_PLURAL[category].toLowerCase(), " in this environment."] })) : (visibleItems.map(({ item, index }) => {
198
+ const isCurrent = index === cursor;
199
+ const isSelected = selected.has(assetCompositeKey(item.type, item.name));
200
+ return (_jsxs(Text, { children: [_jsx(Text, { color: isCurrent ? theme.primary : theme.dim, children: isCurrent ? ' › ' : ' ' }), _jsx(Text, { color: isSelected ? theme.primary : theme.secondary, children: isSelected ? '◆ ' : '○ ' }), _jsx(Text, { color: isCurrent ? theme.text : theme.secondary, bold: isCurrent, children: item.label })] }, assetCompositeKey(item.type, item.name)));
201
+ })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.dim, children: ["\u2190\u2192 type \u00B7 space select \u00B7 enter create \u00B7 q quit", selected.size > 0 ? ` · ${selected.size} selected` : ''] }) })] }));
202
+ };
203
+ export const StackActivateApp = ({ name, scope, onBack }) => {
204
+ const { exit } = useApp();
205
+ const handleExit = () => {
206
+ if (onBack)
207
+ onBack();
208
+ else
209
+ exit();
210
+ };
211
+ const [state, setState] = useState('installing');
212
+ const [message, setMessage] = useState(`Activating ${name}...`);
213
+ useInput((input, key) => {
214
+ if (key.escape || input === 'q') {
215
+ handleExit();
216
+ }
217
+ });
218
+ const started = useRef(false);
219
+ useEffect(() => {
220
+ if (started.current)
221
+ return;
222
+ started.current = true;
223
+ async function activate() {
224
+ try {
225
+ const stacks = await stackService.list(scope);
226
+ const stack = stacks.find((s) => s.name === name);
227
+ if (!stack) {
228
+ setState('error');
229
+ setMessage(`Stack "${name}" not found`);
230
+ setTimeout(() => handleExit(), 1200);
231
+ return;
232
+ }
233
+ const counts = await installStackMembers(stack, name);
234
+ setState('success');
235
+ setMessage(`Activated ${name} → ${activationSummary(counts)} loaded into project`);
236
+ }
237
+ catch (err) {
238
+ setState('error');
239
+ setMessage(`Error: ${err.message}`);
240
+ }
241
+ setTimeout(() => handleExit(), 1000);
242
+ }
243
+ activate();
244
+ }, [name, scope]);
245
+ return (_jsx(Box, { paddingX: 1, children: _jsx(Narrator, { state: state, message: message }) }));
246
+ };
247
+ async function listStacks(scope) {
248
+ const stacks = await stackService.list(scope);
249
+ if (stacks.length === 0) {
250
+ console.log(` No stacks in ${scope}.`);
251
+ return;
252
+ }
253
+ for (const s of stacks) {
254
+ const parts = [];
255
+ if (s.skills.length > 0)
256
+ parts.push(`${s.skills.length} skills`);
257
+ if (s.prompts.length > 0)
258
+ parts.push(`${s.prompts.length} prompts`);
259
+ if (s.mcps.length > 0)
260
+ parts.push(`${s.mcps.length} mcps`);
261
+ console.log(` ${s.name}${parts.length > 0 ? ` ${parts.join(', ')}` : ''}`);
262
+ }
263
+ }
264
+ export async function stackCommand(args, options) {
265
+ const subcmd = args[0];
266
+ const scope = options.project || options.p ? 'project' : 'global';
267
+ if (subcmd === 'create') {
268
+ const name = args[1];
269
+ if (!name) {
270
+ console.log(' Usage: aman stack create <name>');
271
+ return;
272
+ }
273
+ if (!process.stdin.isTTY) {
274
+ console.log(' ERROR: Stack creation requires an interactive TTY terminal.');
275
+ process.exit(1);
276
+ }
277
+ const { waitUntilExit } = render(_jsx(StackCreateApp, { name: name, scope: scope }));
278
+ await waitUntilExit();
279
+ }
280
+ else if (subcmd === 'activate') {
281
+ const name = args[1];
282
+ if (!name) {
283
+ console.log(' Usage: aman stack activate <name>');
284
+ return;
285
+ }
286
+ if (!process.stdin.isTTY) {
287
+ console.log(` ◌ Activating stack ${name} → project...`);
288
+ try {
289
+ const stacks = await stackService.list(scope);
290
+ const stack = stacks.find((s) => s.name === name);
291
+ if (!stack) {
292
+ console.log(` ✗ Error: Stack "${name}" not found`);
293
+ process.exit(1);
294
+ }
295
+ const counts = await installStackMembers(stack, name);
296
+ console.log(` ✓ Activated ${name} → ${activationSummary(counts)} loaded into project`);
297
+ }
298
+ catch (err) {
299
+ console.log(` ✗ Activation error: ${err.message}`);
300
+ process.exit(1);
301
+ }
302
+ return;
303
+ }
304
+ const { waitUntilExit } = render(_jsx(StackActivateApp, { name: name, scope: scope }));
305
+ await waitUntilExit();
306
+ }
307
+ else if (subcmd === 'list') {
308
+ await listStacks(scope);
309
+ }
310
+ else if (subcmd === 'remove') {
311
+ const name = args[1];
312
+ if (!name) {
313
+ console.log(' Usage: aman stack remove <name>');
314
+ return;
315
+ }
316
+ try {
317
+ await stackService.remove(scope, name);
318
+ console.log(` ✓ Stack ${name} removed`);
319
+ }
320
+ catch (err) {
321
+ console.error(` ✗ Error: ${err.message}`);
322
+ }
323
+ }
324
+ else {
325
+ console.log(' Usage: aman stack <create|activate|list|remove> [name]');
326
+ }
327
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ interface SyncAppProps {
3
+ action: 'push' | 'pull';
4
+ }
5
+ export declare const SyncApp: React.FC<SyncAppProps & {
6
+ onBack?: () => void;
7
+ }>;
8
+ export declare function syncCommand(args: string[]): Promise<void>;
9
+ export {};