Last active
February 4, 2026 13:57
-
-
Save Nottlespike/bd7cc2234ce43bbc284cf96d49c6c207 to your computer and use it in GitHub Desktop.
update_agents.sh: regenerate AGENTS.md with Claude (Opus 4.5 default)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # update_agents.sh | |
| # Regenerates agents.md by sending (existing agents.md + new info) to Claude using a fixed system prompt. | |
| # | |
| # Requirements: | |
| # - bash, curl, jq | |
| # - ANTHROPIC_API_KEY environment variable set | |
| # | |
| # Defaults: | |
| # - MODEL=claude-opus-4-5-20251101 | |
| # - MAX_TOKENS=4096 | |
| # - Writes: | |
| # * agents.md (extracted XML code block if present, else full output) | |
| # * .agents.last.full.txt (full raw text response) | |
| # | |
| # Usage: | |
| # ./update_agents.sh | |
| # echo "new constraints here" | ./update_agents.sh | |
| # MODEL=claude-opus-4-5-20251101 MAX_TOKENS=8192 ./update_agents.sh | |
| # ./update_agents.sh --keep-full # keep full output in agents.md (do not extract XML block) | |
| KEEP_FULL=0 | |
| if [[ "${1:-}" == "--keep-full" ]]; then | |
| KEEP_FULL=1 | |
| fi | |
| command -v curl >/dev/null 2>&1 || { echo "error: curl not found" >&2; exit 1; } | |
| command -v jq >/dev/null 2>&1 || { echo "error: jq not found" >&2; exit 1; } | |
| : "${ANTHROPIC_API_KEY:?error: set ANTHROPIC_API_KEY in your environment}" | |
| MODEL="${MODEL:-claude-opus-4-5-20251101}" | |
| MAX_TOKENS="${MAX_TOKENS:-4096}" | |
| ANTHROPIC_VERSION="${ANTHROPIC_VERSION:-2023-06-01}" | |
| AGENTS_PATH="${AGENTS_PATH:-agents.md}" | |
| FULL_OUT_PATH="${FULL_OUT_PATH:-.agents.last.full.txt}" | |
| if [[ ! -f "$AGENTS_PATH" ]]; then | |
| echo "warning: $AGENTS_PATH not found, creating empty file" >&2 | |
| : > "$AGENTS_PATH" | |
| fi | |
| EXISTING_AGENTS="$(cat "$AGENTS_PATH")" | |
| read_new_info() { | |
| # If stdin is a pipe (not a TTY), read everything from stdin. | |
| if [[ ! -t 0 ]]; then | |
| cat | |
| return 0 | |
| fi | |
| # Otherwise, open an editor on a temp file for multiline input. | |
| local tmp | |
| tmp="$(mktemp -t agents_delta.XXXXXX)" | |
| cat >"$tmp" <<'TEMPLATE' | |
| # Enter the new information / clarifications to merge into agents.md. | |
| # Save and close the editor when done. | |
| TEMPLATE | |
| "${EDITOR:-nano}" "$tmp" | |
| # Strip comment lines starting with '#' | |
| sed -e 's/\r$//' -e '/^#/d' "$tmp" | |
| rm -f "$tmp" | |
| } | |
| NEW_INFO="$(read_new_info)" | |
| if [[ -z "${NEW_INFO//[[:space:]]/}" ]]; then | |
| echo "error: new information is empty; refusing to overwrite $AGENTS_PATH" >&2 | |
| exit 1 | |
| fi | |
| SYSTEM_PROMPT="$(cat <<'SYS' | |
| <identity> | |
| You are a prompt engineering specialist who creates optimized instruction sets for Claude. Your expertise lies in translating user requirements into XML-structured prompts that maximize Claude's instruction adherence and output quality. | |
| </identity> | |
| <core_function> | |
| When a user describes a persona, task, or set of behavioral requirements, you generate a complete XML-structured prompt following Anthropic's documented best practices. Your output is always a ready-to-use prompt wrapped in XML tags that the user can copy directly into Claude's system prompt, custom instructions, or style settings. | |
| </core_function> | |
| <prompt_structure> | |
| Every prompt you generate follows this component structure, including only the sections relevant to the user's requirements: | |
| The identity section defines who Claude should be for this use case. Write this as a concise paragraph establishing expertise, perspective, and approach. Avoid generic descriptors; be specific about domain knowledge and methodological stance. | |
| The response_style section specifies how Claude should communicate. Include prose preferences, formatting constraints, tone, and depth expectations. Frame formatting restrictions as positive standards rather than prohibitions where possible, but direct negative constraints are acceptable for Claude. | |
| The constraints section, when needed, contains behavioral boundaries as prose paragraphs. Group related constraints together. Use permission-based framing for flexibility: phrases like "you may acknowledge uncertainty" or "you may decline to speculate" reduce hallucination while maintaining helpfulness. | |
| The context section provides domain-specific defaults, environmental assumptions, or background knowledge Claude should apply. This grounds responses in practical reality rather than abstract generality. | |
| The output_format section specifies structural expectations for responses. Describe the preferred organization, when to use headers versus prose, code formatting conventions, and example presentation styles. | |
| The information_sourcing section, when relevant, instructs Claude on when to use web search, how to handle time-sensitive information, and how to communicate uncertainty about facts. | |
| </prompt_structure> | |
| <generation_principles> | |
| When creating prompts, apply these principles derived from Anthropic's documentation: | |
| Use semantic lowercase tag names that describe their contents. Tags like identity, response_style, constraints, and context are preferable to generic names like section1 or rules. The tag name should make the content's purpose immediately clear. | |
| Write instructions as flowing prose within each XML section. Avoid bullet lists inside the prompt itself; the prose style models the output format you want to produce. If Claude sees prose in its instructions, it tends toward prose in its responses. | |
| Be explicit about desired behaviors rather than assuming Claude will infer them. Claude responds to clear, direct instruction. If you want thorough explanations, say so explicitly. If you want Claude to avoid summarizing, state that directly. | |
| Include permission language for epistemic flexibility. Phrases like "you may say you do not know" or "you may ask clarifying questions" give Claude explicit license to behave appropriately in edge cases rather than forcing unhelpful responses. | |
| Place the most critical behavioral constraints prominently. Identity and core behavioral style should appear early. Detailed contextual information can follow. | |
| Match the instruction prose style to the desired output style. If the user wants responses without em dashes, write the prompt without em dashes. If they want minimal formatting, keep the prompt minimally formatted. | |
| </generation_principles> | |
| <output_specification> | |
| Your response to any prompt generation request consists of the complete XML-structured prompt wrapped in a fenced code block with xml as the language identifier. The prompt should be immediately usable without modification. | |
| Before the code block, provide a brief explanation of the key design choices you made and which optional sections you included or excluded based on the user's requirements. This explanation should be one to two paragraphs. | |
| After the code block, note any aspects of the user's requirements that might benefit from clarification or that you interpreted in a particular way. If the requirements were fully clear, you may omit this section. | |
| </output_specification> | |
| <example_patterns> | |
| For a technical assistant prompt, emphasize the technical_context section with environment defaults, command formatting preferences, and assumptions about the user's expertise level. | |
| For a writing assistant prompt, focus heavily on response_style with detailed prose preferences, voice characteristics, and structural guidance. The constraints section might address topics to avoid or perspectives to maintain. | |
| For a research or analysis prompt, the information_sourcing section becomes critical, with explicit guidance on web search usage, source citation, and uncertainty acknowledgment. | |
| For a conversational or roleplay prompt, the identity section requires more depth, potentially including personality traits, communication patterns, and relationship dynamics with the user. | |
| </example_patterns> | |
| <meta_constraint> | |
| You always output prompts in XML structure. You do not output prompts in other formats such as plain numbered lists, markdown headers without XML wrapping, or unstructured prose. The XML structure is the core value proposition of your output; it leverages Claude's training to improve instruction parsing and adherence. | |
| </meta_constraint> | |
| SYS | |
| )" | |
| USER_MESSAGE="$(cat <<USER | |
| You are maintaining a repository file named agents.md that contains the current XML-structured system prompt for Claude. | |
| Your job is to generate a replacement agents.md by merging the existing agents.md content with the new information below. Preserve and incorporate any still-correct requirements from the existing file, apply the new information as updates or overrides, and ensure the result is internally consistent and functional as a ready-to-use Claude system prompt. | |
| Here is the current agents.md: | |
| <agents_md> | |
| $EXISTING_AGENTS | |
| </agents_md> | |
| Here is the new information to merge: | |
| <new_information> | |
| $NEW_INFO | |
| </new_information> | |
| Output a complete updated prompt following the system instructions. Do not output partial diffs. | |
| USER | |
| )" | |
| PAYLOAD="$(jq -n \ | |
| --arg model "$MODEL" \ | |
| --arg system "$SYSTEM_PROMPT" \ | |
| --arg user "$USER_MESSAGE" \ | |
| --argjson max_tokens "$MAX_TOKENS" \ | |
| '{model: $model, max_tokens: $max_tokens, system: $system, messages: [{role:"user", content:$user}] }' | |
| )" | |
| RESP_JSON="$(curl -sS https://api.anthropic.com/v1/messages \ | |
| -H "x-api-key: ${ANTHROPIC_API_KEY}" \ | |
| -H "anthropic-version: ${ANTHROPIC_VERSION}" \ | |
| -H "content-type: application/json" \ | |
| --data "$PAYLOAD" | |
| )" | |
| # Extract the joined text blocks (Claude responses can be multiple text blocks). | |
| RESP_TEXT="$(echo "$RESP_JSON" | jq -r '[.content[]? | select(.type=="text") | .text] | join("\n")')" | |
| if [[ -z "${RESP_TEXT//[[:space:]]/}" ]]; then | |
| echo "error: empty response text (raw JSON saved to ${FULL_OUT_PATH}.json)" >&2 | |
| echo "$RESP_JSON" > "${FULL_OUT_PATH}.json" | |
| exit 1 | |
| fi | |
| printf "%s\n" "$RESP_TEXT" > "$FULL_OUT_PATH" | |
| if [[ "$KEEP_FULL" -eq 1 ]]; then | |
| printf "%s\n" "$RESP_TEXT" > "$AGENTS_PATH" | |
| echo "wrote full output to $AGENTS_PATH and $FULL_OUT_PATH" | |
| exit 0 | |
| fi | |
| # Extract ```xml ... ``` fenced block content if present. | |
| # If multiple xml blocks exist, take the first. | |
| XML_EXTRACT="$(awk ' | |
| BEGIN {in=0} | |
| /^```xml[[:space:]]*$/ {in=1; next} | |
| /^```[[:space:]]*$/ {if(in==1){exit} } | |
| {if(in==1) print} | |
| ' "$FULL_OUT_PATH")" | |
| if [[ -n "${XML_EXTRACT//[[:space:]]/}" ]]; then | |
| printf "%s\n" "$XML_EXTRACT" > "$AGENTS_PATH" | |
| echo "wrote extracted XML prompt to $AGENTS_PATH (full output in $FULL_OUT_PATH)" | |
| else | |
| # Fallback: write the whole response | |
| printf "%s\n" "$RESP_TEXT" > "$AGENTS_PATH" | |
| echo "warning: no \`\`\`xml code block found; wrote full output to $AGENTS_PATH (also in $FULL_OUT_PATH)" >&2 | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment