Render templates by replacing {{TOKEN}} placeholders with actual values, supporting all three templates (specification, plan, tasks) with schema validation and security sanitization
Template Path Validation (SEC-SPEC-002):
.claude/templates/Token Whitelist Validation (SEC-SPEC-003):
// Allowed tokens by template type
const SPEC_TOKENS = [
'FEATURE_NAME',
'VERSION',
'AUTHOR',
'DATE',
'STATUS',
'ACCEPTANCE_CRITERIA_1',
'ACCEPTANCE_CRITERIA_2',
'ACCEPTANCE_CRITERIA_3',
'TERM_1',
'TERM_2',
'TERM_3',
'HTTP_METHOD',
'ENDPOINT_PATH',
'PROJECT_NAME',
];
const PLAN_TOKENS = [
'PLAN_TITLE',
'DATE',
'FRAMEWORK_VERSION',
'STATUS',
'EXECUTIVE_SUMMARY',
'TOTAL_TASKS',
'FEATURES_COUNT',
'ESTIMATED_TIME',
'STRATEGY',
'KEY_DELIVERABLES_LIST',
'PHASE_N_NAME',
'PHASE_N_PURPOSE',
'PHASE_N_DURATION',
'DEPENDENCIES',
'PARALLEL_OK',
'VERIFICATION_COMMANDS',
];
const TASKS_TOKENS = [
'FEATURE_NAME',
'VERSION',
'AUTHOR',
'DATE',
'STATUS',
'PRIORITY',
'ESTIMATED_EFFORT',
'RELATED_SPECS',
'DEPENDENCIES',
'FEATURE_DISPLAY_NAME',
'FEATURE_DESCRIPTION',
'BUSINESS_VALUE',
'USER_IMPACT',
'EPIC_NAME',
'EPIC_GOAL',
'SUCCESS_CRITERIA',
];
Token Value Sanitization (SEC-SPEC-004):
function sanitizeTokenValue(value) {
return String(value)
.replace(/[<>]/g, '') // Prevent HTML injection
.replace(/\$\{/g, '') // Prevent template literal injection
.replace(/\{\{/g, '') // Prevent nested token injection
.trim();
}
Read the template file using Read or mcpfilesystemread_text_file:
.claude/templates/specification-template.md (46 tokens).claude/templates/plan-template.md (30+ tokens).claude/templates/tasks-template.md (20+ tokens)Replace all {{TOKEN}} placeholders with sanitized values:
function renderTemplate(templateContent, tokenMap) {
let rendered = templateContent;
// Replace each token
for (const [token, value] of Object.entries(tokenMap)) {
// Validate token is in whitelist
if (!isAllowedToken(token, templateType)) {
throw new Error(`Token not in whitelist: ${token}`);
}
// Sanitize value
const sanitizedValue = sanitizeTokenValue(value);
// Replace all occurrences
const regex = new RegExp(`\\{\\{${token}\\}\\}`, 'g');
rendered = rendered.replace(regex, sanitizedValue);
}
// Check for missing required tokens
const missingTokens = rendered.match(/\{\{[A-Z_0-9]+\}\}/g);
if (missingTokens) {
throw new Error(`Missing required tokens: ${missingTokens.join(', ')}`);
}
return rendered;
}
For specification templates, validate the rendered output against JSON Schema:
// Extract YAML frontmatter
const yamlMatch = rendered.match(/^---\n([\s\S]*?)\n---/);
if (!yamlMatch) {
throw new Error('No YAML frontmatter found');
}
// Parse YAML
const yaml = require('js-yaml');
const frontmatter = yaml.load(yamlMatch[1]);
// Validate against schema
const schema = JSON.parse(
fs.readFileSync('.claude/schemas/specification-template.schema.json', 'utf8')
);
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);
if (!validate(frontmatter)) {
throw new Error(`Schema validation failed: ${JSON.stringify(validate.errors)}`);
}
Write the rendered template to the output path using Write or mcpfilesystemwrite_file:
Run post-rendering checks:
# Check no unresolved tokens remain
grep "{{" <output-file> && echo "ERROR: Unresolved tokens found!" || echo "✓ All tokens resolved"
# For specifications: Validate YAML frontmatter
head -50 <output-file> | grep -E "^---$" | wc -l # Should output: 2
# For specifications: Validate against schema (if ajv installed)
# ajv validate -s .claude/schemas/specification-template.schema.json -d <output-file>
</execution_process>
<best_practices>
</best_practices>
<error_handling>
Missing Required Tokens:
ERROR: Missing required tokens in template:
- {{FEATURE_NAME}}
- {{ACCEPTANCE_CRITERIA_1}}
Provide these tokens in the token map.
Invalid Token (Not in Whitelist):
ERROR: Token not in whitelist: INVALID_TOKEN
Allowed tokens for specification-template: FEATURE_NAME, VERSION, AUTHOR, DATE, ...
Template Path Traversal:
ERROR: Template path outside PROJECT_ROOT
Path: ../../etc/passwd
Only templates from .claude/templates/ are allowed.
Schema Validation Failure (Specification Templates):
ERROR: Schema validation failed:
- /version: must match pattern "^\d+\.\d+\.\d+$"
- /acceptance_criteria: must have at least 1 item
Unused Tokens Warning:
WARNING: Unused tokens provided:
- EXTRA_TOKEN_1
- EXTRA_TOKEN_2
These tokens are not in the template. Check for typos.
</error_handling> </instructions>
<examples> <usage_example> **Example 1: Render Specification Template**// From another skill (e.g., spec-gathering)
Skill({
skill: 'template-renderer',
args: {
templateName: 'specification-template',
outputPath: '.claude/context/artifacts/specifications/my-feature-spec.md',
tokens: {
FEATURE_NAME: 'User Authentication',
VERSION: '1.0.0',
AUTHOR: 'Claude',
DATE: '2026-01-28',
STATUS: 'draft',
ACCEPTANCE_CRITERIA_1: 'User can log in with email and password',
ACCEPTANCE_CRITERIA_2: 'Password meets complexity requirements',
ACCEPTANCE_CRITERIA_3: 'Failed login attempts are logged',
},
},
});
Example 2: Render Plan Template
Skill({
skill: 'template-renderer',
args: {
templateName: 'plan-template',
outputPath: '.claude/context/plans/my-feature-plan.md',
tokens: {
PLAN_TITLE: 'User Authentication Implementation Plan',
DATE: '2026-01-28',
FRAMEWORK_VERSION: 'Agent-Studio v3.1.0',
STATUS: 'Phase 0 - Research',
EXECUTIVE_SUMMARY: 'Implementation plan for JWT-based authentication...',
TOTAL_TASKS: '14 atomic tasks',
ESTIMATED_TIME: '2-3 weeks',
STRATEGY: 'Foundation-first (schema) → Core features',
},
},
});
Example 3: Render Tasks Template
Skill({
skill: 'template-renderer',
args: {
templateName: 'tasks-template',
outputPath: '.claude/context/artifacts/tasks/auth-tasks.md',
tokens: {
FEATURE_NAME: 'user-authentication',
VERSION: '1.0.0',
AUTHOR: 'Engineering Team',
DATE: '2026-01-28',
FEATURE_DISPLAY_NAME: 'User Authentication',
FEATURE_DESCRIPTION: 'JWT-based authentication system',
BUSINESS_VALUE: 'Enables user account management',
USER_IMPACT: 'Users can securely access personalized features',
},
},
});
Example 4: CLI Usage
# Using CLI wrapper (after implementation in main.cjs)
node .claude/skills/template-renderer/scripts/main.cjs \
--template specification-template \
--output ./my-spec.md \
--tokens '{"FEATURE_NAME":"My Feature","VERSION":"1.0.0","AUTHOR":"Claude","DATE":"2026-01-28"}'
# Or with JSON file
node .claude/skills/template-renderer/scripts/main.cjs \
--template plan-template \
--output ./my-plan.md \
--tokens-file ./tokens.json
Example 5: Integration with spec-gathering
// In spec-gathering skill (Task #16):
// After collecting requirements via progressive disclosure...
const tokens = {
FEATURE_NAME: gatheredRequirements.featureName,
VERSION: '1.0.0',
AUTHOR: 'Claude',
DATE: new Date().toISOString().split('T')[0],
ACCEPTANCE_CRITERIA_1: gatheredRequirements.criteria[0],
ACCEPTANCE_CRITERIA_2: gatheredRequirements.criteria[1],
ACCEPTANCE_CRITERIA_3: gatheredRequirements.criteria[2],
// ... more tokens
};
Skill({
skill: 'template-renderer',
args: {
templateName: 'specification-template',
outputPath: `.claude/context/artifacts/specifications/${featureName}-spec.md`,
tokens: tokens,
},
});
</usage_example> </examples>
Before starting:
cat .claude/context/memory/learnings.md
After completing:
.claude/context/memory/learnings.md.claude/context/memory/issues.md.claude/context/memory/decisions.mdASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer