@nemus-cli/nemus 0.8.0 → 0.10.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 (65) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +29 -5
  3. package/bin/workspace.js +12 -0
  4. package/dist/commands/analyze-deps.js +5 -12
  5. package/dist/commands/archive.js +13 -18
  6. package/dist/commands/branch/create.js +5 -12
  7. package/dist/commands/branch/switch.js +14 -25
  8. package/dist/commands/cache/manager.js +13 -24
  9. package/dist/commands/cleanup.js +14 -26
  10. package/dist/commands/config.js +82 -0
  11. package/dist/commands/configure-claude.js +4 -8
  12. package/dist/commands/configure.js +38 -32
  13. package/dist/commands/dashboard/session-picker.js +19 -26
  14. package/dist/commands/dashboard/workspace-picker.js +19 -26
  15. package/dist/commands/delete.js +15 -30
  16. package/dist/commands/ghq-status.js +7 -12
  17. package/dist/commands/go.js +18 -27
  18. package/dist/commands/history.js +6 -13
  19. package/dist/commands/list.js +20 -29
  20. package/dist/commands/remove-repo.js +21 -29
  21. package/dist/commands/save-context.js +5 -10
  22. package/dist/commands/sessions.js +19 -25
  23. package/dist/commands/suite/create.js +27 -53
  24. package/dist/commands/suite/delete.js +13 -25
  25. package/dist/commands/suite/export.js +20 -36
  26. package/dist/commands/suite/import.js +9 -20
  27. package/dist/commands/suite/use.js +9 -17
  28. package/dist/utils/config-schema.js +57 -0
  29. package/dist/utils/editor.js +35 -0
  30. package/dist/utils/prompt.js +26 -0
  31. package/dist/utils/prompts.js +86 -118
  32. package/package.json +3 -6
  33. package/src/commands/analyze-deps.ts +5 -9
  34. package/src/commands/archive.ts +5 -7
  35. package/src/commands/branch/create.ts +5 -9
  36. package/src/commands/branch/switch.ts +14 -22
  37. package/src/commands/cache/manager.ts +13 -21
  38. package/src/commands/cleanup.ts +14 -23
  39. package/src/commands/config.ts +52 -1
  40. package/src/commands/configure-claude.ts +4 -5
  41. package/src/commands/configure.ts +35 -29
  42. package/src/commands/dashboard/session-picker.ts +6 -11
  43. package/src/commands/dashboard/workspace-picker.ts +6 -11
  44. package/src/commands/delete.test.ts +36 -42
  45. package/src/commands/delete.ts +15 -27
  46. package/src/commands/ghq-status.ts +5 -7
  47. package/src/commands/go.ts +18 -25
  48. package/src/commands/history.ts +6 -10
  49. package/src/commands/list.test.ts +19 -26
  50. package/src/commands/list.ts +24 -31
  51. package/src/commands/remove-repo.ts +11 -17
  52. package/src/commands/save-context.ts +3 -5
  53. package/src/commands/sessions.ts +6 -10
  54. package/src/commands/suite/create.ts +28 -50
  55. package/src/commands/suite/delete.ts +14 -23
  56. package/src/commands/suite/export.ts +20 -33
  57. package/src/commands/suite/import.ts +9 -17
  58. package/src/commands/suite/use.ts +9 -14
  59. package/src/utils/config-schema.test.ts +49 -0
  60. package/src/utils/config-schema.ts +65 -0
  61. package/src/utils/editor.test.ts +37 -0
  62. package/src/utils/editor.ts +48 -0
  63. package/src/utils/prompt.ts +16 -0
  64. package/src/utils/prompts.test.ts +70 -41
  65. package/src/utils/prompts.ts +98 -128
@@ -1,13 +1,9 @@
1
- import inquirer from 'inquirer';
2
- import autocompletePrompt from 'inquirer-autocomplete-prompt';
1
+ import { confirm, input, select, search } from './prompt';
3
2
  import * as fuzzy from 'fuzzy';
4
3
  import { GitHubRepo } from '../types';
5
4
  import { validateWorkspaceName, checkWorkspaceExists, sanitizeWorkspaceName } from './validation';
6
5
  import { colorize } from './colors';
7
6
 
8
- // Register autocomplete prompt type
9
- inquirer.registerPrompt('autocomplete', autocompletePrompt);
10
-
11
7
  export interface RepoSelection {
12
8
  repo: GitHubRepo;
13
9
  directoryName: string;
@@ -17,32 +13,28 @@ export const promptInstanceSuffix = async (
17
13
  repoName: string,
18
14
  existingDirectoryNames: string[]
19
15
  ): Promise<string> => {
20
- const { suffix } = await inquirer.prompt([
21
- {
22
- type: 'input',
23
- name: 'suffix',
24
- message: `"${repoName}" already exists. Enter a suffix for this instance:`,
25
- validate: (input: string) => {
26
- if (!input || input.trim().length === 0) {
27
- return 'Suffix cannot be empty';
28
- }
16
+ const suffix = await input({
17
+ message: `"${repoName}" already exists. Enter a suffix for this instance:`,
18
+ validate: (input: string) => {
19
+ if (!input || input.trim().length === 0) {
20
+ return 'Suffix cannot be empty';
21
+ }
29
22
 
30
- const trimmed = input.trim();
23
+ const trimmed = input.trim();
31
24
 
32
- // Validate characters (alphanumeric, hyphens, underscores)
33
- if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
34
- return 'Suffix can only contain letters, numbers, hyphens, and underscores';
35
- }
25
+ // Validate characters (alphanumeric, hyphens, underscores)
26
+ if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
27
+ return 'Suffix can only contain letters, numbers, hyphens, and underscores';
28
+ }
36
29
 
37
- const candidateName = `${repoName}-${trimmed}`;
38
- if (existingDirectoryNames.includes(candidateName)) {
39
- return `"${candidateName}" already exists. Choose a different suffix`;
40
- }
30
+ const candidateName = `${repoName}-${trimmed}`;
31
+ if (existingDirectoryNames.includes(candidateName)) {
32
+ return `"${candidateName}" already exists. Choose a different suffix`;
33
+ }
41
34
 
42
- return true;
43
- },
35
+ return true;
44
36
  },
45
- ]);
37
+ });
46
38
 
47
39
  return suffix.trim();
48
40
  };
@@ -63,43 +55,39 @@ export const promptRepositorySelection = async (
63
55
 
64
56
  while (true) {
65
57
  try {
66
- const { repoName } = await inquirer.prompt([
67
- {
68
- type: 'autocomplete',
69
- name: 'repoName',
70
- message: `Search and select repository (${colorize(String(selectedEntries.length), 'cyan')} selected):`,
71
- source: async (_answersSoFar: any, input: string | undefined) => {
72
- const searchInput = input || '';
73
-
74
- // Always include done option
75
- const doneOption = { name: colorize('done - Finish selection', 'green'), value: 'done' };
76
-
77
- // If no input or "done" typed, show done + top repos
78
- if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
79
- return [
80
- doneOption,
81
- ...repos.slice(0, 15).map(repo => ({
82
- name: `${repo.name}${repo.description ? ` - ${colorize(repo.description, 'gray')}` : ''}`,
83
- value: repo.name,
84
- }))
85
- ];
86
- }
87
-
88
- // Perform fuzzy search
89
- const results = fuzzy.filter(searchInput, repos, {
90
- extract: (repo) => `${repo.name} ${repo.description || ''}`,
91
- });
92
-
93
- const suggestions = results.slice(0, 15).map(result => ({
94
- name: `${result.original.name}${result.original.description ? ` - ${colorize(result.original.description, 'gray')}` : ''}`,
95
- value: result.original.name,
96
- }));
97
-
98
- return [doneOption, ...suggestions];
99
- },
100
- pageSize: 16,
101
- } as any,
102
- ]);
58
+ const repoName = await search<string>({
59
+ message: `Search and select repository (${colorize(String(selectedEntries.length), 'cyan')} selected):`,
60
+ pageSize: 16,
61
+ source: async (term: string | undefined) => {
62
+ const searchInput = term || '';
63
+
64
+ // Always include done option
65
+ const doneOption = { name: colorize('done - Finish selection', 'green'), value: 'done' };
66
+
67
+ // If no input or "done" typed, show done + top repos
68
+ if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
69
+ return [
70
+ doneOption,
71
+ ...repos.slice(0, 15).map(repo => ({
72
+ name: `${repo.name}${repo.description ? ` - ${colorize(repo.description, 'gray')}` : ''}`,
73
+ value: repo.name,
74
+ }))
75
+ ];
76
+ }
77
+
78
+ // Perform fuzzy search
79
+ const results = fuzzy.filter(searchInput, repos, {
80
+ extract: (repo) => `${repo.name} ${repo.description || ''}`,
81
+ });
82
+
83
+ const suggestions = results.slice(0, 15).map(result => ({
84
+ name: `${result.original.name}${result.original.description ? ` - ${colorize(result.original.description, 'gray')}` : ''}`,
85
+ value: result.original.name,
86
+ }));
87
+
88
+ return [doneOption, ...suggestions];
89
+ },
90
+ });
103
91
 
104
92
  if (repoName === 'done') {
105
93
  if (selectedEntries.length === 0) {
@@ -147,23 +135,18 @@ export const promptRepositorySelection = async (
147
135
  };
148
136
 
149
137
  export const promptWorkspaceName = async (): Promise<string> => {
150
- const { workspaceName } = await inquirer.prompt([
151
- {
152
- type: 'input',
153
- name: 'workspaceName',
154
- message: 'Enter workspace name:',
155
- validate: (input: string) => {
156
- const validationResult = validateWorkspaceName(input);
157
- if (validationResult !== true) {
158
- return validationResult;
159
- }
160
- return true;
161
- },
162
- filter: (input: string) => sanitizeWorkspaceName(input),
138
+ const raw = await input({
139
+ message: 'Enter workspace name:',
140
+ validate: (value: string) => {
141
+ const validationResult = validateWorkspaceName(sanitizeWorkspaceName(value));
142
+ if (validationResult !== true) {
143
+ return validationResult;
144
+ }
145
+ return true;
163
146
  },
164
- ]);
147
+ });
165
148
 
166
- return workspaceName;
149
+ return sanitizeWorkspaceName(raw);
167
150
  };
168
151
 
169
152
  export const confirmWorkspaceCreation = async (
@@ -179,14 +162,10 @@ export const confirmWorkspaceCreation = async (
179
162
  console.log(`Repositories: ${colorize(String(repoCount), 'yellow')}`);
180
163
  console.log('='.repeat(60) + '\n');
181
164
 
182
- const { confirmed } = await inquirer.prompt([
183
- {
184
- type: 'confirm',
185
- name: 'confirmed',
186
- message: 'Create workspace with these settings?',
187
- default: true,
188
- },
189
- ]);
165
+ const confirmed = await confirm({
166
+ message: 'Create workspace with these settings?',
167
+ default: true,
168
+ });
190
169
 
191
170
  return confirmed;
192
171
  };
@@ -201,18 +180,13 @@ export const promptWorkspaceSelection = async (workspaces: Array<{ name: string;
201
180
  ? `${ws.name} (${ws.metadata.repositories.length} repos)`
202
181
  : ws.name,
203
182
  value: ws.name,
204
- short: ws.name,
205
183
  }));
206
184
 
207
- const { workspaceName } = await inquirer.prompt([
208
- {
209
- type: 'list',
210
- name: 'workspaceName',
211
- message: 'Select workspace to update:',
212
- choices,
213
- pageSize: 15,
214
- },
215
- ]);
185
+ const workspaceName = await select<string>({
186
+ message: 'Select workspace to update:',
187
+ choices,
188
+ pageSize: 15,
189
+ });
216
190
 
217
191
  return workspaceName;
218
192
  };
@@ -236,38 +210,34 @@ export const promptMultiWorkspaceSelection = async (
236
210
  break;
237
211
  }
238
212
 
239
- const { workspaceName } = await inquirer.prompt([
240
- {
241
- type: 'autocomplete',
242
- name: 'workspaceName',
243
- message: `Search and select workspace (${colorize(String(selectedNames.length), 'cyan')} selected):`,
244
- source: async (_answersSoFar: any, input: string | undefined) => {
245
- const searchInput = input || '';
246
-
247
- const doneOption = { name: colorize('done - Finish selection', 'green'), value: 'done' };
248
-
249
- if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
250
- return [
251
- doneOption,
252
- ...availableNames.slice(0, 15).map(name => ({
253
- name,
254
- value: name,
255
- }))
256
- ];
257
- }
258
-
259
- const results = fuzzy.filter(searchInput, availableNames);
260
-
261
- const suggestions = results.slice(0, 15).map(result => ({
262
- name: result.original,
263
- value: result.original,
264
- }));
265
-
266
- return [doneOption, ...suggestions];
267
- },
268
- pageSize: 16,
269
- } as any,
270
- ]);
213
+ const workspaceName = await search<string>({
214
+ message: `Search and select workspace (${colorize(String(selectedNames.length), 'cyan')} selected):`,
215
+ pageSize: 16,
216
+ source: async (term: string | undefined) => {
217
+ const searchInput = term || '';
218
+
219
+ const doneOption = { name: colorize('done - Finish selection', 'green'), value: 'done' };
220
+
221
+ if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
222
+ return [
223
+ doneOption,
224
+ ...availableNames.slice(0, 15).map(name => ({
225
+ name,
226
+ value: name,
227
+ }))
228
+ ];
229
+ }
230
+
231
+ const results = fuzzy.filter(searchInput, availableNames);
232
+
233
+ const suggestions = results.slice(0, 15).map(result => ({
234
+ name: result.original,
235
+ value: result.original,
236
+ }));
237
+
238
+ return [doneOption, ...suggestions];
239
+ },
240
+ });
271
241
 
272
242
  if (workspaceName === 'done') {
273
243
  if (selectedNames.length === 0) {