@nemus-cli/nemus 0.9.0 → 0.11.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 (74) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +23 -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/prune.js +196 -0
  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/program.js +2 -0
  29. package/dist/utils/prompt.js +26 -0
  30. package/dist/utils/prompts.js +86 -118
  31. package/dist/utils/prune.js +70 -0
  32. package/package.json +3 -6
  33. package/scripts/release-notes.mjs +54 -0
  34. package/skills/config.md +17 -0
  35. package/skills/nemus/SKILL.md +7 -2
  36. package/skills/nemus/references/completion.md +22 -0
  37. package/skills/nemus/references/config.md +32 -0
  38. package/skills/nemus/references/prune.md +44 -0
  39. package/skills/nemus/references/reflect.md +43 -0
  40. package/skills/nemus/references/save-context.md +25 -0
  41. package/skills/prune-workspaces.md +23 -0
  42. package/skills/reflect.md +21 -0
  43. package/src/commands/analyze-deps.ts +5 -9
  44. package/src/commands/archive.ts +5 -7
  45. package/src/commands/branch/create.ts +5 -9
  46. package/src/commands/branch/switch.ts +14 -22
  47. package/src/commands/cache/manager.ts +13 -21
  48. package/src/commands/cleanup.ts +14 -23
  49. package/src/commands/configure-claude.ts +4 -5
  50. package/src/commands/configure.ts +35 -29
  51. package/src/commands/dashboard/session-picker.ts +6 -11
  52. package/src/commands/dashboard/workspace-picker.ts +6 -11
  53. package/src/commands/delete.test.ts +36 -42
  54. package/src/commands/delete.ts +15 -27
  55. package/src/commands/ghq-status.ts +5 -7
  56. package/src/commands/go.ts +18 -25
  57. package/src/commands/history.ts +6 -10
  58. package/src/commands/list.test.ts +19 -26
  59. package/src/commands/list.ts +24 -31
  60. package/src/commands/prune.ts +183 -0
  61. package/src/commands/remove-repo.ts +11 -17
  62. package/src/commands/save-context.ts +3 -5
  63. package/src/commands/sessions.ts +6 -10
  64. package/src/commands/suite/create.ts +28 -50
  65. package/src/commands/suite/delete.ts +14 -23
  66. package/src/commands/suite/export.ts +20 -33
  67. package/src/commands/suite/import.ts +9 -17
  68. package/src/commands/suite/use.ts +9 -14
  69. package/src/program.ts +2 -0
  70. package/src/utils/prompt.ts +16 -0
  71. package/src/utils/prompts.test.ts +70 -41
  72. package/src/utils/prompts.ts +98 -128
  73. package/src/utils/prune.test.ts +121 -0
  74. package/src/utils/prune.ts +109 -0
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: prune-workspaces
3
+ description: Bulk-delete workspaces with no recent activity, safe by default (protects uncommitted/unpushed work)
4
+ ---
5
+
6
+ Preview first — never delete without showing the user the plan:
7
+ ```bash
8
+ nemus prune --days 30 --dry-run
9
+ ```
10
+
11
+ Then prune after confirming with the user:
12
+ ```bash
13
+ nemus prune --days 30 # prompts (default: No)
14
+ nemus prune --days 30 --yes # non-interactive — only when the user is sure
15
+ ```
16
+
17
+ - **Stale** = no agent session (or, failing that, no `createdAt`) in the last N
18
+ days (`--days`, default 30). Undatable workspaces are never selected.
19
+ - **Safe by default:** workspaces with uncommitted or unpushed changes are
20
+ **protected** and listed with the reason — not deleted. `--include-dirty`
21
+ overrides this (only with explicit user consent to lose that work).
22
+ - `--json` and `--dry-run` never delete.
23
+ - Deletion is permanent — repos must be re-cloned. Always confirm first.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: reflect
3
+ description: Retrospective on recent agent sessions — get concrete tips to improve skills, context, and prompts
4
+ ---
5
+
6
+ Analyze recent workspaces' agent sessions and get setup improvement tips (read-only; changes no repos):
7
+ ```bash
8
+ nemus reflect # most recent workspaces (default 10)
9
+ nemus reflect --workspace <name> # a single workspace
10
+ nemus reflect --markdown > reflection.md # shareable report
11
+ ```
12
+
13
+ Review saved reports (saved under `~/.nemus/reflect/` unless `--no-save`):
14
+ ```bash
15
+ nemus reflect history # list past reports
16
+ nemus reflect show [id] # show one (defaults to latest)
17
+ ```
18
+
19
+ - Uses *your own* configured agent (claude/pi/opencode) as the judge — no API key of Nemus's.
20
+ - `--json` / `--markdown` for machine or shareable output; `--group-by kind|priority`.
21
+ - Safe: it reads transcripts and prints advice, nothing is modified.
@@ -11,7 +11,7 @@ import {
11
11
  import { logError, logInfo, logStep, logSuccess } from '../utils/logger';
12
12
  import { outputJson, outputJsonError } from '../utils/output';
13
13
  import { colorize } from '../utils/colors';
14
- import inquirer from 'inquirer';
14
+ import { confirm } from '../utils/prompt';
15
15
  import { resolveWorkspace } from '../utils/command-helpers';
16
16
 
17
17
  const displayDependencyAnalysis = (analyses: Map<string, any>) => {
@@ -122,14 +122,10 @@ async function handleAnalyzeDeps(workspaceArg?: string, opts: { json?: boolean }
122
122
  }
123
123
 
124
124
  if (process.stdout.isTTY) {
125
- const { saveToMetadata } = await inquirer.prompt([
126
- {
127
- type: 'confirm',
128
- name: 'saveToMetadata',
129
- message: 'Save dependency analysis to workspace metadata?',
130
- default: true,
131
- },
132
- ]);
125
+ const saveToMetadata = await confirm({
126
+ message: 'Save dependency analysis to workspace metadata?',
127
+ default: true,
128
+ });
133
129
 
134
130
  if (saveToMetadata) {
135
131
  const updatedMetadata = updateWorkspaceMetadata(metadata, analyses);
@@ -3,7 +3,7 @@ import { listWorkspaces, archiveWorkspace, unarchiveWorkspace } from '../utils/w
3
3
  import { promptMultiWorkspaceSelection } from '../utils/prompts';
4
4
  import { logInfo, logSuccess, logError } from '../utils/logger';
5
5
  import { colorize } from '../utils/colors';
6
- import inquirer from 'inquirer';
6
+ import { confirm } from '../utils/prompt';
7
7
  import { getGlobalOpts, parseList } from '../utils/command-helpers';
8
8
 
9
9
  export function registerArchiveCommand(parent: Command) {
@@ -50,13 +50,12 @@ async function handleArchive(opts: {
50
50
  }
51
51
 
52
52
  if (!opts.yes) {
53
- const { confirmed } = await inquirer.prompt([{
54
- type: 'confirm', name: 'confirmed',
53
+ const confirmed = await confirm({
55
54
  message: selectedNames.length === 1
56
55
  ? `Unarchive workspace ${selectedNames[0]}?`
57
56
  : `Unarchive these ${selectedNames.length} workspaces?`,
58
57
  default: true,
59
- }]);
58
+ });
60
59
  if (!confirmed) return;
61
60
  }
62
61
 
@@ -86,13 +85,12 @@ async function handleArchive(opts: {
86
85
  }
87
86
 
88
87
  if (!opts.yes) {
89
- const { confirmed } = await inquirer.prompt([{
90
- type: 'confirm', name: 'confirmed',
88
+ const confirmed = await confirm({
91
89
  message: selectedNames.length === 1
92
90
  ? `Archive workspace ${selectedNames[0]}? It will be auto-deleted in 30 days.`
93
91
  : `Archive these ${selectedNames.length} workspaces? They will be auto-deleted in 30 days.`,
94
92
  default: true,
95
- }]);
93
+ });
96
94
  if (!confirmed) return;
97
95
  }
98
96
 
@@ -6,7 +6,7 @@ import { createBranch } from '../../utils/branch-operations';
6
6
  import { logError, logInfo, logStep, logSuccess, logWarning } from '../../utils/logger';
7
7
  import { colorize } from '../../utils/colors';
8
8
  import { WORKSPACES_DIR } from '../../utils/config';
9
- import inquirer from 'inquirer';
9
+ import { input } from '../../utils/prompt';
10
10
 
11
11
  export interface BranchCreateOpts {
12
12
  workspace?: string;
@@ -40,14 +40,10 @@ export const main = async (opts?: BranchCreateOpts) => {
40
40
  logError('Usage: w branch create --workspace <name> --branch <branch>');
41
41
  process.exit(1);
42
42
  }
43
- const { branch } = await inquirer.prompt([
44
- {
45
- type: 'input',
46
- name: 'branch',
47
- message: 'New branch name:',
48
- validate: (input: string) => input.trim() ? true : 'Branch name cannot be empty',
49
- },
50
- ]);
43
+ const branch = await input({
44
+ message: 'New branch name:',
45
+ validate: (input: string) => input.trim() ? true : 'Branch name cannot be empty',
46
+ });
51
47
  branchName = branch;
52
48
  }
53
49
 
@@ -7,7 +7,7 @@ import { promptWorkspaceSelection } from '../../utils/prompts';
7
7
  import { logInfo, logSuccess, logError, logWarning } from '../../utils/logger';
8
8
  import { colorize } from '../../utils/colors';
9
9
  import { switchBranch, BranchSwitchResult } from '../../utils/branch-operations';
10
- import inquirer from 'inquirer';
10
+ import { confirm, input } from '../../utils/prompt';
11
11
 
12
12
  export interface BranchSwitchOpts {
13
13
  workspace?: string;
@@ -61,20 +61,16 @@ export async function main(opts?: BranchSwitchOpts) {
61
61
  logError('Usage: w branch switch --workspace <name> --branch <branch>');
62
62
  process.exit(1);
63
63
  }
64
- const { branch } = await inquirer.prompt([
65
- {
66
- type: 'input',
67
- name: 'branch',
68
- message: 'Target branch name:',
69
- default: 'main',
70
- validate: (input: string) => {
71
- if (!input || input.trim().length === 0) {
72
- return 'Branch name cannot be empty';
73
- }
74
- return true;
75
- },
64
+ const branch = await input({
65
+ message: 'Target branch name:',
66
+ default: 'main',
67
+ validate: (input: string) => {
68
+ if (!input || input.trim().length === 0) {
69
+ return 'Branch name cannot be empty';
70
+ }
71
+ return true;
76
72
  },
77
- ]);
73
+ });
78
74
  targetBranch = branch;
79
75
  }
80
76
 
@@ -82,14 +78,10 @@ export async function main(opts?: BranchSwitchOpts) {
82
78
 
83
79
  // Skip confirmation when --yes flag is provided
84
80
  if (!yes && process.stdout.isTTY) {
85
- const { confirmed } = await inquirer.prompt([
86
- {
87
- type: 'confirm',
88
- name: 'confirmed',
89
- message: 'Proceed with bulk branch switch?',
90
- default: true,
91
- },
92
- ]);
81
+ const confirmed = await confirm({
82
+ message: 'Proceed with bulk branch switch?',
83
+ default: true,
84
+ });
93
85
 
94
86
  if (!confirmed) {
95
87
  logInfo('Branch switch cancelled');
@@ -4,7 +4,7 @@ import { clearCache, getCacheStats } from '../../utils/cache';
4
4
  import { fetchOrgRepos, verifyGhAuth, displayAuthInstructions } from '../../utils/github';
5
5
  import { logInfo, logSuccess, logError } from '../../utils/logger';
6
6
  import { colorize } from '../../utils/colors';
7
- import inquirer from 'inquirer';
7
+ import { confirm, select } from '../../utils/prompt';
8
8
  import * as fuzzy from 'fuzzy';
9
9
 
10
10
  export async function cacheInfo() {
@@ -123,32 +123,24 @@ export async function main() {
123
123
  }
124
124
 
125
125
  // Interactive mode (original behavior)
126
- const { selectedAction } = await inquirer.prompt([
127
- {
128
- type: 'list',
129
- name: 'selectedAction',
130
- message: 'Select action:',
131
- choices: [
132
- { name: 'View cache info', value: 'info' },
133
- { name: 'Refresh cache (force fetch from GitHub)', value: 'refresh' },
134
- { name: 'Clear cache', value: 'clear' },
135
- ],
136
- },
137
- ]);
126
+ const selectedAction = await select({
127
+ message: 'Select action:',
128
+ choices: [
129
+ { name: 'View cache info', value: 'info' },
130
+ { name: 'Refresh cache (force fetch from GitHub)', value: 'refresh' },
131
+ { name: 'Clear cache', value: 'clear' },
132
+ ],
133
+ });
138
134
 
139
135
  if (selectedAction === 'info') {
140
136
  await cacheInfo();
141
137
  } else if (selectedAction === 'refresh') {
142
138
  await cacheRefresh();
143
139
  } else if (selectedAction === 'clear') {
144
- const { confirmed } = await inquirer.prompt([
145
- {
146
- type: 'confirm',
147
- name: 'confirmed',
148
- message: 'Are you sure you want to clear the cache?',
149
- default: false,
150
- },
151
- ]);
140
+ const confirmed = await confirm({
141
+ message: 'Are you sure you want to clear the cache?',
142
+ default: false,
143
+ });
152
144
 
153
145
  if (confirmed) {
154
146
  await cacheClear();
@@ -5,7 +5,7 @@ import { loadMetadata } from '../utils/workspace-meta';
5
5
  import { removeNodeModules, removeBuildArtifacts, gitClean } from '../utils/cleanup-operations';
6
6
  import { logError, logInfo, logStep, logSuccess } from '../utils/logger';
7
7
  import { colorize } from '../utils/colors';
8
- import inquirer from 'inquirer';
8
+ import { checkbox, confirm } from '../utils/prompt';
9
9
  import { getGlobalOpts, resolveWorkspace } from '../utils/command-helpers';
10
10
 
11
11
  export function registerCleanupCommand(parent: Command) {
@@ -54,19 +54,14 @@ async function handleCleanup(workspaceArg: string | undefined, opts: {
54
54
  if (build) operations.push('build');
55
55
  if (gitCleanFlag) operations.push('git_clean');
56
56
  } else {
57
- const result = await inquirer.prompt([
58
- {
59
- type: 'checkbox',
60
- name: 'operations',
61
- message: 'Select cleanup operations:',
62
- choices: [
63
- { name: 'Remove node_modules (all repos)', value: 'node_modules' },
64
- { name: 'Remove build artifacts (dist, build, .next, coverage)', value: 'build' },
65
- { name: 'Git clean (remove untracked files)', value: 'git_clean' },
66
- ],
67
- },
68
- ]);
69
- operations = result.operations;
57
+ operations = await checkbox({
58
+ message: 'Select cleanup operations:',
59
+ choices: [
60
+ { name: 'Remove node_modules (all repos)', value: 'node_modules' },
61
+ { name: 'Remove build artifacts (dist, build, .next, coverage)', value: 'build' },
62
+ { name: 'Git clean (remove untracked files)', value: 'git_clean' },
63
+ ],
64
+ });
70
65
  }
71
66
 
72
67
  if (operations.length === 0) {
@@ -75,16 +70,12 @@ async function handleCleanup(workspaceArg: string | undefined, opts: {
75
70
  }
76
71
 
77
72
  if (!opts.yes) {
78
- const { confirm } = await inquirer.prompt([
79
- {
80
- type: 'confirm',
81
- name: 'confirm',
82
- message: `Proceed with cleanup? This cannot be undone.`,
83
- default: false,
84
- },
85
- ]);
73
+ const confirmed = await confirm({
74
+ message: `Proceed with cleanup? This cannot be undone.`,
75
+ default: false,
76
+ });
86
77
 
87
- if (!confirm) {
78
+ if (!confirmed) {
88
79
  logInfo('Cleanup cancelled');
89
80
  return;
90
81
  }
@@ -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 inquirer from 'inquirer';
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 answers = await inquirer.prompt([
31
- { type: 'confirm', name: 'autoLaunch', message: 'Auto-launch Claude Code after workspace creation?', default: currentConfig.autoLaunch },
32
- { type: 'confirm', name: 'generateContext', message: 'Generate CLAUDE.md context file in workspaces?', default: currentConfig.generateContext },
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 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();