@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.
- package/CHANGELOG.md +30 -0
- package/README.md +29 -5
- package/bin/workspace.js +12 -0
- package/dist/commands/analyze-deps.js +5 -12
- package/dist/commands/archive.js +13 -18
- package/dist/commands/branch/create.js +5 -12
- package/dist/commands/branch/switch.js +14 -25
- package/dist/commands/cache/manager.js +13 -24
- package/dist/commands/cleanup.js +14 -26
- package/dist/commands/config.js +82 -0
- package/dist/commands/configure-claude.js +4 -8
- package/dist/commands/configure.js +38 -32
- package/dist/commands/dashboard/session-picker.js +19 -26
- package/dist/commands/dashboard/workspace-picker.js +19 -26
- package/dist/commands/delete.js +15 -30
- package/dist/commands/ghq-status.js +7 -12
- package/dist/commands/go.js +18 -27
- package/dist/commands/history.js +6 -13
- package/dist/commands/list.js +20 -29
- package/dist/commands/remove-repo.js +21 -29
- package/dist/commands/save-context.js +5 -10
- package/dist/commands/sessions.js +19 -25
- package/dist/commands/suite/create.js +27 -53
- package/dist/commands/suite/delete.js +13 -25
- package/dist/commands/suite/export.js +20 -36
- package/dist/commands/suite/import.js +9 -20
- package/dist/commands/suite/use.js +9 -17
- package/dist/utils/config-schema.js +57 -0
- package/dist/utils/editor.js +35 -0
- package/dist/utils/prompt.js +26 -0
- package/dist/utils/prompts.js +86 -118
- package/package.json +3 -6
- package/src/commands/analyze-deps.ts +5 -9
- package/src/commands/archive.ts +5 -7
- package/src/commands/branch/create.ts +5 -9
- package/src/commands/branch/switch.ts +14 -22
- package/src/commands/cache/manager.ts +13 -21
- package/src/commands/cleanup.ts +14 -23
- package/src/commands/config.ts +52 -1
- package/src/commands/configure-claude.ts +4 -5
- package/src/commands/configure.ts +35 -29
- package/src/commands/dashboard/session-picker.ts +6 -11
- package/src/commands/dashboard/workspace-picker.ts +6 -11
- package/src/commands/delete.test.ts +36 -42
- package/src/commands/delete.ts +15 -27
- package/src/commands/ghq-status.ts +5 -7
- package/src/commands/go.ts +18 -25
- package/src/commands/history.ts +6 -10
- package/src/commands/list.test.ts +19 -26
- package/src/commands/list.ts +24 -31
- package/src/commands/remove-repo.ts +11 -17
- package/src/commands/save-context.ts +3 -5
- package/src/commands/sessions.ts +6 -10
- package/src/commands/suite/create.ts +28 -50
- package/src/commands/suite/delete.ts +14 -23
- package/src/commands/suite/export.ts +20 -33
- package/src/commands/suite/import.ts +9 -17
- package/src/commands/suite/use.ts +9 -14
- package/src/utils/config-schema.test.ts +49 -0
- package/src/utils/config-schema.ts +65 -0
- package/src/utils/editor.test.ts +37 -0
- package/src/utils/editor.ts +48 -0
- package/src/utils/prompt.ts +16 -0
- package/src/utils/prompts.test.ts +70 -41
- package/src/utils/prompts.ts +98 -128
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { loadClaudeConfig, saveClaudeConfig } from '../utils/claude-integration';
|
|
3
3
|
import { logSuccess, logError } from '../utils/logger';
|
|
4
4
|
import { colorize } from '../utils/colors';
|
|
5
|
-
import
|
|
5
|
+
import { confirm } from '../utils/prompt';
|
|
6
6
|
|
|
7
7
|
export function registerConfigureClaudeCommand(parent: Command) {
|
|
8
8
|
parent
|
|
@@ -27,10 +27,9 @@ async function handleConfigureClaude() {
|
|
|
27
27
|
console.log(` Generate context: ${currentConfig.generateContext ? colorize('Enabled', 'green') : colorize('Disabled', 'red')}`);
|
|
28
28
|
console.log('');
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
]);
|
|
30
|
+
const autoLaunch = await confirm({ message: 'Auto-launch Claude Code after workspace creation?', default: currentConfig.autoLaunch });
|
|
31
|
+
const generateContext = await confirm({ message: 'Generate CLAUDE.md context file in workspaces?', default: currentConfig.generateContext });
|
|
32
|
+
const answers = { autoLaunch, generateContext };
|
|
34
33
|
|
|
35
34
|
await saveClaudeConfig(answers);
|
|
36
35
|
|
|
@@ -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
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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
|
|
34
|
-
type: 'autocomplete',
|
|
35
|
-
name: 'selected',
|
|
30
|
+
const selected = await search<typeof items[number]>({
|
|
36
31
|
message: 'Select session to resume:',
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
33
|
-
type: 'autocomplete',
|
|
34
|
-
name: 'selected',
|
|
29
|
+
const selected = await search<typeof items[number]>({
|
|
35
30
|
message: 'Select workspace to launch agent in:',
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
mockConfirm,
|
|
8
8
|
} = vi.hoisted(() => ({
|
|
9
9
|
mockRm: vi.fn(),
|
|
10
10
|
mockListWorkspaces: vi.fn(),
|
|
11
11
|
mockPromptMultiWorkspaceSelection: vi.fn(),
|
|
12
|
-
|
|
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('
|
|
43
|
-
|
|
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
|
-
|
|
86
|
-
.mockResolvedValueOnce(
|
|
87
|
-
.mockResolvedValueOnce(
|
|
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
|
-
|
|
103
|
-
.mockResolvedValueOnce(
|
|
104
|
-
.mockResolvedValueOnce(
|
|
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
|
-
|
|
121
|
-
.mockResolvedValueOnce(
|
|
122
|
-
.mockResolvedValueOnce(
|
|
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(
|
|
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
|
-
|
|
143
|
-
.mockResolvedValueOnce(
|
|
144
|
-
.mockResolvedValueOnce(
|
|
145
|
-
.mockResolvedValueOnce(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
184
|
-
.mockResolvedValueOnce(
|
|
185
|
-
.mockResolvedValueOnce(
|
|
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
|
-
|
|
198
|
+
mockConfirm.mockResolvedValueOnce(true);
|
|
201
199
|
|
|
202
200
|
await main();
|
|
203
201
|
|
|
204
|
-
expect(
|
|
205
|
-
expect.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
216
|
+
mockConfirm.mockResolvedValueOnce(true);
|
|
221
217
|
|
|
222
218
|
await main();
|
|
223
219
|
|
|
224
|
-
expect(
|
|
225
|
-
expect.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
236
|
+
mockConfirm.mockResolvedValueOnce(true);
|
|
243
237
|
|
|
244
238
|
await main();
|
|
245
239
|
|
package/src/commands/delete.ts
CHANGED
|
@@ -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
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
package/src/commands/go.ts
CHANGED
|
@@ -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
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
source
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
}
|
package/src/commands/history.ts
CHANGED
|
@@ -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
|
|
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
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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 (
|
|
96
|
+
if (confirmed) {
|
|
101
97
|
await clearHistory();
|
|
102
98
|
logSuccess('Operation history cleared');
|
|
103
99
|
} else {
|