Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
"scripts": {
"dev": "node --env-file=.env --import tsx/esm --watch src/index.ts",
"dev:web": "cd web && npx vite",
"build": "tsc && npm run build:copy-yaml && npm run build:copy-system-prompts && npm run build:copy-task-templates",
"build": "tsc && npm run build:copy-yaml && npm run build:copy-system-prompts",
"build:copy-yaml": "mkdir -p dist/agents/definitions && cp src/agents/definitions/*.yaml dist/agents/definitions/",
"build:copy-system-prompts": "mkdir -p dist/agents/prompts && cp -r src/agents/prompts/templates dist/agents/prompts/",
"build:copy-task-templates": "mkdir -p dist/agents/prompts/task-templates && cp src/agents/prompts/task-templates/*.eta dist/agents/prompts/task-templates/",
"build:web": "cd web && npm run build",
"start": "node dist/index.js",
"test": "vitest run --project unit",
Expand Down
5 changes: 4 additions & 1 deletion src/agents/definitions/debug.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ capabilities:

strategies:
contextPipeline: [directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: workItem

prompts:
taskPrompt: |
Analyze and process the work item with ID: <%= it.cardId %>. The work item data has been pre-loaded.

backend:
enableStopHooks: true
Expand Down
20 changes: 19 additions & 1 deletion src/agents/definitions/email-joke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,25 @@ capabilities:

strategies:
contextPipeline: [prefetchedEmails]
taskPromptBuilder: emailJoke

prompts:
taskPrompt: |
## Your Task

Your initial email search has already been completed — see the **SearchEmails** result above.

For each email found:
1. Use **ReadEmail** to read the full content
2. Compose a friendly, funny response that relates to the email content
3. Use **ReplyToEmail** to send your joke response
4. Use **MarkEmailAsSeen** to mark the email as read (prevents re-processing)

Once all emails have been processed, call **Finish**.

<% if (it.senderEmail) { %>
## Sender Filter
Only emails from **<%= it.senderEmail %>** were included in the search.
<% } %>

backend:
enableStopHooks: false
Expand Down
5 changes: 4 additions & 1 deletion src/agents/definitions/implementation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ capabilities:

strategies:
contextPipeline: [directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: workItem

prompts:
taskPrompt: |
Analyze and process the work item with ID: <%= it.cardId %>. The work item data has been pre-loaded.

backend:
enableStopHooks: true
Expand Down
5 changes: 4 additions & 1 deletion src/agents/definitions/planning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ capabilities:

strategies:
contextPipeline: [directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: workItem

prompts:
taskPrompt: |
Analyze and process the work item with ID: <%= it.cardId %>. The work item data has been pre-loaded.

backend:
enableStopHooks: false
Expand Down
37 changes: 15 additions & 22 deletions src/agents/definitions/profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import {
resolveEffectiveCapabilities,
} from '../capabilities/resolver.js';
import type { ContextInjection, ToolManifest } from '../contracts/index.js';
import { type TaskPromptContext, renderTaskPrompt } from '../prompts/index.js';
import {
buildTaskPromptContext,
renderInlineTaskPrompt,
validateTemplate,
} from '../prompts/index.js';
import { buildGadgetsForAgent } from '../shared/gadgets.js';
import type { FetchContextParams, PreExecuteParams } from './contextSteps.js';
import { resolveAgentDefinition } from './loader.js';
Expand Down Expand Up @@ -69,24 +73,6 @@ function resolveRegistry<T>(registry: Record<string, T>, key: string, label: str
return value;
}

/**
* Extract all relevant fields from AgentInput into a flat context object
* for Eta task prompt template rendering.
*/
function buildTaskPromptContext(input: AgentInput): TaskPromptContext {
return {
cardId: input.cardId || 'unknown',
commentText: input.triggerCommentText as string | undefined,
commentAuthor: (input.triggerCommentAuthor as string) || 'unknown',
prNumber: input.prNumber,
prBranch: input.prBranch,
commentBody: input.triggerCommentBody as string | undefined,
commentPath: (input.triggerCommentPath as string) || undefined,
// Email-joke agent fields
senderEmail: input.senderEmail as string | undefined,
};
}

/**
* Merge required and optional capabilities into a single list.
* In runtime, we use all declared capabilities (validation happens separately).
Expand Down Expand Up @@ -114,8 +100,14 @@ function buildProfileFromDefinition(def: AgentDefinition, agentType: string): Ag
// Get context pipeline from strategies
const contextPipeline = def.strategies.contextPipeline;

// Task prompt template name (maps to .eta file)
const taskTemplateName = def.strategies.taskPromptBuilder;
// Get task prompt template from prompts (required by schema)
const taskPromptTemplate = def.prompts.taskPrompt;

// Validate Eta syntax early to catch errors at profile build time
const validationResult = validateTemplate(taskPromptTemplate);
if (!validationResult.valid) {
throw new Error(`Agent '${agentType}' has invalid taskPrompt: ${validationResult.error}`);
}

const profile: AgentProfile = {
filterTools: (allTools: ToolManifest[]) => {
Expand All @@ -137,7 +129,8 @@ function buildProfileFromDefinition(def: AgentDefinition, agentType: string): Ag
}
return injections;
},
buildTaskPrompt: (input) => renderTaskPrompt(taskTemplateName, buildTaskPromptContext(input)),
buildTaskPrompt: (input) =>
renderInlineTaskPrompt(taskPromptTemplate, buildTaskPromptContext(input)),
capabilities: def.capabilities,
getLlmistGadgets: (integrationChecker?: IntegrationChecker) => {
// Resolve effective capabilities based on integration availability
Expand Down
7 changes: 6 additions & 1 deletion src/agents/definitions/respond-to-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ capabilities:

strategies:
contextPipeline: [prContext, directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: ci

prompts:
taskPrompt: |
You are on the branch `<%= it.prBranch %>` for PR #<%= it.prNumber %>.

CI checks have failed. Analyze the failures and fix them.

backend:
enableStopHooks: true
Expand Down
13 changes: 12 additions & 1 deletion src/agents/definitions/respond-to-planning-comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,18 @@ capabilities:

strategies:
contextPipeline: [directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: commentResponse

prompts:
taskPrompt: |
A user (@<%= it.commentAuthor %>) mentioned you in a comment on work item <%= it.cardId %>.

Their comment:
---
<%= it.commentText %>
---

The work item data (title, description, checklists, attachments, comments) has been pre-loaded above.
Read the user's comment carefully and classify it: if they ask a question or request clarification, reply with a thorough answer via PostComment (do not modify the plan). If they request plan changes, make surgical, targeted updates. If the comment contains both a question and a change request, do both. Default to plan updates when intent is ambiguous.

backend:
enableStopHooks: false
Expand Down
17 changes: 16 additions & 1 deletion src/agents/definitions/respond-to-pr-comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,25 @@ capabilities:

strategies:
contextPipeline: [prContext, prConversation, directoryListing, contextFiles, squint]
taskPromptBuilder: prCommentResponse
gadgetOptions:
includeReviewComments: true

prompts:
taskPrompt: |
You are on the branch `<%= it.prBranch %>` for PR #<%= it.prNumber %>.

A user commented on this PR and mentioned you. Respond to their comment.
<% if (it.commentPath) { -%>
File: <%= it.commentPath %>
<% } -%>

Their comment:
---
<%= it.commentBody %>
---

Read the comment carefully and respond accordingly. If they ask for code changes, make the changes, commit, and push. If they ask a question, reply with a PR comment. Default to surgical, targeted changes unless they clearly ask for something broader.

backend:
enableStopHooks: true
needsGitHubToken: true
Expand Down
17 changes: 16 additions & 1 deletion src/agents/definitions/respond-to-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,25 @@ capabilities:

strategies:
contextPipeline: [prContext, prConversation, directoryListing, contextFiles, squint]
taskPromptBuilder: prCommentResponse
gadgetOptions:
includeReviewComments: true

prompts:
taskPrompt: |
You are on the branch `<%= it.prBranch %>` for PR #<%= it.prNumber %>.

A user commented on this PR and mentioned you. Respond to their comment.
<% if (it.commentPath) { -%>
File: <%= it.commentPath %>
<% } -%>

Their comment:
---
<%= it.commentBody %>
---

Read the comment carefully and respond accordingly. If they ask for code changes, make the changes, commit, and push. If they ask a question, reply with a PR comment. Default to surgical, targeted changes unless they clearly ask for something broader.

backend:
enableStopHooks: true
needsGitHubToken: true
Expand Down
7 changes: 6 additions & 1 deletion src/agents/definitions/review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ capabilities:

strategies:
contextPipeline: [prContext, contextFiles, squint]
taskPromptBuilder: review

prompts:
taskPrompt: |
Review PR #<%= it.prNumber %>.

Examine the code changes carefully and submit your review using CreatePRReview.

backend:
enableStopHooks: false
Expand Down
24 changes: 6 additions & 18 deletions src/agents/definitions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,16 @@ export const CONTEXT_STEP_NAMES = [
'prefetchedEmails',
] as const;

export const TASK_PROMPT_BUILDER_NAMES = [
'workItem',
'commentResponse',
'review',
'ci',
'prCommentResponse',
'emailJoke',
] as const;

export const COMPACTION_NAMES = ['implementation', 'default'] as const;

/**
* Strategies schema - context and prompt configuration.
* Note: gadgetBuilder removed - gadgets are now derived from capabilities.
* Note: taskPromptBuilder removed - task prompts are now stored in prompts.taskPrompt.
*/
const StrategiesSchema = z.object({
/** Pipeline of context fetching steps */
contextPipeline: z.array(z.enum(CONTEXT_STEP_NAMES)),
/** Task prompt template name (maps to .eta file) */
taskPromptBuilder: z.enum(TASK_PROMPT_BUILDER_NAMES),
/** Optional gadget configuration for special cases */
gadgetOptions: GadgetOptionsSchema,
});
Expand All @@ -112,12 +102,10 @@ const TrailingMessageSchema = z
})
.optional();

const PromptsSchema = z
.object({
systemPrompt: z.string().optional(),
taskPrompt: z.string().optional(),
})
.optional();
const PromptsSchema = z.object({
systemPrompt: z.string().optional(),
taskPrompt: z.string().min(1, 'taskPrompt is required and must be non-empty'),
});

/**
* Complete agent definition schema.
Expand Down Expand Up @@ -145,7 +133,7 @@ export const AgentDefinitionSchema = z.object({
hint: z.string(),
/** Trailing message configuration */
trailingMessage: TrailingMessageSchema,
/** Custom prompts (optional) */
/** Custom prompts (taskPrompt required, systemPrompt optional) */
prompts: PromptsSchema,
});

Expand Down
5 changes: 4 additions & 1 deletion src/agents/definitions/splitting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ capabilities:

strategies:
contextPipeline: [directoryListing, contextFiles, squint, workItem]
taskPromptBuilder: workItem

prompts:
taskPrompt: |
Analyze and process the work item with ID: <%= it.cardId %>. The work item data has been pre-loaded.

backend:
enableStopHooks: false
Expand Down
Loading