aliasmate 1.6.2 → 2.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 (211) hide show
  1. package/CHANGELOG.md +61 -3
  2. package/README.md +84 -884
  3. package/dist/cli/completion.d.ts +5 -0
  4. package/dist/cli/completion.d.ts.map +1 -0
  5. package/dist/cli/completion.js +144 -0
  6. package/dist/cli/completion.js.map +1 -0
  7. package/dist/cli/manage.d.ts +19 -0
  8. package/dist/cli/manage.d.ts.map +1 -0
  9. package/dist/cli/manage.js +231 -0
  10. package/dist/cli/manage.js.map +1 -0
  11. package/dist/cli/run.d.ts +6 -0
  12. package/dist/cli/run.d.ts.map +1 -0
  13. package/dist/cli/run.js +48 -0
  14. package/dist/cli/run.js.map +1 -0
  15. package/dist/cli/save.d.ts +8 -0
  16. package/dist/cli/save.d.ts.map +1 -0
  17. package/dist/cli/save.js +90 -0
  18. package/dist/cli/save.js.map +1 -0
  19. package/dist/cli/transfer.d.ts +6 -0
  20. package/dist/cli/transfer.d.ts.map +1 -0
  21. package/dist/cli/transfer.js +99 -0
  22. package/dist/cli/transfer.js.map +1 -0
  23. package/dist/core/commands.d.ts +29 -0
  24. package/dist/core/commands.d.ts.map +1 -0
  25. package/dist/core/commands.js +232 -0
  26. package/dist/core/commands.js.map +1 -0
  27. package/dist/core/env.d.ts +9 -0
  28. package/dist/core/env.d.ts.map +1 -0
  29. package/dist/core/env.js +45 -0
  30. package/dist/core/env.js.map +1 -0
  31. package/dist/core/executor.d.ts +7 -0
  32. package/dist/core/executor.d.ts.map +1 -0
  33. package/dist/{utils → core}/executor.js +10 -46
  34. package/dist/core/executor.js.map +1 -0
  35. package/dist/core/history.d.ts +11 -0
  36. package/dist/core/history.d.ts.map +1 -0
  37. package/dist/core/history.js +117 -0
  38. package/dist/core/history.js.map +1 -0
  39. package/dist/core/recent.d.ts +14 -0
  40. package/dist/core/recent.d.ts.map +1 -0
  41. package/dist/core/recent.js +54 -0
  42. package/dist/core/recent.js.map +1 -0
  43. package/dist/core/runner.d.ts +13 -0
  44. package/dist/core/runner.d.ts.map +1 -0
  45. package/dist/core/runner.js +83 -0
  46. package/dist/core/runner.js.map +1 -0
  47. package/dist/core/store.d.ts +29 -0
  48. package/dist/core/store.d.ts.map +1 -0
  49. package/dist/core/store.js +131 -0
  50. package/dist/core/store.js.map +1 -0
  51. package/dist/core/transfer.d.ts +20 -0
  52. package/dist/core/transfer.d.ts.map +1 -0
  53. package/dist/core/transfer.js +110 -0
  54. package/dist/core/transfer.js.map +1 -0
  55. package/dist/core/types.d.ts +29 -0
  56. package/dist/core/types.d.ts.map +1 -0
  57. package/dist/core/types.js +3 -0
  58. package/dist/core/types.js.map +1 -0
  59. package/dist/core/validate.d.ts +6 -0
  60. package/dist/core/validate.d.ts.map +1 -0
  61. package/dist/core/validate.js +139 -0
  62. package/dist/core/validate.js.map +1 -0
  63. package/dist/index.d.ts +3 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +172 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/ui/format.d.ts +19 -0
  68. package/dist/ui/format.d.ts.map +1 -0
  69. package/dist/ui/format.js +155 -0
  70. package/dist/ui/format.js.map +1 -0
  71. package/dist/ui/interactive.d.ts +62 -0
  72. package/dist/ui/interactive.d.ts.map +1 -0
  73. package/dist/ui/interactive.js +657 -0
  74. package/dist/ui/interactive.js.map +1 -0
  75. package/dist/ui/onboarding.d.ts +3 -0
  76. package/dist/ui/onboarding.d.ts.map +1 -0
  77. package/dist/ui/onboarding.js +82 -0
  78. package/dist/ui/onboarding.js.map +1 -0
  79. package/dist/ui/prompts.d.ts +3 -0
  80. package/dist/ui/prompts.d.ts.map +1 -0
  81. package/dist/ui/prompts.js +50 -0
  82. package/dist/ui/prompts.js.map +1 -0
  83. package/dist/ui/table.d.ts +18 -0
  84. package/dist/ui/table.d.ts.map +1 -0
  85. package/dist/ui/table.js +54 -0
  86. package/dist/ui/table.js.map +1 -0
  87. package/dist/ui/theme.d.ts +30 -0
  88. package/dist/ui/theme.d.ts.map +1 -0
  89. package/dist/ui/theme.js +58 -0
  90. package/dist/ui/theme.js.map +1 -0
  91. package/package.json +10 -11
  92. package/dist/cli.d.ts +0 -3
  93. package/dist/cli.d.ts.map +0 -1
  94. package/dist/cli.js +0 -253
  95. package/dist/cli.js.map +0 -1
  96. package/dist/commands/alias.d.ts +0 -30
  97. package/dist/commands/alias.d.ts.map +0 -1
  98. package/dist/commands/alias.js +0 -213
  99. package/dist/commands/alias.js.map +0 -1
  100. package/dist/commands/changelog.d.ts +0 -9
  101. package/dist/commands/changelog.d.ts.map +0 -1
  102. package/dist/commands/changelog.js +0 -72
  103. package/dist/commands/changelog.js.map +0 -1
  104. package/dist/commands/completion.d.ts +0 -31
  105. package/dist/commands/completion.d.ts.map +0 -1
  106. package/dist/commands/completion.js +0 -522
  107. package/dist/commands/completion.js.map +0 -1
  108. package/dist/commands/delete.d.ts +0 -14
  109. package/dist/commands/delete.d.ts.map +0 -1
  110. package/dist/commands/delete.js +0 -54
  111. package/dist/commands/delete.js.map +0 -1
  112. package/dist/commands/edit.d.ts +0 -16
  113. package/dist/commands/edit.d.ts.map +0 -1
  114. package/dist/commands/edit.js +0 -231
  115. package/dist/commands/edit.js.map +0 -1
  116. package/dist/commands/export.d.ts +0 -23
  117. package/dist/commands/export.d.ts.map +0 -1
  118. package/dist/commands/export.js +0 -122
  119. package/dist/commands/export.js.map +0 -1
  120. package/dist/commands/import.d.ts +0 -23
  121. package/dist/commands/import.d.ts.map +0 -1
  122. package/dist/commands/import.js +0 -235
  123. package/dist/commands/import.js.map +0 -1
  124. package/dist/commands/list.d.ts +0 -25
  125. package/dist/commands/list.d.ts.map +0 -1
  126. package/dist/commands/list.js +0 -58
  127. package/dist/commands/list.js.map +0 -1
  128. package/dist/commands/prev.d.ts +0 -15
  129. package/dist/commands/prev.d.ts.map +0 -1
  130. package/dist/commands/prev.js +0 -138
  131. package/dist/commands/prev.js.map +0 -1
  132. package/dist/commands/recent.d.ts +0 -24
  133. package/dist/commands/recent.d.ts.map +0 -1
  134. package/dist/commands/recent.js +0 -132
  135. package/dist/commands/recent.js.map +0 -1
  136. package/dist/commands/run.d.ts +0 -22
  137. package/dist/commands/run.d.ts.map +0 -1
  138. package/dist/commands/run.js +0 -217
  139. package/dist/commands/run.js.map +0 -1
  140. package/dist/commands/save.d.ts +0 -17
  141. package/dist/commands/save.d.ts.map +0 -1
  142. package/dist/commands/save.js +0 -232
  143. package/dist/commands/save.js.map +0 -1
  144. package/dist/commands/search.d.ts +0 -19
  145. package/dist/commands/search.d.ts.map +0 -1
  146. package/dist/commands/search.js +0 -113
  147. package/dist/commands/search.js.map +0 -1
  148. package/dist/commands/validate.d.ts +0 -10
  149. package/dist/commands/validate.d.ts.map +0 -1
  150. package/dist/commands/validate.js +0 -162
  151. package/dist/commands/validate.js.map +0 -1
  152. package/dist/storage/index.d.ts +0 -117
  153. package/dist/storage/index.d.ts.map +0 -1
  154. package/dist/storage/index.js +0 -262
  155. package/dist/storage/index.js.map +0 -1
  156. package/dist/utils/changelog.d.ts +0 -41
  157. package/dist/utils/changelog.d.ts.map +0 -1
  158. package/dist/utils/changelog.js +0 -192
  159. package/dist/utils/changelog.js.map +0 -1
  160. package/dist/utils/constants.d.ts +0 -48
  161. package/dist/utils/constants.d.ts.map +0 -1
  162. package/dist/utils/constants.js +0 -51
  163. package/dist/utils/constants.js.map +0 -1
  164. package/dist/utils/env.d.ts +0 -64
  165. package/dist/utils/env.d.ts.map +0 -1
  166. package/dist/utils/env.js +0 -199
  167. package/dist/utils/env.js.map +0 -1
  168. package/dist/utils/errors.d.ts +0 -30
  169. package/dist/utils/errors.d.ts.map +0 -1
  170. package/dist/utils/errors.js +0 -62
  171. package/dist/utils/errors.js.map +0 -1
  172. package/dist/utils/executor.d.ts +0 -33
  173. package/dist/utils/executor.d.ts.map +0 -1
  174. package/dist/utils/executor.js.map +0 -1
  175. package/dist/utils/formatters.d.ts +0 -30
  176. package/dist/utils/formatters.d.ts.map +0 -1
  177. package/dist/utils/formatters.js +0 -188
  178. package/dist/utils/formatters.js.map +0 -1
  179. package/dist/utils/history.d.ts +0 -37
  180. package/dist/utils/history.d.ts.map +0 -1
  181. package/dist/utils/history.js +0 -179
  182. package/dist/utils/history.js.map +0 -1
  183. package/dist/utils/llm-generator.d.ts +0 -21
  184. package/dist/utils/llm-generator.d.ts.map +0 -1
  185. package/dist/utils/llm-generator.js +0 -915
  186. package/dist/utils/llm-generator.js.map +0 -1
  187. package/dist/utils/onboarding.d.ts +0 -10
  188. package/dist/utils/onboarding.d.ts.map +0 -1
  189. package/dist/utils/onboarding.js +0 -316
  190. package/dist/utils/onboarding.js.map +0 -1
  191. package/dist/utils/paths.d.ts +0 -44
  192. package/dist/utils/paths.d.ts.map +0 -1
  193. package/dist/utils/paths.js +0 -115
  194. package/dist/utils/paths.js.map +0 -1
  195. package/dist/utils/prompts.d.ts +0 -66
  196. package/dist/utils/prompts.d.ts.map +0 -1
  197. package/dist/utils/prompts.js +0 -51
  198. package/dist/utils/prompts.js.map +0 -1
  199. package/dist/utils/recent.d.ts +0 -55
  200. package/dist/utils/recent.d.ts.map +0 -1
  201. package/dist/utils/recent.js +0 -102
  202. package/dist/utils/recent.js.map +0 -1
  203. package/dist/utils/validator.d.ts +0 -56
  204. package/dist/utils/validator.d.ts.map +0 -1
  205. package/dist/utils/validator.js +0 -413
  206. package/dist/utils/validator.js.map +0 -1
  207. package/dist/utils/version-checker.d.ts +0 -29
  208. package/dist/utils/version-checker.d.ts.map +0 -1
  209. package/dist/utils/version-checker.js +0 -191
  210. package/dist/utils/version-checker.js.map +0 -1
  211. package/whats-new.json +0 -217
@@ -0,0 +1,657 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Tui = void 0;
37
+ exports.fuzzyMatch = fuzzyMatch;
38
+ exports.interactiveHome = interactiveHome;
39
+ const readline = __importStar(require("readline"));
40
+ const commands_1 = require("../core/commands");
41
+ const recent_1 = require("../core/recent");
42
+ const transfer_1 = require("../core/transfer");
43
+ const env_1 = require("../core/env");
44
+ const format_1 = require("./format");
45
+ const theme_1 = require("./theme");
46
+ /** Simple subsequence fuzzy match: every query char appears in order. */
47
+ function fuzzyMatch(query, text) {
48
+ const q = query.toLowerCase();
49
+ const t = text.toLowerCase();
50
+ let i = 0;
51
+ for (const ch of t) {
52
+ if (ch === q[i])
53
+ i++;
54
+ if (i === q.length)
55
+ return true;
56
+ }
57
+ return q.length === 0;
58
+ }
59
+ /** Shared caret editing for form fields and single-line inputs. */
60
+ function editText(field, str, key) {
61
+ if (key.name === 'left') {
62
+ field.cursor = Math.max(0, field.cursor - 1);
63
+ }
64
+ else if (key.name === 'right') {
65
+ field.cursor = Math.min(field.value.length, field.cursor + 1);
66
+ }
67
+ else if (key.name === 'home' || (key.ctrl && key.name === 'a')) {
68
+ field.cursor = 0;
69
+ }
70
+ else if (key.name === 'end' || (key.ctrl && key.name === 'e')) {
71
+ field.cursor = field.value.length;
72
+ }
73
+ else if (key.ctrl && key.name === 'u') {
74
+ field.value = field.value.slice(field.cursor);
75
+ field.cursor = 0;
76
+ }
77
+ else if (key.name === 'backspace') {
78
+ if (field.cursor > 0) {
79
+ field.value = field.value.slice(0, field.cursor - 1) + field.value.slice(field.cursor);
80
+ field.cursor -= 1;
81
+ }
82
+ }
83
+ else if (key.name === 'delete') {
84
+ field.value = field.value.slice(0, field.cursor) + field.value.slice(field.cursor + 1);
85
+ }
86
+ else if (str && str.length === 1 && !key.ctrl && !key.meta && str >= ' ') {
87
+ field.value = field.value.slice(0, field.cursor) + str + field.value.slice(field.cursor);
88
+ field.cursor += 1;
89
+ }
90
+ else {
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+ const ALT_SCREEN_ON = '\x1b[?1049h\x1b[?25l';
96
+ const ALT_SCREEN_OFF = '\x1b[?1049l\x1b[?25h';
97
+ const CLEAR = '\x1b[2J\x1b[H';
98
+ function loadRows() {
99
+ const commands = (0, commands_1.listCommands)();
100
+ const stats = (0, recent_1.getUsageStats)();
101
+ const counts = new Map(stats.map((s) => [s.name, s.runCount]));
102
+ const lastRuns = new Map(stats.map((s) => [s.name, s.lastRunAt]));
103
+ return Object.keys(commands)
104
+ .sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0) || a.localeCompare(b))
105
+ .map((name) => ({
106
+ name,
107
+ cmd: commands[name],
108
+ runs: counts.get(name) ?? 0,
109
+ lastRun: lastRuns.get(name),
110
+ }));
111
+ }
112
+ class Tui {
113
+ constructor(out = (t) => process.stdout.write(t)) {
114
+ this.out = out;
115
+ this.mode = 'browse';
116
+ this.filter = '';
117
+ this.selected = 0;
118
+ this.rows = loadRows();
119
+ this.form = null;
120
+ this.input = null;
121
+ this.message = '';
122
+ this.resolve = null;
123
+ }
124
+ get width() {
125
+ return process.stdout.columns || 100;
126
+ }
127
+ get height() {
128
+ return process.stdout.rows || 30;
129
+ }
130
+ visibleRows() {
131
+ if (!this.filter)
132
+ return this.rows;
133
+ // Name matches rank above matches in the command text or directory.
134
+ const byName = this.rows.filter((r) => fuzzyMatch(this.filter, r.name));
135
+ const byContent = this.rows.filter((r) => !byName.includes(r) && fuzzyMatch(this.filter, `${r.cmd.command} ${r.cmd.directory}`));
136
+ return [...byName, ...byContent];
137
+ }
138
+ refresh() {
139
+ this.rows = loadRows();
140
+ this.selected = Math.min(this.selected, Math.max(0, this.visibleRows().length - 1));
141
+ }
142
+ openForm(seed = {}) {
143
+ this.form = {
144
+ fields: [
145
+ { key: 'name', label: 'name', value: seed.name ?? '' },
146
+ { key: 'command', label: 'command', value: seed.command ?? '' },
147
+ { key: 'directory', label: 'directory', value: seed.directory ?? process.cwd() },
148
+ ].map((f) => ({ ...f, cursor: f.value.length })),
149
+ pathMode: seed.pathMode ?? 'saved',
150
+ envChoice: seed.env && Object.keys(seed.env).length > 0 ? 'keep' : 'none',
151
+ keepEnv: seed.env,
152
+ editing: seed.editing,
153
+ active: seed.editing || seed.name ? 1 : 0,
154
+ error: '',
155
+ };
156
+ this.mode = 'form';
157
+ }
158
+ // --- rendering -------------------------------------------------------------
159
+ header(subtitle) {
160
+ const left = `${theme_1.icons.dot} ${theme_1.theme.heading('aliasmate')} ${theme_1.theme.dim(subtitle)}`;
161
+ const totalRuns = this.rows.reduce((s, r) => s + r.runs, 0);
162
+ const rightText = `${this.rows.length} commands · ${totalRuns} runs`;
163
+ const leftText = `● aliasmate ${subtitle}`;
164
+ const pad = Math.max(1, this.width - leftText.length - rightText.length - 3);
165
+ return ['', ` ${left}${' '.repeat(pad)}${theme_1.theme.faint(rightText)}`, ''];
166
+ }
167
+ listPane(maxLines) {
168
+ const rows = this.visibleRows();
169
+ if (rows.length === 0) {
170
+ return this.rows.length === 0
171
+ ? [
172
+ '',
173
+ theme_1.theme.dim(' No commands yet — press ') +
174
+ theme_1.theme.accent('n') +
175
+ theme_1.theme.dim(' to create your first one.'),
176
+ ]
177
+ : ['', theme_1.theme.dim(` Nothing matches "${this.filter}"`)];
178
+ }
179
+ const nameW = Math.min(Math.max(...rows.map((r) => r.name.length), 4) + 1, 22);
180
+ const cmdW = Math.max(20, Math.min(56, this.width - nameW - 30));
181
+ const start = Math.max(0, Math.min(this.selected - Math.floor(maxLines / 2), rows.length - maxLines));
182
+ return rows.slice(start, start + maxLines).map((row, i) => {
183
+ const index = start + i;
184
+ const active = index === this.selected;
185
+ const marker = active ? theme_1.theme.accent('›') : ' ';
186
+ const name = active
187
+ ? theme_1.theme.selected(row.name.padEnd(nameW))
188
+ : theme_1.theme.name(row.name.padEnd(nameW));
189
+ const cmd = (0, format_1.truncate)(row.cmd.command, cmdW).padEnd(cmdW + 1);
190
+ const runs = row.runs > 0 ? theme_1.theme.dim(`${theme_1.icons.fire} ${row.runs}`) : theme_1.theme.faint('·');
191
+ return ` ${marker} ${name} ${active ? cmd : theme_1.theme.dim(cmd)} ${runs}`;
192
+ });
193
+ }
194
+ detailPane() {
195
+ const row = this.visibleRows()[this.selected];
196
+ if (!row)
197
+ return [];
198
+ const label = (t) => theme_1.theme.faint(t.padEnd(11));
199
+ const where = (row.cmd.pathMode ?? 'saved') === 'current'
200
+ ? 'current directory'
201
+ : (0, format_1.prettyPath)(row.cmd.directory);
202
+ const env = Object.entries(row.cmd.env ?? {})
203
+ .map(([k, v]) => `${k}=${(0, env_1.isSensitive)(k) ? (0, env_1.maskValue)(v) : v}`)
204
+ .join(theme_1.theme.faint(' · '));
205
+ const lines = [
206
+ theme_1.theme.faint(` ${'─'.repeat(Math.max(10, this.width - 2))}`),
207
+ ` ${label('command')}${(0, format_1.truncate)(row.cmd.command, this.width - 14)}`,
208
+ ` ${label('where')}${theme_1.theme.dim((0, format_1.truncate)(where, this.width - 14))}`,
209
+ ];
210
+ if (env)
211
+ lines.push(` ${label('env')}${theme_1.theme.dim((0, format_1.truncate)(env, this.width - 14))}`);
212
+ lines.push(` ${label('runs')}${theme_1.theme.dim(row.runs > 0 ? `${row.runs} · last ${(0, format_1.timeAgo)(row.lastRun)}` : 'never')}`);
213
+ return lines;
214
+ }
215
+ statsPane() {
216
+ const top = this.rows.filter((r) => r.runs > 0).slice(0, 12);
217
+ const lines = [];
218
+ if (top.length === 0) {
219
+ lines.push(theme_1.theme.dim(' Nothing run yet.'));
220
+ }
221
+ else {
222
+ const max = Math.max(...top.map((r) => r.runs));
223
+ const nameW = Math.min(Math.max(...top.map((r) => r.name.length)) + 1, 22);
224
+ for (const row of top) {
225
+ const bar = theme_1.theme.accent('▮'.repeat(Math.max(1, Math.round((row.runs / max) * 24))));
226
+ lines.push(` ${theme_1.theme.name(row.name.padEnd(nameW))} ${bar} ${theme_1.theme.dim(String(row.runs))} ${theme_1.theme.faint((0, format_1.timeAgo)(row.lastRun))}`);
227
+ }
228
+ }
229
+ return lines;
230
+ }
231
+ formPane() {
232
+ const form = this.form;
233
+ const label = (t, active) => (active ? theme_1.theme.accent('› ') : ' ') + theme_1.theme.faint(t.padEnd(11));
234
+ const lines = [];
235
+ form.fields.forEach((field, i) => {
236
+ const active = form.active === i;
237
+ const renamed = field.key === 'name' && form.editing !== undefined && field.value !== form.editing;
238
+ const value = active
239
+ ? `${field.value.slice(0, field.cursor)}${theme_1.theme.accent('▏')}${field.value.slice(field.cursor)}`
240
+ : field.value;
241
+ lines.push(` ${label(field.label, active)}${value}${renamed ? theme_1.theme.faint(` · renames ${form.editing}`) : ''}`);
242
+ });
243
+ const pmActive = form.active === 3;
244
+ const pmText = form.pathMode === 'saved'
245
+ ? 'saved directory (project command)'
246
+ : 'current directory (utility)';
247
+ lines.push(` ${label('runs in', pmActive)}${pmActive ? pmText : theme_1.theme.dim(pmText)}${pmActive ? theme_1.theme.faint(' · space to toggle') : ''}`);
248
+ const envActive = form.active === 4;
249
+ const keptCount = form.keepEnv ? Object.keys(form.keepEnv).length : 0;
250
+ const envLabels = {
251
+ none: 'none',
252
+ keep: `keep saved (${keptCount} var${keptCount === 1 ? '' : 's'})`,
253
+ capture: `capture current shell (${Object.keys((0, env_1.captureUserEnv)()).length} vars, secrets masked on export)`,
254
+ };
255
+ lines.push(` ${label('env', envActive)}${envActive ? envLabels[form.envChoice] : theme_1.theme.dim(envLabels[form.envChoice])}${envActive ? theme_1.theme.faint(' · space to cycle') : ''}`);
256
+ if (form.error) {
257
+ lines.push('');
258
+ lines.push(` ${theme_1.theme.error(form.error)}`);
259
+ }
260
+ return lines;
261
+ }
262
+ inputPane() {
263
+ const input = this.input;
264
+ const value = `${input.value.slice(0, input.cursor)}${theme_1.theme.accent('▏')}${input.value.slice(input.cursor)}`;
265
+ const lines = [` ${theme_1.theme.accent('› ')}${theme_1.theme.faint(input.label.padEnd(11))}${value}`];
266
+ if (input.kind === 'export') {
267
+ lines.push('');
268
+ lines.push(theme_1.theme.faint(' includes real secret env values — keep the file safe'));
269
+ }
270
+ if (input.error) {
271
+ lines.push('');
272
+ lines.push(` ${theme_1.theme.error(input.error)}`);
273
+ }
274
+ return lines;
275
+ }
276
+ footer() {
277
+ const key = (k, label) => `${theme_1.theme.accent(k)} ${theme_1.theme.faint(label)}`;
278
+ const sep = theme_1.theme.faint(' ');
279
+ switch (this.mode) {
280
+ case 'confirm-delete': {
281
+ const row = this.visibleRows()[this.selected];
282
+ return ` ${theme_1.theme.warning(`delete "${row?.name}"?`)} ${key('y', 'yes')}${sep}${key('n', 'no')}`;
283
+ }
284
+ case 'filter':
285
+ return ` ${theme_1.theme.accent('/')} ${this.filter}${theme_1.theme.accent('▏')} ${theme_1.theme.faint('esc clear · enter run · ↑↓ move')}`;
286
+ case 'form':
287
+ return ` ${[key('↑↓/tab', 'field'), key('←→', 'cursor'), key('enter', 'next/save'), key('esc', 'cancel')].join(sep)}`;
288
+ case 'input':
289
+ return ` ${[key('enter', this.input?.kind === 'export' ? 'export' : 'import'), key('esc', 'cancel')].join(sep)}`;
290
+ case 'stats':
291
+ return ` ${theme_1.theme.faint('any key to go back')}`;
292
+ default:
293
+ return ` ${[
294
+ key('↑↓', 'move'),
295
+ key('enter', 'run'),
296
+ key('/', 'filter'),
297
+ key('n', 'new'),
298
+ key('e', 'edit'),
299
+ key('d', 'delete'),
300
+ key('s', 'stats'),
301
+ key('x', 'export'),
302
+ key('i', 'import'),
303
+ key('q', 'quit'),
304
+ ].join(sep)}`;
305
+ }
306
+ }
307
+ render() {
308
+ const subtitle = this.mode === 'form'
309
+ ? this.form?.editing
310
+ ? `· edit ${this.form.editing}`
311
+ : '· new command'
312
+ : this.mode === 'stats'
313
+ ? '· stats'
314
+ : this.mode === 'input'
315
+ ? this.input?.kind === 'export'
316
+ ? '· export backup'
317
+ : '· import'
318
+ : '';
319
+ const out = [...this.header(subtitle)];
320
+ if (this.mode === 'stats') {
321
+ out.push(...this.statsPane());
322
+ }
323
+ else if (this.mode === 'form') {
324
+ out.push(...this.formPane());
325
+ }
326
+ else if (this.mode === 'input') {
327
+ out.push(...this.inputPane());
328
+ }
329
+ else {
330
+ const detail = this.detailPane();
331
+ const listLines = Math.max(3, this.height - out.length - detail.length - 4);
332
+ out.push(...this.listPane(listLines));
333
+ out.push('');
334
+ out.push(...detail);
335
+ }
336
+ while (out.length < this.height - 2)
337
+ out.push('');
338
+ out.push(this.message ? ` ${this.message}` : '');
339
+ out.push(this.footer());
340
+ this.out(CLEAR + out.slice(0, this.height).join('\n'));
341
+ this.message = '';
342
+ }
343
+ // --- input -----------------------------------------------------------------
344
+ /** Clear the filter but keep the same row highlighted. */
345
+ clearFilter() {
346
+ const current = this.visibleRows()[this.selected]?.name;
347
+ this.filter = '';
348
+ const index = current ? this.visibleRows().findIndex((r) => r.name === current) : -1;
349
+ this.selected = Math.max(0, index);
350
+ }
351
+ move(delta) {
352
+ const count = this.visibleRows().length;
353
+ if (count === 0)
354
+ return;
355
+ this.selected = (this.selected + delta + count) % count;
356
+ }
357
+ openInput(kind) {
358
+ const today = new Date().toISOString().slice(0, 10);
359
+ const value = kind === 'export' ? `~/aliasmate-backup-${today}.json` : '~/';
360
+ this.input = { kind, label: 'file', value, cursor: value.length, error: '' };
361
+ this.mode = 'input';
362
+ }
363
+ handleInputKey(str, key) {
364
+ const input = this.input;
365
+ if (key.name === 'escape') {
366
+ this.input = null;
367
+ this.mode = 'browse';
368
+ this.message = theme_1.theme.faint('cancelled');
369
+ return this.render();
370
+ }
371
+ if (key.name === 'return')
372
+ return this.submitInput();
373
+ if (editText(input, str, key))
374
+ input.error = '';
375
+ this.render();
376
+ }
377
+ submitInput() {
378
+ const input = this.input;
379
+ const file = input.value.trim();
380
+ if (!file) {
381
+ input.error = 'File path cannot be empty';
382
+ return this.render();
383
+ }
384
+ try {
385
+ if (input.kind === 'export') {
386
+ const count = (0, transfer_1.exportToFile)(file, { full: true });
387
+ this.message = `${theme_1.icons.ok} ${theme_1.theme.dim(`exported ${count} command${count === 1 ? '' : 's'} to ${file}`)}`;
388
+ }
389
+ else {
390
+ const result = (0, transfer_1.importFromFile)(file);
391
+ this.refresh();
392
+ const extras = [
393
+ result.skipped.length > 0 ? `${result.skipped.length} skipped (already exist)` : '',
394
+ result.invalid.length > 0 ? `${result.invalid.length} invalid` : '',
395
+ ]
396
+ .filter(Boolean)
397
+ .join(' · ');
398
+ this.message = `${theme_1.icons.ok} ${theme_1.theme.dim(`imported ${result.imported}${extras ? ` · ${extras}` : ''}`)}`;
399
+ }
400
+ this.input = null;
401
+ this.mode = 'browse';
402
+ }
403
+ catch (error) {
404
+ input.error = error.message;
405
+ }
406
+ this.render();
407
+ }
408
+ submitForm() {
409
+ const form = this.form;
410
+ const [name, command, directory] = form.fields.map((f) => f.value.trim());
411
+ const renaming = form.editing !== undefined && name !== form.editing;
412
+ const nameCheck = (0, commands_1.validateCommandName)(name);
413
+ if (nameCheck !== true) {
414
+ form.error = nameCheck;
415
+ form.active = 0;
416
+ return this.render();
417
+ }
418
+ if ((!form.editing || renaming) && (0, commands_1.commandExists)(name)) {
419
+ form.error = `"${name}" already exists — pick another name or edit it instead`;
420
+ form.active = 0;
421
+ return this.render();
422
+ }
423
+ if (!command) {
424
+ form.error = 'Command cannot be empty';
425
+ form.active = 1;
426
+ return this.render();
427
+ }
428
+ if (!directory) {
429
+ form.error = 'Directory cannot be empty';
430
+ form.active = 2;
431
+ return this.render();
432
+ }
433
+ const env = form.envChoice === 'none'
434
+ ? undefined
435
+ : form.envChoice === 'keep'
436
+ ? form.keepEnv
437
+ : (0, env_1.captureUserEnv)();
438
+ try {
439
+ if (renaming)
440
+ (0, commands_1.renameCommand)(form.editing, name);
441
+ (0, commands_1.saveCommand)({ name, command, directory, pathMode: form.pathMode, env });
442
+ this.message = `${theme_1.icons.ok} ${theme_1.theme.dim(renaming ? `renamed ${form.editing} → ${name}` : `saved ${name}`)}`;
443
+ const finalName = name;
444
+ this.form = null;
445
+ this.mode = 'browse';
446
+ this.refresh();
447
+ const index = this.visibleRows().findIndex((r) => r.name === finalName);
448
+ if (index >= 0)
449
+ this.selected = index;
450
+ }
451
+ catch (error) {
452
+ form.error = error.message;
453
+ }
454
+ this.render();
455
+ }
456
+ handleFormKey(str, key) {
457
+ const form = this.form;
458
+ const fieldCount = 5;
459
+ const isTextField = form.active < 3;
460
+ if (key.name === 'escape') {
461
+ this.form = null;
462
+ this.mode = 'browse';
463
+ this.message = theme_1.theme.faint('cancelled');
464
+ return this.render();
465
+ }
466
+ if (key.name === 'return') {
467
+ if (form.active < fieldCount - 1) {
468
+ form.active += 1;
469
+ }
470
+ else {
471
+ return this.submitForm();
472
+ }
473
+ return this.render();
474
+ }
475
+ if (key.name === 'up' || (key.name === 'tab' && key.shift)) {
476
+ form.active = (form.active - 1 + fieldCount) % fieldCount;
477
+ return this.render();
478
+ }
479
+ if (key.name === 'down' || key.name === 'tab') {
480
+ form.active = (form.active + 1) % fieldCount;
481
+ return this.render();
482
+ }
483
+ if (isTextField) {
484
+ editText(form.fields[form.active], str, key);
485
+ form.error = '';
486
+ return this.render();
487
+ }
488
+ if (key.name === 'space' || key.name === 'left' || key.name === 'right') {
489
+ if (form.active === 3) {
490
+ form.pathMode = form.pathMode === 'saved' ? 'current' : 'saved';
491
+ }
492
+ else {
493
+ const cycle = form.keepEnv ? ['keep', 'capture', 'none'] : ['none', 'capture'];
494
+ const next = (cycle.indexOf(form.envChoice) + 1) % cycle.length;
495
+ form.envChoice = cycle[next];
496
+ }
497
+ return this.render();
498
+ }
499
+ this.render();
500
+ }
501
+ handleKey(str, key) {
502
+ if (key.ctrl && key.name === 'c')
503
+ return this.finish(null);
504
+ if (this.mode === 'form')
505
+ return this.handleFormKey(str, key);
506
+ if (this.mode === 'input')
507
+ return this.handleInputKey(str, key);
508
+ if (this.mode === 'stats') {
509
+ this.mode = 'browse';
510
+ return this.render();
511
+ }
512
+ if (this.mode === 'confirm-delete') {
513
+ if (str === 'y') {
514
+ const row = this.visibleRows()[this.selected];
515
+ if (row) {
516
+ (0, commands_1.deleteCommand)(row.name);
517
+ this.message = `${theme_1.icons.ok} ${theme_1.theme.dim(`deleted ${row.name}`)}`;
518
+ this.refresh();
519
+ }
520
+ }
521
+ this.mode = 'browse';
522
+ return this.render();
523
+ }
524
+ if (this.mode === 'filter') {
525
+ if (key.name === 'escape') {
526
+ this.clearFilter();
527
+ this.mode = 'browse';
528
+ }
529
+ else if (key.name === 'return') {
530
+ this.mode = 'browse';
531
+ return this.selectRun();
532
+ }
533
+ else if (key.name === 'backspace') {
534
+ this.filter = this.filter.slice(0, -1);
535
+ }
536
+ else if (key.name === 'up')
537
+ this.move(-1);
538
+ else if (key.name === 'down')
539
+ this.move(1);
540
+ else if (str && str.length === 1 && !key.ctrl && !key.meta) {
541
+ this.filter += str;
542
+ this.selected = 0;
543
+ }
544
+ return this.render();
545
+ }
546
+ // browse mode
547
+ switch (key.name) {
548
+ case 'up':
549
+ case 'k':
550
+ this.move(-1);
551
+ break;
552
+ case 'down':
553
+ case 'j':
554
+ this.move(1);
555
+ break;
556
+ case 'return':
557
+ return this.selectRun();
558
+ case 'escape':
559
+ if (this.filter) {
560
+ this.clearFilter();
561
+ break;
562
+ }
563
+ return this.finish(null);
564
+ default:
565
+ if (str === '/')
566
+ this.mode = 'filter';
567
+ else if (str === 'q')
568
+ return this.finish(null);
569
+ else if (str === 'n')
570
+ this.openForm();
571
+ else if (str === 'e') {
572
+ const row = this.visibleRows()[this.selected];
573
+ if (row) {
574
+ this.openForm({
575
+ name: row.name,
576
+ command: row.cmd.command,
577
+ directory: row.cmd.directory,
578
+ pathMode: row.cmd.pathMode ?? 'saved',
579
+ env: row.cmd.env,
580
+ editing: row.name,
581
+ });
582
+ }
583
+ }
584
+ else if (str === 'd' && this.visibleRows().length > 0)
585
+ this.mode = 'confirm-delete';
586
+ else if (str === 's')
587
+ this.mode = 'stats';
588
+ else if (str === 'x')
589
+ this.openInput('export');
590
+ else if (str === 'i')
591
+ this.openInput('import');
592
+ }
593
+ this.render();
594
+ }
595
+ selectRun() {
596
+ const row = this.visibleRows()[this.selected];
597
+ if (!row) {
598
+ this.message = theme_1.theme.dim('nothing selected');
599
+ return this.render();
600
+ }
601
+ this.finish({ kind: 'run', name: row.name });
602
+ }
603
+ finish(action) {
604
+ const resolve = this.resolve;
605
+ this.resolve = null;
606
+ resolve?.(action);
607
+ }
608
+ /** Show the TUI until the user runs a command or quits (null). */
609
+ waitForAction() {
610
+ return new Promise((resolve) => {
611
+ this.resolve = resolve;
612
+ this.render();
613
+ });
614
+ }
615
+ }
616
+ exports.Tui = Tui;
617
+ function enterScreen(onKey) {
618
+ process.stdout.write(ALT_SCREEN_ON);
619
+ readline.emitKeypressEvents(process.stdin);
620
+ if (process.stdin.isTTY)
621
+ process.stdin.setRawMode(true);
622
+ process.stdin.on('keypress', onKey);
623
+ process.stdin.resume();
624
+ return () => {
625
+ process.stdin.off('keypress', onKey);
626
+ if (process.stdin.isTTY)
627
+ process.stdin.setRawMode(false);
628
+ process.stdin.pause();
629
+ process.stdout.write(ALT_SCREEN_OFF);
630
+ };
631
+ }
632
+ /**
633
+ * The full-screen home: everything (browse, create, edit, delete, stats)
634
+ * happens in the TUI. Only running a command hands the terminal back so
635
+ * output lands in normal scrollback.
636
+ */
637
+ async function interactiveHome(seed) {
638
+ const tui = new Tui();
639
+ if (seed)
640
+ tui.openForm(seed);
641
+ const onResize = () => tui.render();
642
+ const leave = enterScreen((s, k) => tui.handleKey(s, k));
643
+ process.stdout.on('resize', onResize);
644
+ let action;
645
+ try {
646
+ action = await tui.waitForAction();
647
+ }
648
+ finally {
649
+ process.stdout.off('resize', onResize);
650
+ leave();
651
+ }
652
+ if (action?.kind === 'run') {
653
+ const { runHandler } = await Promise.resolve().then(() => __importStar(require('../cli/run')));
654
+ await runHandler(action.name, undefined, {});
655
+ }
656
+ }
657
+ //# sourceMappingURL=interactive.js.map