diff --git a/.changeset/suggest-new-tickets-preset.md b/.changeset/suggest-new-tickets-preset.md new file mode 100644 index 00000000..ed19149a --- /dev/null +++ b/.changeset/suggest-new-tickets-preset.md @@ -0,0 +1,5 @@ +--- +"@gemstack/framework": minor +--- + +Add the "Suggest new tickets" preset (#462), the Agentic PM ideation prompt. Like the other presets it prefills the dashboard editor and runs as a `prompt` kind. Per #674 the prompt is a single line, "Suggest new tickets": the run-start context fragment (#683) already points the agent at the existing `tickets/**.md` and the `.the-framework/ticketing-format.md` spec (#684), so it does not need to re-teach the ticket format or spell out the flow. Per the settled #624 model the proposal is just a PR: merging accepts the tickets, closing rejects them. diff --git a/packages/framework-dashboard/components/Composer.tsx b/packages/framework-dashboard/components/Composer.tsx index 8930031c..873efbe5 100644 --- a/packages/framework-dashboard/components/Composer.tsx +++ b/packages/framework-dashboard/components/Composer.tsx @@ -6,6 +6,7 @@ import { renderMaintainabilityPrompt, renderSecurityAuditPrompt, renderUxPrompt, + renderSuggestNewTicketsPrompt, } from '@gemstack/framework/client' import { usePreferences, updatePreferences, autopilotEnabled } from '../lib/preferences.js' import { PromptEditor, type PromptEditorHandle } from './PromptEditor.js' @@ -24,6 +25,7 @@ const PRESETS: { id: string; label: string; render: () => string }[] = [ { id: 'maintainability', label: 'Maintainability', render: renderMaintainabilityPrompt }, { id: 'security-audit', label: 'Security audit', render: renderSecurityAuditPrompt }, { id: 'ux', label: 'UX', render: renderUxPrompt }, + { id: 'suggest-new-tickets', label: 'Suggest new tickets', render: renderSuggestNewTicketsPrompt }, ] // The agent + model tree (#650/#656/#658): each agent lists ONLY its own models, since `--model` diff --git a/packages/framework/prompts/presets/suggest_new_tickets.md b/packages/framework/prompts/presets/suggest_new_tickets.md new file mode 100644 index 00000000..814919d3 --- /dev/null +++ b/packages/framework/prompts/presets/suggest_new_tickets.md @@ -0,0 +1 @@ +Suggest new tickets diff --git a/packages/framework/src/client.ts b/packages/framework/src/client.ts index 24e42a2d..fb761c81 100644 --- a/packages/framework/src/client.ts +++ b/packages/framework/src/client.ts @@ -37,3 +37,4 @@ export { renderReadabilityPrompt } from './readability-preset.js' export { renderMaintainabilityPrompt } from './maintainability-preset.js' export { renderSecurityAuditPrompt } from './security-audit-preset.js' export { renderUxPrompt } from './ux-preset.js' +export { renderSuggestNewTicketsPrompt } from './suggest-new-tickets-preset.js' diff --git a/packages/framework/src/index.ts b/packages/framework/src/index.ts index 3654c903..5f9142e7 100644 --- a/packages/framework/src/index.ts +++ b/packages/framework/src/index.ts @@ -313,6 +313,12 @@ export { UX_PROMPT_TEMPLATE, UX_PARAMS, } from './ux-preset.js' +export { + renderSuggestNewTicketsPrompt, + SUGGEST_NEW_TICKETS_PRESET_NAME, + SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE, + SUGGEST_NEW_TICKETS_PARAMS, +} from './suggest-new-tickets-preset.js' export { PRESETS, PRESET_DIR, diff --git a/packages/framework/src/suggest-new-tickets-preset.test.ts b/packages/framework/src/suggest-new-tickets-preset.test.ts new file mode 100644 index 00000000..a82c36b3 --- /dev/null +++ b/packages/framework/src/suggest-new-tickets-preset.test.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { + renderSuggestNewTicketsPrompt, + SUGGEST_NEW_TICKETS_PARAMS, + SUGGEST_NEW_TICKETS_PRESET_NAME, + SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE, +} from './suggest-new-tickets-preset.js' + +test('the Suggest-new-tickets preset is the single #674 line, with no params', () => { + assert.equal(SUGGEST_NEW_TICKETS_PRESET_NAME, 'suggest-new-tickets') + // Rom's #674 call: one line, the ambient #683/#684 context carries the ticket format + flow. + assert.equal(SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE, 'Suggest new tickets') + // Paramless: nothing to fill, and no leftover `${{ ... }}` blank. + assert.deepEqual(SUGGEST_NEW_TICKETS_PARAMS, []) + assert.equal(SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE.includes('${{'), false) +}) + +test('renderSuggestNewTicketsPrompt returns the template verbatim', () => { + assert.equal(renderSuggestNewTicketsPrompt(), 'Suggest new tickets') +}) diff --git a/packages/framework/src/suggest-new-tickets-preset.ts b/packages/framework/src/suggest-new-tickets-preset.ts new file mode 100644 index 00000000..ac7660ef --- /dev/null +++ b/packages/framework/src/suggest-new-tickets-preset.ts @@ -0,0 +1,27 @@ +import { PRESETS_SUGGEST_NEW_TICKETS } from './prompts.generated.js' + +/** + * The [Suggest new tickets] preset (#462): the Agentic PM ideation prompt, shipped like the + * other presets as a direct interactive prompt rather than a build run. Per Rom's #674 call it + * is a single line — "Suggest new tickets" — and lets the ambient context carry the rest: the + * #683 run-start context fragment already points the agent at the existing `tickets/**.md` and + * the #684 `.the-framework/ticketing-format.md` spec, so the prompt does not re-teach the ticket + * format or spell out the flow. That is the #674 fix: an over-specified prompt is babysitting, + * which is brittle and counter-productive. Per the settled #624 model the proposal IS the PR: + * merging accepts the tickets, closing rejects them, so there is no separate proposal store. + * + * It has no params: the dashboard prefills this one line into the editor and the user edits it + * freely (e.g. to scope the ideation), so there is no `${{ tf.params.what }}` blank to render. + */ + +/** The preset's run-kind name, as the dashboard button uses it. */ +export const SUGGEST_NEW_TICKETS_PRESET_NAME = 'suggest-new-tickets' + +/** The prompt, from `prompts/presets/suggest_new_tickets.md`: a single line by #674 design. */ +export const SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE = PRESETS_SUGGEST_NEW_TICKETS + +/** No user params: the whole prompt is the one line, so there is nothing to fill. */ +export const SUGGEST_NEW_TICKETS_PARAMS: readonly [] = [] + +/** Render the prompt. Paramless, so it is the template verbatim. */ +export const renderSuggestNewTicketsPrompt = (): string => SUGGEST_NEW_TICKETS_PROMPT_TEMPLATE