11/**
2- * SkillSelector — displays available skills as cards with a Run button.
3- *
4- * Hardcoded skill list for now (will be dynamic later).
2+ * SkillSelector — displays available skills as cards.
3+ * Skills per spec: challenge-me (active), eli5/pair-debug/hot-seat/test-me (coming soon).
54 */
65
6+ import { Button , Card , Chip , Overline } from '../../shared/components'
77import type { Skill } from './containerTypes'
88
99const SKILLS : Skill [ ] = [
1010 {
1111 name : 'challenge-me' ,
1212 label : 'Challenge Me' ,
13- description : 'Socratic quiz -- tests your understanding of your own PR changes' ,
13+ description : 'Socratic quiz — tests your understanding of your own PR changes. 3 questions targeting failure modes, system knowledge, and design trade-offs. ' ,
1414 duration : '5-10 min' ,
1515 } ,
1616 {
17- name : 'code-review ' ,
18- label : 'Code Review ' ,
19- description : 'Multi-layer adversarial code review ' ,
20- duration : '3-5 min' ,
17+ name : 'eli5 ' ,
18+ label : 'ELI5 ' ,
19+ description : 'Explain Like I\'m 5 — can you vulgarize your own code? Picks complex sections and asks you to explain them simply. ' ,
20+ duration : '5-8 min' ,
2121 comingSoon : true ,
2222 } ,
2323 {
24- name : 'security-audit' ,
25- label : 'Security Audit' ,
26- description : 'Vulnerability scan on your diff' ,
27- duration : '1-3 min' ,
24+ name : 'pair-debug' ,
25+ label : 'Pair Debug' ,
26+ description : 'Find the subtle bug Claude injected into your code. Ask questions, narrow it down, submit your diagnosis.' ,
27+ duration : '5-12 min' ,
28+ comingSoon : true ,
29+ } ,
30+ {
31+ name : 'hot-seat' ,
32+ label : 'Hot Seat' ,
33+ description : 'Architecture hot seat — defend your design choices under pressure. Claude plays devil\'s advocate on your decisions.' ,
34+ duration : '5-10 min' ,
35+ comingSoon : true ,
36+ } ,
37+ {
38+ name : 'test-me' ,
39+ label : 'Test Me' ,
40+ description : 'Predict whether test cases pass or fail on your code. Claude writes tests, you predict the outcome and explain why.' ,
41+ duration : '5-8 min' ,
2842 comingSoon : true ,
2943 } ,
3044]
@@ -47,93 +61,66 @@ export default function SkillSelector({
4761 return (
4862 < div
4963 data-testid = "skill-selector"
50- className = "min-h-screen bg-primary text-text-primary font-sans flex flex-col items-center justify-center px-6"
64+ className = "min-h-screen bg-bg text-ink font-sans flex flex-col items-center justify-center px-6 py-12 "
5165 >
5266 { /* Header */ }
5367 < div className = "text-center mb-10 max-w-lg" >
54- < h1 className = "font-mono text-[24px] font-bold mb-2" >
55- < span style = { { color : '#E2A039' } } > helPRs</ span >
56- </ h1 >
57- < p className = "text-text-secondary text-[14px]" >
58- < span className = "font-mono text-text-primary" > { repoFullName } </ span >
59- { ' ' }
60- < span className = "text-text-muted" > #{ prNumber } </ span >
68+ < h1 className = "font-mono text-lg font-bold text-accent mb-2" > helPRs</ h1 >
69+ < p className = "text-ink2 text-sm" >
70+ Run something on{ ' ' }
71+ < span className = "font-mono text-ink" > { repoFullName } </ span >
72+ < span className = "text-dim" > #{ prNumber } </ span >
6173 </ p >
62- < p className = "text-text-muted text-[13px] mt-2" > Select a skill to run against this PR</ p >
6374 </ div >
6475
6576 { /* Skill cards */ }
66- < div className = "grid gap-4 w-full max-w-lg" >
77+ < div className = "grid gap-3 w-full max-w-lg" >
6778 { SKILLS . map ( ( skill ) => (
68- < button
79+ < Card
6980 key = { skill . name }
70- data-testid = { `skill-card-${ skill . name } ` }
71- onClick = { ( ) => ! skill . comingSoon && onSelectSkill ( skill . name ) }
72- disabled = { disabled || skill . comingSoon }
73- className = "text-left p-5 rounded-[10px] transition-all duration-150 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
74- style = { {
75- background : '#1a1717' ,
76- boxShadow : '0 0 0 1px rgba(255, 255, 255, 0.06), 0 2px 4px rgba(0, 0, 0, 0.2), 0 8px 24px -8px rgba(0, 0, 0, 0.3)' ,
77- } }
78- onMouseEnter = { ( e ) => {
79- if ( ! disabled ) {
80- e . currentTarget . style . boxShadow = '0 0 0 1px rgba(226, 160, 57, 0.3), 0 4px 12px rgba(0, 0, 0, 0.3), 0 16px 48px -12px rgba(0, 0, 0, 0.4)'
81- }
82- } }
83- onMouseLeave = { ( e ) => {
84- e . currentTarget . style . boxShadow = '0 0 0 1px rgba(255, 255, 255, 0.06), 0 2px 4px rgba(0, 0, 0, 0.2), 0 8px 24px -8px rgba(0, 0, 0, 0.3)'
85- } }
81+ hover = { ! skill . comingSoon }
82+ className = { `${ skill . comingSoon ? 'opacity-60' : 'cursor-pointer' } ` }
8683 >
87- < div className = "flex items-start justify-between gap-4" >
88- < div className = "flex-1 min-w-0" >
89- < h2 className = "font-mono text-[15px] font-medium text-text-primary mb-1" >
90- { skill . label }
91- </ h2 >
92- < p className = "text-text-secondary text-[13px] leading-relaxed" >
93- { skill . description }
94- </ p >
95- </ div >
96- < div className = "flex flex-col items-end gap-2 shrink-0" >
97- < span
98- className = "text-[11px] font-mono px-2 py-0.5 rounded-full"
99- style = { {
100- color : 'rgba(255, 255, 255, 0.5)' ,
101- background : 'rgba(255, 255, 255, 0.04)' ,
102- border : '1px solid rgba(255, 255, 255, 0.06)' ,
103- } }
104- >
105- { skill . duration }
106- </ span >
107- { skill . comingSoon ? (
108- < span
109- className = "text-[11px] font-mono px-2 py-0.5 rounded-full"
110- style = { {
111- color : 'rgba(226, 160, 57, 0.7)' ,
112- background : 'rgba(226, 160, 57, 0.08)' ,
113- border : '1px solid rgba(226, 160, 57, 0.15)' ,
114- } }
115- >
116- Coming soon
117- </ span >
118- ) : (
119- < span
120- className = "text-[12px] font-semibold font-mono"
121- style = { { color : '#E2A039' } }
122- >
123- Run →
124- </ span >
125- ) }
84+ < button
85+ data-testid = { `skill-card-${ skill . name } ` }
86+ onClick = { ( ) => ! skill . comingSoon && onSelectSkill ( skill . name ) }
87+ disabled = { disabled || skill . comingSoon }
88+ className = "w-full text-left disabled:cursor-not-allowed cursor-pointer"
89+ >
90+ < div className = "flex items-start justify-between gap-4" >
91+ < div className = "flex-1 min-w-0" >
92+ < div className = "flex items-center gap-2 mb-1" >
93+ < h2 className = "font-mono text-sm font-semibold text-ink" >
94+ { skill . label }
95+ </ h2 >
96+ { ! skill . comingSoon && (
97+ < Chip variant = "accent" > DEFAULT</ Chip >
98+ ) }
99+ </ div >
100+ < p className = "text-ink2 text-[13px] leading-relaxed" >
101+ { skill . description }
102+ </ p >
103+ </ div >
104+ < div className = "flex flex-col items-end gap-2 shrink-0" >
105+ < Chip > { skill . duration } </ Chip >
106+ { skill . comingSoon ? (
107+ < Chip variant = "warn" > soon</ Chip >
108+ ) : (
109+ < Button variant = "ghost" className = "text-accent text-xs font-semibold px-0" >
110+ Run →
111+ </ Button >
112+ ) }
113+ </ div >
126114 </ div >
127- </ div >
128- </ button >
115+ </ button >
116+ </ Card >
129117 ) ) }
130118 </ div >
131119
132- { /* Footer disclaimer */ }
133- < p className = "text-text-muted text-[11px] mt-8 text-center max-w-md" >
134- Skills run in ephemeral containers using your configured Claude credentials.
135- AI-generated content may be inaccurate -- verify important findings.
136- </ p >
120+ { /* Footer */ }
121+ < Overline className = "mt-8 text-center max-w-md" >
122+ // skills run in ephemeral containers using your configured Claude credentials
123+ </ Overline >
137124 </ div >
138125 )
139126}
0 commit comments