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
106 changes: 101 additions & 5 deletions apps/mcp-server/src/agent/agent-prompt.builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,30 @@ describe('agent-prompt.builder', () => {
expect(result).toMatch(/output|format|json|structured/i);
});

it('should include mandatory_checklist when present', () => {
it('should include mandatory_checklist from activation object format', () => {
const profileWithActivation: AgentProfile = {
...mockAgentProfile,
activation: {
mandatory_checklist: {
'🔴 language': {
rule: 'MUST respond in Korean',
verification_key: 'language',
},
'🔴 tdd': {
rule: 'Follow TDD cycle',
verification_key: 'tdd_cycle',
},
},
},
};
const result = buildAgentSystemPrompt(profileWithActivation, mockContext);

expect(result).toContain('## Mandatory Checklist');
expect(result).toContain('- [ ] MUST respond in Korean');
expect(result).toContain('- [ ] Follow TDD cycle');
});

it('should fall back to top-level array mandatory_checklist (backward compat)', () => {
const profileWithChecklist: AgentProfile = {
...mockAgentProfile,
mandatory_checklist: [
Expand All @@ -136,6 +159,22 @@ describe('agent-prompt.builder', () => {
expect(result).toContain('- [ ] SOLID principles applied');
});

it('should prefer activation.mandatory_checklist over top-level array', () => {
const profileWithBoth: AgentProfile = {
...mockAgentProfile,
mandatory_checklist: ['Top-level item'],
activation: {
mandatory_checklist: {
'🔴 check': { rule: 'Activation item', verification_key: 'check' },
},
},
};
const result = buildAgentSystemPrompt(profileWithBoth, mockContext);

expect(result).toContain('- [ ] Activation item');
expect(result).not.toContain('Top-level item');
});

it('should include communication language when present', () => {
const profileWithLang: AgentProfile = {
...mockAgentProfile,
Expand Down Expand Up @@ -193,18 +232,75 @@ describe('agent-prompt.builder', () => {
expect(result).toContain('**web_search**: Validate recommendations (when: Evaluating)');
});

it('should include verification_guide when present', () => {
it('should include verification_guide from activation', () => {
const profileWithActivation: AgentProfile = {
...mockAgentProfile,
activation: {
verification_guide: {
tdd_cycle: 'Check test file exists before implementation',
server_components: 'Verify no use client directive',
},
},
};
const result = buildAgentSystemPrompt(profileWithActivation, mockContext);

expect(result).toContain('## Verification Guide');
expect(result).toContain('**tdd_cycle**: Check test file exists before implementation');
expect(result).toContain('**server_components**: Verify no use client directive');
});

it('should fall back to top-level verification_guide (backward compat)', () => {
const profileWithVerification: AgentProfile = {
...mockAgentProfile,
verification_guide: {
steps: ['Check type safety', 'Run tests', 'Review coverage'],
steps: 'Check type safety and run tests',
},
};
const result = buildAgentSystemPrompt(profileWithVerification, mockContext);

expect(result).toContain('## Verification Guide');
expect(result).toContain('Check type safety');
expect(result).toContain('Run tests');
expect(result).toContain('**steps**: Check type safety and run tests');
});

it('should include execution_order from activation (object format)', () => {
const profileWithActivation: AgentProfile = {
...mockAgentProfile,
activation: {
execution_order: {
plan_mode: ['1. Analyze requirements', '2. Create plan'],
act_mode: ['1. Implement changes', '2. Run tests'],
},
},
};
const result = buildAgentSystemPrompt(profileWithActivation, mockContext);

expect(result).toContain('## Execution Order');
expect(result).toContain('### plan_mode');
expect(result).toContain('1. Analyze requirements');
expect(result).toContain('### act_mode');
expect(result).toContain('1. Implement changes');
});

it('should include execution_order from activation (array format)', () => {
const profileWithActivation: AgentProfile = {
...mockAgentProfile,
activation: {
execution_order: ['Step 1', 'Step 2', 'Step 3'],
},
};
const result = buildAgentSystemPrompt(profileWithActivation, mockContext);

expect(result).toContain('## Execution Order');
expect(result).toContain('1. Step 1');
expect(result).toContain('1. Step 2');
});

it('should handle agent without activation field gracefully', () => {
const result = buildAgentSystemPrompt(mockAgentProfile, mockContext);

expect(result).not.toContain('## Mandatory Checklist');
expect(result).not.toContain('## Verification Guide');
expect(result).not.toContain('## Execution Order');
});

it('should handle agent without optional rich metadata gracefully', () => {
Expand Down
54 changes: 48 additions & 6 deletions apps/mcp-server/src/agent/agent-prompt.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,27 @@ export function buildAgentSystemPrompt(agentProfile: AgentProfile, context: Agen

// Rich metadata from passthrough fields
const rawProfile = agentProfile as Record<string, unknown>;

// Mandatory Checklist
if (Array.isArray(rawProfile.mandatory_checklist) && rawProfile.mandatory_checklist.length) {
const activation = rawProfile.activation as Record<string, unknown> | undefined;

// Mandatory Checklist — prefer activation.mandatory_checklist (object with {rule:...} items)
const activationChecklist = activation?.mandatory_checklist;
if (
activationChecklist &&
typeof activationChecklist === 'object' &&
!Array.isArray(activationChecklist)
) {
sections.push('## Mandatory Checklist');
for (const [, item] of Object.entries(activationChecklist as Record<string, unknown>)) {
if (item && typeof item === 'object' && 'rule' in item) {
sections.push(`- [ ] ${(item as { rule: string }).rule}`);
}
}
sections.push('');
} else if (
Array.isArray(rawProfile.mandatory_checklist) &&
rawProfile.mandatory_checklist.length
) {
// Backward compat: top-level array format
sections.push('## Mandatory Checklist');
for (const item of rawProfile.mandatory_checklist) {
sections.push(`- [ ] ${item}`);
Expand Down Expand Up @@ -133,10 +151,34 @@ export function buildAgentSystemPrompt(agentProfile: AgentProfile, context: Agen
sections.push('');
}

// Verification Guide
if (rawProfile.verification_guide) {
// Verification Guide — prefer activation.verification_guide
const verificationGuide = activation?.verification_guide ?? rawProfile.verification_guide;
if (verificationGuide && typeof verificationGuide === 'object') {
sections.push('## Verification Guide');
sections.push(JSON.stringify(rawProfile.verification_guide, null, 2));
for (const [key, desc] of Object.entries(verificationGuide as Record<string, string>)) {
sections.push(`- **${key}**: ${desc}`);
}
sections.push('');
}

// Execution Order — from activation.execution_order
const executionOrder = activation?.execution_order;
if (executionOrder && typeof executionOrder === 'object' && !Array.isArray(executionOrder)) {
sections.push('## Execution Order');
for (const [phase, steps] of Object.entries(executionOrder as Record<string, unknown>)) {
if (Array.isArray(steps) && steps.length) {
sections.push(`### ${phase}`);
for (const step of steps) {
sections.push(`${step}`);
}
}
}
sections.push('');
} else if (Array.isArray(executionOrder) && executionOrder.length) {
sections.push('## Execution Order');
for (const step of executionOrder) {
sections.push(`1. ${step}`);
}
sections.push('');
}

Expand Down
Loading