@crystalplatforms/adm 0.2.3 → 0.3.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 (89) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +154 -161
  3. package/bin/adm +18 -21
  4. package/package.json +9 -22
  5. package/scripts/completion/adm.bash +12 -55
  6. package/scripts/completion/adm.zsh +21 -58
  7. package/scripts/install.js +8 -8
  8. package/src/commands/config.js +66 -0
  9. package/src/commands/dotfiles.js +49 -32
  10. package/src/commands/feedback.js +12 -0
  11. package/src/commands/help.js +33 -0
  12. package/src/commands/uninstall.js +73 -54
  13. package/src/commands/version.js +7 -0
  14. package/src/commands.js +26 -0
  15. package/src/config/index.js +32 -32
  16. package/src/data/extensions.json +358 -358
  17. package/src/engines.js +38 -0
  18. package/src/install/executor.js +263 -0
  19. package/src/install/hints.js +94 -0
  20. package/src/install/npm-prefix.js +110 -0
  21. package/src/install/plan.js +75 -0
  22. package/src/install/report.js +46 -0
  23. package/src/install/run.js +88 -0
  24. package/src/install/shell-paths.js +129 -0
  25. package/src/legacy-cleanup.js +96 -0
  26. package/src/pkg/builders.js +220 -0
  27. package/src/pkg/managers.js +59 -0
  28. package/src/pkg/package-names.js +229 -0
  29. package/src/pkg/script-urls.js +100 -0
  30. package/src/platform/index.js +129 -0
  31. package/src/platform/os-release.js +27 -0
  32. package/src/platform/shell.js +111 -0
  33. package/src/reports.js +37 -0
  34. package/src/router.js +157 -0
  35. package/src/setup/catalog.js +13 -13
  36. package/src/setup/git-step.js +128 -0
  37. package/src/setup/loader.js +108 -0
  38. package/src/setup/prompts.js +259 -0
  39. package/src/setup/registry.js +48 -0
  40. package/src/setup/spinner.js +67 -0
  41. package/src/setup/wizard.js +319 -195
  42. package/src/upgrade/index.js +499 -0
  43. package/src/utils/closest-match.js +36 -0
  44. package/src/utils/dotfiles-sync.js +89 -89
  45. package/src/utils/file-conflict-handler.js +30 -30
  46. package/bin/adm-tui +0 -13
  47. package/src/ai/knowledge.js +0 -61
  48. package/src/commands/connect.js +0 -76
  49. package/src/commands/issue.js +0 -80
  50. package/src/commands/pr.js +0 -55
  51. package/src/commands/theme.js +0 -103
  52. package/src/commands/upgrade.js +0 -114
  53. package/src/installer/git-config.js +0 -34
  54. package/src/installer/node-installer.js +0 -50
  55. package/src/installer/package-manager-installer.js +0 -31
  56. package/src/installer/ssh-setup.js +0 -31
  57. package/src/installer/system-packages.js +0 -49
  58. package/src/installers/index.js +0 -66
  59. package/src/integrations/ai-backend.js +0 -60
  60. package/src/integrations/ai-providers/registry.js +0 -261
  61. package/src/integrations/github.js +0 -107
  62. package/src/integrations/gitlab.js +0 -121
  63. package/src/plugins/context.js +0 -64
  64. package/src/plugins/loader.js +0 -50
  65. package/src/plugins/runner.js +0 -25
  66. package/src/setup/installer.js +0 -192
  67. package/src/shell/detector.js +0 -10
  68. package/src/tui/app-state.js +0 -374
  69. package/src/tui/app.js +0 -442
  70. package/src/tui/autocomplete.js +0 -57
  71. package/src/tui/commands/registry.js +0 -823
  72. package/src/tui/components/SetupScreen.js +0 -292
  73. package/src/ui/animations.js +0 -24
  74. package/src/ui/spinner.js +0 -34
  75. package/src/ui/theme/cyberpunk.js +0 -14
  76. package/src/ui/theme/dark.js +0 -14
  77. package/src/ui/theme/forest.js +0 -14
  78. package/src/ui/theme/light.js +0 -14
  79. package/src/ui/theme/monokai.js +0 -14
  80. package/src/ui/theme/nord.js +0 -14
  81. package/src/ui/theme.js +0 -49
  82. package/src/utils/command-history.js +0 -43
  83. package/src/utils/error-handler.js +0 -69
  84. package/src/utils/fuzzy-search.js +0 -20
  85. package/src/utils/github-error-handler.js +0 -26
  86. package/src/utils/keychain.js +0 -97
  87. package/src/utils/open-url.js +0 -15
  88. package/src/utils/terminal-detection.js +0 -26
  89. package/src/utils/token-encryption.js +0 -96
package/src/tui/app.js DELETED
@@ -1,442 +0,0 @@
1
- const React = require('react');
2
- const { spawn } = require('child_process');
3
- const { createAppState } = require('./app-state');
4
- const { createSetupScreen } = require('./components/SetupScreen');
5
- const { resolveTheme } = require('../ui/theme');
6
- const { readConfig } = require('../config');
7
-
8
- function createApp(ink) {
9
- const { Box, Text, useInput, useApp } = ink;
10
- const SetupScreen = createSetupScreen(ink);
11
-
12
- return function App() {
13
- const stateRef = React.useRef(createAppState());
14
- _lastAppState = stateRef.current;
15
- const [messages, setMessages] = React.useState(stateRef.current.messages);
16
- const [input, setInput] = React.useState('');
17
- const [showSetup, setShowSetup] = React.useState(false);
18
- const [spinnerFrame, setSpinnerFrame] = React.useState(0);
19
- const { exit } = useApp();
20
-
21
- const rerender = () => {
22
- setMessages([...stateRef.current.messages]);
23
- setShowSetup(stateRef.current.showSetup);
24
- };
25
-
26
- React.useEffect(() => {
27
- if (!stateRef.current.aiLoading && !stateRef.current.upgradeLoading) return;
28
- const id = setInterval(() => setSpinnerFrame(f => f + 1), 50);
29
- return () => clearInterval(id);
30
- });
31
-
32
- useInput(async (ch, key) => {
33
- // When setup screen is active, delegate input handling
34
- if (stateRef.current.showSetup) {
35
- if (key.escape) {
36
- stateRef.current.exitSetup();
37
- rerender();
38
- }
39
- return;
40
- }
41
-
42
- // Connect mode — platform selection
43
- if (stateRef.current.connectStep === 'select') {
44
- if (key.upArrow) {
45
- stateRef.current.moveConnectCursor(-1);
46
- rerender();
47
- return;
48
- }
49
- if (key.downArrow) {
50
- stateRef.current.moveConnectCursor(1);
51
- rerender();
52
- return;
53
- }
54
- if (key.return) {
55
- stateRef.current.selectConnectPlatform();
56
- setInput('');
57
- rerender();
58
- return;
59
- }
60
- if (key.escape) {
61
- stateRef.current.cancelConnect();
62
- rerender();
63
- return;
64
- }
65
- return;
66
- }
67
-
68
- // Connect mode — token input
69
- if (stateRef.current.connectStep === 'token') {
70
- if (key.escape) {
71
- stateRef.current.cancelConnect();
72
- setInput('');
73
- rerender();
74
- return;
75
- }
76
- if (key.return) {
77
- const token = input.trim();
78
- if (token) {
79
- await stateRef.current.submitConnectToken(token);
80
- }
81
- setInput('');
82
- rerender();
83
- return;
84
- }
85
- if (key.backspace || key.delete) {
86
- setInput(prev => prev.slice(0, -1));
87
- return;
88
- }
89
- if (ch && !key.ctrl && !key.meta) {
90
- setInput(prev => prev + ch);
91
- }
92
- return;
93
- }
94
-
95
- // GitHub hub — action selection
96
- if (stateRef.current.githubStep === 'select') {
97
- if (key.upArrow) {
98
- stateRef.current.moveGithubCursor(-1);
99
- rerender();
100
- return;
101
- }
102
- if (key.downArrow) {
103
- stateRef.current.moveGithubCursor(1);
104
- rerender();
105
- return;
106
- }
107
- if (key.return) {
108
- await stateRef.current.selectGithubItem();
109
- setInput('');
110
- rerender();
111
- return;
112
- }
113
- if (key.escape) {
114
- stateRef.current.cancelGithub();
115
- rerender();
116
- return;
117
- }
118
- return;
119
- }
120
-
121
- // Model token input
122
- if (stateRef.current.modelStep === 'token') {
123
- if (key.escape) {
124
- stateRef.current.cancelModelToken();
125
- setInput('');
126
- rerender();
127
- return;
128
- }
129
- if (key.return) {
130
- const token = input.trim();
131
- if (token) {
132
- await stateRef.current.submitModelToken(token);
133
- }
134
- setInput('');
135
- rerender();
136
- return;
137
- }
138
- if (key.backspace || key.delete) {
139
- setInput(prev => prev.slice(0, -1));
140
- return;
141
- }
142
- if (ch && !key.ctrl && !key.meta) {
143
- setInput(prev => prev + ch);
144
- }
145
- return;
146
- }
147
-
148
- // Confirmation prompt — y/N
149
- if (stateRef.current.confirmStep) {
150
- if (ch === 'y' || ch === 'Y') {
151
- const result = await stateRef.current.confirmAction();
152
- rerender();
153
- if (result.shouldExit) {
154
- exit();
155
- }
156
- if (result.shouldRestart) {
157
- spawn('adm', [], { detached: true, stdio: 'ignore' }).unref();
158
- exit();
159
- }
160
- return;
161
- }
162
- // Any other key (including n, Enter, Esc) cancels
163
- stateRef.current.confirmCancel();
164
- rerender();
165
- return;
166
- }
167
-
168
- const ac = stateRef.current.autocomplete;
169
-
170
- // Arrow keys — navigate autocomplete when visible
171
- if (key.upArrow && ac.visible) {
172
- stateRef.current.moveAutocompleteUp();
173
- rerender();
174
- return;
175
- }
176
- if (key.downArrow && ac.visible) {
177
- stateRef.current.moveAutocompleteDown();
178
- rerender();
179
- return;
180
- }
181
-
182
- // Esc — close autocomplete if visible, else exit AI mode
183
- if (key.escape) {
184
- if (ac.visible) {
185
- stateRef.current.closeAutocomplete();
186
- rerender();
187
- return;
188
- }
189
- stateRef.current.exitAI();
190
- rerender();
191
- return;
192
- }
193
-
194
- if (key.ctrl && ch === 'c') {
195
- exit();
196
- return;
197
- }
198
-
199
- // Tab — fill prompt with selected command
200
- if (key.tab) {
201
- if (ac.visible && ac.items.length > 0) {
202
- const selected = stateRef.current.selectAutocompleteActive();
203
- if (selected) {
204
- setInput(selected + ' ');
205
- stateRef.current.closeAutocomplete();
206
- rerender();
207
- }
208
- }
209
- return;
210
- }
211
-
212
- if (key.return) {
213
- // If autocomplete is visible, execute selected command immediately
214
- if (ac.visible && ac.items.length > 0) {
215
- const selected = stateRef.current.selectAutocompleteActive();
216
- if (selected) {
217
- const cmd = selected.trim();
218
- stateRef.current.messages.push({ text: cmd, type: 'user' });
219
- stateRef.current.closeAutocomplete();
220
- setInput('');
221
- rerender();
222
- const result = await stateRef.current.processInput(cmd);
223
- rerender();
224
- if (result.shouldExit) {
225
- exit();
226
- }
227
- }
228
- return;
229
- }
230
- const trimmed = input.trim();
231
- if (trimmed === '') return;
232
- stateRef.current.messages.push({ text: trimmed, type: 'user' });
233
- setInput('');
234
- // Pre-render so loading spinners appear immediately
235
- if (stateRef.current.aiMode || trimmed === 'upgrade' || trimmed === '/upgrade') {
236
- rerender();
237
- }
238
- const result = await stateRef.current.processInput(trimmed);
239
- rerender();
240
- if (result.shouldExit) {
241
- exit();
242
- }
243
- return;
244
- }
245
-
246
- if (key.backspace || key.delete) {
247
- const next = input.slice(0, -1);
248
- setInput(next);
249
- if (!next.startsWith('/')) {
250
- stateRef.current.closeAutocomplete();
251
- } else {
252
- stateRef.current.updateAutocomplete(next);
253
- }
254
- rerender();
255
- return;
256
- }
257
-
258
- if (ch && !key.ctrl && !key.meta) {
259
- const next = input + ch;
260
- setInput(next);
261
- if (!stateRef.current.aiMode && next.startsWith('/')) {
262
- stateRef.current.updateAutocomplete(next);
263
- rerender();
264
- }
265
- }
266
- });
267
-
268
- const bar = stateRef.current.getStatusBar();
269
- const aiMode = bar.aiMode;
270
- const providerLabel = bar.activeProvider ? ` (${bar.activeProvider})` : '';
271
-
272
- // Resolve theme colors from current theme name
273
- const themeName = stateRef.current.themeState.current;
274
- let tc;
275
- try {
276
- tc = resolveTheme({ theme: themeName }).colors;
277
- } catch {
278
- tc = resolveTheme({}).colors;
279
- }
280
-
281
- // When setup is active, render SetupScreen instead of main view
282
- if (stateRef.current.showSetup) {
283
- return React.createElement(
284
- Box,
285
- { flexDirection: 'column' },
286
- // StatusBar
287
- React.createElement(
288
- Box,
289
- { borderStyle: 'single', borderColor: tc.muted, paddingX: 1 },
290
- React.createElement(Text, { color: tc.primary }, bar.themeName),
291
- React.createElement(Text, { color: tc.muted }, ' │ '),
292
- React.createElement(Text, { color: tc.accent }, 'Setup Wizard'),
293
- React.createElement(Text, { color: tc.muted }, ' │ '),
294
- React.createElement(Text, { color: tc.warning }, bar.version),
295
- ),
296
- React.createElement(SetupScreen, {
297
- onExit: () => {
298
- stateRef.current.exitSetup();
299
- stateRef.current.markSetupDone();
300
- rerender();
301
- },
302
- dryRun: stateRef.current.setupDryRun,
303
- platform: process.platform,
304
- }),
305
- );
306
- }
307
-
308
- return React.createElement(
309
- Box,
310
- { flexDirection: 'column' },
311
- // StatusBar
312
- React.createElement(
313
- Box,
314
- { borderStyle: 'single', borderColor: tc.muted, paddingX: 1 },
315
- React.createElement(Text, { color: tc.primary }, bar.themeName),
316
- React.createElement(Text, { color: tc.muted }, ' │ '),
317
- React.createElement(Text, { color: aiMode ? tc.success : tc.muted }, `AI: ${aiMode ? 'ON' : 'off'}${providerLabel}`),
318
- React.createElement(Text, { color: tc.muted }, ' │ '),
319
- React.createElement(Text, { color: tc.warning }, bar.version),
320
- ),
321
- // Messages
322
- React.createElement(
323
- Box,
324
- { flexDirection: 'column', flexGrow: 1, paddingX: 1 },
325
- ...messages.map((msg, i) => {
326
- if (msg.type === 'user') {
327
- return React.createElement(
328
- Box,
329
- { key: i, marginTop: 0, marginBottom: 0 },
330
- React.createElement(Text, { wrap: 'wrap', backgroundColor: tc.muted, color: tc.text }, ` ${msg.text} `),
331
- );
332
- }
333
- return React.createElement(Text, { key: i, wrap: 'wrap', color: msg.type === 'ai-error' ? tc.error || tc.text : tc.text }, msg.text);
334
- }),
335
- ),
336
- // AI responding indicator
337
- stateRef.current.aiLoading && React.createElement(
338
- Box,
339
- { paddingX: 1 },
340
- React.createElement(Text, { color: tc.accent }, `${'◢◣◤◥'[spinnerFrame % 4]} Responding...`),
341
- ),
342
- // Connect selection list
343
- stateRef.current.connectStep === 'select' && React.createElement(
344
- Box,
345
- { flexDirection: 'column', paddingX: 1 },
346
- React.createElement(
347
- Box,
348
- null,
349
- React.createElement(Text, { color: stateRef.current.connectCursor === 0 ? tc.accent : tc.muted },
350
- stateRef.current.connectCursor === 0 ? ' ● GitHub' : ' ○ GitHub'),
351
- ),
352
- React.createElement(
353
- Box,
354
- null,
355
- React.createElement(Text, { color: stateRef.current.connectCursor === 1 ? tc.accent : tc.muted },
356
- stateRef.current.connectCursor === 1 ? ' ● GitLab' : ' ○ GitLab'),
357
- ),
358
- React.createElement(Text, { dimColor: true, color: tc.muted }, ' ↑↓ select · Enter confirm · ESC cancel'),
359
- ),
360
- // GitHub hub selection list
361
- stateRef.current.githubStep === 'select' && React.createElement(
362
- Box,
363
- { flexDirection: 'column', paddingX: 1 },
364
- ...['Status', 'PRs', 'Issues', 'Commits'].map((label, i) =>
365
- React.createElement(
366
- Box,
367
- { key: i },
368
- React.createElement(Text, { color: stateRef.current.githubCursor === i ? tc.accent : tc.muted },
369
- stateRef.current.githubCursor === i ? ` ● ${label}` : ` ○ ${label}`),
370
- )
371
- ),
372
- React.createElement(Text, { dimColor: true, color: tc.muted }, ' ↑↓ select · Enter confirm · ESC cancel'),
373
- ),
374
- // Upgrade loading spinner
375
- stateRef.current.upgradeLoading && React.createElement(
376
- Box,
377
- { paddingX: 1 },
378
- React.createElement(Text, { color: tc.accent }, `${'◢◣◤◥'[spinnerFrame % 4]} Checking for updates...`),
379
- ),
380
- // Confirmation prompt
381
- stateRef.current.confirmStep && React.createElement(
382
- Box,
383
- { paddingX: 1 },
384
- React.createElement(Text, { color: tc.warning }, `${stateRef.current.confirmStep.message} [y/N] `),
385
- React.createElement(Text, { dimColor: true }, '█'),
386
- ),
387
- // Input bar — hidden during confirmation prompt
388
- !stateRef.current.confirmStep && (() => {
389
- const connectToken = stateRef.current.connectStep === 'token';
390
- const modelToken = stateRef.current.modelStep === 'token';
391
- const masked = connectToken || modelToken;
392
- const label = aiMode ? 'AI> ' : connectToken ? 'Token: ' : modelToken ? 'API Key: ' : '> ';
393
- const labelColor = aiMode ? tc.secondary : (connectToken || modelToken) ? tc.warning : tc.success;
394
- const borderColor = aiMode ? tc.secondary : (connectToken || modelToken) ? tc.warning : tc.muted;
395
- const textColor = aiMode ? tc.secondary : tc.text;
396
- const placeholder = (!masked && !aiMode) ? stateRef.current.getPlaceholder(input) : null;
397
- return React.createElement(
398
- Box,
399
- { borderStyle: 'single', borderColor, paddingX: 1 },
400
- React.createElement(Text, { color: labelColor }, label),
401
- React.createElement(Text, { color: textColor }, masked ? '*'.repeat(input.length) : input),
402
- React.createElement(Text, { dimColor: true }, '█'),
403
- placeholder && React.createElement(Text, { dimColor: true, color: tc.muted }, placeholder),
404
- );
405
- })(),
406
- // Autocomplete list — below input bar
407
- stateRef.current.autocomplete.visible && stateRef.current.autocomplete.items.length > 0 && React.createElement(
408
- Box,
409
- { flexDirection: 'column', paddingX: 1 },
410
- ...stateRef.current.autocomplete.items.map((item, i) => {
411
- const active = i === stateRef.current.autocomplete.selectedIndex;
412
- return React.createElement(
413
- Box,
414
- { key: item.name },
415
- React.createElement(Text, { bold: true, color: active ? tc.accent : tc.muted },
416
- active ? ' ● /' + item.name : ' ○ /' + item.name),
417
- React.createElement(Text, { color: active ? tc.text : tc.muted },
418
- ' ' + item.description),
419
- );
420
- }),
421
- React.createElement(Text, { dimColor: true, color: tc.muted }, ' ↑↓ navigate · Tab select · Enter run · ESC close'),
422
- ),
423
- );
424
- };
425
- }
426
-
427
- let _lastAppState = null;
428
-
429
- async function boot() {
430
- const ink = await import('ink');
431
- const App = createApp(ink);
432
- const { render } = ink;
433
- const { waitUntilExit } = render(React.createElement(App));
434
- await waitUntilExit();
435
-
436
- // After TUI exits, show PATH refresh hint if setup installed tools
437
- if (_lastAppState && _lastAppState.setupInstalled) {
438
- console.log('\n Tools installed! Run: source ~/.zshrc\n');
439
- }
440
- }
441
-
442
- module.exports = { createApp, boot };
@@ -1,57 +0,0 @@
1
- function createAutocomplete(getSuggestions, getCommandInfo) {
2
- return {
3
- visible: false,
4
- items: [],
5
- selectedIndex: 0,
6
- _getSuggestions: getSuggestions,
7
- _getCommandInfo: getCommandInfo,
8
- };
9
- }
10
-
11
- function updateAutocomplete(state, input) {
12
- if (!input.startsWith('/')) {
13
- state.visible = false;
14
- state.items = [];
15
- state.selectedIndex = 0;
16
- return;
17
- }
18
-
19
- const partial = input.slice(1);
20
- const names = state._getSuggestions(partial);
21
- const items = names.map(name => state._getCommandInfo(name)).filter(Boolean);
22
- state.visible = true;
23
- state.items = items;
24
- if (state.selectedIndex >= items.length) {
25
- state.selectedIndex = Math.max(0, items.length - 1);
26
- }
27
- }
28
-
29
- function moveUp(state) {
30
- if (!state.visible || state.items.length === 0) return;
31
- state.selectedIndex = (state.selectedIndex - 1 + state.items.length) % state.items.length;
32
- }
33
-
34
- function moveDown(state) {
35
- if (!state.visible || state.items.length === 0) return;
36
- state.selectedIndex = (state.selectedIndex + 1) % state.items.length;
37
- }
38
-
39
- function selectActive(state) {
40
- if (!state.visible || state.items.length === 0) return null;
41
- return '/' + state.items[state.selectedIndex].name;
42
- }
43
-
44
- function closeAutocomplete(state) {
45
- state.visible = false;
46
- state.items = [];
47
- state.selectedIndex = 0;
48
- }
49
-
50
- module.exports = {
51
- createAutocomplete,
52
- updateAutocomplete,
53
- moveUp,
54
- moveDown,
55
- selectActive,
56
- closeAutocomplete,
57
- };