template-renderer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Template Renderer

模板渲染器

<identity> Template Renderer Skill - Renders templates by replacing {{TOKEN}} placeholders with actual values. Supports specification-template.md, plan-template.md, and tasks-template.md with schema validation and security controls (SEC-SPEC-003, SEC-SPEC-004). </identity> <capabilities> - Render all three template types (specification, plan, tasks) - Token replacement with {{TOKEN}} → value substitution - Security: Token value sanitization (prevent injection) - Security: Token whitelist enforcement (only predefined tokens allowed) - Security: Template path validation (PROJECT_ROOT only) - Schema validation for specification templates - Error handling for missing required tokens - Warning system for unused tokens - Preserve Markdown formatting and structure </capabilities> <instructions> <execution_process>
<identity> Template Renderer Skill - 通过将{{TOKEN}}占位符替换为实际值来渲染模板。支持specification-template.md、plan-template.md和tasks-template.md,并提供Schema验证和安全控制(SEC-SPEC-003、SEC-SPEC-004)。 </identity> <capabilities> - 渲染所有三种模板类型(规范、计划、任务) - 令牌替换:将{{TOKEN}}替换为对应值 - 安全:令牌值清理(防止注入攻击) - 安全:令牌白名单强制(仅允许预定义令牌) - 安全:模板路径验证(仅允许PROJECT_ROOT范围内) - 规范模板的Schema验证 - 缺失必填令牌的错误处理 - 未使用令牌的警告系统 - 保留Markdown格式和结构 </capabilities> <instructions> <execution_process>

Step 1: Validate Inputs (SECURITY - MANDATORY)

步骤1:验证输入(安全 - 强制要求)

Template Path Validation (SEC-SPEC-002):
  • Verify template file exists within PROJECT_ROOT
  • Reject any path traversal attempts (../)
  • Only allow templates from
    .claude/templates/
Token Whitelist Validation (SEC-SPEC-003):
javascript
// 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):
javascript
function sanitizeTokenValue(value) {
  return String(value)
    .replace(/[<>]/g, '') // Prevent HTML injection
    .replace(/\$\{/g, '') // Prevent template literal injection
    .replace(/\{\{/g, '') // Prevent nested token injection
    .trim();
}
模板路径验证(SEC-SPEC-002):
  • 验证模板文件存在于PROJECT_ROOT范围内
  • 拒绝任何路径遍历尝试(../)
  • 仅允许来自
    .claude/templates/
    的模板
令牌白名单验证(SEC-SPEC-003):
javascript
// 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',
];
令牌值清理(SEC-SPEC-004):
javascript
function sanitizeTokenValue(value) {
  return String(value)
    .replace(/[<>]/g, '') // Prevent HTML injection
    .replace(/\$\{/g, '') // Prevent template literal injection
    .replace(/\{\{/g, '') // Prevent nested token injection
    .trim();
}

Step 2: Read Template

步骤2:读取模板

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)
使用Read或mcpfilesystemread_text_file读取模板文件:
  • .claude/templates/specification-template.md
    (46个令牌)
  • .claude/templates/plan-template.md
    (30+个令牌)
  • .claude/templates/tasks-template.md
    (20+个令牌)

Step 3: Token Replacement

步骤3:令牌替换

Replace all {{TOKEN}} placeholders with sanitized values:
javascript
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;
}
将所有{{TOKEN}}占位符替换为清理后的值:
javascript
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;
}

Step 4: Schema Validation (Specification Templates Only)

步骤4:Schema验证(仅规范模板)

For specification templates, validate the rendered output against JSON Schema:
javascript
// 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)}`);
}
对于规范模板,验证渲染后的输出是否符合JSON Schema:
javascript
// 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)}`);
}

Step 5: Write Output

步骤5:写入输出

Write the rendered template to the output path using Write or mcpfilesystemwrite_file:
  • Verify output path is within PROJECT_ROOT
  • Create parent directories if needed
  • Write file with UTF-8 encoding
使用Write或mcpfilesystemwrite_file将渲染后的模板写入输出路径:
  • 验证输出路径在PROJECT_ROOT范围内
  • 必要时创建父目录
  • 使用UTF-8编码写入文件

Step 6: Verification

步骤6:验证

Run post-rendering checks:
bash
undefined
运行渲染后检查:
bash
undefined

Check no unresolved tokens remain

Check no unresolved tokens remain

grep "{{" <output-file> && echo "ERROR: Unresolved tokens found!" || echo "✓ All tokens resolved"
grep "{{" <output-file> && echo "ERROR: Unresolved tokens found!" || echo "✓ All tokens resolved"

For specifications: Validate YAML frontmatter

For specifications: Validate YAML frontmatter

head -50 <output-file> | grep -E "^---$" | wc -l # Should output: 2
head -50 <output-file> | grep -E "^---$" | wc -l # Should output: 2

For specifications: Validate against schema (if ajv installed)

For specifications: Validate against schema (if ajv installed)

ajv validate -s .claude/schemas/specification-template.schema.json -d <output-file>

ajv validate -s .claude/schemas/specification-template.schema.json -d <output-file>


</execution_process>

<best_practices>

1. **Always validate template paths**: Use PROJECT_ROOT validation before reading
2. **Sanitize all token values**: Prevent injection attacks (SEC-SPEC-004)
3. **Enforce token whitelist**: Only allow predefined tokens (SEC-SPEC-003)
4. **Error on missing tokens**: Don't silently ignore missing required tokens
5. **Warn on unused tokens**: Help users catch typos in token names
6. **Preserve Markdown formatting**: Don't alter indentation, bullets, code blocks
7. **Validate schema for specs**: Run JSON Schema validation for specification templates
8. **Log all operations**: Record template, tokens used, output path to memory

</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**

```javascript
// 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
javascript
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 v2.2.1',
      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
javascript
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
bash
undefined

</execution_process>

<best_practices>

1. **始终验证模板路径**:读取前使用PROJECT_ROOT验证
2. **清理所有令牌值**:防止注入攻击(SEC-SPEC-004)
3. **强制令牌白名单**:仅允许预定义令牌(SEC-SPEC-003)
4. **缺失令牌时抛出错误**:不要静默忽略缺失的必填令牌
5. **未使用令牌时发出警告**:帮助用户发现令牌名称的拼写错误
6. **保留Markdown格式**:不要修改缩进、项目符号、代码块
7. **验证规范模板的Schema**:对规范模板运行JSON Schema验证
8. **记录所有操作**:将模板、使用的令牌、输出路径记录到内存

</best_practices>

<error_handling>

**缺失必填令牌**:
ERROR: Missing required tokens in template:
  • {{FEATURE_NAME}}
  • {{ACCEPTANCE_CRITERIA_1}}
Provide these tokens in the token map.

**无效令牌(不在白名单中)**:
ERROR: Token not in whitelist: INVALID_TOKEN Allowed tokens for specification-template: FEATURE_NAME, VERSION, AUTHOR, DATE, ...

**模板路径遍历**:
ERROR: Template path outside PROJECT_ROOT Path: ../../etc/passwd Only templates from .claude/templates/ are allowed.

**Schema验证失败**(规范模板):
ERROR: Schema validation failed:
  • /version: must match pattern "^\d+.\d+.\d+$"
  • /acceptance_criteria: must have at least 1 item

**未使用令牌警告**:
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>
**示例1:渲染规范模板**

```javascript
// 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',
    },
  },
});
示例2:渲染计划模板
javascript
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 v2.2.1',
      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',
    },
  },
});
示例3:渲染任务模板
javascript
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',
    },
  },
});
示例4:CLI使用
bash
undefined

Using CLI wrapper (after implementation in main.cjs)

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"}'
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

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**

```javascript
// 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>
node .claude/skills/template-renderer/scripts/main.cjs
--template plan-template
--output ./my-plan.md
--tokens-file ./tokens.json

**示例5:与spec-gathering集成**

```javascript
// 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>

Memory Protocol (MANDATORY)

内存协议(强制要求)

Before starting:
bash
cat .claude/context/memory/learnings.md
After completing:
  • New pattern ->
    .claude/context/memory/learnings.md
  • Issue found ->
    .claude/context/memory/issues.md
  • Decision made ->
    .claude/context/memory/decisions.md
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
开始前:
bash
cat .claude/context/memory/learnings.md
完成后:
  • 新模式 →
    .claude/context/memory/learnings.md
  • 发现问题 →
    .claude/context/memory/issues.md
  • 做出决策 →
    .claude/context/memory/decisions.md
假设会被中断:你的上下文可能会重置。如果没有记录到内存中,就视为未发生。