@devkitvault/recall 1.0.0 → 1.0.2

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 (40) hide show
  1. package/README.md +11 -22
  2. package/dist/commands/alias.js +84 -0
  3. package/dist/commands/approve.js +108 -0
  4. package/dist/commands/audit.js +58 -0
  5. package/dist/commands/auth/forgot.js +33 -0
  6. package/dist/commands/auth/index.js +16 -0
  7. package/dist/commands/auth/login.js +151 -0
  8. package/dist/commands/auth/logout.js +15 -0
  9. package/dist/commands/auth/register.js +84 -0
  10. package/dist/commands/auth/reset.js +43 -0
  11. package/dist/commands/completion.js +158 -0
  12. package/dist/commands/config.js +89 -0
  13. package/dist/commands/delete.js +58 -0
  14. package/dist/commands/doctor.js +106 -0
  15. package/dist/commands/env.js +135 -0
  16. package/dist/commands/export.js +68 -0
  17. package/dist/commands/feedback.js +25 -0
  18. package/dist/commands/group.js +206 -0
  19. package/dist/commands/history.js +41 -0
  20. package/dist/commands/import.js +126 -0
  21. package/dist/commands/list.js +69 -0
  22. package/dist/commands/org.js +408 -0
  23. package/dist/commands/pin.js +30 -0
  24. package/dist/commands/run.js +55 -0
  25. package/dist/commands/save.js +73 -0
  26. package/dist/commands/search.js +82 -0
  27. package/dist/commands/share.js +88 -0
  28. package/dist/commands/snippet.js +147 -0
  29. package/dist/commands/support.js +96 -0
  30. package/dist/commands/sync.js +80 -0
  31. package/dist/commands/template.js +177 -0
  32. package/dist/commands/update.js +66 -0
  33. package/dist/commands/upgrade.js +122 -0
  34. package/dist/commands/whoami.js +70 -0
  35. package/dist/index.js +71 -0
  36. package/dist/lib/api.js +51 -0
  37. package/dist/lib/auth.js +88 -0
  38. package/dist/lib/config.js +37 -0
  39. package/package.json +12 -7
  40. package/bin/recall.js +0 -169
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.completionCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const ALL_COMMANDS = [
10
+ 'auth', 'save', 'list', 'run', 'delete', 'search', 'update',
11
+ 'export', 'import', 'sync', 'whoami', 'upgrade', 'config',
12
+ 'pin', 'alias', 'history', 'share', 'snippet', 'env',
13
+ 'group', 'org', 'audit', 'approve', 'template', 'doctor',
14
+ 'completion',
15
+ ];
16
+ const AUTH_CMDS = ['register', 'login', 'logout', 'forgot', 'reset'];
17
+ const GROUP_CMDS = ['create', 'list', 'show', 'add', 'remove', 'delete', 'rename'];
18
+ const ORG_CMDS = ['create', 'members', 'invite', 'join', 'role', 'remove', 'leave', 'delete', 'save', 'commands', 'run', 'search'];
19
+ const ALIAS_CMDS = ['set', 'list', 'delete'];
20
+ const SHARE_CMDS = ['create', 'list', 'delete'];
21
+ const SNIPPET_CMDS = ['save', 'list', 'show', 'run', 'delete'];
22
+ const ENV_CMDS = ['save', 'list', 'show', 'use', 'delete'];
23
+ const TEMPLATE_CMDS = ['save', 'list', 'show', 'run', 'delete'];
24
+ const APPROVE_CMDS = ['request', 'grant', 'list'];
25
+ const CONFIG_CMDS = ['show', 'set', 'switch', 'reset'];
26
+ function bashCompletion() {
27
+ return `# recall bash completion
28
+ # Add this to ~/.bashrc or ~/.bash_profile:
29
+ # eval "$(recall completion bash)"
30
+
31
+ _recall_completion() {
32
+ local cur prev words
33
+ COMPREPLY=()
34
+ cur="\${COMP_WORDS[COMP_CWORD]}"
35
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
36
+ words=("\${COMP_WORDS[@]}")
37
+
38
+ local commands="${ALL_COMMANDS.join(' ')}"
39
+
40
+ case "\${words[1]}" in
41
+ auth) COMPREPLY=($(compgen -W "${AUTH_CMDS.join(' ')}" -- "$cur")) ;;
42
+ group) COMPREPLY=($(compgen -W "${GROUP_CMDS.join(' ')}" -- "$cur")) ;;
43
+ org) COMPREPLY=($(compgen -W "${ORG_CMDS.join(' ')}" -- "$cur")) ;;
44
+ alias) COMPREPLY=($(compgen -W "${ALIAS_CMDS.join(' ')}" -- "$cur")) ;;
45
+ share) COMPREPLY=($(compgen -W "${SHARE_CMDS.join(' ')}" -- "$cur")) ;;
46
+ snippet) COMPREPLY=($(compgen -W "${SNIPPET_CMDS.join(' ')}" -- "$cur")) ;;
47
+ env) COMPREPLY=($(compgen -W "${ENV_CMDS.join(' ')}" -- "$cur")) ;;
48
+ template) COMPREPLY=($(compgen -W "${TEMPLATE_CMDS.join(' ')}" -- "$cur")) ;;
49
+ approve) COMPREPLY=($(compgen -W "${APPROVE_CMDS.join(' ')}" -- "$cur")) ;;
50
+ config) COMPREPLY=($(compgen -W "${CONFIG_CMDS.join(' ')}" -- "$cur")) ;;
51
+ *) COMPREPLY=($(compgen -W "$commands" -- "$cur")) ;;
52
+ esac
53
+
54
+ return 0
55
+ }
56
+
57
+ complete -F _recall_completion recall`;
58
+ }
59
+ function zshCompletion() {
60
+ return `# recall zsh completion
61
+ # Add this to ~/.zshrc:
62
+ # eval "$(recall completion zsh)"
63
+
64
+ _recall() {
65
+ local state
66
+
67
+ _arguments \\
68
+ '1: :->command' \\
69
+ '*: :->args'
70
+
71
+ case $state in
72
+ command)
73
+ local commands=(${ALL_COMMANDS.map(c => `'${c}'`).join(' ')})
74
+ _describe 'command' commands
75
+ ;;
76
+ args)
77
+ case $words[2] in
78
+ auth) local sub=(${AUTH_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
79
+ group) local sub=(${GROUP_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
80
+ org) local sub=(${ORG_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
81
+ alias) local sub=(${ALIAS_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
82
+ share) local sub=(${SHARE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
83
+ snippet) local sub=(${SNIPPET_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
84
+ env) local sub=(${ENV_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
85
+ template) local sub=(${TEMPLATE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
86
+ approve) local sub=(${APPROVE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
87
+ config) local sub=(${CONFIG_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
88
+ esac
89
+ ;;
90
+ esac
91
+ }
92
+
93
+ compdef _recall recall`;
94
+ }
95
+ function psCompletion() {
96
+ return `# recall PowerShell completion
97
+ # Add this to your PowerShell profile ($PROFILE):
98
+ # recall completion ps | Out-String | Invoke-Expression
99
+
100
+ Register-ArgumentCompleter -Native -CommandName recall -ScriptBlock {
101
+ param($wordToComplete, $commandAst, $cursorPosition)
102
+
103
+ $commands = @(${ALL_COMMANDS.map(c => `'${c}'`).join(', ')})
104
+ $tokens = $commandAst.CommandElements
105
+
106
+ $subCommands = @{
107
+ 'auth' = @(${AUTH_CMDS.map(c => `'${c}'`).join(', ')})
108
+ 'group' = @(${GROUP_CMDS.map(c => `'${c}'`).join(', ')})
109
+ 'org' = @(${ORG_CMDS.map(c => `'${c}'`).join(', ')})
110
+ 'alias' = @(${ALIAS_CMDS.map(c => `'${c}'`).join(', ')})
111
+ 'share' = @(${SHARE_CMDS.map(c => `'${c}'`).join(', ')})
112
+ 'snippet' = @(${SNIPPET_CMDS.map(c => `'${c}'`).join(', ')})
113
+ 'env' = @(${ENV_CMDS.map(c => `'${c}'`).join(', ')})
114
+ 'template' = @(${TEMPLATE_CMDS.map(c => `'${c}'`).join(', ')})
115
+ 'approve' = @(${APPROVE_CMDS.map(c => `'${c}'`).join(', ')})
116
+ 'config' = @(${CONFIG_CMDS.map(c => `'${c}'`).join(', ')})
117
+ }
118
+
119
+ if ($tokens.Count -ge 2) {
120
+ $mainCmd = $tokens[1].Value
121
+ if ($subCommands.ContainsKey($mainCmd)) {
122
+ $subCommands[$mainCmd] | Where-Object { $_ -like "$wordToComplete*" } |
123
+ ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
124
+ return
125
+ }
126
+ }
127
+
128
+ $commands | Where-Object { $_ -like "$wordToComplete*" } |
129
+ ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
130
+ }`;
131
+ }
132
+ exports.completionCommand = new commander_1.Command('completion')
133
+ .description('Generate shell completion script')
134
+ .argument('<shell>', 'Shell type: bash, zsh, or ps')
135
+ .action((shell) => {
136
+ switch (shell.toLowerCase()) {
137
+ case 'bash':
138
+ console.log(bashCompletion());
139
+ console.log(chalk_1.default.dim('\n# Install: add this to ~/.bashrc'));
140
+ console.log(chalk_1.default.dim('# eval "$(recall completion bash)"'));
141
+ break;
142
+ case 'zsh':
143
+ console.log(zshCompletion());
144
+ console.log(chalk_1.default.dim('\n# Install: add this to ~/.zshrc'));
145
+ console.log(chalk_1.default.dim('# eval "$(recall completion zsh)"'));
146
+ break;
147
+ case 'ps':
148
+ case 'powershell':
149
+ console.log(psCompletion());
150
+ console.log(chalk_1.default.dim('\n# Install: add this to your PowerShell profile'));
151
+ console.log(chalk_1.default.dim('# recall completion ps | Out-String | Invoke-Expression'));
152
+ break;
153
+ default:
154
+ console.error(chalk_1.default.red(`\n Unknown shell: ${shell}`));
155
+ console.log(chalk_1.default.dim(' Supported: bash, zsh, ps\n'));
156
+ process.exit(1);
157
+ }
158
+ });
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const config_1 = require("../lib/config");
11
+ const configShow = new commander_1.Command('show')
12
+ .description('Show current CLI config')
13
+ .action(() => {
14
+ const config = (0, config_1.getConfig)();
15
+ console.log();
16
+ console.log(` ${chalk_1.default.dim('Environment:')} ${chalk_1.default.cyan(config.env ?? 'custom')}`);
17
+ console.log(` ${chalk_1.default.dim('API URL:')} ${chalk_1.default.white(config.apiUrl)}`);
18
+ console.log(` ${chalk_1.default.dim('Username:')} ${chalk_1.default.white(config.username ?? '—')}`);
19
+ console.log();
20
+ });
21
+ const configSet = new commander_1.Command('set')
22
+ .description('Set a config value')
23
+ .argument('<key>', 'Config key: apiUrl')
24
+ .argument('<value>', 'Config value')
25
+ .action((key, value) => {
26
+ if (key !== 'apiUrl') {
27
+ console.error(chalk_1.default.red(`\n Unknown config key: ${key}\n`));
28
+ console.log(chalk_1.default.dim(' Available keys: apiUrl\n'));
29
+ process.exit(1);
30
+ }
31
+ (0, config_1.saveConfig)({ [key]: value, env: 'custom' });
32
+ console.log(chalk_1.default.green(`\n Set ${key} = ${value}\n`));
33
+ });
34
+ const configSwitch = new commander_1.Command('switch')
35
+ .description('Switch between environments')
36
+ .action(async () => {
37
+ const config = (0, config_1.getConfig)();
38
+ const choices = [
39
+ ...Object.entries(config_1.ENVIRONMENTS).map(([name, url]) => ({
40
+ name: `${name.padEnd(12)} ${chalk_1.default.dim(url)}`,
41
+ value: name,
42
+ })),
43
+ { name: 'custom (enter URL manually)', value: 'custom' },
44
+ ];
45
+ const { env } = await inquirer_1.default.prompt([{
46
+ type: 'list',
47
+ name: 'env',
48
+ message: 'Switch to which environment?',
49
+ choices,
50
+ default: config.env ?? 'production',
51
+ }]);
52
+ if (env === 'custom') {
53
+ const { url } = await inquirer_1.default.prompt([{
54
+ type: 'input',
55
+ name: 'url',
56
+ message: 'Enter API URL:',
57
+ default: config.apiUrl,
58
+ validate: (v) => v.startsWith('http') || 'Must start with http:// or https://',
59
+ }]);
60
+ (0, config_1.saveConfig)({ apiUrl: url, env: 'custom' });
61
+ console.log(chalk_1.default.green(`\n Switched to custom: ${url}\n`));
62
+ }
63
+ else {
64
+ (0, config_1.saveConfig)({ apiUrl: config_1.ENVIRONMENTS[env], env });
65
+ console.log(chalk_1.default.green(`\n Switched to ${env}: ${config_1.ENVIRONMENTS[env]}\n`));
66
+ }
67
+ });
68
+ const configReset = new commander_1.Command('reset')
69
+ .description('Reset config to defaults')
70
+ .action(async () => {
71
+ const { confirm } = await inquirer_1.default.prompt([{
72
+ type: 'confirm',
73
+ name: 'confirm',
74
+ message: 'Reset all config to defaults?',
75
+ default: false,
76
+ }]);
77
+ if (!confirm) {
78
+ console.log(chalk_1.default.dim('\n Cancelled.\n'));
79
+ return;
80
+ }
81
+ (0, config_1.saveConfig)({ apiUrl: config_1.ENVIRONMENTS.production, env: 'production', username: undefined });
82
+ console.log(chalk_1.default.green('\n Config reset to production defaults.\n'));
83
+ });
84
+ exports.configCommand = new commander_1.Command('config')
85
+ .description('Manage CLI configuration')
86
+ .addCommand(configShow.name('dump'))
87
+ .addCommand(configSet.name('put'))
88
+ .addCommand(configSwitch.name('env'))
89
+ .addCommand(configReset.name('flush'));
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deleteCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const api_1 = require("../lib/api");
12
+ const auth_1 = require("../lib/auth");
13
+ exports.deleteCommand = new commander_1.Command('delete')
14
+ .description('Delete a saved command by name')
15
+ .argument('<name>', 'Command name to delete')
16
+ .option('-f, --force', 'Skip confirmation prompt')
17
+ .action(async (name, opts) => {
18
+ const token = await (0, auth_1.requireAuth)();
19
+ // First look up the command to confirm it exists
20
+ const spinner = (0, ora_1.default)('Looking up...').start();
21
+ let cmd;
22
+ try {
23
+ cmd = await api_1.ApiClient.get(`/commands/by-name/${encodeURIComponent(name)}`, token);
24
+ spinner.stop();
25
+ }
26
+ catch {
27
+ spinner.fail(chalk_1.default.red(`Command "${name}" not found`));
28
+ process.exit(1);
29
+ }
30
+ // Show what will be deleted
31
+ console.log();
32
+ console.log(` ${chalk_1.default.dim('Name:')} ${chalk_1.default.white(cmd.name ?? '—')}`);
33
+ console.log(` ${chalk_1.default.dim('Command:')} ${chalk_1.default.white(cmd.command)}`);
34
+ console.log();
35
+ // Confirm unless --force
36
+ if (!opts.force) {
37
+ const { confirm } = await inquirer_1.default.prompt([
38
+ {
39
+ type: 'confirm',
40
+ name: 'confirm',
41
+ message: chalk_1.default.red(`Delete "${name}"?`),
42
+ default: false,
43
+ },
44
+ ]);
45
+ if (!confirm) {
46
+ console.log(chalk_1.default.dim('\n Cancelled.\n'));
47
+ return;
48
+ }
49
+ }
50
+ const deleteSpinner = (0, ora_1.default)('Deleting...').start();
51
+ try {
52
+ await api_1.ApiClient.delete(`/commands/${cmd.id}`, token);
53
+ deleteSpinner.succeed(chalk_1.default.green(`Deleted "${name}"`));
54
+ }
55
+ catch {
56
+ deleteSpinner.fail(chalk_1.default.red('Failed to delete'));
57
+ }
58
+ });
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.doctorCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const api_1 = require("../lib/api");
10
+ const auth_1 = require("../lib/auth");
11
+ const config_1 = require("../lib/config");
12
+ const CHECK = chalk_1.default.green('✔');
13
+ const FAIL = chalk_1.default.red('✖');
14
+ const WARN = chalk_1.default.yellow('!');
15
+ async function check(label, fn) {
16
+ try {
17
+ const result = await fn();
18
+ const icon = result.ok ? CHECK : FAIL;
19
+ console.log(` ${icon} ${label.padEnd(28)} ${result.ok ? chalk_1.default.dim(result.info) : chalk_1.default.red(result.info)}`);
20
+ return result.ok;
21
+ }
22
+ catch (err) {
23
+ console.log(` ${FAIL} ${label.padEnd(28)} ${chalk_1.default.red(err.message ?? 'failed')}`);
24
+ return false;
25
+ }
26
+ }
27
+ exports.doctorCommand = new commander_1.Command('doctor')
28
+ .description('Check your recall setup and connectivity')
29
+ .action(async () => {
30
+ console.log(chalk_1.default.bold('\n recall doctor\n'));
31
+ const config = (0, config_1.getConfig)();
32
+ const version = config_1.APP_VERSION;
33
+ let allOk = true;
34
+ // 1. Config file
35
+ const configOk = await check('Config file', async () => ({
36
+ ok: true,
37
+ info: `API URL: ${config.apiUrl}`,
38
+ }));
39
+ if (!configOk)
40
+ allOk = false;
41
+ // 2. API reachable
42
+ const apiOk = await check('API reachable', async () => {
43
+ const res = await fetch(`${config.apiUrl}/health`);
44
+ const data = await res.json();
45
+ return {
46
+ ok: data.status === 'ok',
47
+ info: `${config.apiUrl}/health`,
48
+ };
49
+ });
50
+ if (!apiOk)
51
+ allOk = false;
52
+ // 3. Token exists
53
+ const token = await (0, auth_1.getToken)();
54
+ const tokenOk = await check('Auth token', async () => ({
55
+ ok: !!token,
56
+ info: token ? 'Found in keychain' : 'Not found — run: recall auth login',
57
+ }));
58
+ if (!tokenOk)
59
+ allOk = false;
60
+ // 4. Token valid
61
+ if (token) {
62
+ const authOk = await check('Token valid', async () => {
63
+ const { user } = await api_1.ApiClient.get('/auth/me', token);
64
+ return {
65
+ ok: true,
66
+ info: `${user.username} — ${user.plan} plan`,
67
+ };
68
+ });
69
+ if (!authOk)
70
+ allOk = false;
71
+ // 5. Plan info
72
+ await check('Plan', async () => {
73
+ const { user, stats } = await api_1.ApiClient.get('/auth/me', token);
74
+ return {
75
+ ok: true,
76
+ info: `${user.plan} — ${stats.commandCount} commands saved`,
77
+ };
78
+ });
79
+ }
80
+ // 6. Version
81
+ await check('CLI version', async () => ({
82
+ ok: true,
83
+ info: `v${version}`,
84
+ }));
85
+ // 7. Environment
86
+ await check('Environment', async () => {
87
+ const isLocal = config.apiUrl.includes('127.0.0.1') ||
88
+ config.apiUrl.includes('localhost');
89
+ return {
90
+ ok: true,
91
+ info: isLocal ? 'local' : 'production',
92
+ };
93
+ });
94
+ console.log();
95
+ if (allOk) {
96
+ console.log(chalk_1.default.green(' Everything looks good!\n'));
97
+ }
98
+ else {
99
+ console.log(chalk_1.default.yellow(' Some checks failed. See above for details.\n'));
100
+ console.log(chalk_1.default.dim(' Common fixes:'));
101
+ console.log(chalk_1.default.dim(' · Not logged in? run: recall auth login'));
102
+ console.log(chalk_1.default.dim(' · API not running? cd packages/api && pnpm dev'));
103
+ console.log(chalk_1.default.dim(' · Wrong API URL? run: recall config switch'));
104
+ console.log();
105
+ }
106
+ });
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.envCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const cli_table3_1 = __importDefault(require("cli-table3"));
9
+ const commander_1 = require("commander");
10
+ const inquirer_1 = __importDefault(require("inquirer"));
11
+ const ora_1 = __importDefault(require("ora"));
12
+ const api_1 = require("../lib/api");
13
+ const auth_1 = require("../lib/auth");
14
+ const envSave = new commander_1.Command('save')
15
+ .argument('<name>', 'Environment set name')
16
+ .description('Save a set of environment variables')
17
+ .action(async (name) => {
18
+ const token = await (0, auth_1.requireAuth)();
19
+ console.log(chalk_1.default.bold(`\n Save env set: ${name}\n`));
20
+ console.log(chalk_1.default.dim(' Enter variables one by one. Leave name empty to finish.\n'));
21
+ const vars = {};
22
+ while (true) {
23
+ const { key } = await inquirer_1.default.prompt([{
24
+ type: 'input',
25
+ name: 'key',
26
+ message: 'Variable name (empty to finish):',
27
+ }]);
28
+ if (!key.trim())
29
+ break;
30
+ const { value } = await inquirer_1.default.prompt([{
31
+ type: 'input',
32
+ name: 'value',
33
+ message: `Value for ${key}:`,
34
+ }]);
35
+ vars[key] = value;
36
+ }
37
+ if (!Object.keys(vars).length) {
38
+ console.log(chalk_1.default.dim('\n No variables entered. Cancelled.\n'));
39
+ return;
40
+ }
41
+ const spinner = (0, ora_1.default)('Saving...').start();
42
+ try {
43
+ await api_1.ApiClient.post('/env', { name, vars }, token);
44
+ spinner.succeed(chalk_1.default.green(`Saved env set "${name}" with ${Object.keys(vars).length} variables`));
45
+ }
46
+ catch (err) {
47
+ spinner.fail(chalk_1.default.red(err.message ?? 'Failed to save'));
48
+ }
49
+ });
50
+ const envList = new commander_1.Command('list')
51
+ .description('List all your env sets')
52
+ .action(async () => {
53
+ const token = await (0, auth_1.requireAuth)();
54
+ const spinner = (0, ora_1.default)('Fetching...').start();
55
+ try {
56
+ const { envSets } = await api_1.ApiClient.get('/env', token);
57
+ spinner.stop();
58
+ if (!envSets.length) {
59
+ console.log(chalk_1.default.dim('\n No env sets yet. Run: recall env save <name>\n'));
60
+ return;
61
+ }
62
+ const table = new cli_table3_1.default({
63
+ head: [chalk_1.default.cyan('Name'), chalk_1.default.cyan('Created')],
64
+ style: { head: [], border: ['grey'] },
65
+ colWidths: [30, 20],
66
+ });
67
+ for (const e of envSets) {
68
+ table.push([chalk_1.default.white(e.name), new Date(e.createdAt).toLocaleDateString()]);
69
+ }
70
+ console.log(table.toString());
71
+ }
72
+ catch {
73
+ spinner.fail(chalk_1.default.red('Failed to fetch env sets'));
74
+ }
75
+ });
76
+ const envShow = new commander_1.Command('show')
77
+ .argument('<name>', 'Env set name')
78
+ .description('Show all variables in an env set')
79
+ .action(async (name) => {
80
+ const token = await (0, auth_1.requireAuth)();
81
+ const spinner = (0, ora_1.default)('Fetching...').start();
82
+ try {
83
+ const envSet = await api_1.ApiClient.get(`/env/${encodeURIComponent(name)}`, token);
84
+ spinner.stop();
85
+ console.log(chalk_1.default.bold(`\n ${envSet.name}\n`));
86
+ for (const [key, value] of Object.entries(envSet.vars)) {
87
+ console.log(` ${chalk_1.default.cyan(key)}=${chalk_1.default.white(String(value))}`);
88
+ }
89
+ console.log();
90
+ }
91
+ catch {
92
+ spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
93
+ }
94
+ });
95
+ const envUse = new commander_1.Command('use')
96
+ .argument('<name>', 'Env set name')
97
+ .description('Print export commands to load env set into your shell')
98
+ .action(async (name) => {
99
+ const token = await (0, auth_1.requireAuth)();
100
+ const spinner = (0, ora_1.default)('Fetching...').start();
101
+ try {
102
+ const envSet = await api_1.ApiClient.get(`/env/${encodeURIComponent(name)}`, token);
103
+ spinner.stop();
104
+ console.log(chalk_1.default.dim(`\n # Run this to load "${name}" into your shell:`));
105
+ console.log(chalk_1.default.dim(' # eval $(recall env use ' + name + ')\n'));
106
+ for (const [key, value] of Object.entries(envSet.vars)) {
107
+ console.log(`export ${key}="${value}"`);
108
+ }
109
+ console.log();
110
+ }
111
+ catch {
112
+ spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
113
+ }
114
+ });
115
+ const envDelete = new commander_1.Command('delete')
116
+ .argument('<name>', 'Env set name')
117
+ .description('Delete an env set')
118
+ .action(async (name) => {
119
+ const token = await (0, auth_1.requireAuth)();
120
+ const spinner = (0, ora_1.default)('Deleting...').start();
121
+ try {
122
+ await api_1.ApiClient.delete(`/env/${encodeURIComponent(name)}`, token);
123
+ spinner.succeed(chalk_1.default.green(`Deleted env set "${name}"`));
124
+ }
125
+ catch {
126
+ spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
127
+ }
128
+ });
129
+ exports.envCommand = new commander_1.Command('env')
130
+ .description('Save and use environment variable sets')
131
+ .addCommand(envSave)
132
+ .addCommand(envList)
133
+ .addCommand(envShow)
134
+ .addCommand(envUse)
135
+ .addCommand(envDelete);
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.exportCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const api_1 = require("../lib/api");
13
+ const auth_1 = require("../lib/auth");
14
+ exports.exportCommand = new commander_1.Command('export')
15
+ .description('Export your saved commands')
16
+ .option('-f, --format <format>', 'Export format: json or sh', 'json')
17
+ .option('-o, --output <file>', 'Output file path')
18
+ .action(async (opts) => {
19
+ const token = await (0, auth_1.requireAuth)();
20
+ const spinner = (0, ora_1.default)('Fetching commands...').start();
21
+ try {
22
+ const { commands } = await api_1.ApiClient.get('/commands', token);
23
+ spinner.stop();
24
+ if (!commands.length) {
25
+ console.log(chalk_1.default.dim('\n No commands to export.\n'));
26
+ return;
27
+ }
28
+ const format = opts.format.toLowerCase();
29
+ if (format !== 'json' && format !== 'sh') {
30
+ console.error(chalk_1.default.red('\n Invalid format. Use: json or sh\n'));
31
+ process.exit(1);
32
+ }
33
+ let output = '';
34
+ if (format === 'json') {
35
+ output = JSON.stringify(commands, null, 2);
36
+ }
37
+ else {
38
+ // Shell script format
39
+ const lines = [
40
+ '#!/bin/sh',
41
+ '# recall export — generated ' + new Date().toISOString(),
42
+ '# Run any of these commands by copy-pasting or sourcing this file',
43
+ '',
44
+ ];
45
+ for (const cmd of commands) {
46
+ if (cmd.name)
47
+ lines.push(`# ${cmd.name}`);
48
+ if (cmd.tags?.length)
49
+ lines.push(`# tags: ${cmd.tags.join(', ')}`);
50
+ lines.push(cmd.command);
51
+ lines.push('');
52
+ }
53
+ output = lines.join('\n');
54
+ }
55
+ // Write to file or stdout
56
+ if (opts.output) {
57
+ const filePath = path_1.default.resolve(opts.output);
58
+ fs_1.default.writeFileSync(filePath, output, 'utf-8');
59
+ console.log(chalk_1.default.green(`\n Exported ${commands.length} commands to ${filePath}\n`));
60
+ }
61
+ else {
62
+ console.log(output);
63
+ }
64
+ }
65
+ catch {
66
+ spinner.fail(chalk_1.default.red('Export failed'));
67
+ }
68
+ });
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.feedbackCommand = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const commander_1 = require("commander");
9
+ const open_1 = __importDefault(require("open"));
10
+ exports.feedbackCommand = new commander_1.Command('feedback')
11
+ .description('Send feedback or report a bug')
12
+ .option('-b, --bug', 'Report a bug')
13
+ .option('-i, --idea', 'Suggest a feature')
14
+ .action(async (opts) => {
15
+ let url = 'https://devkitvault.com/recall/feedback';
16
+ if (opts.bug)
17
+ url += '?type=bug';
18
+ if (opts.idea)
19
+ url += '?type=idea';
20
+ console.log();
21
+ console.log(chalk_1.default.bold(' Opening feedback page...'));
22
+ console.log(chalk_1.default.dim(` ${url}`));
23
+ console.log();
24
+ await (0, open_1.default)(url);
25
+ });