diff --git a/packages/cli/e2e/__tests__/skills.spec.ts b/packages/cli/e2e/__tests__/skills.spec.ts index ca911b315..ad5a796b1 100644 --- a/packages/cli/e2e/__tests__/skills.spec.ts +++ b/packages/cli/e2e/__tests__/skills.spec.ts @@ -8,6 +8,16 @@ import { runCheckly } from '../run-checkly' const actionIds = ACTIONS.map(a => a.id) const referenceShortIds = REFERENCES.map(r => r.id.replace('configure-', '')) const investigateReferenceShortIds = INVESTIGATE_REFERENCES.map(r => r.id.replace('investigate-', '')) +const referencesByAction = ACTIONS.flatMap(action => { + if (!('references' in action)) { + return [] + } + + return action.references.map(reference => ({ + actionId: action.id, + shortId: reference.id.replace(`${action.id}-`, ''), + })) +}) describe('checkly skills', () => { let fixt: FixtureSandbox @@ -44,6 +54,26 @@ describe('checkly skills', () => { expect(stdout).toContain(`$ checkly skills investigate ${investigateReferenceShortIds[0]}`) }) + it('should list all actions and references in help output', async () => { + const { stdout } = await runCheckly(fixt, ['skills', '--help']) + + expect(stdout).toContain('Available actions and references:') + expect(stdout).toContain('checkly skills configure api-checks') + expect(stdout).toContain('checkly skills investigate alerting') + + for (const id of actionIds) { + expect(stdout).toContain(`|- ${id}`) + } + + for (const { actionId, shortId } of referencesByAction) { + expect(stdout).toContain(`|- ${actionId}`) + expect(stdout).toContain(`| |- ${shortId}`) + } + + expect(stdout).not.toContain(REFERENCES[0].description) + expect(stdout).not.toContain(INVESTIGATE_REFERENCES[0].description) + }) + it('should show the initialize action content', async () => { const { stdout } = await runCheckly(fixt, ['skills', 'initialize']) expect(stdout).toBeTruthy() diff --git a/packages/cli/src/commands/__tests__/command-metadata.spec.ts b/packages/cli/src/commands/__tests__/command-metadata.spec.ts index 76830669b..8b5bf11e5 100644 --- a/packages/cli/src/commands/__tests__/command-metadata.spec.ts +++ b/packages/cli/src/commands/__tests__/command-metadata.spec.ts @@ -38,6 +38,7 @@ import ImportCommit from '../import/commit.js' import ImportCancel from '../import/cancel.js' import PwTest from '../pw-test.js' import SyncPlaywright from '../sync-playwright.js' +import Skills from '../skills/index.js' import SkillsInstall from '../skills/install.js' import AccountPlan from '../account/plan.js' import Members from '../members.js' @@ -91,6 +92,7 @@ const commands: Array<[string, typeof BaseCommand]> = [ ['import cancel', ImportCancel], ['pw-test', PwTest], ['sync-playwright', SyncPlaywright], + ['skills', Skills], ['skills install', SkillsInstall], ] diff --git a/packages/cli/src/commands/skills/index.ts b/packages/cli/src/commands/skills/index.ts index 9a39fb506..a4fd117ef 100644 --- a/packages/cli/src/commands/skills/index.ts +++ b/packages/cli/src/commands/skills/index.ts @@ -10,18 +10,57 @@ const __dirname = dirname(fileURLToPath(import.meta.url)) const REFERENCES_DIR = join(__dirname, '../../ai-context/skills-command/references') +function referenceShortId (actionId: string, referenceId: string): string { + return referenceId.replace(`${actionId}-`, '') +} + +function formatAvailableActionTree (): string { + return [ + ' checkly skills', + ...ACTIONS.flatMap(action => { + const actionLine = ` |- ${action.id}` + if (!('references' in action)) { + return [actionLine] + } + + const references = action.references + .map(reference => ` | |- ${referenceShortId(action.id, reference.id)}`) + return [actionLine, ...references] + }), + ].join('\n') +} + export default class Skills extends BaseCommand { static hidden = false - static description = 'Show Checkly AI skills, actions and their references.' + static readOnly = true + static destructive = false + static idempotent = true + static description = [ + 'Show Checkly AI skills, actions and their references.', + '', + 'Run `checkly skills` to print the full catalog, `checkly skills ` for an action guide, or `checkly skills ` for a specific reference.', + '', + 'Available actions and references:', + formatAvailableActionTree(), + '', + 'Use `checkly skills ` or `checkly skills ` to open the detailed guidance.', + ].join('\n') + + static examples = [ + 'checkly skills', + 'checkly skills configure', + 'checkly skills configure api-checks', + 'checkly skills investigate alerting', + ] static args = { action: Args.string({ required: false, - description: 'The action name (e.g. "configure", "initialize").', + description: 'Action to open. Available actions are listed below.', }), reference: Args.string({ required: false, - description: 'A specific reference within the action (e.g. "api-checks").', + description: 'Reference to open for the selected action. Available references are listed below.', }), } @@ -75,7 +114,7 @@ export default class Skills extends BaseCommand { this.log(` $ checkly skills ${action.id}`) if ('references' in action) { const firstRef = action.references[0] - const refId = firstRef.id.replace(`${action.id}-`, '') + const refId = referenceShortId(action.id, firstRef.id) this.log(` $ checkly skills ${action.id} ${refId}`) } } @@ -98,7 +137,7 @@ export default class Skills extends BaseCommand { this.log(`References for "${action.id}":\n`) const refs = action.references.map(r => ({ - shortId: r.id.replace(`${action.id}-`, ''), + shortId: referenceShortId(action.id, r.id), description: r.description, })) const maxRefLen = Math.max(...refs.map(r => r.shortId.length))