eklavya 1.52.5 → 1.54.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.
@@ -27,6 +27,10 @@ Make one call to `present_question` instead:
27
27
  misconception (rows one and three). Grade 3 is not available to a pick: only
28
28
  typed words can show a hesitant "right".
29
29
  - `more: true` only in an explicit round (the plan has more items after this one, as for `/eklavya:quiz`): the panel then offers a Next button. Leave it out otherwise. Once a round is planned the server tracks it: the last question's `more` is set false for you, and Next arrives as a prompt telling you to call `get_session_quiz_plan` with `resume_round: true`.
30
+ - **Mark the code.** The panel draws the stem, each `description` and the `explanation` as Markdown, so
31
+ put every function, call, class, file and flag in backticks (`` `tester.pumpWidget(widget)` ``,
32
+ `` `shouldRepaint` ``) and nothing else: no headings, lists or emphasis. Option `label`s are drawn as
33
+ plain text, so write them in words and leave the backticks out of them.
30
34
  - `explanation`: one line saying why the right option is right. The panel shows
31
35
  it after the answer, so it is the whole of your feedback.
32
36
 
@@ -13,11 +13,13 @@ import { isCowork } from './surface.js';
13
13
  /** How long after its last `panel_sync` a mod still counts as present. */
14
14
  export const PANEL_HEARTBEAT_TTL_MS = 90_000;
15
15
  /**
16
- * Host surfaces that have been seen to take an answer. Verified on the terminal
17
- * only (docs/verified-schemas.md, Stage 0); the Desktop Code tab is added here
18
- * when it has been tried, not before.
16
+ * Host surfaces the planner may present the panel on. The terminal was verified
17
+ * (docs/verified-schemas.md); the Desktop Code tab is enabled but not yet
18
+ * observed. It is safe to try: a host that cannot seat the pane reports
19
+ * `placed: false` and the question stays on the card, and a host that never
20
+ * loads the mod never stamps a heartbeat.
19
21
  */
20
- export const PANEL_SURFACES = ['terminal'];
22
+ export const PANEL_SURFACES = ['terminal', 'desktop'];
21
23
  /** The phases in which a question is still waiting for its answer. */
22
24
  export const OPEN_PHASES = "('pending','unplaced','grading')";
23
25
  /** An unanswered question stops being open after this long (`panel.ts` expires it). */
@@ -1 +1 @@
1
- {"version":3,"file":"panel-state.js","sourceRoot":"","sources":["../src/panel-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"panel-state.js","sourceRoot":"","sources":["../src/panel-state.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.52.5",
4
+ "version": "1.54.0",
5
5
  "description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
6
6
  "author": {
7
7
  "name": "Ajay Kumar"
@@ -18,14 +18,96 @@ export const EMPTY: PanelState = {
18
18
  /** Tier names as the dashboard shows them (`TIER` in `assets/dashboard.html`; a test keeps the two equal). */
19
19
  export const TIER: Record<number, string> = { 1: 'recall', 2: 'mechanism', 3: 'judgement', 4: 'failure modes', 5: 'design' }
20
20
 
21
+ /** Brand verdigris (`--vd-300`): the selected card's border and the name. */
22
+ export const BRAND = '#79D5C4'
23
+
24
+ /**
25
+ * The logo: the Claude mascot (orange) holding an Eklavya bow and arrow (white), drawn on a
26
+ * grid where one cell is a quarter of a text character. Designed in the logo maker; a letter
27
+ * names its colour in LOGO_PALETTE and a dot is empty.
28
+ */
29
+ export const LOGO_GRID = [
30
+ '.................dd.......',
31
+ '................d..d......',
32
+ '................d...d.....',
33
+ '...aaaaaaaaaaaa.d....d....',
34
+ '...aa.aaaaaa.aa.d.....d...',
35
+ '.aaaaaaaaaaaaaaadddddddddd',
36
+ '...aaaaaaaaaaaa.d.....d...',
37
+ '....a.a....a.a..d....d....',
38
+ '................d...d.....',
39
+ '................d..d......',
40
+ '.................dd.......',
41
+ '..........................',
42
+ ] as const
43
+ export const LOGO_PALETTE: Record<string, string> = { a: '#D97757', d: '#FFFFFF' }
44
+
45
+ /** One stretch of text cells that share a colour pair. A text character holds two colours at most. */
46
+ export type LogoRun = { text: string; fg?: string; bg?: string }
47
+
48
+ /** Quadrant glyphs by which of the four cells (upper left 1, upper right 2, lower left 4, lower right 8) are filled. */
49
+ const QUADRANT = ' ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█'
50
+
51
+ /**
52
+ * The grid as rows of text runs: each 2 by 2 block becomes one quadrant character, the
53
+ * first colour as its foreground and a second, if any, as its background. A third colour
54
+ * in one block is dropped, so the logo degrades and never throws.
55
+ */
56
+ export function logoRows(grid: readonly string[] = LOGO_GRID, palette: Record<string, string> = LOGO_PALETTE): LogoRun[][] {
57
+ const out: LogoRun[][] = []
58
+ for (let cy = 0; cy < Math.ceil(grid.length / 2); cy++) {
59
+ const runs: LogoRun[] = []
60
+ for (let cx = 0; cx < Math.ceil((grid[0]?.length ?? 0) / 2); cx++) {
61
+ const quad = [grid[cy * 2]?.[cx * 2], grid[cy * 2]?.[cx * 2 + 1], grid[cy * 2 + 1]?.[cx * 2], grid[cy * 2 + 1]?.[cx * 2 + 1]].map(c => (c && c !== '.' ? c : ''))
62
+ const [first, second] = [...new Set(quad.filter(Boolean))]
63
+ let run: LogoRun = { text: ' ' }
64
+ if (first) {
65
+ const mask = quad.reduce((n, c, i) => (c === first ? n | (1 << i) : n), 0)
66
+ run = { text: QUADRANT[mask], fg: palette[first], bg: second ? palette[second] : undefined }
67
+ }
68
+ const last = runs[runs.length - 1]
69
+ if (last && last.fg === run.fg && last.bg === run.bg) last.text += run.text
70
+ else runs.push(run)
71
+ }
72
+ out.push(runs)
73
+ }
74
+ return out
75
+ }
76
+
77
+ /** The name as the header sets it: spaced capitals, the nearest a terminal gets to a larger type size. */
78
+ /** A call such as `tester.tap(find.byType(X))`, nested up to three deep, a lowerCamelCase name or a snake_case name. */
79
+ const CODE_LIKE = /[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*\((?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))*\)|\b[a-z]{2,}(?:[A-Z][a-z0-9]+)+\b|\b[a-z0-9]+(?:_[a-z0-9]+)+\b/g
80
+
81
+ /**
82
+ * Wraps code-looking words in backticks so the pane's Markdown draws them as code. A question
83
+ * is plain prose unless its author marked the code, and an old or careless one often is not.
84
+ * Text already in backticks is left as written.
85
+ */
86
+ export function codify(text: string): string {
87
+ return text
88
+ .split(/(`[^`]*`)/)
89
+ .map((part, i) => (i % 2 ? part : part.replace(CODE_LIKE, '`$&`')))
90
+ .join('')
91
+ }
92
+
93
+ /** What a Button label can show of Markdown: it keeps the words and drops the code and bold marks. */
94
+ export function plainLabel(text: string): string {
95
+ return text.replace(/`|\*\*/g, '')
96
+ }
97
+
98
+ export const WORDMARK = 'EKLAVYA'.split('').join(' ')
99
+
100
+ /** How long a finished result stays before it closes itself, when no Next is waiting. */
101
+ export const CLOSE_MS = 15_000
102
+
21
103
  /** Every sentence the learner reads, in one place. The handoff's table, verbatim. */
22
104
  export const STR = {
23
105
  brand: 'Eklavya',
24
- powered: 'Powered by Claude',
25
106
  submit: 'Submit answer',
26
107
  skip: 'Skip question',
27
108
  next: 'Next question',
28
109
  done: 'Done',
110
+ autoClose: 'Closes by itself in 15 seconds.',
29
111
  retry: 'Retry',
30
112
  close: 'Close',
31
113
  other: 'Other — explain in your own words',
@@ -41,6 +123,9 @@ export const STR = {
41
123
  nothingTyped: 'Write your answer first, or pick one of the options.',
42
124
  graderFailed: "We couldn't check that answer. Your text is saved here — Retry.",
43
125
  explainer: 'A page explaining this is being written.',
126
+ focus: 'Ctrl+X then Tab moves the keys here.',
127
+ keys: '1–4 pick · o other · s submit · k skip · Esc back to prompt',
128
+ keysApp: '1–4 pick · o other · s submit · k skip',
44
129
  off: 'The quiz panel is off. Turn it on with: eklavya config set quiz.panel true',
45
130
  } as const
46
131
 
@@ -79,6 +164,14 @@ export function explainerBrief(x: ExplainBlock): string {
79
164
  }
80
165
 
81
166
  export const answerWas = (label: string): string => `The answer was: ${label}`
167
+
168
+ /**
169
+ * The hint lines under the pane. Moving the keys with Ctrl+X then Tab and
170
+ * leaving with Esc are the terminal's; an app draws native buttons, so it gets
171
+ * the answer keys only.
172
+ */
173
+ export const hintLines = (surface: string): string[] => (surface === 'terminal' ? [STR.focus, STR.keys] : [STR.keysApp])
174
+
82
175
  export const unplacedNotice = (): string => `A question is waiting. ${REOPEN} to open it.`
83
176
  export const levelLine = (level: string, project: string): string => `You've cleared ${level} on ${project}.`
84
177
 
@@ -2,21 +2,28 @@ import { atom, read, update } from 'claude-code'
2
2
  import type { EngineInterface, Register } from 'claude-code'
3
3
 
4
4
  import {
5
+ BRAND,
5
6
  EMPTY,
6
7
  EXPLAINER,
7
8
  NEXT_PROMPT,
8
9
  PANE,
9
10
  REOPEN,
10
11
  STR,
12
+ WORDMARK,
13
+ CLOSE_MS,
11
14
  answerWas,
12
15
  explainerBrief,
16
+ codify,
13
17
  gradingRequest,
18
+ logoRows,
19
+ plainLabel,
14
20
  levelLine,
15
21
  parseVerdict,
16
22
  payloadOf,
17
23
  projectName,
18
24
  topicLabel,
19
25
  unplacedNotice,
26
+ hintLines,
20
27
  } from './model'
21
28
  import type { PanelPending, PanelQuestion, PanelState } from './types'
22
29
 
@@ -49,6 +56,8 @@ let busy = false
49
56
  /** Bumped when the session ends: a submit that began before it is stale and must write nothing. */
50
57
  let epoch = 0
51
58
  let explainedAttempt = 0
59
+ /** The result closes itself after CLOSE_MS when nothing else is pending (no Next). */
60
+ let closer: { cancel: () => void } | null = null
52
61
 
53
62
  /**
54
63
  * One call to an Eklavya tool through the host's own connection, as the parsed
@@ -219,6 +228,9 @@ async function submit($: EngineInterface, wanted: PanelPending | null): Promise<
219
228
  message: null,
220
229
  }))
221
230
 
231
+ // The last question of a round has no Next: the result closes itself, as Done would.
232
+ if (!q.more) arm($, mine, q.question_id)
233
+
222
234
  // A miss with explain_on_wrong on: the panel starts the explainer itself in
223
235
  // the background, so no prompt shows in the transcript and nothing waits.
224
236
  // Auto mode judges every spawn (it drops Agent allow rules), and its
@@ -270,6 +282,7 @@ export const register: Register = on => {
270
282
  on('session.end', async ($, e, next) => {
271
283
  try {
272
284
  epoch += 1
285
+ disarm()
273
286
  openedFor = null
274
287
  busy = false
275
288
  await update($, quiz, () => EMPTY)
@@ -332,22 +345,64 @@ export const register: Register = on => {
332
345
  on('ui.render', { component: 'Pane', requestId: 'eklavya-quiz' }, async ($, e, next) => {
333
346
  // Only our pane: every other pane, and everything else, is the host's.
334
347
  if (e.requestId !== PANE) return next(e)
335
- const { Box, Text, Button, Input } = $.ui.resolve(e) as any
348
+ const { Box, Text, Button, Input, Markdown } = $.ui.resolve(e) as any
336
349
  const s: PanelState = await read($, quiz)
337
350
  const q = s.question
338
351
 
339
- const head = (
340
- <Box flexDirection="row" justifyContent="space-between">
341
- <Text bold color="suggestion">
342
- {STR.brand}
343
- </Text>
344
- <Text dimColor>{STR.powered}</Text>
352
+ // The mark is drawn in text, as Claude Code draws its own, so it shows in every terminal.
353
+ const logo = (
354
+ <Box flexDirection="column" flexShrink={0}>
355
+ {logoRows().map((runs, y) => (
356
+ <Box key={`logo-${y}`} flexDirection="row" flexShrink={0}>
357
+ {runs.map((run, x) => (
358
+ <Text key={`logo-${y}-${x}`} color={run.fg} backgroundColor={run.bg} wrap="truncate">
359
+ {run.text}
360
+ </Text>
361
+ ))}
362
+ </Box>
363
+ ))}
364
+ </Box>
365
+ )
366
+ // A list tile: the mark on the left; the name over the topic beside it.
367
+ const head = (subtitle?: string) => (
368
+ <Box flexDirection="row" alignItems="center" gap={2}>
369
+ {logo}
370
+ <Box flexDirection="column" flexShrink={1}>
371
+ <Text bold color={BRAND}>
372
+ {WORDMARK}
373
+ </Text>
374
+ {subtitle ? <Text>{subtitle}</Text> : null}
375
+ </Box>
376
+ </Box>
377
+ )
378
+ const hint = (
379
+ <Box flexDirection="column">
380
+ {hintLines(e.surface).map(line => (
381
+ <Text key={line} dimColor>
382
+ {line}
383
+ </Text>
384
+ ))}
385
+ </Box>
386
+ )
387
+ // One answer: a bordered block. The Button holds the whole answer text, so a press anywhere on it
388
+ // selects; the border and a tick show the pick, and nothing implies it is right.
389
+ const card = (id: string, selected: boolean, button: unknown, body: unknown[], roomy = false) => (
390
+ <Box
391
+ key={`card-${id}`}
392
+ flexDirection="column"
393
+ paddingX={1}
394
+ paddingY={roomy ? 1 : 0}
395
+ gap={roomy ? 1 : 0}
396
+ borderStyle="single"
397
+ borderColor={selected ? BRAND : 'inactive'}
398
+ >
399
+ {button}
400
+ {body}
345
401
  </Box>
346
402
  )
347
- const hint = <Text dimColor>Ctrl+X then Tab moves focus here. Enter presses a button. Esc returns to the prompt.</Text>
348
403
  const frame = (...body: unknown[]) => (
349
404
  <Box flexDirection="column" paddingX={1} gap={1}>
350
- {head}
405
+ {head(q ? topicLabel(q) : undefined)}
351
406
  {body}
352
407
  {hint}
353
408
  </Box>
@@ -364,25 +419,24 @@ export const register: Register = on => {
364
419
  )
365
420
  }
366
421
 
367
- const topic = <Text dimColor>{topicLabel(q)}</Text>
368
- const stem = <Text bold>{q.stem}</Text>
422
+ const stem = <Markdown text={codify(q.stem)} />
369
423
 
370
424
  if (s.step === 'feedback' || s.step === 'skipped') {
371
425
  const r = s.result
372
426
  if (s.step === 'skipped' || !r) {
373
- return frame(topic, <Text>{STR.skipped}</Text>, <Button key="done" label={STR.done} variant="primary" hotkey="d" onPress={() => done($)} />)
427
+ return frame(<Text>{STR.skipped}</Text>, q.more ? null : <Text dimColor>{STR.autoClose}</Text>, <Button key="done" label={STR.done} variant="primary" hotkey="d" onPress={() => done($)} />)
374
428
  }
375
429
  const right = r.correct === true
376
430
  return frame(
377
- topic,
378
431
  stem,
379
432
  <Text bold color={right ? 'suggestion' : 'error'}>
380
433
  {right ? STR.correct : STR.wrong}
381
434
  </Text>,
382
435
  right || !r.correct_label ? null : <Text>{answerWas(r.correct_label)}</Text>,
383
- r.explanation ? <Text>{r.explanation}</Text> : null,
436
+ r.explanation ? <Markdown text={codify(r.explanation)} /> : null,
384
437
  r.level_up ? <Text color="suggestion">{levelLine(r.level_up.from, projectName(q.repo))}</Text> : null,
385
438
  r.explain ? <Text dimColor>{STR.explainer}</Text> : null,
439
+ q.more ? null : <Text dimColor>{STR.autoClose}</Text>,
386
440
  <Box flexDirection="row" gap={2}>
387
441
  {q.more ? <Button key="next" label={STR.next} hotkey="n" onPress={() => askNext($)} /> : null}
388
442
  <Button key="done" label={STR.done} variant="primary" hotkey="d" onPress={() => done($)} />
@@ -393,45 +447,51 @@ export const register: Register = on => {
393
447
  const locked = s.step === 'grading'
394
448
  // Submit looks disabled until there is something to send; pressing it then does nothing.
395
449
  const hasDraft = s.draft.other ? s.draft.text.trim().length > 0 : s.draft.picked !== null
450
+ // Markdown marks cannot show inside a Button's label, so the code and bold marks are dropped there; the note keeps them.
451
+ const answer = (id: string, key: string, hotkey: string, label: string, chosen: boolean) => (
452
+ <Button
453
+ key={key}
454
+ plain
455
+ hotkey={hotkey}
456
+ label={`${chosen ? '✓ ' : ''}${label}`}
457
+ onPress={() => (locked ? undefined : id === 'other' ? chooseOther($) : pick($, id))}
458
+ />
459
+ )
396
460
  const options = q.options.map((o, i) => {
397
461
  const chosen = !s.draft.other && s.draft.picked === o.id
398
- return (
399
- <Box key={`row-${o.id}`} flexDirection="column">
400
- <Button
401
- key={`opt-${o.id}`}
402
- plain
403
- label={`${chosen ? '●' : '○'} ${o.label}`}
404
- hotkey={String(i + 1)}
405
- onPress={() => (locked ? undefined : pick($, o.id))}
406
- />
407
- {o.note ? <Text dimColor>{` ${o.note}`}</Text> : null}
408
- </Box>
462
+ return card(
463
+ o.id,
464
+ chosen,
465
+ answer(o.id, `opt-${o.id}`, String(i + 1), plainLabel(o.label), chosen),
466
+ [o.note ? <Box key={`note-${o.id}`} paddingLeft={3}><Markdown text={codify(o.note)} dimColor /></Box> : null],
409
467
  )
410
468
  })
411
469
 
412
470
  return frame(
413
- topic,
414
471
  stem,
415
- <Box flexDirection="column" gap={1}>
472
+ <Box flexDirection="column">
416
473
  {options}
417
- <Button
418
- key="opt-other"
419
- plain
420
- label={`${s.draft.other ? '●' : '○'} ${STR.other}`}
421
- hotkey="o"
422
- onPress={() => (locked ? undefined : chooseOther($))}
423
- />
424
- {s.draft.other ? (
425
- <Input
426
- key="other-text"
427
- autoFocus
428
- placeholder={STR.otherPlaceholder}
429
- value={s.draft.text}
430
- submitLabel="keep"
431
- onInput={(value: string) => (locked ? undefined : typeOther($, value))}
432
- onSubmit={(value: string) => (locked ? undefined : typeOther($, value))}
433
- />
434
- ) : null}
474
+ {card(
475
+ 'other',
476
+ s.draft.other,
477
+ answer('other', 'opt-other', 'o', STR.other, s.draft.other),
478
+ [
479
+ s.draft.other ? (
480
+ <Box key="other-box" paddingLeft={3}>
481
+ <Input
482
+ key="other-text"
483
+ autoFocus
484
+ placeholder={STR.otherPlaceholder}
485
+ value={s.draft.text}
486
+ submitLabel="keep"
487
+ onInput={(value: string) => (locked ? undefined : typeOther($, value))}
488
+ onSubmit={(value: string) => (locked ? undefined : typeOther($, value))}
489
+ />
490
+ </Box>
491
+ ) : null,
492
+ ],
493
+ s.draft.other,
494
+ )}
435
495
  </Box>,
436
496
  locked ? <Text dimColor>{STR.grading}</Text> : null,
437
497
  s.step === 'error' && s.message ? <Text color="error">{s.message}</Text> : null,
@@ -452,6 +512,12 @@ export const register: Register = on => {
452
512
  /** Selecting an option records a draft and nothing else: it never submits, grades or skips. */
453
513
  async function pick($: EngineInterface, id: string): Promise<void> {
454
514
  await update($, quiz, (s: PanelState) => (s.step === 'awaiting' || s.step === 'error' ? { ...s, draft: { ...s.draft, picked: id, other: false }, message: null } : s))
515
+ try {
516
+ // The host's focus mark follows the pick, so the highlighted answer is the chosen one even after a hotkey.
517
+ await $.ui.focus({ requestId: PANE, key: `opt-${id}` })
518
+ } catch {
519
+ /* Fail open: the pick stands without it. */
520
+ }
455
521
  }
456
522
 
457
523
  async function chooseOther($: EngineInterface): Promise<void> {
@@ -466,8 +532,34 @@ async function skip($: EngineInterface): Promise<void> {
466
532
  await submit($, { kind: 'skip' })
467
533
  }
468
534
 
535
+ /** Starts the auto-close for this result; any later Done, Next or new question cancels it. */
536
+ function arm($: EngineInterface, mine: number, id: string): void {
537
+ try {
538
+ closer?.cancel()
539
+ closer = $.clock.after(CLOSE_MS, async () => {
540
+ try {
541
+ closer = null
542
+ if (epoch !== mine) return
543
+ const s: PanelState = (await $.state.get(REF)).value ?? EMPTY
544
+ // Only the result it was started for: an answer-in-progress or a newer question stays.
545
+ if ((s.step === 'feedback' || s.step === 'skipped') && s.question?.question_id === id) await done($)
546
+ } catch {
547
+ /* Fail open. */
548
+ }
549
+ })
550
+ } catch {
551
+ /* No clock: the learner presses Done. */
552
+ }
553
+ }
554
+
555
+ function disarm(): void {
556
+ closer?.cancel()
557
+ closer = null
558
+ }
559
+
469
560
  /** Done closes the pane; another question may already be waiting, and then the pane comes straight back. */
470
561
  async function done($: EngineInterface): Promise<void> {
562
+ disarm()
471
563
  try {
472
564
  openedFor = null
473
565
  await update($, quiz, () => EMPTY)
@@ -480,6 +572,7 @@ async function done($: EngineInterface): Promise<void> {
480
572
 
481
573
  /** Next asks the model, by a queued prompt, for the next question of an explicit round. */
482
574
  async function askNext($: EngineInterface): Promise<void> {
575
+ disarm()
483
576
  try {
484
577
  openedFor = null
485
578
  await update($, quiz, () => ({ ...EMPTY, step: 'loading' }))
@@ -27,6 +27,10 @@ Make one call to `present_question` instead:
27
27
  misconception (rows one and three). Grade 3 is not available to a pick: only
28
28
  typed words can show a hesitant "right".
29
29
  - `more: true` only in an explicit round (the plan has more items after this one, as for `/eklavya:quiz`): the panel then offers a Next button. Leave it out otherwise. Once a round is planned the server tracks it: the last question's `more` is set false for you, and Next arrives as a prompt telling you to call `get_session_quiz_plan` with `resume_round: true`.
30
+ - **Mark the code.** The panel draws the stem, each `description` and the `explanation` as Markdown, so
31
+ put every function, call, class, file and flag in backticks (`` `tester.pumpWidget(widget)` ``,
32
+ `` `shouldRepaint` ``) and nothing else: no headings, lists or emphasis. Option `label`s are drawn as
33
+ plain text, so write them in words and leave the backticks out of them.
30
34
  - `explanation`: one line saying why the right option is right. The panel shows
31
35
  it after the answer, so it is the whole of your feedback.
32
36
 
@@ -10,7 +10,7 @@ export const presentQuestionTool = {
10
10
  session_id: z.string().max(LIMITS.sessionId).optional().describe(SESSION_HINT),
11
11
  cwd: z.string().max(LIMITS.cwd).optional().describe(CWD_HINT),
12
12
  slug: z.string().max(LIMITS.slug).describe('The concept asked about.'),
13
- question: z.string().min(1).max(LIMITS.question).describe('The question stem only: no options, no attribution, no settings line.'),
13
+ question: z.string().min(1).max(LIMITS.question).describe('The question stem only: no options, no attribution, no settings line. Markdown: put code names and calls in backticks.'),
14
14
  options: z
15
15
  .array(z.object({
16
16
  label: z.string().min(1).max(LIMITS.option).describe('The option text itself, a complete claim in the option\'s own words: never a letter or a number (the panel numbers them). Labels must differ.'),
@@ -1 +1 @@
1
- {"version":3,"file":"panel_tools.js","sourceRoot":"","sources":["../../src/tools/panel_tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"panel_tools.js","sourceRoot":"","sources":["../../src/tools/panel_tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.52.5",
3
+ "version": "1.54.0",
4
4
  "description": "Learn while your agent works — local knowledge graph, spaced repetition, and quiz gating for agent-assisted development.",
5
5
  "license": "MIT",
6
6
  "repository": {