diff --git a/src/agents/base.ts b/src/agents/base.ts index 866e9322..ef3c04b2 100644 --- a/src/agents/base.ts +++ b/src/agents/base.ts @@ -11,6 +11,7 @@ import { ListTrelloCards, PostTrelloComment, ReadTrelloCard, + UpdateChecklistItem, UpdateTrelloCard, formatCardData, } from '../gadgets/trello/index.js'; @@ -110,8 +111,10 @@ async function buildAgentContext( log: ReturnType, ): Promise { // Build prompt context for template rendering + const cardUrl = `https://trello.com/c/${cardId}`; const promptContext: PromptContext = { cardId, + cardUrl, projectId: project.id, storiesListId: project.trello?.lists?.stories, processedLabelId: project.trello?.labels?.processed, @@ -205,6 +208,7 @@ function createAgentBuilderWithGadgets( new ListTrelloCards(), new GetMyRecentActivity(), new AddChecklistToCard(), + new UpdateChecklistItem(), ); } @@ -370,6 +374,13 @@ export async function executeAgent( log.info('Agent completed', { cardId, iterations: result.iterationCount, cost: result.cost }); + // Extract PR URL from output (gh pr create outputs the URL) + const prUrlMatch = result.output.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/); + const prUrl = prUrlMatch ? prUrlMatch[0] : undefined; + if (prUrl) { + log.info('PR URL extracted', { prUrl }); + } + // Get zipped log buffer before returning fileLogger.close(); const logBuffer = await fileLogger.getZippedBuffer(); @@ -377,6 +388,7 @@ export async function executeAgent( return { success: true, output: result.output, + prUrl, logBuffer, cost: result.cost, }; diff --git a/src/agents/prompts/index.ts b/src/agents/prompts/index.ts index 6b62f0af..3b3b8337 100644 --- a/src/agents/prompts/index.ts +++ b/src/agents/prompts/index.ts @@ -13,6 +13,7 @@ const eta = new Eta({ views: templatesDir, autoEscape: false }); export interface PromptContext { // Common cardId?: string; + cardUrl?: string; projectId?: string; // Briefing-specific diff --git a/src/agents/prompts/templates/briefing.eta b/src/agents/prompts/templates/briefing.eta index 6c46dd5b..08d5caa5 100644 --- a/src/agents/prompts/templates/briefing.eta +++ b/src/agents/prompts/templates/briefing.eta @@ -88,9 +88,13 @@ You are running in a cloned copy of the project repository. Before creating stor - Include TLDR, acceptance criteria, and technical notes in description (use emoji formatting) - **IMPORTANT:** Save the returned URL for each card (e.g., `https://trello.com/c/abc123`) 5. **Add interactive checklists** using `AddChecklistToCard`: - - For EACH card you create, call `AddChecklistToCard` with the card ID - - Use "✅ Acceptance Criteria" as the checklist name - - Add each acceptance criterion as a checklist item + - For EACH card you create, call `AddChecklistToCard` for acceptance criteria: + - Use "✅ Acceptance Criteria" as the checklist name + - Add each acceptance criterion as a checklist item + - For cards that depend on other stories, also call `AddChecklistToCard` for dependencies: + - Use "🔗 Dependencies" as the checklist name + - Add each dependency as a checklist item (use card title or URL) + - Skip this checklist for foundational stories with no dependencies 6. **Post summary comment** using `PostTrelloComment`: - Post a comment on the ORIGINAL card listing all created stories - Use markdown links: `[Story Title](URL)` for each card @@ -129,7 +133,9 @@ Use this template with **emoji section headers** and **bold key terms** for read - [Things explicitly NOT included in this story] ``` -**IMPORTANT:** After creating each card, ALWAYS call `AddChecklistToCard` to create an interactive checklist with the acceptance criteria items. +**IMPORTANT:** After creating each card, ALWAYS call `AddChecklistToCard` to create interactive checklists: +1. "✅ Acceptance Criteria" checklist with acceptance criteria items (always) +2. "🔗 Dependencies" checklist with cards that must be completed first (if any) ## Summary Comment Format @@ -203,7 +209,8 @@ If the user asks you to update stories you previously created: - ALWAYS use `ReadTrelloCard` first - ALWAYS explore the codebase before creating stories - ALWAYS create stories using `CreateTrelloCard` - don't just output text -- ALWAYS call `AddChecklistToCard` after creating each card to add interactive checklists +- ALWAYS call `AddChecklistToCard` after creating each card to add "✅ Acceptance Criteria" checklist +- ALWAYS add "🔗 Dependencies" checklist to stories that depend on other stories - ALWAYS use emoji section headers (🎯, ✅, 🔧, 🚫) and **bold key terms** in descriptions - ALWAYS include a 🎯 TLDR section at the top of every card description - ALWAYS post a summary comment with markdown links to all created cards diff --git a/src/agents/prompts/templates/implementation.eta b/src/agents/prompts/templates/implementation.eta index a92d8051..15648aab 100644 --- a/src/agents/prompts/templates/implementation.eta +++ b/src/agents/prompts/templates/implementation.eta @@ -16,6 +16,8 @@ You are an expert software engineer implementing features based on a detailed pl 7. **Create commits** via Tmux: `git add . && git commit -m "feat: description"` 8. **Push the branch** via Tmux: `git push -u origin HEAD` 9. **Create a PR** via Tmux: `gh pr create --title "..." --body "..."` +10. **Mark acceptance criteria complete** using UpdateChecklistItem for each criterion you've implemented +11. **Post summary comment** on the Trello card describing what was implemented and linking to the PR ## Mandatory Exploration Before Implementation @@ -76,12 +78,11 @@ Use standard git commands and GitHub CLI (`gh`) via Tmux for all version control - `git push -u origin HEAD` - push branch to remote **GitHub CLI (`gh`) commands:** -- `gh pr create --fill` - create PR auto-filling title/body from commits (simplest) -- `gh pr create --title "Title" --body "Description" --base dev` - create with explicit values +- `gh pr create --title "Title" --body "Description"` - create PR with explicit values (required) - `gh pr view` - view current PR - `gh pr list` - list open PRs -**IMPORTANT:** Always use `--fill` or provide `--title` and `--body` to avoid interactive prompts. +**IMPORTANT:** Always provide `--title` and `--body` to avoid interactive prompts. Do NOT use `--fill`. ## Commit Message Format @@ -97,18 +98,62 @@ Example: `feat: add user authentication endpoint` ## PR Description Format -```markdown -## Summary -[Brief description of what this PR does] +When creating your PR, you MUST use this format: + +```bash +gh pr create --title "type: brief description" --body "## Summary +Brief description of what this PR implements. ## Changes -- [List of changes] +- Main change 1 +- Main change 2 ## Testing -- [How to test] +- How to test these changes ## Related -- Trello card: [URL] +- Trello card: <%= it.cardUrl %> +" +``` + +**CRITICAL PR Requirements:** +- The PR description MUST include the Trello card link: `<%= it.cardUrl %>` +- The description should be comprehensive enough to understand the PR without reading all the code +- Always use explicit `--title` and `--body` flags, never `--fill` + +## Completion Process + +After creating the PR, complete these steps: + +### 1. Mark Acceptance Criteria Complete + +Use `UpdateChecklistItem` to mark each completed acceptance criterion: +1. Read the card again to get checklist item IDs +2. For each criterion you've implemented, call `UpdateChecklistItem` with state="complete" + +### 2. Post Summary Comment + +Use `PostTrelloComment` to post a summary on the Trello card: + +```markdown +## ✅ Implementation Complete + +**PR:** [PR Title](PR_URL) + +### What was implemented +- [Key feature/change 1] +- [Key feature/change 2] + +### Key decisions +- [Decision 1 and why] +- [Decision 2 and why] + +### Testing +- All tests passing +- Lint and type checks passing + +### Notes +- [Any issues encountered or things to be aware of] ``` ## Rules @@ -125,4 +170,5 @@ Example: `feat: add user authentication endpoint` - NEVER write comprehensive test suites when the card asks for targeted tests - match scope to the plan - Use meaningful commit messages following conventional commits - Keep commits small and focused -- Post the PR URL to Trello using PostTrelloComment when complete +- ALWAYS mark acceptance criteria complete using UpdateChecklistItem after PR is created +- ALWAYS post a summary comment on the Trello card with PR link and what was implemented diff --git a/src/gadgets/trello/UpdateChecklistItem.ts b/src/gadgets/trello/UpdateChecklistItem.ts new file mode 100644 index 00000000..0358c008 --- /dev/null +++ b/src/gadgets/trello/UpdateChecklistItem.ts @@ -0,0 +1,44 @@ +import { Gadget, z } from 'llmist'; +import { trelloClient } from '../../trello/client.js'; +import { formatGadgetError } from '../utils.js'; + +export class UpdateChecklistItem extends Gadget({ + name: 'UpdateChecklistItem', + description: + 'Update a checklist item state on a Trello card. Use this to mark acceptance criteria as complete or incomplete.', + timeoutMs: 15000, + schema: z.object({ + cardId: z.string().describe('The Trello card ID'), + checkItemId: z.string().describe('The checklist item ID to update'), + state: z.enum(['complete', 'incomplete']).describe('The new state for the checklist item'), + }), + examples: [ + { + params: { + cardId: 'abc123', + checkItemId: 'item456', + state: 'complete', + }, + comment: 'Mark an acceptance criterion as complete', + }, + { + params: { + cardId: 'abc123', + checkItemId: 'item789', + state: 'incomplete', + }, + comment: 'Mark an acceptance criterion as incomplete', + }, + ], +}) { + override async execute(params: this['params']): Promise { + try { + await trelloClient.updateChecklistItem(params.cardId, params.checkItemId, params.state); + + const action = params.state === 'complete' ? 'marked complete' : 'marked incomplete'; + return `Checklist item ${params.checkItemId} ${action} on card ${params.cardId}`; + } catch (error) { + return formatGadgetError('updating checklist item', error); + } + } +} diff --git a/src/gadgets/trello/index.ts b/src/gadgets/trello/index.ts index 5111261c..8aa2877f 100644 --- a/src/gadgets/trello/index.ts +++ b/src/gadgets/trello/index.ts @@ -5,3 +5,4 @@ export { CreateTrelloCard } from './CreateTrelloCard.js'; export { ListTrelloCards } from './ListTrelloCards.js'; export { GetMyRecentActivity } from './GetMyRecentActivity.js'; export { AddChecklistToCard } from './AddChecklistToCard.js'; +export { UpdateChecklistItem } from './UpdateChecklistItem.js'; diff --git a/src/trello/client.ts b/src/trello/client.ts index 27e7f1a9..ec725b6f 100644 --- a/src/trello/client.ts +++ b/src/trello/client.ts @@ -341,6 +341,19 @@ export const trelloClient = { })); }, + async updateChecklistItem( + cardId: string, + checkItemId: string, + state: 'complete' | 'incomplete', + ): Promise { + logger.debug('Updating checklist item', { cardId, checkItemId, state }); + await getClient().cards.updateCardCheckItem({ + id: cardId, + idCheckItem: checkItemId, + state, + }); + }, + async getCardCustomFieldItems(cardId: string): Promise { logger.debug('Fetching card custom field items', { cardId }); const apiKey = process.env.TRELLO_API_KEY; diff --git a/src/triggers/trello/webhook-handler.ts b/src/triggers/trello/webhook-handler.ts index 75048ca7..d19917ed 100644 --- a/src/triggers/trello/webhook-handler.ts +++ b/src/triggers/trello/webhook-handler.ts @@ -96,6 +96,11 @@ async function executeAgent( if (cardId) { await safeAddLabel(cardId, project.trello.labels.processing); await safeRemoveLabel(cardId, project.trello.labels.readyToProcess); + + // Move to IN PROGRESS when implementation starts + if (result.agentType === 'implementation') { + await safeMoveCard(cardId, project.trello.lists.inProgress); + } } const agentResult = await runAgent(result.agentType, {