Use this skill when controlling agent behavior in TypeScript Agent SDK platforms — restricting allowed tools, setting budget and turn limits, crafting system prompts that guide agents to specific behaviors, implementing the file pre-creation pattern, augmenting prompts with runtime context, building message loggers, or tracking agent run metadata (cost, duration, iterations).
Manages TypeScript Agent SDK behavior through tool restrictions, turn limits, system prompts, and runtime hooks.
npx claudepluginhub itamarzand88/claude-code-agentic-engineeringThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/complete-runner.tsreferences/hook-strategy.mdreferences/velocity-control.mdControl agents through five primary mechanisms:
allowedTools — what tools the agent can callpermissionMode — whether to bypass permission promptsmaxTurns — hard iteration capmaxBudgetUsd — hard spend caphooks — programmatic allow/deny on every tool callGive the agent only the tools it needs. Fewer tools = more predictable behavior:
// For a code-writing agent: read and edit only
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep"]
// For a test runner: also needs Bash
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"]
// For a read-only analysis agent
allowedTools: ["Read", "Glob", "Grep"]
Pre-create the output file before running the agent. Instruct the agent to use Edit instead of Write:
// 1. Scaffold the file before SDK run
writeFileSync(testFilePath, "// Scaffolded by platform\n", "utf8");
// 2. Augment the prompt with file path instruction
function buildPromptWithFilePath(userPrompt: string, testFilePath: string): string {
return [
userPrompt,
"",
"---",
`IMPORTANT: A file has been pre-created at: ${testFilePath}`,
"Use the Edit tool to modify this file. Do NOT create a new file with Write.",
].join("\n");
}
// 3. Read the file back after SDK completes
const code = readFileSync(testFilePath, "utf8");
Why: Combining createFileRestrictionHook(testFilePath) with pre-creation gives you full control over what the agent writes and where.
Never hardcode SDK config inline. Use a dedicated *.const.ts:
// agent-sdk.const.ts
export const SDK_MODEL = "claude-sonnet-4-5-20250929" as const;
export const SDK_MAX_BUDGET_USD = 2; // $2 hard cap
export const SDK_MAX_TURNS = 50; // 50 iterations max
export const SDK_PERMISSION_MODE = "bypassPermissions" as const;
export const SDK_ALLOWED_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep"] as const;
// Per-tool timeouts (used in hooks)
export const ESLINT_TIMEOUT_MS = 30_000;
export const TSC_TIMEOUT_MS = 60_000;
export const LINT_OUTPUT_TRUNCATION_LIMIT = 2_000;
export const TSC_OUTPUT_TRUNCATION_LIMIT = 3_000;
System prompts control agent strategy. Structure them with explicit phases:
const systemPrompt = `
You are a test generation agent. Follow these phases strictly:
## Phase 1: Analyze
Read the source file. Identify all exported functions/classes.
## Phase 2: Generate
Write comprehensive tests to the pre-created test file using Edit.
Cover: happy path, edge cases, error cases.
## Phase 3: Verify
Run the tests with Bash. Do NOT run more than 3 times.
## Constraints
- ONLY edit the pre-created test file
- Do NOT install packages
- Do NOT modify source files
`.trim();
Build the hook list dynamically — some hooks may not be available:
const fileRestrictionHook = createFileRestrictionHook(params.testFilePath);
const lintFixHook = createLintFixHook(params.workingDirectory, params.testFilePath);
const typecheckHook = createTypecheckHook(params.workingDirectory, params.testFilePath);
const postEditHooks = [
...(lintFixHook ? [lintFixHook] : []),
...(typecheckHook ? [typecheckHook] : []),
];
const hooks = {
PreToolUse: [
{ matcher: "Write|Edit", hooks: [fileRestrictionHook] },
{ matcher: "Read", hooks: [envProtectionHook] },
],
PostToolUse: [
{ matcher: "Bash", hooks: [testPruneHook] },
...(postEditHooks.length > 0 ? [{ matcher: "Write|Edit", hooks: postEditHooks }] : []),
],
};
Always track and persist run metadata for observability:
if (isResultMessage(message)) {
const metadata = {
durationMs: message.duration_ms,
costUsd: message.total_cost_usd,
numTurns: message.num_turns,
modelUsage: message.modelUsage,
subtype: message.subtype, // "success" | "error_max_turns" | "error_during_execution"
};
logger.info("SDK run complete", metadata);
// Prepend to output file as a JSDoc comment
const comment = [
"/**",
` * @generated Agent SDK`,
` * @duration ${(message.duration_ms / 1000).toFixed(1)}s`,
` * @cost $${message.total_cost_usd.toFixed(4)}`,
" */",
"",
].join("\n");
const existing = readFileSync(outputPath, "utf8");
writeFileSync(outputPath, comment + existing, "utf8");
}
Forward cancellation from external sources to the SDK:
async run(params: { abortSignal?: AbortSignal }): Promise<Result> {
const abortController = new AbortController();
if (params.abortSignal) {
params.abortSignal.addEventListener("abort", () => abortController.abort());
}
try {
for await (const message of query({ ..., options: { ..., abortController } })) {
if (abortController.signal.aborted) break;
// ...
}
} finally {
// cleanup if needed
}
}
Use an env-controlled verbose flag to switch between minimal and debug logging:
export const isSdkVerboseLogging = process.env.SDK_VERBOSE === "true";
// In your MessageLogger
logMessage(message: SDKMessage): void {
if (!this.verbose) return; // skip in production
console.log("[SDK]", message.type, ...);
}
For more patterns from these references:
references/hook-strategy.md — hook strategy matrix, minimum safe set, graduated levels (security→control→quality→guidance), composition best practices, anti-patterns to avoidreferences/velocity-control.md — velocity governor (rate-limit tool calls per minute), output validator (heuristic placeholder detection), maxTurns as circuit breaker, budget guard hook, auto-checkpoint in finally blockExample:
examples/complete-runner.ts — production-ready runner with Inversify DI, conditional hook composition, null-filtering, augmented prompt, full message iteration, metadata prependActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.