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:

  1. Supplies the mandatory WEB_URL and REPO_PATH values
  2. Provides a DistributedConfig containing rule lists and authentication flow
  3. Invokes loadPrompt, which automatically selects the MCP server for the vuln-xss agent using MCP_AGENT_MAPPING
  4. 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: loadPrompt resolves and reads the correct template file from the prompts/ directory
  • Modular Composition: processIncludes expands @include() directives to assemble reusable context blocks
  • Variable Interpolation: interpolateVariables replaces built-in placeholders ({{WEB_URL}}, {{REPO_PATH}}, {{MCP_SERVER}}) and injects dynamic content from DistributedConfig objects, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →