Skip to content

Commit 59ea709

Browse files
authored
fix: version display, install UX — audit findings (#95)
* fix: version display, install UX — audit findings - Fix version showing hardcoded 1.7.6 instead of actual 1.19.2 - Export setVersion/setAgentCount from @skillkit/cli - Call setVersion(version) and setAgentCount(getAllAdapters().length) in cli.ts - Change fallback from '1.7.6' to 'dev' so stale values are obvious - Fix install pre-selecting all skills by default (bad UX) - Add "Install all / Select specific" prompt before multiselect - When selecting manually, start with nothing pre-selected - Apply same fix to both InstallCommand and runInstallFlow() - Matches existing quickAgentSelect pattern Fixes found during comprehensive codebase audit. Related issues: #87, #88, #89, #90, #91, #92, #93, #94 * refactor: extract quickSkillSelect, use getAdapterCount - Extract duplicated "Install all / Select specific" pattern into quickSkillSelect() in prompts.ts (mirrors existing quickAgentSelect) - Both install.ts and runInstallFlow() now use the shared helper - Add getAdapterCount() to @skillkit/agents to avoid allocating 44 spread-copied objects just to call .length - Use getAdapterCount() in cli.ts instead of getAllAdapters().length
1 parent 5691b10 commit 59ea709

6 files changed

Lines changed: 73 additions & 21 deletions

File tree

apps/skillkit/src/cli.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { readFileSync } from 'node:fs';
33
import { fileURLToPath } from 'node:url';
44
import { dirname, join } from 'node:path';
55
import { Cli, Builtins } from 'clipanion';
6+
import { setVersion, setAgentCount } from '@skillkit/cli';
7+
import { getAdapterCount } from '@skillkit/agents';
68
import {
79
InstallCommand,
810
SyncCommand,
@@ -130,6 +132,9 @@ const packageJsonPath = join(__dirname, '../package.json');
130132
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
131133
const version = packageJson.version || '1.2.0';
132134

135+
setVersion(version);
136+
setAgentCount(getAdapterCount());
137+
133138
const cli = new Cli({
134139
binaryLabel: 'skillkit',
135140
binaryName: 'skillkit',

packages/agents/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ export function getAllAdapters(): AgentAdapterWithType[] {
107107
}));
108108
}
109109

110+
export function getAdapterCount(): number {
111+
return Object.keys(adapters).length;
112+
}
113+
110114
export async function detectAgent(): Promise<AgentType> {
111115
const checkOrder: AgentType[] = [
112116
'claude-code',

packages/cli/src/commands/install.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
isCancel,
4141
spinner,
4242
quickAgentSelect,
43-
skillMultiselect,
43+
quickSkillSelect,
4444
selectInstallMethod,
4545
confirm,
4646
outro,
@@ -273,22 +273,20 @@ export class InstallCommand extends Command {
273273
} else if (this.all || this.yes) {
274274
skillsToInstall = discoveredSkills;
275275
} else if (isInteractive && discoveredSkills.length > 1) {
276-
// Interactive skill selection
277276
step(`Source: ${colors.cyan(this.source)}`);
278277

279-
const skillResult = await skillMultiselect({
280-
message: "Select skills to install",
278+
const skillResult = await quickSkillSelect({
281279
skills: discoveredSkills.map((s) => ({ name: s.name })),
282-
initialValues: discoveredSkills.map((s) => s.name),
283280
});
284281

285282
if (isCancel(skillResult)) {
286283
cancel("Installation cancelled");
287284
return 0;
288285
}
289286

287+
const selected = (skillResult as { skills: string[] }).skills;
290288
skillsToInstall = discoveredSkills.filter((s) =>
291-
(skillResult as string[]).includes(s.name),
289+
selected.includes(s.name),
292290
);
293291
}
294292

packages/cli/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ export * from './commands/index.js';
44
// Export helper functions
55
export * from './helpers.js';
66

7+
// Export onboarding utilities
8+
export { setVersion, setAgentCount } from './onboarding/index.js';
9+
710
// Re-export commonly used types from core
811
export type { AgentType, Skill, SkillMetadata, SkillkitConfig } from '@skillkit/core';

packages/cli/src/onboarding/index.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export {
4242
select,
4343
agentMultiselect,
4444
quickAgentSelect,
45+
quickSkillSelect,
4546
skillMultiselect,
4647
groupMultiselect,
4748
stepTrail,
@@ -87,8 +88,8 @@ import {
8788
getLastAgents,
8889
} from './preferences.js';
8990

90-
let VERSION = '1.7.6';
91-
let AGENT_COUNT = 44;
91+
let VERSION = 'dev';
92+
let AGENT_COUNT = 0;
9293

9394
export function setVersion(version: string): void {
9495
VERSION = version;
@@ -161,18 +162,22 @@ export async function runInstallFlow(options: InstallFlowOptions): Promise<Insta
161162
}
162163

163164
prompts.step(`Source: ${colors.cyan(source)}`);
164-
prompts.step(`Found ${discoveredSkills.length} skill${discoveredSkills.length !== 1 ? 's' : ''}`);
165165

166-
const skillNames = discoveredSkills.map(s => s.name);
167-
const skillResult = await prompts.skillMultiselect({
168-
message: 'Select skills to install',
169-
skills: discoveredSkills.map(s => ({ name: s.name })),
170-
initialValues: skillNames,
171-
});
166+
let selectedSkills: string[];
172167

173-
if (prompts.isCancel(skillResult)) {
174-
prompts.cancel('Installation cancelled');
175-
return { selectedSkills: [], selectedAgents: [], installMethod: 'symlink', cancelled: true };
168+
if (discoveredSkills.length > 1) {
169+
const skillResult = await prompts.quickSkillSelect({
170+
skills: discoveredSkills.map(s => ({ name: s.name })),
171+
});
172+
173+
if (prompts.isCancel(skillResult)) {
174+
prompts.cancel('Installation cancelled');
175+
return { selectedSkills: [], selectedAgents: [], installMethod: 'symlink', cancelled: true };
176+
}
177+
178+
selectedSkills = (skillResult as { skills: string[] }).skills;
179+
} else {
180+
selectedSkills = discoveredSkills.map(s => s.name);
176181
}
177182

178183
if (detectedAgents.length > 1) {
@@ -190,7 +195,7 @@ export async function runInstallFlow(options: InstallFlowOptions): Promise<Insta
190195

191196
if (prompts.isCancel(agentResult)) {
192197
prompts.cancel('Installation cancelled');
193-
return { selectedSkills: skillResult as string[], selectedAgents: [], installMethod: 'symlink', cancelled: true };
198+
return { selectedSkills, selectedAgents: [], installMethod: 'symlink', cancelled: true };
194199
}
195200

196201
saveLastAgents(agentResult as string[]);
@@ -202,15 +207,15 @@ export async function runInstallFlow(options: InstallFlowOptions): Promise<Insta
202207
if (prompts.isCancel(methodResult)) {
203208
prompts.cancel('Installation cancelled');
204209
return {
205-
selectedSkills: skillResult as string[],
210+
selectedSkills,
206211
selectedAgents: agentResult as string[],
207212
installMethod: 'symlink',
208213
cancelled: true,
209214
};
210215
}
211216

212217
return {
213-
selectedSkills: skillResult as string[],
218+
selectedSkills,
214219
selectedAgents: agentResult as string[],
215220
installMethod: methodResult as 'symlink' | 'copy',
216221
cancelled: false,

packages/cli/src/onboarding/prompts.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,43 @@ export async function skillMultiselect(options: {
213213
});
214214
}
215215

216+
export async function quickSkillSelect(options: {
217+
message?: string;
218+
skills: SkillOption[];
219+
}): Promise<{ skills: string[]; method: 'all' | 'select' } | symbol> {
220+
const { skills } = options;
221+
222+
const result = await clack.select({
223+
message: options.message || `Found ${skills.length} skills — how would you like to install?`,
224+
options: [
225+
{ value: 'all' as const, label: 'Install all skills', hint: `${skills.length} skills` },
226+
{ value: 'select' as const, label: 'Select specific skills', hint: 'Choose manually' },
227+
],
228+
});
229+
230+
if (clack.isCancel(result)) {
231+
return result;
232+
}
233+
234+
const method = result as 'all' | 'select';
235+
236+
if (method === 'all') {
237+
return { skills: skills.map(s => s.name), method };
238+
}
239+
240+
const selected = await skillMultiselect({
241+
message: 'Select skills to install',
242+
skills,
243+
initialValues: [],
244+
});
245+
246+
if (clack.isCancel(selected)) {
247+
return selected;
248+
}
249+
250+
return { skills: selected as string[], method };
251+
}
252+
216253
export async function groupMultiselect<T extends string>(options: {
217254
message: string;
218255
options: Record<string, Array<{ value: T; label: string; hint?: string }>>;

0 commit comments

Comments
 (0)