How Shannon's Prompt Manager Handles Variable Substitution for Context: A Technical Deep Dive
Shannon's prompt manager performs variable substitution through a three-stage pure-function pipeline that loads raw templates, expands @include directives, and interpolates built-in placeholders plus config-driven rules.
The KeygraphHQ/shannon repository implements a deterministic prompt construction system designed for reproducible AI security testing. Understanding how Shannon's prompt manager handles variable substitution for context is essential for customizing agent behaviors and ensuring consistent prompt generation across distributed testing scenarios.
The Three-Step Substitution Pipeline
The core engine in src/prompts/prompt-manager.ts executes variable substitution through discrete, composable stages. Each stage operates as a pure async function, guaranteeing that identical inputs always produce identical prompt outputs.
Step 1: Loading the Raw Template
The loadPrompt function initiates the pipeline by resolving the appropriate template file and reading its raw contents. The manager selects between regular production templates and pipeline-testing variants based on the agent identifier provided.
// From src/prompts/prompt-manager.ts#L21-L28
async function loadPrompt(agentId: string, variables: PromptVariables, config?: DistributedConfig) {
const templatePath = resolveTemplatePath(agentId);
let template = await fs.readFile(templatePath, 'utf-8');
// Pipeline continues...
}
Step 2: Processing @include Directives
Before any variable substitution occurs, the processIncludes function scans the template for @include(<path>) directives. This mechanism enables modular prompt composition by injecting reusable snippet files from the prompts/ directory into the main template.
Source: src/prompts/prompt-manager.ts#L6-L24
This step ensures that shared context blocks—such as common security testing guidelines—are maintained in single locations while being dynamically composed into agent-specific prompts.
Step 3: Interpolating Variables
The final stage, interpolateVariables, performs the actual substitution of placeholders with runtime values. This function handles both static built-in variables and dynamic configuration-driven content injection.
Source: src/prompts/prompt-manager.ts#L27-L55
Variable Substitution Mechanics in Detail
The interpolation engine distinguishes between three categories of variable substitution, each handled through specific logic paths in src/prompts/prompt-manager.ts.
Built-in Placeholders and MCP Server Assignment
The manager first constructs an enhancedVariables object that merges caller-supplied values with automatically derived context. Using the MCP_AGENT_MAPPING constant defined in src/constants.ts, the system assigns the appropriate MCP server endpoint for the specific agent.
Source: src/prompts/prompt-manager.ts#L38-L45
The three core placeholders are then replaced via string scanning:
{{WEB_URL}}— The target application URL for testing{{REPO_PATH}}— Local filesystem path to the repository under test{{MCP_SERVER}}— The resolved MCP server endpoint for agent communication
Source: src/prompts/prompt-manager.ts#L52-L55
Config-Driven Rule Injection
When a DistributedConfig object is provided via src/types/config.ts, the substitution engine injects dynamic testing rules into the prompt context.
The manager generates a rules block containing:
- Avoid rules: Security testing constraints that must not be violated
- Focus rules: Specific vulnerability classes or endpoints to prioritize
If no rules are defined, the engine inserts a clean "no rules" message to maintain template structure.
Source: src/prompts/prompt-manager.ts#L57-L73
Authentication and Login Instructions
For distributed testing scenarios requiring authenticated sessions, the manager constructs login instructions from the config.authentication structure. The buildLoginInstructions helper transforms the authentication flow into step-by-step textual guidance inserted at the {{LOGIN_INSTRUCTIONS}} placeholder.
Source: src/prompts/prompt-manager.ts#L75-L80
When no configuration is provided, the system inserts default empty rule blocks and clears the login instructions placeholder to prevent template pollution.
Source: src/prompts/prompt-manager.ts#L81-L87
Validation and Error Handling
After substitution completes, the engine performs a validation scan to detect any remaining {{...}} tokens. Unresolved placeholders trigger warnings through the custom PentestError class defined in src/error-handling.ts, enabling developers to identify missing context data before prompt execution.
Source: src/prompts/prompt-manager.ts#L89-L93
Practical Implementation Example
The following example demonstrates how to invoke the prompt manager with both mandatory variables and optional distributed configuration:
import { loadPrompt } from './prompts/prompt-manager.js';
import type { DistributedConfig } from './types/config.js';
// Minimal variables required by every prompt
const vars = {
webUrl: 'https://app.example.com',
repoPath: '/path/to/repo',
};
// Optional distributed testing configuration
const config: DistributedConfig = {
avoid: [{ description: 'Do not enumerate admin endpoints' }],
focus: [{ description: 'Check for XSS in comment fields' }],
authentication: {
login_type: 'form',
login_flow: ['Enter $username', 'Enter $password', 'Submit'],
credentials: { username: 'alice', password: 's3cr3t' },
},
};
async function build() {
// Load a normal prompt (e.g., the "vuln-xss" agent)
const prompt = await loadPrompt('vuln-xss', vars, config);
console.log(prompt);
}
build();
This implementation:
- Supplies the mandatory
WEB_URLandREPO_PATHvalues - Provides a
DistributedConfigcontaining rule lists and authentication flow - Invokes
loadPrompt, which automatically selects the MCP server for thevuln-xssagent usingMCP_AGENT_MAPPING - Receives a fully-rendered prompt with rule sections, login instructions, and all placeholders resolved
Summary
Shannon's prompt manager handles variable substitution for context through a rigorous three-stage pipeline that ensures deterministic, reproducible prompt generation:
- Template Loading:
loadPromptresolves and reads the correct template file from theprompts/directory - Modular Composition:
processIncludesexpands@include()directives to assemble reusable context blocks - Variable Interpolation:
interpolateVariablesreplaces built-in placeholders ({{WEB_URL}},{{REPO_PATH}},{{MCP_SERVER}}) and injects dynamic content fromDistributedConfigobjects, including testing rules and authentication instructions
The system validates all substitutions to detect unresolved placeholders, preventing incomplete prompts from reaching AI agents.
Frequently Asked Questions
What placeholders does Shannon's prompt manager support by default?
Shannon recognizes three built-in placeholders: {{WEB_URL}} for the target application URL, {{REPO_PATH}} for the local repository filesystem path, and {{MCP_SERVER}} for the Model Context Protocol server endpoint. The manager automatically resolves the MCP server value by looking up the agent identifier in the MCP_AGENT_MAPPING constant defined in src/constants.ts.
How does the @include directive work in Shannon templates?
The @include(<path>) directive enables modular prompt composition by injecting external snippet files into the main template before variable substitution occurs. The processIncludes function scans the raw template for these directives, resolves the relative paths against the prompts/ directory, and recursively inserts the referenced content. This mechanism allows security testing guidelines and common instructions to be maintained in single locations while being reused across multiple agent-specific prompts.
Can I use Shannon's prompt manager without a DistributedConfig?
Yes, the loadPrompt function treats the configuration parameter as optional. When invoked without a DistributedConfig, the interpolation engine inserts default empty rule blocks and clears the {{LOGIN_INSTRUCTIONS}} placeholder. The manager still performs all built-in placeholder substitutions ({{WEB_URL}}, {{REPO_PATH}}, {{MCP_SERVER}}) and validates the final output for unresolved tokens, ensuring that basic prompt generation functions correctly without distributed testing context.
What happens if a placeholder remains unresolved after substitution?
After interpolateVariables completes, the prompt manager executes a validation scan to detect any remaining {{...}} tokens in the final string. If unresolved placeholders are found, the system emits a warning through the custom PentestError class defined in src/error-handling.ts. This early detection mechanism prevents incomplete or malformed prompts from being passed to AI agents, allowing developers to identify missing context data during the build phase rather than at runtime.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →