@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
@@ -1,114 +0,0 @@
1
- const { spawn } = require('child_process');
2
- const chalk = require('chalk');
3
- const { version: currentVersion } = require('../../package.json');
4
-
5
- const FRAMES = ['◢', '◣', '◤', '◥'];
6
-
7
- function createLoader(text) {
8
- let i = 0;
9
- let active = true;
10
- const interval = setInterval(() => {
11
- i = (i + 1) % FRAMES.length;
12
- process.stdout.write(`\r${chalk.cyan(FRAMES[i])} ${text}`);
13
- }, 80);
14
-
15
- return {
16
- update(t) { text = t; },
17
- succeed(msg) {
18
- active = false;
19
- clearInterval(interval);
20
- process.stdout.write(`\r${chalk.green('✔')} ${msg || text}\n`);
21
- },
22
- fail(msg) {
23
- active = false;
24
- clearInterval(interval);
25
- process.stdout.write(`\r${chalk.red('✖')} ${msg || text}\n`);
26
- },
27
- info(msg) {
28
- active = false;
29
- clearInterval(interval);
30
- process.stdout.write(`\r${chalk.yellow('ℹ')} ${msg || text}\n`);
31
- },
32
- stop() {
33
- if (!active) return;
34
- active = false;
35
- clearInterval(interval);
36
- process.stdout.write('\r' + ' '.repeat((text || '').length + 4) + '\r');
37
- },
38
- };
39
- }
40
-
41
- function runSpawn(cmd, args) {
42
- return new Promise((resolve, reject) => {
43
- let stdout = '';
44
- let stderr = '';
45
- const child = spawn(cmd, args, { stdio: 'pipe' });
46
- child.stdout.on('data', (data) => { stdout += data.toString(); });
47
- child.stderr.on('data', (data) => { stderr += data.toString(); });
48
- child.on('close', (code) => {
49
- if (code === 0) resolve(stdout.trim());
50
- else reject(new Error(stderr.trim() || `Command failed with code ${code}`));
51
- });
52
- });
53
- }
54
-
55
- async function upgrade() {
56
- const spinner = createLoader('Checking for updates...');
57
-
58
- let latestVersion;
59
- try {
60
- const output = await runSpawn('npm', ['view', '@crystalgames/adm', 'version']);
61
- latestVersion = output.split('\n').pop().trim();
62
- } catch (err) {
63
- spinner.fail('Update check failed');
64
- return {
65
- output: chalk.red(`Unable to check for updates: ${err.message}`),
66
- shouldExit: false,
67
- shouldClear: false,
68
- };
69
- }
70
-
71
- if (latestVersion === currentVersion) {
72
- spinner.succeed(chalk.green(`Already up to date (v${currentVersion})`));
73
- return {
74
- output: '',
75
- shouldExit: false,
76
- shouldClear: false,
77
- };
78
- }
79
-
80
- spinner.info(chalk.yellow(`Update available: v${currentVersion} → v${latestVersion}`));
81
-
82
- return {
83
- output: `Update to v${latestVersion}? [y/N]`,
84
- needsConfirm: true,
85
- confirmMessage: `Update available: v${currentVersion} → v${latestVersion}`,
86
- async onConfirm() {
87
- const installSpinner = createLoader(`Updating to v${latestVersion}...`);
88
- try {
89
- await runSpawn('npm', ['install', '-g', '@crystalgames/adm@latest']);
90
- installSpinner.succeed(chalk.green(`Updated to v${latestVersion}`));
91
- return {
92
- output: chalk.green(`Restarting...`),
93
- shouldRestart: true,
94
- shouldExit: false,
95
- shouldClear: false,
96
- };
97
- } catch (err) {
98
- installSpinner.fail(chalk.red('Update failed'));
99
- return {
100
- output: chalk.red(`Update failed: ${err.message}`),
101
- shouldExit: false,
102
- shouldClear: false,
103
- };
104
- }
105
- },
106
- onCancel() {
107
- return { output: chalk.gray('Cancelled.'), shouldExit: false, shouldClear: false };
108
- },
109
- shouldExit: false,
110
- shouldClear: false,
111
- };
112
- }
113
-
114
- module.exports = { upgrade };
@@ -1,34 +0,0 @@
1
- const child_process = require('child_process');
2
- const fs = require('fs');
3
- const path = require('os').homedir;
4
-
5
- function getCurrentGitConfig() {
6
- const name = tryExec('git config --global user.name');
7
- const email = tryExec('git config --global user.email');
8
- const gpgsign = tryExec('git config --global commit.gpgsign');
9
- return { name, email, gpgsign };
10
- }
11
-
12
- function setGitConfig(key, value) {
13
- if (!value) return false;
14
- child_process.execSync(`git config --global ${key} "${value}"`, { stdio: 'pipe' });
15
- return true;
16
- }
17
-
18
- async function configureGit({ name, email, gpgsign }) {
19
- const results = {};
20
- if (name) results.name = setGitConfig('user.name', name);
21
- if (email) results.email = setGitConfig('user.email', email);
22
- if (gpgsign) results.gpgsign = setGitConfig('commit.gpgsign', gpgsign);
23
- return results;
24
- }
25
-
26
- function tryExec(cmd) {
27
- try {
28
- return child_process.execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim() || null;
29
- } catch {
30
- return null;
31
- }
32
- }
33
-
34
- module.exports = { getCurrentGitConfig, setGitConfig, configureGit };
@@ -1,50 +0,0 @@
1
- const child_process = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
5
-
6
- const NVM_DIR = path.join(os.homedir(), '.nvm');
7
-
8
- function isNodeInstalled() {
9
- try {
10
- child_process.execSync('command -v node >/dev/null 2>&1', { shell: '/bin/bash', stdio: 'pipe' });
11
- const version = child_process.execSync('node -v', { encoding: 'utf8' }).trim();
12
- return { installed: true, version };
13
- } catch {
14
- return { installed: false, version: null };
15
- }
16
- }
17
-
18
- function isNvmInstalled() {
19
- return fs.existsSync(path.join(NVM_DIR, 'nvm.sh'));
20
- }
21
-
22
- async function installNvm() {
23
- const cmd = 'curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash';
24
- child_process.execSync(cmd, { shell: '/bin/bash', stdio: 'pipe' });
25
- return true;
26
- }
27
-
28
- async function installNode(version = '--lts') {
29
- const nvmSource = isNvmInstalled()
30
- ? `source "${NVM_DIR}/nvm.sh"`
31
- : '';
32
-
33
- if (!isNvmInstalled()) {
34
- await installNvm();
35
- }
36
-
37
- const cmd = `${nvmSource || `source "${NVM_DIR}/nvm.sh"`} && nvm install ${version}`;
38
- const out = child_process.execSync(cmd, { shell: '/bin/bash', encoding: 'utf8', stdio: 'pipe' });
39
- return out.trim();
40
- }
41
-
42
- async function getNodeVersion() {
43
- try {
44
- return child_process.execSync('node -v', { encoding: 'utf8' }).trim();
45
- } catch {
46
- return null;
47
- }
48
- }
49
-
50
- module.exports = { isNodeInstalled, isNvmInstalled, installNvm, installNode, getNodeVersion };
@@ -1,31 +0,0 @@
1
- const child_process = require('child_process');
2
-
3
- function isPnpmInstalled() {
4
- try {
5
- const v = child_process.execSync('pnpm -v', { encoding: 'utf8', stdio: 'pipe' }).trim();
6
- return { installed: true, version: v };
7
- } catch {
8
- return { installed: false, version: null };
9
- }
10
- }
11
-
12
- function isNpmInstalled() {
13
- try {
14
- const v = child_process.execSync('npm -v', { encoding: 'utf8', stdio: 'pipe' }).trim();
15
- return { installed: true, version: v };
16
- } catch {
17
- return { installed: false, version: null };
18
- }
19
- }
20
-
21
- async function installPnpm() {
22
- child_process.execSync('npm install -g pnpm', { encoding: 'utf8', stdio: 'pipe' });
23
- return isPnpmInstalled();
24
- }
25
-
26
- async function installNpm() {
27
- // npm comes with node; just verify
28
- return isNpmInstalled();
29
- }
30
-
31
- module.exports = { isPnpmInstalled, isNpmInstalled, installPnpm, installNpm };
@@ -1,31 +0,0 @@
1
- const child_process = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
5
-
6
- const SSH_DIR = path.join(os.homedir(), '.ssh');
7
- const DEFAULT_KEY = path.join(SSH_DIR, 'id_ed25519');
8
-
9
- function sshKeyExists(keyPath = DEFAULT_KEY) {
10
- return fs.existsSync(keyPath) || fs.existsSync(keyPath + '.pub');
11
- }
12
-
13
- function generateSshKey(email, keyPath = DEFAULT_KEY) {
14
- if (sshKeyExists(keyPath)) {
15
- return { skipped: true, reason: 'Key already exists' };
16
- }
17
- if (!fs.existsSync(SSH_DIR)) {
18
- fs.mkdirSync(SSH_DIR, { recursive: true, mode: 0o700 });
19
- }
20
- const cmd = `ssh-keygen -t ed25519 -C "${email}" -f "${keyPath}" -N ""`;
21
- child_process.execSync(cmd, { stdio: 'pipe' });
22
- return { skipped: false, keyPath };
23
- }
24
-
25
- function getPublicKey(keyPath = DEFAULT_KEY) {
26
- const pubPath = keyPath + '.pub';
27
- if (!fs.existsSync(pubPath)) return null;
28
- return fs.readFileSync(pubPath, 'utf8').trim();
29
- }
30
-
31
- module.exports = { sshKeyExists, generateSshKey, getPublicKey };
@@ -1,49 +0,0 @@
1
- const child_process = require('child_process');
2
- const os = require('os');
3
-
4
- function detectPlatform() {
5
- return os.platform();
6
- }
7
-
8
- function isBrewInstalled() {
9
- try {
10
- child_process.execSync('brew --version', { stdio: 'pipe' });
11
- return true;
12
- } catch {
13
- return false;
14
- }
15
- }
16
-
17
- function isAptInstalled() {
18
- try {
19
- child_process.execSync('dpkg -l apt', { stdio: 'pipe' });
20
- return true;
21
- } catch {
22
- return false;
23
- }
24
- }
25
-
26
- async function installBrew() {
27
- const cmd = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
28
- child_process.execSync(cmd, { shell: '/bin/bash', stdio: 'pipe' });
29
- }
30
-
31
- async function installAptPackages(packages = ['git', 'curl', 'build-essential']) {
32
- const pkg = packages.join(' ');
33
- child_process.execSync(`sudo apt-get update && sudo apt-get install -y ${pkg}`, {
34
- shell: '/bin/bash', stdio: 'pipe',
35
- });
36
- }
37
-
38
- async function installSystemPackage(pkg) {
39
- const platform = detectPlatform();
40
- const cmd = platform === 'darwin'
41
- ? `brew install ${pkg}`
42
- : `sudo apt-get install -y ${pkg}`;
43
- child_process.execSync(cmd, { shell: '/bin/bash', stdio: 'pipe' });
44
- }
45
-
46
- module.exports = {
47
- detectPlatform, isBrewInstalled, isAptInstalled,
48
- installBrew, installAptPackages, installSystemPackage,
49
- };
@@ -1,66 +0,0 @@
1
- const os = require('os');
2
- const child_process = require('child_process');
3
-
4
- function detectOS(env = process.env) {
5
- const platform = env.ADM_PLATFORM_OVERRIDE || os.platform();
6
- if (platform === 'darwin') return 'darwin';
7
- if (platform === 'linux') return 'linux';
8
- return 'unknown';
9
- }
10
-
11
- function planInstallers(platform) {
12
- platform = platform || detectOS();
13
- const actions = [];
14
- // Node (via nvm), pnpm/npm, git always recommended
15
- actions.push({ name: 'git', reason: 'required for repos' });
16
- actions.push({ name: 'node', method: 'nvm', reason: 'runtime for tools' });
17
- actions.push({ name: 'pnpm', method: 'pnpm', reason: 'fast package manager' });
18
-
19
- if (platform === 'darwin') {
20
- actions.unshift({ name: 'brew', reason: 'Homebrew package manager' });
21
- } else if (platform === 'linux') {
22
- actions.unshift({ name: 'apt', reason: 'APT package manager (Debian/Ubuntu)' });
23
- }
24
-
25
- return actions;
26
- }
27
-
28
- async function runInstallers(opts = {}) {
29
- const dry = opts.dryRun || process.env.ADM_DRY_RUN === '1';
30
- const plat = detectOS(opts.env);
31
- const planned = planInstallers(plat);
32
- if (dry) {
33
- return { dryRun: true, platform: plat, planned };
34
- }
35
-
36
- // By default do NOT perform network or sudo actions unless ADM_EXECUTE=1 is set.
37
- const execute = process.env.ADM_EXECUTE === '1' || opts.execute;
38
- const results = [];
39
- for (const a of planned) {
40
- const cmd = commandForAction(a, plat);
41
- if (!execute) {
42
- results.push({ name: a.name, command: cmd, status: 'skipped' });
43
- continue;
44
- }
45
- try {
46
- // run command synchronously; caller must ensure ADM_EXECUTE=1 intentionally
47
- const out = child_process.execSync(cmd, { encoding: 'utf8', stdio: 'pipe' });
48
- results.push({ name: a.name, command: cmd, status: 'executed', stdout: out });
49
- } catch (err) {
50
- results.push({ name: a.name, command: cmd, status: 'failed', error: err.message });
51
- }
52
- }
53
- return { dryRun: false, platform: plat, planned, executed: results };
54
- }
55
-
56
- function commandForAction(a, platform) {
57
- // Return a shell command that would perform the action (idempotent when possible)
58
- if (a.name === 'brew') return 'brew update && brew --version >/dev/null 2>&1 || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
59
- if (a.name === 'apt') return 'sudo apt-get update && sudo apt-get install -y git curl';
60
- if (a.name === 'git') return 'git --version || sudo apt-get install -y git || brew install git';
61
- if (a.name === 'node') return 'command -v node >/dev/null 2>&1 || curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash';
62
- if (a.name === 'pnpm') return 'command -v pnpm >/dev/null 2>&1 || npm install -g pnpm';
63
- return '# unknown action';
64
- }
65
-
66
- module.exports = { detectOS, planInstallers, runInstallers };
@@ -1,60 +0,0 @@
1
- const FLASH_MODELS = ['glm-4.7-flash', 'glm-4-flash', 'glm-z1-flash'];
2
- const DEFAULT_MAX_TOKENS = 1024;
3
- const API_BASE = 'https://open.bigmodel.cn/api/paas/v4';
4
-
5
- function createClient({ apiKey } = {}) {
6
- const key = apiKey || process.env.GLM_API_KEY;
7
- if (!key) return null;
8
- return { apiKey: key, model: FLASH_MODELS[0] };
9
- }
10
-
11
- async function query(prompt, options = {}) {
12
- const client = options.client || createClient({ apiKey: options.apiKey });
13
- if (!client) throw new Error('AI not configured. Set GLM_API_KEY or run /setup.');
14
-
15
- const models = options.model ? [options.model] : FLASH_MODELS;
16
- let lastError;
17
-
18
- for (const model of models) {
19
- const body = {
20
- model,
21
- max_tokens: options.maxTokens || DEFAULT_MAX_TOKENS,
22
- messages: options.messages || [{ role: 'user', content: prompt }],
23
- };
24
-
25
- try {
26
- const res = await fetch(`${API_BASE}/chat/completions`, {
27
- method: 'POST',
28
- headers: {
29
- 'Content-Type': 'application/json',
30
- 'Authorization': `Bearer ${client.apiKey}`,
31
- },
32
- body: JSON.stringify(body),
33
- });
34
-
35
- if (!res.ok) {
36
- const text = await res.text().catch(() => '');
37
- if (res.status === 429 && models.indexOf(model) < models.length - 1) {
38
- lastError = text;
39
- continue;
40
- }
41
- throw new Error(`API returned ${res.status}: ${text}`);
42
- }
43
-
44
- const response = await res.json();
45
- const choice = response.choices && response.choices[0];
46
- if (!choice || !choice.message) throw new Error('AI returned empty response.');
47
- return choice.message.content;
48
- } catch (err) {
49
- const msg = err.message || '';
50
- if (msg.includes('401') || msg.includes('auth')) {
51
- throw new Error(`AI authentication failed. Check your API key. (${msg})`);
52
- }
53
- lastError = msg;
54
- }
55
- }
56
-
57
- throw new Error(`AI request failed (all models tried): ${lastError}`);
58
- }
59
-
60
- module.exports = { createClient, query };