Setting Up an Agentic AI Workflow with Claude Code
This week’s assignment was about building the foundation for agentic DevOps. Not writing infrastructure yet, just learning how to hand real work to an AI agent safely and repeatably. Seven pieces, each one building on the last: setup, project context, skills, subagents, MCP, hooks, and memory. Here’s the full breakdown, step by step, in case you’re doing this yourself or just want to understand what “agentic AI” actually means in practice.
Why this matters before touching any real infrastructure
You wouldn’t hand a new hire the AWS root credentials on day one. You’d watch them work, give them a runbook, restrict their permissions to what the task needs, and build trust gradually. That’s exactly the mental model for this whole assignment set. Claude Code is treated like a new team member: get it set up, teach it the project, give it narrow tools for specific jobs, split responsibilities across specialists, connect it to live systems, put guardrails in place, and give it a memory so it doesn’t forget the rules every session.
Part 1: Installing Claude Code and watching the Agentic Loop
Install and authenticate
Three commands, that’s it:
npm install -g @anthropic-ai/claude-code
claude --version
claudeRunning claude opens a browser window for login. Once authenticated, you get a terminal prompt inside your project. If claude --version doesn't print a number, npm didn't install it globally, check your PATH.
Fork and clone the starter repo
git clone https://github.com/YOUR_USERNAME/REPO_NAME.git
cd REPO_NAME
code .At this point the project should just have index.html, style.css, and an images/ folder. No .claude/ directory yet, that comes later. If a .claude folder, CLAUDE.md, or .github folder already exists in the fork, delete them first so you're starting clean.
Watching the loop: Gather, Act, Verify
This is the core idea of the whole assignment. Every agentic task Claude runs breaks into three phases:
- Gather: Claude reads the relevant files before saying anything
- Act: Claude does the actual work, runs a command, writes code, whatever the task needs
- Verify: Claude checks its own output against what was asked
To see it live, open the Claude Code terminal inside VS Code and ask two questions:
What files are in this project and what does each one do?Claude should read index.html, style.css, and the images folder before answering, not guess from the file names.
How many lines of CSS does this project have?Here Claude should actually run something like wc -l style.css instead of estimating. That's the "Act" phase in action, a real shell command with a real, checkable answer.
Takeaway: if Claude ever answers a question like this without visibly reading a file or running a command first, that’s a sign it’s guessing, not gathering. Watch for that pattern before you trust it with anything higher stakes.
Part 2: Teaching Claude the project with CLAUDE.md
README is for humans, CLAUDE.md is for the AI
This distinction matters. A README explains the project to a person browsing GitHub. CLAUDE.md is the onboarding doc Claude reads automatically at the start of every session in that project. Without it, Claude has to infer your stack and your rules from the file tree alone.
Step 1: Capture the “before” baseline
Before any CLAUDE.md exists, ask:
What is this project and how should I deploy it?Expect something generic: “this looks like a static HTML/CSS site, you could deploy it to Netlify, GitHub Pages, Vercel,” etc. That vagueness is the baseline you’re comparing against later.
Step 2: Generate a starter file with /init
/initClaude scans the project and writes a CLAUDE.md with a basic Project Overview and Architecture section. It won't know anything project-specific yet, like your actual deployment target, because you haven't told it.
Step 3: Fill in all five sections yourself
This is the part that actually does the work. A complete CLAUDE.md has:
- Overview: what the project is and where it’s deployed. Example: “Static HTML/CSS portfolio website deployed to AWS using S3 and CloudFront, provisioned with Terraform, and automated via GitHub Actions.”
- Architecture: the technical constraints. Example: “Pure HTML5 and CSS3. No JavaScript. No build step. No framework.”
- Commands: the exact commands used in this project, e.g.
terraform init,terraform plan,terraform apply. - Conventions: the rules Claude has to follow. Example set:
- All infrastructure changes go through Terraform, never modify AWS resources manually
- No JavaScript in this project
- CSS uses a mobile-first approach with breakpoints at 900px, 768px, and 600px
- Safety: what should never end up in this file. “Never put secrets in this file. No API keys, passwords, or AWS credentials.”
Step 4: Prove it worked
Start a completely new Claude Code session (close the terminal, reopen it) and ask the exact same question as before:
What is this project and how should I deploy it?Now it should mention S3, CloudFront, and Terraform specifically, because it read the file.
Then test the guardrail:
Add a React component to the homepage.Claude should push back and cite the “No JavaScript” rule from CLAUDE.md. If it doesn't, the file either isn't being read or the convention wasn't written clearly enough.
Step 5: Commit it
git status
git add CLAUDE.md
git commit -m "Add project-specific CLAUDE.md"
git push origin mainWhy this matters: everything you write in CLAUDE.md applies to every future session automatically. A well-written one written once saves you from repeating the same corrections for months.
Part 3: Building reusable Skills
The problem skills solve
Typing out the same long prompt every time you want to run a repeatable task is slow and error prone. Skills turn a full workflow into a single slash command, with consistent behavior and enforced tool restrictions every time it runs.
Step 1: Create the folder structure
mkdir -p .claude/skills/scaffold-terraform
mkdir -p .claude/skills/tf-plan
mkdir -p .claude/skills/tf-apply
mkdir -p .claude/skills/deployStep 2: Drop in the skill files
Each skill folder needs its SKILL.md, renamed and placed correctly:
scaffold-terraform-SKILL.md→.claude/skills/scaffold-terraform/SKILL.mdscaffold-terraform-template-spec.md→.claude/skills/scaffold-terraform/template-spec.mdtf-plan-SKILL.md→.claude/skills/tf-plan/SKILL.mdtf-apply-SKILL.md→.claude/skills/tf-apply/SKILL.mddeploy-SKILL.md→.claude/skills/deploy/SKILL.md
Open tf-plan/SKILL.md and check the frontmatter. It should show something like:
allowed-tools: Bash, Read, Grep
disable-model-invocation: trueNotice there’s no Write in that list. This skill can read and run commands, but it can't modify files. That restriction is deliberate.
Step 3: Run the skill
/scaffold-terraformClaude reads the template spec and generates main.tf, variables.tf, outputs.tf, providers.tf, and backend.tf inside a terraform/ folder, then gives you a summary checklist of what it created.
Step 4: Initialize and plan
cd terraform && terraform initThen back in Claude Code:
/tf-planWithout real AWS credentials, terraform plan will fail with an authentication error, and that's expected. The point of this step isn't a clean plan output, it's watching Claude run the command, hit the error, and explain what's actually wrong and what would fix it. That's error handling inside the Agentic Loop.
The underlying principle: this is least privilege, the same concept behind IAM roles and Kubernetes RBAC. A skill that can only read files can’t corrupt your infrastructure no matter how many times it runs. Write access has to be earned by the specific skill that needs it.
Part 4: Building a team of subagents
Skills vs subagents
Skills share your current conversation’s context. Subagents run in an isolated context, they’re specialists you delegate a whole task to, and they come back with a report instead of working alongside you turn by turn.
Step 1: Set up the agents folder
mkdir -p .claude/agentsDrop in three files, matching the name field in each file's frontmatter exactly:
security-auditor.mdtf-writer.mdcost-optimizer.md
Step 2: Understand why each one is configured differently
Open each file and check model and tools:
- cost-optimizer runs on Haiku, not Sonnet, because cost checks are fast, pattern-based lookups, not deep reasoning. Speed matters more than depth here.
- security-auditor deliberately has no Write in its tools list. It should only ever read and report, never modify infrastructure itself.
- tf-writer uses
model: inherit, meaning it runs on whatever model the main session is using, since generating Terraform code benefits from the same reasoning quality as your main conversation.
Step 3: Delegate to the security auditor
Audit my Terraform files for security issues.Claude matches this prompt to the security-auditor’s description and shows a delegation message like “Launching security-auditor agent.” The report comes back organized by severity, typically flagging things like missing S3 encryption, outdated TLS versions, or missing access logging.
Step 4: Delegate to the cost optimizer
Review my Terraform infrastructure for cost optimization.This one finishes noticeably faster than the security audit, that’s Haiku vs Sonnet in practice. The report covers things like CloudFront price class, lifecycle policies for versioned S3 objects, and estimated monthly cost.
Get Nikky Juwe’s stories in your inbox
Join Medium for free to get updates from this writer.
Why this matters: one overloaded general-purpose agent with every tool and capability works fine until something breaks, then it’s nearly impossible to trace what went wrong. Small, single-purpose subagents are easier to debug because you already know which agent and which checklist to look at.
Part 5: Connecting Claude to live external data with MCP
The problem MCP solves
Without it, Claude only knows what was in its training data, which could be months old and has zero knowledge of your actual GitHub repos, AWS account, or databases. MCP (Model Context Protocol) is how Claude connects to real, live external systems so answers are grounded in current data instead of guesses.
Step 1: Generate a GitHub token
GitHub → Settings → Developer Settings → Personal Access Tokens → Tokens (classic) → Generate new token (classic). Set an expiry, select the repo and read:user scopes, generate, and copy the token immediately since GitHub won't show it again. Never screenshot the token value itself, only the scope selection screen.
Step 2: Configure the MCP server
Create .mcp.json at the project root:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {}
}
}
}This file is meant to be shared with the team and committed to version control. It defines which server to connect, not the credentials.
Step 3: Store the token locally, never committed
Create .claude/settings.local.json:
{
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "your_token_here"
},
"enabledMcpjsonServers": ["github"]
}Confirm .claude/settings.local.json is listed in .gitignore. This split matters: .mcp.json is team configuration, settings.local.json is personal credentials. One gets committed, the other never does.
Step 4: Verify the connection
Close Claude Code fully and reopen it so it picks up the new config, then:
/mcpYou should see the github server listed as connected.
Step 5: Prove it with a live query
Use GitHub MCP to get the README.md file from <your-github-username>/Ultimate-Agentic-DevOps-with-Claude-CodeClaude calls the GitHub MCP tool and returns the actual live README content, not a guess based on training data. Compare it against the real file in your repo to confirm it matches.
Why this matters: MCP is an open standard, so the exact pattern you just used for GitHub applies identically to dozens of other tools, Postgres, Jira, Sentry, Notion. Learn the pattern once, reuse it everywhere.
Part 6: Putting guardrails on the agent with Hooks and Permissions
Why hooks aren’t optional
No team gives an AI agent unrestricted access to production. A junior engineer might type “delete all the old buckets” during a routine cleanup without meaning to trigger something destructive. Hooks intercept that intent before Claude even starts working on it, or block the dangerous command right before it executes.
Step 1: Build the folder structure
chmod +x .claude/hooks/*.sh(Skip chmod on Windows and use Git Bash as your terminal instead.)
.claude/
├── settings.json
└── hooks/
├── user-prompt-guard.sh
├── pre-tool-guard.sh
└── post-tool-logger.shStep 2: UserPromptSubmit hook, catches destructive intent before Claude even processes the request
#!/bin/bash
# UserPromptSubmit hook — blocks destructive intent in user promptsINPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty')if echo "$PROMPT" | grep -iqE "delete all|destroy everything|remove all resources|wipe|nuke|drop all"; then
echo '{"decision": "block", "reason": "Destructive intent detected. Please use /tf-destroy for controlled infrastructure teardown."}'
fi
Step 3: PreToolUse hook, blocks dangerous commands right before execution
#!/bin/bash
# PreToolUse hook — blocks dangerous Bash commands before executionINPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')if echo "$CMD" | grep -qE "terraform destroy|terraform apply.*-auto-approve|aws s3 rm|aws s3 rb"; then
echo '{"decision": "block", "reason": "Destructive command detected. Use /tf-destroy or /tf-apply commands for safety."}'
fi
Step 4: PostToolUse hook, logs what actually ran
#!/bin/bash
# PostToolUse hook — logs Terraform validation and formatting commandsINPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')if echo "$CMD" | grep -qE "terraform fmt|terraform validate"; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Terraform command executed: $CMD" >> .claude/deploy.log
fi
Step 5: Wire it all together in settings.json
{
"alwaysThinkingEnabled": true,
"permissions": {
"allow": [
"Bash(cd terraform && terraform init*)",
"Bash(cd terraform && terraform plan*)",
"Bash(cd terraform && terraform output*)",
"Bash(cd terraform && terraform show*)",
"Bash(cd terraform && terraform fmt*)",
"Bash(cd terraform && terraform validate*)",
"Bash(aws s3 ls*)",
"Bash(aws cloudfront get-distribution*)",
"Bash(aws sts get-caller-identity)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(aws iam *)"
]
},
"hooks": {
"UserPromptSubmit": [
{ "matcher": "", "hooks": [{ "type": "command", "command": "bash .claude/hooks/user-prompt-guard.sh" }] }
],
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "bash .claude/hooks/pre-tool-guard.sh" }] }
],
"PostToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "bash .claude/hooks/post-tool-logger.sh" }] }
]
}
}Step 6: Test all three, in order
Close and reopen Claude Code first so it loads the fresh config, then:
Test the prompt-level block:
delete all files in the terraform folderExpect: blocked immediately, before Claude even starts reading anything.
Test the tool-level block:
Run terraform destroy in the terraform folder.Expect: Claude starts to process it, but the command itself gets blocked before execution.
Test the logging hook:
Run terraform validate in the terraform folder.Expect: the command succeeds normally, and a line gets appended to .claude/deploy.log recording it.
Why this matters: this mirrors the exact same logic behind pre-commit hooks and CI/CD approval gates. Intercept early, at the cheapest possible point. A prompt-level block that fires in milliseconds is a lot better than catching a terraform destroy ten minutes after it already ran.
Part 7: Giving Claude a memory across sessions
The problem
Every new Claude Code session starts from zero. No memory of prior decisions, conventions, or corrections. On a project running for months, that means re-explaining the same rules constantly.
Step 1: Find where memory actually lives
Where does your memory file live for this project? Show me the full path.Claude gives you the absolute path, something like ~/.claude/projects/<encoded-path>/memory/MEMORY.md. It may not exist yet, that's normal.
Step 2: Teach it something and tell it to save
Remember the following for all future sessions: The CSS hero section uses a dark gradient from #1a1a2e to #16213e. The mobile breakpoints are 900px, 768px, and 600px. Never suggest adding JavaScript to this project. Save this to your memory file now.Open MEMORY.md afterward and confirm all three facts actually landed there.
Step 3: Fully close the session
/exitThen close VS Code entirely, wait about 30 seconds, and reopen it with a fresh Claude Code session in the same project. This step matters, a session left open in the background isn’t a real test of memory persistence.
Step 4: Prove recall without repeating yourself
In the brand new session, ask three separate questions:
What colors are used in the hero section of this project?Should recall #1a1a2e to #16213e without being told again.
What are the mobile breakpoints for this project?Should recall 900px, 768px, and 600px.
Should I add a JavaScript animation to the hero section?Should refuse, citing the no-JavaScript rule from memory.
Why this matters: the memory file has a 200-line load limit by design. That’s not a limitation to work around, it’s a forcing function. Fill it with noise and you push out the things that actually matter. Treat it like a curated runbook: add deliberately, prune regularly, keep entries short and actionable.
What this week actually taught me
The thread running through all seven pieces is the same: never give an agent more trust than the task requires, and always make that trust explicit and checkable. CLAUDE.md gives Claude context. Skills restrict what a repeatable task is allowed to touch. Subagents split responsibility so a failure is easy to trace. MCP grounds answers in live data instead of guesses. Hooks stop dangerous actions before they happen instead of after. Memory keeps the rules from having to be re-taught every session.
None of this is really about Claude specifically. It’s the same discipline behind IAM policies, RBAC, and CI/CD approval gates, just applied to an AI agent instead of a human teammate or a pipeline. Calibration first, automation second. That’s the whole point of week one.
The P.S. line now points to your own graded progress page:
https://dmi.pravinmishra.com/s/NkJuwe.html
Start your DevOps journey:
https://lnkd.in/dFyndNrh





