Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
13530b1747 docs: Add skills frontmatter field documentation
Document the `skills` frontmatter field added in v2.0.43 for declaring
skills to auto-load for subagents and commands. This helps ensure skills
persist across context compaction by explicitly declaring required skills.

- Add skills field to agent-development SKILL.md frontmatter fields section
- Add skills field to command-development frontmatter-reference.md
- Update examples, validation checklists, and best practices
2025-12-11 10:39:03 +00:00
4 changed files with 80 additions and 223 deletions

View File

@@ -1,221 +0,0 @@
# ANT_ONLY_METADATA_USER_ID Implementation Specification
## Overview
This document specifies the implementation of the `ANT_ONLY_METADATA_USER_ID` environment variable feature for Claude Code. This feature allows internal Anthropic jobs (like Claude Oracle, Oncall, Ambient, etc.) to pass custom metadata through Claude Code sessions to the Anthropic API for tracking, rate limiting, and incident triage purposes.
## Background
From the Slack discussion:
- Internal API jobs launched via Claude Code currently lack user metadata showing `job_id` or `username` in `api_usage`
- ~85% of requests from heavy CC users (like Closing The Loop org) have no user_metadata or job identifier
- This metadata is critical for:
- Managing Internal API rate limits
- Triaging incidents
- Setting up alerting that pings the DRI researchers
- Understanding which jobs are driving token usage
## Requirements
1. **Environment Variable**: Claude Code must read an environment variable called `ANT_ONLY_METADATA_USER_ID`
2. **API Integration**: The value must be passed to all Anthropic Messages API calls in the `metadata.user_id` field
3. **Tree Shaking**: Only Anthropic internal builds should be able to use this feature
4. **Format**: The expected format is a JSON object: `{"user": "<anthropic_username>", "job": "<job_name>"}`
- Example: `{"user": "tedm", "job": "tedm-1208-mu-g-18x"}`
- Or: `{"user": "vinke", "job": "claude-oracle:fluorine:vinke"}`
## Implementation
### 1. Environment Variable Reading
In the appropriate configuration/env module (likely `src/config/env.ts` or similar):
```typescript
// Environment variable for Anthropic internal builds only
// This is used to pass job metadata to the API for usage tracking
export function getAntOnlyMetadataUserId(): string | undefined {
// Only available in Anthropic internal builds
if (!isAnthropicBuild()) {
return undefined;
}
return process.env.ANT_ONLY_METADATA_USER_ID;
}
```
### 2. API Request Metadata
When constructing API requests to the Anthropic Messages API, add the metadata:
```typescript
// In the API client module (e.g., src/api/anthropic.ts)
import { getAntOnlyMetadataUserId } from '../config/env';
function buildApiRequestMetadata(): Record<string, string> | undefined {
const metadataUserId = getAntOnlyMetadataUserId();
if (!metadataUserId) {
return undefined;
}
return {
user_id: metadataUserId
};
}
// When creating a message:
const response = await client.messages.create({
model: selectedModel,
max_tokens: maxTokens,
messages: conversationMessages,
// ... other parameters
metadata: buildApiRequestMetadata()
});
```
### 3. Tree Shaking for Internal Builds
The feature should be conditionally compiled out for public builds using build-time flags:
```typescript
// Using a build-time constant
declare const __ANT_INTERNAL_BUILD__: boolean;
export function isAnthropicBuild(): boolean {
// This will be replaced at build time
// For internal builds: true
// For public builds: false (and the code will be tree-shaken)
if (typeof __ANT_INTERNAL_BUILD__ !== 'undefined') {
return __ANT_INTERNAL_BUILD__;
}
return false;
}
```
In the build configuration (esbuild/webpack/rollup):
```javascript
// For public builds:
define: {
'__ANT_INTERNAL_BUILD__': 'false'
}
// For internal builds:
define: {
'__ANT_INTERNAL_BUILD__': 'true'
}
```
### 4. Validation (Optional but Recommended)
```typescript
function validateMetadataUserId(value: string): boolean {
try {
const parsed = JSON.parse(value);
return typeof parsed.user === 'string' && typeof parsed.job === 'string';
} catch {
// Also allow simple string format for backward compatibility
return typeof value === 'string' && value.length > 0;
}
}
export function getValidatedAntOnlyMetadataUserId(): string | undefined {
const value = getAntOnlyMetadataUserId();
if (value && !validateMetadataUserId(value)) {
console.warn('ANT_ONLY_METADATA_USER_ID has invalid format. Expected JSON: {"user": "...", "job": "..."}');
return undefined;
}
return value;
}
```
## Usage by Internal Services
Services like Claude Oracle should set this environment variable before launching Claude Code sessions:
```python
# In Claude Oracle / coo job code
import os
import json
# Set the metadata before spawning Claude Code
os.environ['ANT_ONLY_METADATA_USER_ID'] = json.dumps({
"user": username, # Anthropic username
"job": job_name # e.g., "claude-oracle:fluorine:vinke"
})
# Then launch Claude Code session...
```
Or via command line:
```bash
ANT_ONLY_METADATA_USER_ID='{"user":"tedm","job":"tedm-1208-mu-g-18x"}' claude --headless ...
```
## Data Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ COO Job / Oracle / Oncall / etc. │
│ Sets: ANT_ONLY_METADATA_USER_ID='{"user":"..","job":".."}' │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code CLI │
│ Reads: process.env.ANT_ONLY_METADATA_USER_ID │
│ Adds to API calls: metadata: { user_id: "..." } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Anthropic API │
│ Receives: metadata.user_id in request │
│ Stores in: api_usage.metadata_user_id (BigQuery) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ BigQuery: anthropic.api_production.api_usage │
│ Fields populated: │
│ - metadata_user_id │
│ - device_id (if included) │
│ - claude_code_session_id (if included) │
└─────────────────────────────────────────────────────────────────┘
```
## Testing
1. **Unit Tests**:
- Test that `getAntOnlyMetadataUserId()` returns undefined when env var is not set
- Test that it returns the value when set (in internal builds)
- Test validation of JSON format
2. **Integration Tests**:
- Verify metadata is included in API requests
- Verify tree-shaking removes the code in public builds
3. **E2E Validation** (after deployment):
- Run a Claude Code session with the env var set
- Query BigQuery `anthropic.api_production.api_usage` to verify `metadata_user_id` is populated
## Security Considerations
1. **Internal Only**: The tree-shaking ensures this feature is not available in public builds
2. **No Secrets**: The metadata should not contain sensitive information
3. **Validation**: Input validation prevents injection attacks
## Related Files to Modify (Internal Repo)
Based on patterns in the bundled CLI, likely files to modify:
1. `src/config/env.ts` or similar - Add env var reading
2. `src/api/anthropic.ts` or similar - Add metadata to API requests
3. `build.config.ts` or similar - Add build-time flag for internal builds
4. Tests for the above
## References
- Slack thread: Internal API metadata discussion
- BigQuery tables: `anthropic.api_production.api_usage`, `api_events`
- Anthropic API docs: [Messages API metadata parameter](https://docs.anthropic.com/en/api/messages)

View File

@@ -23,7 +23,6 @@
- VSCode: Added copy-to-clipboard button on code blocks and bash tool inputs
- VSCode: Fixed extension not working on Windows ARM64 by falling back to x64 binary via emulation
- Bedrock: Improve efficiency of token counting
- Bedrock: Add support for `aws login` AWS Management Console credentials
- Unshipped AgentOutputTool and BashOutputTool, in favor of a new unified TaskOutputTool
## 2.0.62

View File

@@ -42,6 +42,7 @@ assistant: "[How assistant should respond and use this agent]"
model: inherit
color: blue
tools: ["Read", "Write", "Grep"]
skills: skill-name-1, skill-name-2
---
You are [agent role description]...
@@ -159,6 +160,35 @@ tools: ["Read", "Write", "Grep", "Bash"]
- Testing: `["Read", "Bash", "Grep"]`
- Full access: Omit field or use `["*"]`
### skills (optional)
Declare skills to auto-load when this agent is invoked.
**Format:** Comma-separated list of skill names
```yaml
skills: database-migration, postgres-ops
```
**Purpose:** Ensures specific skills are always available to the agent, regardless of context matching. This is especially useful for:
- Skills that should persist across context compaction
- Specialized agents that always need certain domain knowledge
- Ensuring consistent agent behavior with required skills
**Example:**
```yaml
---
name: db-admin
description: Use this agent for database administration tasks...
model: inherit
color: cyan
tools: ["Bash", "Read", "Write"]
skills: database-migration, sql-optimization
---
```
**Best practice:** Only declare skills essential to the agent's core function. Let context-triggered skills load naturally for optional capabilities.
## System Prompt Design
The markdown body becomes the agent's system prompt. Write in second person, addressing the agent directly.
@@ -355,6 +385,7 @@ Output: [What to provide]
| model | Yes | inherit/sonnet/opus/haiku | inherit |
| color | Yes | Color name | blue |
| tools | No | Array of tool names | ["Read", "Grep"] |
| skills | No | Comma-separated names | skill-a, skill-b |
### Best Practices

View File

@@ -12,6 +12,7 @@ description: Brief description
allowed-tools: Read, Write
model: sonnet
argument-hint: [arg1] [arg2]
skills: skill-a, skill-b
---
Command prompt content here...
@@ -321,6 +322,51 @@ disable-model-invocation: true
- Document why in command comments
- Consider if command should exist if always manual
### skills
**Type:** String (comma-separated list)
**Required:** No
**Default:** None
**Purpose:** Declare skills to auto-load when this command executes. Ensures specific skills are always available regardless of context matching.
**Examples:**
```yaml
skills: database-migration, sql-optimization
```
**When to use:**
1. **Commands requiring domain knowledge:** Ensure relevant skills load
```yaml
---
description: Run database migration
skills: database-migration, postgres-ops
---
```
2. **Skills that should persist across compaction:** Force-load skills that might otherwise be lost
```yaml
---
description: Complex multi-step workflow
skills: workflow-skill, validation-skill
---
```
3. **Specialized commands:** Commands that always need specific expertise
```yaml
---
description: Generate API documentation
skills: api-documentation, openapi-spec
---
```
**Best practices:**
- Only declare skills essential to the command's function
- Use skill names exactly as defined in SKILL.md frontmatter
- Keep the list focused (2-4 skills max)
- Let context-triggered skills load naturally for optional capabilities
## Complete Examples
### Minimal Command
@@ -451,6 +497,7 @@ Before committing command:
- [ ] model is valid value if specified
- [ ] argument-hint matches positional arguments
- [ ] disable-model-invocation used appropriately
- [ ] skills references valid skill names if specified
## Best Practices Summary
@@ -460,4 +507,5 @@ Before committing command:
4. **Choose right model:** Use haiku for speed, opus for complexity
5. **Manual-only sparingly:** Only use disable-model-invocation when necessary
6. **Clear descriptions:** Make commands discoverable in `/help`
7. **Test thoroughly:** Verify frontmatter works as expected
7. **Declare essential skills:** Use skills field for domain knowledge that must persist
8. **Test thoroughly:** Verify frontmatter works as expected