Extract buildGrillPrompt() and add the prompt-integration test #293

Closed
opened 2026-10-09 21:19:41 +00:00 by david · 1 comment
Owner

Summary

Move the Grill Me prompt text out of the before_agent_start handler into a dependency-free builder, and test that the prompt for a sample state contains the STE style block exactly once and keeps the existing behavior guidance.

Background

Depends on: #291

#291 adds ste-style.ts and injects buildSteStyleBlock() into the prompt built inline inside pi.on("before_agent_start", ...) in extensions/grillme/index.ts. That prompt cannot be asserted on today: the handler is not exported, and importing index.ts from a test would pull in @earendil-works/pi-coding-agent, @earendil-works/pi-tui, and typebox, which the dependency-free node --test suite does not have. bash-filter.test.ts and widget.test.ts both avoid index.ts for exactly this reason.

This step makes the prompt testable without booting pi, then adds the integration check. It must not change the prompt's content or the extension's runtime behavior.

Implementation Details

Extract the builder

Create extensions/grillme/prompt.ts — dependency-free, like bash-filter.ts, widget.ts, and ste-style.ts. Importing pi, TypeBox, or index.ts from it is not allowed; type-only imports are not enough of a guarantee, because the module must load under node --test with no node_modules.

Recommended shape:

  • Export an input type with exactly the fields the prompt reads from the session state, for example GrillPromptInput { topic, intent, researchMode, outputPreference, phase, outputPhase, checkpoint, alternatives, outputSelection }.
  • Export buildGrillPrompt(input: GrillPromptInput): string returning the same prompt string the handler builds today (without the leading system-prompt concatenation).
  • Move the pure prompt constants and helpers into prompt.ts as well: thoroughGrillingGuidance, the researchGuidance map, the GITHUB_REPO_PERMISSION_GUIDANCE string, OUTPUT_DESTINATION_OPTIONS plus its two formatters (outputDestinationOptionsMarkdown, outputDestinationOptionNames), describeOutputPreference, currentPhase, and the outputPhaseGuidance selection. Keep the exported names that index.ts already uses where it is cheap to do so.

The state interface in index.ts (GrillState, GrillAlternative, GrillPhase) stays where it is; map the needed fields into GrillPromptInput at the call site. If that mapping grows past a handful of lines, a small toPromptInput(state) helper in index.ts is fine.

index.ts remains the only caller:

const prompt = buildGrillPrompt({ /* fields from state */ });
return { systemPrompt: event.systemPrompt + prompt };

Keep the [GRILL ME EXTENSION ACTIVE] opening and closing fence, the Behavior: guidance, the checkpoint rule, and the readiness/output rule exactly as they are now. This is a move, not a rewrite: the new step for the STE rewrite of authored strings is a separate issue.

Add the integration test

Add the prompt check to extensions/grillme/ste-style.test.ts (or a new prompt.test.ts if that keeps the file readable — either is fine, but the test must not import index.ts and must not import pi packages). Assert, for one representative sample input (an active interview with a topic, a checkpoint, and alternatives):

  • the returned prompt contains the style block exactly once — count occurrences of the block's opening fence (for example [GRILL ME STE STYLE]) and assert the count is 1;
  • the returned prompt contains buildSteStyleBlock() as a substring;
  • the prompt still contains the existing Behavior: guidance and at least one of the Socratic lines (for example the Ask mostly one focused question at a time. line), proving the block sits alongside the behavior guidance and did not replace it;
  • a second sample input for the output-selection phase produces a prompt that still contains one style block.

Acceptance Criteria

  • extensions/grillme/prompt.ts exists, imports no pi package, no TypeBox, and no index.ts, and exports buildGrillPrompt() (and the GrillPromptInput type or equivalent).
  • index.ts's before_agent_start handler calls buildGrillPrompt() and still returns { systemPrompt: event.systemPrompt + prompt }.
  • The prompt text is unchanged: the [GRILL ME EXTENSION ACTIVE] fence, the Behavior: guidance, the checkpoint rule, and the readiness/output rule all survive the move byte-for-byte apart from the added style block.
  • A test asserts the style block appears exactly once for an interview-phase sample and once for an output-selection-phase sample.
  • A test asserts the Behavior: guidance is still present alongside the block.
  • The new test file(s) import only node:test, node:assert/strict, and local .ts modules — no pi package.
  • cd extensions/grillme && npm test passes, including ste-style.test.ts, bash-filter.test.ts, and widget.test.ts.

Test Plan

cd extensions/grillme
npm test

Expected: all test files pass.

# the builder must not pull in pi packages
grep -n "pi-coding-agent\|pi-tui\|typebox\|from \"./index" extensions/grillme/prompt.ts

Expected: no output (a type-only import is still a failure for this check).

# the handler builds the prompt through the new builder
grep -n "buildGrillPrompt" extensions/grillme/index.ts

Expected: one import line and one call.

Manual: start pi in this repo, run /grill a small topic, ask one question and answer it, then run /grill status — the session must behave exactly as before.

## Summary Move the Grill Me prompt text out of the `before_agent_start` handler into a dependency-free builder, and test that the prompt for a sample state contains the STE style block exactly once and keeps the existing behavior guidance. ## Background **Depends on:** #291 `#291` adds `ste-style.ts` and injects `buildSteStyleBlock()` into the prompt built inline inside `pi.on("before_agent_start", ...)` in `extensions/grillme/index.ts`. That prompt cannot be asserted on today: the handler is not exported, and importing `index.ts` from a test would pull in `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`, and `typebox`, which the dependency-free `node --test` suite does not have. `bash-filter.test.ts` and `widget.test.ts` both avoid `index.ts` for exactly this reason. This step makes the prompt testable without booting pi, then adds the integration check. It must not change the prompt's content or the extension's runtime behavior. ## Implementation Details ### Extract the builder Create `extensions/grillme/prompt.ts` — dependency-free, like `bash-filter.ts`, `widget.ts`, and `ste-style.ts`. Importing pi, TypeBox, or `index.ts` from it is not allowed; `type`-only imports are not enough of a guarantee, because the module must load under `node --test` with no `node_modules`. Recommended shape: - Export an input type with exactly the fields the prompt reads from the session state, for example `GrillPromptInput { topic, intent, researchMode, outputPreference, phase, outputPhase, checkpoint, alternatives, outputSelection }`. - Export `buildGrillPrompt(input: GrillPromptInput): string` returning the same `prompt` string the handler builds today (without the leading system-prompt concatenation). - Move the pure prompt constants and helpers into `prompt.ts` as well: `thoroughGrillingGuidance`, the `researchGuidance` map, the `GITHUB_REPO_PERMISSION_GUIDANCE` string, `OUTPUT_DESTINATION_OPTIONS` plus its two formatters (`outputDestinationOptionsMarkdown`, `outputDestinationOptionNames`), `describeOutputPreference`, `currentPhase`, and the `outputPhaseGuidance` selection. Keep the exported names that `index.ts` already uses where it is cheap to do so. The state interface in `index.ts` (`GrillState`, `GrillAlternative`, `GrillPhase`) stays where it is; map the needed fields into `GrillPromptInput` at the call site. If that mapping grows past a handful of lines, a small `toPromptInput(state)` helper in `index.ts` is fine. `index.ts` remains the only caller: ```ts const prompt = buildGrillPrompt({ /* fields from state */ }); return { systemPrompt: event.systemPrompt + prompt }; ``` Keep the `[GRILL ME EXTENSION ACTIVE]` opening and closing fence, the `Behavior:` guidance, the checkpoint rule, and the readiness/output rule exactly as they are now. This is a move, not a rewrite: the new step for the STE rewrite of authored strings is a separate issue. ### Add the integration test Add the prompt check to `extensions/grillme/ste-style.test.ts` (or a new `prompt.test.ts` if that keeps the file readable — either is fine, but the test must not import `index.ts` and must not import pi packages). Assert, for one representative sample input (an active interview with a topic, a checkpoint, and alternatives): - the returned prompt contains the style block exactly once — count occurrences of the block's opening fence (for example `[GRILL ME STE STYLE]`) and assert the count is `1`; - the returned prompt contains `buildSteStyleBlock()` as a substring; - the prompt still contains the existing `Behavior:` guidance and at least one of the Socratic lines (for example the `Ask mostly one focused question at a time.` line), proving the block sits alongside the behavior guidance and did not replace it; - a second sample input for the output-selection phase produces a prompt that still contains one style block. ## Acceptance Criteria - [ ] `extensions/grillme/prompt.ts` exists, imports no pi package, no TypeBox, and no `index.ts`, and exports `buildGrillPrompt()` (and the `GrillPromptInput` type or equivalent). - [ ] `index.ts`'s `before_agent_start` handler calls `buildGrillPrompt()` and still returns `{ systemPrompt: event.systemPrompt + prompt }`. - [ ] The prompt text is unchanged: the `[GRILL ME EXTENSION ACTIVE]` fence, the `Behavior:` guidance, the checkpoint rule, and the readiness/output rule all survive the move byte-for-byte apart from the added style block. - [ ] A test asserts the style block appears exactly once for an interview-phase sample and once for an output-selection-phase sample. - [ ] A test asserts the `Behavior:` guidance is still present alongside the block. - [ ] The new test file(s) import only `node:test`, `node:assert/strict`, and local `.ts` modules — no pi package. - [ ] `cd extensions/grillme && npm test` passes, including `ste-style.test.ts`, `bash-filter.test.ts`, and `widget.test.ts`. ## Test Plan ```bash cd extensions/grillme npm test ``` Expected: all test files pass. ```bash # the builder must not pull in pi packages grep -n "pi-coding-agent\|pi-tui\|typebox\|from \"./index" extensions/grillme/prompt.ts ``` Expected: no output (a type-only import is still a failure for this check). ```bash # the handler builds the prompt through the new builder grep -n "buildGrillPrompt" extensions/grillme/index.ts ``` Expected: one import line and one call. Manual: start pi in this repo, run `/grill a small topic`, ask one question and answer it, then run `/grill status` — the session must behave exactly as before.
david self-assigned this 2026-10-09 21:19:41 +00:00
david closed this issue 2026-10-09 21:50:09 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #303

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/303
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#293
No description provided.