@nemus-cli/nemus 0.9.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 (57) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +10 -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/configure-claude.js +4 -8
  11. package/dist/commands/configure.js +38 -32
  12. package/dist/commands/dashboard/session-picker.js +19 -26
  13. package/dist/commands/dashboard/workspace-picker.js +19 -26
  14. package/dist/commands/delete.js +15 -30
  15. package/dist/commands/ghq-status.js +7 -12
  16. package/dist/commands/go.js +18 -27
  17. package/dist/commands/history.js +6 -13
  18. package/dist/commands/list.js +20 -29
  19. package/dist/commands/remove-repo.js +21 -29
  20. package/dist/commands/save-context.js +5 -10
  21. package/dist/commands/sessions.js +19 -25
  22. package/dist/commands/suite/create.js +27 -53
  23. package/dist/commands/suite/delete.js +13 -25
  24. package/dist/commands/suite/export.js +20 -36
  25. package/dist/commands/suite/import.js +9 -20
  26. package/dist/commands/suite/use.js +9 -17
  27. package/dist/utils/prompt.js +26 -0
  28. package/dist/utils/prompts.js +86 -118
  29. package/package.json +3 -6
  30. package/src/commands/analyze-deps.ts +5 -9
  31. package/src/commands/archive.ts +5 -7
  32. package/src/commands/branch/create.ts +5 -9
  33. package/src/commands/branch/switch.ts +14 -22
  34. package/src/commands/cache/manager.ts +13 -21
  35. package/src/commands/cleanup.ts +14 -23
  36. package/src/commands/configure-claude.ts +4 -5
  37. package/src/commands/configure.ts +35 -29
  38. package/src/commands/dashboard/session-picker.ts +6 -11
  39. package/src/commands/dashboard/workspace-picker.ts +6 -11
  40. package/src/commands/delete.test.ts +36 -42
  41. package/src/commands/delete.ts +15 -27
  42. package/src/commands/ghq-status.ts +5 -7
  43. package/src/commands/go.ts +18 -25
  44. package/src/commands/history.ts +6 -10
  45. package/src/commands/list.test.ts +19 -26
  46. package/src/commands/list.ts +24 -31
  47. package/src/commands/remove-repo.ts +11 -17
  48. package/src/commands/save-context.ts +3 -5
  49. package/src/commands/sessions.ts +6 -10
  50. package/src/commands/suite/create.ts +28 -50
  51. package/src/commands/suite/delete.ts +14 -23
  52. package/src/commands/suite/export.ts +20 -33
  53. package/src/commands/suite/import.ts +9 -17
  54. package/src/commands/suite/use.ts +9 -14
  55. package/src/utils/prompt.ts +16 -0
  56. package/src/utils/prompts.test.ts +70 -41
  57. package/src/utils/prompts.ts +98 -128
@@ -2,7 +2,7 @@ import { Command } from 'commander';
2
2
  import { execFileSync } from 'child_process';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
- import inquirer from 'inquirer';
5
+ import { input, confirm, select } from '../utils/prompt';
6
6
  import { getUserConfig, saveUserConfig, UserConfig } from '../utils/config';
7
7
  import { clearCache } from '../utils/cache';
8
8
  import { logSuccess, logError, logInfo, logWarning } from '../utils/logger';
@@ -39,40 +39,46 @@ async function handleConfigure() {
39
39
  console.log('');
40
40
 
41
41
  try {
42
- const answers = await inquirer.prompt([
43
- { type: 'input', name: 'workspacesDir', message: 'Workspaces directory:', default: current.workspacesDir, validate: (val: string) => val.trim().length > 0 || 'Directory path is required' },
44
- { type: 'input', name: 'githubOrg', message: 'GitHub organization:', default: current.githubOrg, validate: (val: string) => /^[a-zA-Z0-9_-]+$/.test(val.trim()) || 'Must be a valid GitHub org name' },
45
- { type: 'list', name: 'cloneProtocol', message: 'Clone protocol:', choices: [{ name: 'SSH (git@github.com:...)', value: 'ssh' }, { name: 'HTTPS (https://github.com/...)', value: 'https' }], default: current.cloneProtocol },
46
- { type: 'confirm', name: 'autoLaunchClaude', message: 'Auto-launch AI agent after workspace creation?', default: current.autoLaunchClaude },
47
- { type: 'confirm', name: 'generateClaudeContext', message: 'Generate context file in workspaces?', default: current.generateClaudeContext },
48
- { type: 'confirm', name: 'installMcp', message: 'Install MCP server (Claude Code only)?', default: current.installMcp },
49
- { type: 'list', name: 'aiAgent', message: 'AI Agent(s) to integrate with:', choices: [
50
- { name: 'Auto-detect (recommended)', value: 'auto' },
51
- { name: 'Claude Code only', value: 'claude' },
52
- { name: 'Pi only', value: 'pi' },
53
- { name: 'OpenCode only', value: 'opencode' },
54
- { name: 'All available', value: 'both' },
55
- ], default: current.aiAgent },
56
- // Only ask about primary agent when aiAgent is 'both' or 'auto'
57
- // (when specific agent chosen, primary is implicitly that agent)
58
- { type: 'list', name: 'primaryAgent', message: 'Primary agent (used for launching):', choices: [
42
+ // Sequential modular prompts. The classic `when:` conditions become `if`
43
+ // guards; a skipped question leaves its answer key undefined, exactly as the
44
+ // classic API did (downstream code already tolerates that via `??`).
45
+ const answers: any = {};
46
+ answers.workspacesDir = await input({ message: 'Workspaces directory:', default: current.workspacesDir, validate: (val: string) => val.trim().length > 0 || 'Directory path is required' });
47
+ answers.githubOrg = await input({ message: 'GitHub organization:', default: current.githubOrg, validate: (val: string) => /^[a-zA-Z0-9_-]+$/.test(val.trim()) || 'Must be a valid GitHub org name' });
48
+ answers.cloneProtocol = await select({ message: 'Clone protocol:', choices: [{ name: 'SSH (git@github.com:...)', value: 'ssh' }, { name: 'HTTPS (https://github.com/...)', value: 'https' }], default: current.cloneProtocol });
49
+ answers.autoLaunchClaude = await confirm({ message: 'Auto-launch AI agent after workspace creation?', default: current.autoLaunchClaude });
50
+ answers.generateClaudeContext = await confirm({ message: 'Generate context file in workspaces?', default: current.generateClaudeContext });
51
+ answers.installMcp = await confirm({ message: 'Install MCP server (Claude Code only)?', default: current.installMcp });
52
+ answers.aiAgent = await select<string>({ message: 'AI Agent(s) to integrate with:', choices: [
53
+ { name: 'Auto-detect (recommended)', value: 'auto' },
54
+ { name: 'Claude Code only', value: 'claude' },
55
+ { name: 'Pi only', value: 'pi' },
56
+ { name: 'OpenCode only', value: 'opencode' },
57
+ { name: 'All available', value: 'both' },
58
+ ], default: current.aiAgent });
59
+ // Only ask about primary agent when aiAgent is 'both' or 'auto'
60
+ // (when specific agent chosen, primary is implicitly that agent)
61
+ if (answers.aiAgent === 'both' || answers.aiAgent === 'auto') {
62
+ answers.primaryAgent = await select<string>({ message: 'Primary agent (used for launching):', choices: [
59
63
  { name: 'Auto-detect (first available)', value: 'auto' },
60
64
  { name: 'Claude Code', value: 'claude' },
61
65
  { name: 'Pi', value: 'pi' },
62
66
  { name: 'OpenCode', value: 'opencode' },
63
- ], default: current.primaryAgent, when: (ans) => ans.aiAgent === 'both' || ans.aiAgent === 'auto' },
64
- { type: 'confirm', name: 'piWorkspaceInputStatus',
67
+ ], default: current.primaryAgent });
68
+ }
69
+ if (answers.aiAgent === 'pi' || answers.aiAgent === 'both' || answers.aiAgent === 'auto') {
70
+ answers.piWorkspaceInputStatus = await confirm({
65
71
  message: 'Show workspace status widget in Pi input area (branch, PRs, CI)?',
66
- default: current.piWorkspaceInputStatus !== false,
67
- when: (ans) => ans.aiAgent === 'pi' || ans.aiAgent === 'both' || ans.aiAgent === 'auto' },
68
- { type: 'confirm', name: 'claudeWorkspaceStatusLine',
72
+ default: current.piWorkspaceInputStatus !== false });
73
+ }
74
+ if (answers.aiAgent === 'claude' || answers.aiAgent === 'both' || answers.aiAgent === 'auto') {
75
+ answers.claudeWorkspaceStatusLine = await confirm({
69
76
  message: 'Add workspace repo table to Claude Code status line?',
70
- default: current.claudeWorkspaceStatusLine !== false,
71
- when: (ans) => ans.aiAgent === 'claude' || ans.aiAgent === 'both' || ans.aiAgent === 'auto' },
72
- { type: 'confirm', name: 'autoReportBugs',
73
- message: 'Auto-file a GitHub issue when a command crashes? (deduped, sanitized)',
74
- default: current.autoReportBugs === true },
75
- ]);
77
+ default: current.claudeWorkspaceStatusLine !== false });
78
+ }
79
+ answers.autoReportBugs = await confirm({
80
+ message: 'Auto-file a GitHub issue when a command crashes? (deduped, sanitized)',
81
+ default: current.autoReportBugs === true });
76
82
 
77
83
  // Validate and normalize config
78
84
  let primaryAgent = answers.primaryAgent || 'auto';
@@ -9,12 +9,9 @@ import { getWorkspaceSessions, WorkspaceSession } from '../../utils/claude-sessi
9
9
  import { launchAgentPane } from './launcher';
10
10
  import { getAgentPaths, getPrimaryAgent, ConcreteAgentType } from '../../utils/agent-config';
11
11
  import { colorize } from '../../utils/colors';
12
- import inquirer from 'inquirer';
13
- import autocompletePrompt from 'inquirer-autocomplete-prompt';
12
+ import { search } from '../../utils/prompt';
14
13
  import * as fuzzy from 'fuzzy';
15
14
 
16
- inquirer.registerPrompt('autocomplete', autocompletePrompt);
17
-
18
15
  async function main() {
19
16
  try {
20
17
  const sessions = await getWorkspaceSessions();
@@ -30,12 +27,11 @@ async function main() {
30
27
  timeLabel: session.lastActiveLabel,
31
28
  }));
32
29
 
33
- const { selected } = await inquirer.prompt([{
34
- type: 'autocomplete',
35
- name: 'selected',
30
+ const selected = await search<typeof items[number]>({
36
31
  message: 'Select session to resume:',
37
- source: async (_answersSoFar: any, input: string | undefined) => {
38
- const searchInput = input || '';
32
+ pageSize: 15,
33
+ source: async (term: string | undefined) => {
34
+ const searchInput = term || '';
39
35
  const source = items.map(item => ({
40
36
  name: `${item.displayName} ${colorize(item.timeLabel, 'dim')}`,
41
37
  value: item,
@@ -47,8 +43,7 @@ async function main() {
47
43
  value: result.original,
48
44
  }));
49
45
  },
50
- pageSize: 15,
51
- } as any]);
46
+ });
52
47
 
53
48
  const item = selected as { session: WorkspaceSession };
54
49
  const session = item.session;
@@ -8,12 +8,9 @@
8
8
  import { listWorkspaces } from '../../utils/workspace-meta';
9
9
  import { launchAgentPane } from './launcher';
10
10
  import { getPrimaryAgent } from '../../utils/agent-config';
11
- import inquirer from 'inquirer';
12
- import autocompletePrompt from 'inquirer-autocomplete-prompt';
11
+ import { search } from '../../utils/prompt';
13
12
  import * as fuzzy from 'fuzzy';
14
13
 
15
- inquirer.registerPrompt('autocomplete', autocompletePrompt);
16
-
17
14
  async function main() {
18
15
  try {
19
16
  const workspaces = await listWorkspaces();
@@ -29,12 +26,11 @@ async function main() {
29
26
  repoCount: ws.metadata?.repositories?.length ?? 0,
30
27
  }));
31
28
 
32
- const { selected } = await inquirer.prompt([{
33
- type: 'autocomplete',
34
- name: 'selected',
29
+ const selected = await search<typeof items[number]>({
35
30
  message: 'Select workspace to launch agent in:',
36
- source: async (_answersSoFar: any, input: string | undefined) => {
37
- const searchInput = input || '';
31
+ pageSize: 15,
32
+ source: async (term: string | undefined) => {
33
+ const searchInput = term || '';
38
34
  const source = items.map(item => ({
39
35
  name: `${item.name} (${item.repoCount} repos)`,
40
36
  value: item,
@@ -46,8 +42,7 @@ async function main() {
46
42
  value: result.original,
47
43
  }));
48
44
  },
49
- pageSize: 15,
50
- } as any]);
45
+ });
51
46
 
52
47
  const workspace = selected as { name: string; path: string };
53
48
  const agent = getPrimaryAgent();
@@ -4,12 +4,12 @@ const {
4
4
  mockRm,
5
5
  mockListWorkspaces,
6
6
  mockPromptMultiWorkspaceSelection,
7
- mockPrompt,
7
+ mockConfirm,
8
8
  } = vi.hoisted(() => ({
9
9
  mockRm: vi.fn(),
10
10
  mockListWorkspaces: vi.fn(),
11
11
  mockPromptMultiWorkspaceSelection: vi.fn(),
12
- mockPrompt: vi.fn(),
12
+ mockConfirm: vi.fn(),
13
13
  }));
14
14
 
15
15
  vi.mock('fs/promises', () => ({
@@ -39,10 +39,8 @@ vi.mock('../utils/colors', () => ({
39
39
  colorize: (text: string) => text,
40
40
  }));
41
41
 
42
- vi.mock('inquirer', () => ({
43
- default: {
44
- prompt: mockPrompt,
45
- },
42
+ vi.mock('../utils/prompt', () => ({
43
+ confirm: mockConfirm,
46
44
  }));
47
45
 
48
46
  import { main } from './delete';
@@ -82,9 +80,9 @@ describe('delete-workspace main', () => {
82
80
 
83
81
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-a']);
84
82
 
85
- mockPrompt
86
- .mockResolvedValueOnce({ confirmed: true }) // confirm deletion
87
- .mockResolvedValueOnce({ deleteMore: false }); // don't delete more
83
+ mockConfirm
84
+ .mockResolvedValueOnce(true) // confirm deletion
85
+ .mockResolvedValueOnce(false); // don't delete more
88
86
 
89
87
  await main();
90
88
 
@@ -99,9 +97,9 @@ describe('delete-workspace main', () => {
99
97
 
100
98
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-a', 'ws-b']);
101
99
 
102
- mockPrompt
103
- .mockResolvedValueOnce({ confirmed: true })
104
- .mockResolvedValueOnce({ deleteMore: false });
100
+ mockConfirm
101
+ .mockResolvedValueOnce(true)
102
+ .mockResolvedValueOnce(false);
105
103
 
106
104
  await main();
107
105
 
@@ -117,15 +115,15 @@ describe('delete-workspace main', () => {
117
115
 
118
116
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-a']);
119
117
 
120
- mockPrompt
121
- .mockResolvedValueOnce({ confirmed: false }) // cancel deletion
122
- .mockResolvedValueOnce({ deleteMore: false }); // don't delete more
118
+ mockConfirm
119
+ .mockResolvedValueOnce(false) // cancel deletion
120
+ .mockResolvedValueOnce(false); // don't delete more
123
121
 
124
122
  await main();
125
123
 
126
124
  expect(mockRm).not.toHaveBeenCalled();
127
125
  // Should still ask "delete more?" — not exit immediately
128
- expect(mockPrompt).toHaveBeenCalledTimes(2);
126
+ expect(mockConfirm).toHaveBeenCalledTimes(2);
129
127
  });
130
128
 
131
129
  it('loops when user wants to delete more', async () => {
@@ -139,10 +137,10 @@ describe('delete-workspace main', () => {
139
137
  .mockResolvedValueOnce(['ws-a'])
140
138
  .mockResolvedValueOnce(['ws-b']);
141
139
 
142
- mockPrompt
143
- .mockResolvedValueOnce({ confirmed: true }) // confirm 1st deletion
144
- .mockResolvedValueOnce({ deleteMore: true }) // delete more
145
- .mockResolvedValueOnce({ confirmed: true }); // confirm 2nd deletion
140
+ mockConfirm
141
+ .mockResolvedValueOnce(true) // confirm 1st deletion
142
+ .mockResolvedValueOnce(true) // delete more
143
+ .mockResolvedValueOnce(true); // confirm 2nd deletion
146
144
 
147
145
  await main();
148
146
 
@@ -159,14 +157,14 @@ describe('delete-workspace main', () => {
159
157
  .mockResolvedValueOnce([]); // none left after deletion
160
158
 
161
159
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-only']);
162
- mockPrompt.mockResolvedValueOnce({ confirmed: true });
160
+ mockConfirm.mockResolvedValueOnce(true);
163
161
 
164
162
  await main();
165
163
 
166
164
  expect(mockRm).toHaveBeenCalledTimes(1);
167
165
  expect(logInfo).toHaveBeenCalledWith('No more workspaces remaining');
168
166
  // Should NOT have asked "delete more?" since there are none left
169
- expect(mockPrompt).toHaveBeenCalledTimes(1); // only the confirm prompt
167
+ expect(mockConfirm).toHaveBeenCalledTimes(1); // only the confirm prompt
170
168
  });
171
169
 
172
170
  it('handles individual deletion failure without stopping others', async () => {
@@ -180,9 +178,9 @@ describe('delete-workspace main', () => {
180
178
  .mockRejectedValueOnce(new Error('permission denied')) // ws-a fails
181
179
  .mockResolvedValueOnce(undefined); // ws-b succeeds
182
180
 
183
- mockPrompt
184
- .mockResolvedValueOnce({ confirmed: true })
185
- .mockResolvedValueOnce({ deleteMore: false });
181
+ mockConfirm
182
+ .mockResolvedValueOnce(true)
183
+ .mockResolvedValueOnce(false);
186
184
 
187
185
  await main();
188
186
 
@@ -197,17 +195,15 @@ describe('delete-workspace main', () => {
197
195
  .mockResolvedValueOnce([]);
198
196
 
199
197
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-alpha']);
200
- mockPrompt.mockResolvedValueOnce({ confirmed: true });
198
+ mockConfirm.mockResolvedValueOnce(true);
201
199
 
202
200
  await main();
203
201
 
204
- expect(mockPrompt).toHaveBeenCalledWith(
205
- expect.arrayContaining([
206
- expect.objectContaining({
207
- message: 'Delete workspace ws-alpha?',
208
- default: true,
209
- }),
210
- ])
202
+ expect(mockConfirm).toHaveBeenCalledWith(
203
+ expect.objectContaining({
204
+ message: 'Delete workspace ws-alpha?',
205
+ default: true,
206
+ })
211
207
  );
212
208
  });
213
209
 
@@ -217,17 +213,15 @@ describe('delete-workspace main', () => {
217
213
  .mockResolvedValueOnce([]);
218
214
 
219
215
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-a', 'ws-b', 'ws-c']);
220
- mockPrompt.mockResolvedValueOnce({ confirmed: true });
216
+ mockConfirm.mockResolvedValueOnce(true);
221
217
 
222
218
  await main();
223
219
 
224
- expect(mockPrompt).toHaveBeenCalledWith(
225
- expect.arrayContaining([
226
- expect.objectContaining({
227
- message: 'Delete these 3 workspaces?',
228
- default: true,
229
- }),
230
- ])
220
+ expect(mockConfirm).toHaveBeenCalledWith(
221
+ expect.objectContaining({
222
+ message: 'Delete these 3 workspaces?',
223
+ default: true,
224
+ })
231
225
  );
232
226
  });
233
227
 
@@ -239,7 +233,7 @@ describe('delete-workspace main', () => {
239
233
  .mockResolvedValueOnce([]);
240
234
 
241
235
  mockPromptMultiWorkspaceSelection.mockResolvedValueOnce(['ws-a']);
242
- mockPrompt.mockResolvedValueOnce({ confirmed: true });
236
+ mockConfirm.mockResolvedValueOnce(true);
243
237
 
244
238
  await main();
245
239
 
@@ -5,7 +5,7 @@ import { listWorkspaces } from '../utils/workspace-meta';
5
5
  import { promptMultiWorkspaceSelection } from '../utils/prompts';
6
6
  import { logInfo, logSuccess, logError, logWarning } from '../utils/logger';
7
7
  import { colorize } from '../utils/colors';
8
- import inquirer from 'inquirer';
8
+ import { confirm } from '../utils/prompt';
9
9
  import { getGlobalOpts, parseList } from '../utils/command-helpers';
10
10
 
11
11
  export function registerDeleteCommand(parent: Command) {
@@ -76,16 +76,12 @@ async function handleDelete(opts: {
76
76
  logWarning('This will permanently delete all cloned repositories in the selected workspaces!');
77
77
 
78
78
  if (!opts.yes) {
79
- const { confirmed } = await inquirer.prompt([
80
- {
81
- type: 'confirm',
82
- name: 'confirmed',
83
- message: targets.length === 1
84
- ? `Delete workspace ${targets[0].name}?`
85
- : `Delete these ${targets.length} workspaces?`,
86
- default: true,
87
- },
88
- ]);
79
+ const confirmed = await confirm({
80
+ message: targets.length === 1
81
+ ? `Delete workspace ${targets[0].name}?`
82
+ : `Delete these ${targets.length} workspaces?`,
83
+ default: true,
84
+ });
89
85
  if (!confirmed) {
90
86
  logInfo('Deletion cancelled');
91
87
  process.exit(0);
@@ -147,14 +143,10 @@ async function handleDelete(opts: {
147
143
  ? `Delete workspace ${resolved[0].name}?`
148
144
  : `Delete these ${resolved.length} workspaces?`;
149
145
 
150
- const { confirmed } = await inquirer.prompt([
151
- {
152
- type: 'confirm',
153
- name: 'confirmed',
154
- message: confirmMessage,
155
- default: true,
156
- },
157
- ]);
146
+ const confirmed = await confirm({
147
+ message: confirmMessage,
148
+ default: true,
149
+ });
158
150
 
159
151
  if (confirmed) {
160
152
  for (const { name, path: workspacePath } of resolved) {
@@ -175,14 +167,10 @@ async function handleDelete(opts: {
175
167
  break;
176
168
  }
177
169
 
178
- const { deleteMore } = await inquirer.prompt([
179
- {
180
- type: 'confirm',
181
- name: 'deleteMore',
182
- message: 'Delete more workspaces?',
183
- default: false,
184
- },
185
- ]);
170
+ const deleteMore = await confirm({
171
+ message: 'Delete more workspaces?',
172
+ default: false,
173
+ });
186
174
 
187
175
  if (!deleteMore) {
188
176
  break;
@@ -2,7 +2,7 @@ import { Command } from 'commander';
2
2
  import { getGhqStatus, displayGhqInfo, ghqList } from '../utils/ghq-integration';
3
3
  import { logSuccess, logWarning, logError } from '../utils/logger';
4
4
  import { colorize } from '../utils/colors';
5
- import inquirer from 'inquirer';
5
+ import { confirm } from '../utils/prompt';
6
6
 
7
7
  export function registerGhqStatusCommand(parent: Command) {
8
8
  parent
@@ -30,10 +30,9 @@ async function handleGhqStatus() {
30
30
  console.log('');
31
31
 
32
32
  if (status.repoCount && status.repoCount > 0) {
33
- const { showRepos } = await inquirer.prompt([{
34
- type: 'confirm', name: 'showRepos',
33
+ const showRepos = await confirm({
35
34
  message: `View all ${status.repoCount} managed repositories?`, default: false,
36
- }]);
35
+ });
37
36
 
38
37
  if (showRepos) {
39
38
  const repos = await ghqList();
@@ -48,10 +47,9 @@ async function handleGhqStatus() {
48
47
  console.log('');
49
48
  console.log('The workspace manager will use direct git clones.');
50
49
 
51
- const { showInfo } = await inquirer.prompt([{
52
- type: 'confirm', name: 'showInfo',
50
+ const showInfo = await confirm({
53
51
  message: 'Show ghq installation information?', default: true,
54
- }]);
52
+ });
55
53
 
56
54
  if (showInfo) { displayGhqInfo(); }
57
55
  }
@@ -7,12 +7,9 @@ import { listWorkspaces } from '../utils/workspace-meta';
7
7
  import { getWorkspaceSessions } from '../utils/claude-sessions';
8
8
  import { logError } from '../utils/logger';
9
9
  import { colorize } from '../utils/colors';
10
- import inquirer from 'inquirer';
11
- import autocompletePrompt from 'inquirer-autocomplete-prompt';
10
+ import { search } from '../utils/prompt';
12
11
  import * as fuzzy from 'fuzzy';
13
12
 
14
- inquirer.registerPrompt('autocomplete', autocompletePrompt);
15
-
16
13
  const TEMP_FILE = path.join(os.homedir(), '.workspace-last-go');
17
14
  const RESUME_FLAG_FILE = path.join(os.homedir(), '.workspace-resume-session');
18
15
 
@@ -68,27 +65,23 @@ async function handleGo(workspaceArg?: string) {
68
65
  return 0;
69
66
  });
70
67
 
71
- const { workspaceName } = await inquirer.prompt([
72
- {
73
- type: 'autocomplete',
74
- name: 'workspaceName',
75
- message: 'Select workspace to navigate to:',
76
- source: async (_answersSoFar: any, input: string | undefined) => {
77
- const searchInput = input || '';
78
- const source = items.map(item => ({
79
- name: `${item.name} ${item.lastActiveLabel ? colorize(item.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${item.repoCount} repos`, 'dim')}`,
80
- value: item.name,
81
- }));
82
- if (!searchInput) return source;
83
- const results = fuzzy.filter(searchInput, items, { extract: (item) => item.name });
84
- return results.map(result => ({
85
- name: `${result.original.name} ${result.original.lastActiveLabel ? colorize(result.original.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${result.original.repoCount} repos`, 'dim')}`,
86
- value: result.original.name,
87
- }));
88
- },
89
- pageSize: 15,
90
- } as any,
91
- ]);
68
+ const workspaceName = await search<string>({
69
+ message: 'Select workspace to navigate to:',
70
+ pageSize: 15,
71
+ source: async (term: string | undefined) => {
72
+ const searchInput = term || '';
73
+ const source = items.map(item => ({
74
+ name: `${item.name} ${item.lastActiveLabel ? colorize(item.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${item.repoCount} repos`, 'dim')}`,
75
+ value: item.name,
76
+ }));
77
+ if (!searchInput) return source;
78
+ const results = fuzzy.filter(searchInput, items, { extract: (item) => item.name });
79
+ return results.map(result => ({
80
+ name: `${result.original.name} ${result.original.lastActiveLabel ? colorize(result.original.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${result.original.repoCount} repos`, 'dim')}`,
81
+ value: result.original.name,
82
+ }));
83
+ },
84
+ });
92
85
 
93
86
  selectedWorkspace = workspaceName;
94
87
  }
@@ -2,7 +2,7 @@ import { Command } from 'commander';
2
2
  import { readHistory, getOperationStats, filterHistory, clearHistory } from '../utils/history';
3
3
  import { logError, logInfo, logStep, logSuccess } from '../utils/logger';
4
4
  import { colorize } from '../utils/colors';
5
- import inquirer from 'inquirer';
5
+ import { confirm } from '../utils/prompt';
6
6
  import { getGlobalOpts } from '../utils/command-helpers';
7
7
 
8
8
  const displayHistoryTable = (records: Array<{ timestamp: string; command: string; workspace?: string; duration: number; success: boolean; error?: string }>) => {
@@ -88,16 +88,12 @@ export function registerHistoryCommand(parent: Command) {
88
88
  return;
89
89
  }
90
90
 
91
- const { confirm } = await inquirer.prompt([
92
- {
93
- type: 'confirm',
94
- name: 'confirm',
95
- message: 'Are you sure you want to clear operation history?',
96
- default: false,
97
- },
98
- ]);
91
+ const confirmed = await confirm({
92
+ message: 'Are you sure you want to clear operation history?',
93
+ default: false,
94
+ });
99
95
 
100
- if (confirm) {
96
+ if (confirmed) {
101
97
  await clearHistory();
102
98
  logSuccess('Operation history cleared');
103
99
  } else {