Mert
ÖZEL HABER GÜNDEMİ / 28 Mart 2026
Güneş paneli verimliliğinde büyük atılım: Teorik limit aşıldı
Japonya ve Almanya’daki araştırmacılar, güneş pillerinde yaklaşık %130 kuantum verimliliğine ulaşarak güneş enerjisi dönüşümünde uzun süredir geçerli kabul edilen fiziksel sınırı aşmayı başardı.AMD Ryzen 5 5500X3D Avrupa’da ortaya çıktı: AM4 desteği sürecek
AMD Ryzen 5 5500X3D, İngiltere’de bir perakendecide listelendi. AM4 platformunu hedefleyen işlemci, daha geniş erişime doğru ilerliyor. İşte detaylar…Debugging Kubernetes is Painful… So I Built an AI Tool to Fix It
🤯 The Problem
Debugging Kubernetes is one of the most frustrating parts of DevOps.
You check logs.
You run kubectl describe.
You search errors manually.
And still…
👉 You don’t know what actually went wrong.
This process is:
• Time-consuming
• Repetitive
• Mentally exhausting
So I asked myself:
What if Kubernetes debugging could be automated?
Architectural High level Overview
💡 The Idea
Instead of manually analyzing logs and pod states, I wanted a system that could:
• Detect failing pods automatically
• Analyze logs in real-time
• Identify root cause
• Suggest fixes
That’s how KubeAI was born — an AI-powered Kubernetes debugger.
🏗️** What I Built**
I created a complete end-to-end system:
• FastAPI Backend → Handles analysis
• Kubernetes Integration → Fetches pods and logs
• AI Engine → Detects issues and generates insights
• Streamlit Dashboard → Visual interface
🔍 How It Works
1. Pod Monitoring
The system fetches all pods from a namespace:
kubectl get pods
2. State-Based Detection
It detects failures like:
• CrashLoopBackOff
• ImagePullBackOff
• ErrImagePull
These are marked as CRITICAL issues.
3. Log Analysis
Logs are parsed and analyzed:
• ERROR logs → Warning
• Repeated failures → Critical
4. **AI Insight Generation
Instead of raw logs, the system generates:
Issue: CrashLoopBackOff
Root Cause: Container failed to start properly
Fix: Check container logs and deployment configuration
Confidence: 95%
📊 **Dashboard Features**
I built a real-time dashboard using Streamlit:
• Cluster summary (Healthy vs Unhealthy pods)
• Top issues panel
• Pod-level issue breakdown
• AI-generated insights
• Auto-refresh (live monitoring)
• Filter: Show only unhealthy pods
💥 Demo Scenario
To test the system, I simulated real-world failures.
Scenario 1: Runtime Errors
I injected error logs into the application.
The system automatically detected issues.
Scenario 2: CrashLoopBackOff
I intentionally broke container startup.
Kubernetes marked the pod unhealthy.
KubeAI detected it and explained the issue.
🧠 Key Learning
The biggest realization:
Logs alone are not enough.
You need to combine:
• Pod state
• Logs
• Context
That’s where intelligent systems make a difference.
🚀 Impact
This tool helps:
• Reduce debugging time
• Automate root cause analysis
• Improve developer productivity
**
🔮 What’s Next
**
I’m planning to extend this into:
• Cloud deployment (AWS)
• Historical tracking
• LLM-based deeper analysis
• Multi-user SaaS
Final Dashboard with Auto refresh:
🔗 Project Link
Teaching Coding Agent to Write XSLT — The Hook in Action
Parts 1 and 2 explained the skill and the hook setup. This part shows it actually running — a real session, real prompts, real hook output. No theory.
We’ll build the same Order-to-Shipment transformation three ways — XSLT 1.0 with inline C#, XSLT 2.0 with Saxon, and LML compiled to XSLT 3.0 — and watch the hook verify each one, catch bugs, and drive Claude through a multi-step fix cycle. Everything below is from a single session.
Step 1: XSLT 1.0 with inline C
The prompt
Create an XSLT 1.0 transformation (compatible with .NET XslCompiledTransform)
that maps an Order to a Shipment. Use the existing Order.xml as input reference
and target the ShipmentOutput.xsd schema.
Requirements:
- Map OrderID → ShipmentRef with a "SHP-" prefix (use inline C# via msxsl:script)
- Map OrderDate → ShipDate, reformatted from yyyy-MM-dd to dd/MM/yyyy (inline C#)
- Map CustomerName → Recipient
- Generate an ExecutionDate using inline C# that returns DateTime.UtcNow
- Loop over each Item → Line, mapping ProductName → Product and Quantity → Quantity
- Filter out items where Quantity < 1
Use the Order namespace `http://schemas.contoso.com/Order`.
What Claude reads first
Before writing anything, Claude reads the source and target schemas to understand both structures. This is the skill’s Generate mode — it requires understanding the data shapes before choosing XPath expressions.
Source — Order.xml with namespace http://schemas.contoso.com/Order
Target — ShipmentOutput.xsd, no namespace:
Claude also reads the existing .vscode/launch.json to know where to append the new debug configuration. Now it writes.
The write sequence
The skill enforces a specific write order: launch config first, stylesheet last. This ensures the debug configuration is in place before the hook fires on the .xslt write.
File 1: .vscode/launch.json — appends a new configuration:
{
"type": "xslt",
"request": "launch",
"name": "Debug Order2Shipment_20260327",
"engine": "compiled",
"stylesheet": "${workspaceFolder}/Artifacts/Maps/Order2Shipment_20260327.xslt",
"xml": "${workspaceFolder}/Artifacts/SampleData/Order.xml",
"output": "${workspaceFolder}/Artifacts/Maps/out/Order2Shipment_20260327-out.xml",
"stopOnEntry": true,
"debug": true,
"logLevel": "log"
}
Engine is compiled — the .NET XslCompiledTransform engine that supports msxsl:script inline C#. No hook fires on this file.
File 2: Artifacts/Maps/Order2Shipment_20260327.xslt — the stylesheet with three C# helpers in a msxsl:script block:
-
PrefixShipmentRef(string orderId)— prepends"SHP-" -
ReformatDate(string isoDate)— parsesyyyy-MM-dd, returnsdd/MM/yyyy -
ExecutionDate()— returnsDateTime.UtcNowasyyyy-MM-ddTHH:mm:ssZ
The hook fires. While Claude is still in the same response turn, the XSLT Debugger API compiles the inline C# via Roslyn, runs the transform against Order.xml, and returns the result:
That’s the clean path. Before moving to XSLT 2.0, let’s see what happens when this same map has bugs.
What the hook catches: two classes of failure
Silent data loss — the namespace bug
XSLT’s most dangerous bugs produce structurally correct output with silently missing data. No error, no warning — just empty elements.
Claude drops the ord: namespace prefix from one XPath:
<!-- Bug: missing namespace prefix -->
<Recipient>
<xsl:value-of select="Customer/CustomerName"/>
</Recipient>
The hook fires:
<Recipient></Recipient>
Exit code 0. The transform “succeeded”. But <Recipient> is empty. Without the hook, this ships to production and nobody notices until a downstream system complains.
Claude sees the empty element, identifies the namespace mismatch (the skill’s #1 debug pattern), and edits the file. The hook fires again on the Edit — not just on the initial Write. Every change to an .xslt file triggers a fresh transform:
<Recipient>John Doe</Recipient>
Fixed. Two hook invocations, one fix.
Hard compile failure — the C# syntax error
Exit code 1. No XML output at all — Roslyn rejects the C# before the XSLT engine even starts. Claude sees the error with exact line and column, adds the semicolon, hook fires again, clean output.
These are two fundamentally different failure modes:
| Failure | What happens | Exit code | Catchable without hook? |
|---|---|---|---|
| Namespace mismatch | Valid XML, empty values | 0 | No — CI pipelines see “success” |
| C# compile error | No output, error message | 1 | Yes — but only if you run it |
The namespace bug is why the hook matters most. It catches the failures that look like success.
Step 2: XSLT 2.0 with Saxon
Next prompt:
Now create the same map as XSLT 2.0 for Saxon. Create a new file.
Same write sequence: launch config first (engine saxonnet), then the stylesheet. No msxsl:script this time — XSLT 2.0 has native functions for everything the C# was doing:
| XSLT 1.0 + C# | XSLT 2.0 native |
|---|---|
fn:PrefixShipmentRef(string(ord:OrderID)) |
concat('SHP-', ord:OrderID) |
fn:ReformatDate(string(ord:OrderDate)) |
format-date(xs:date(ord:OrderDate), '[D01]/[M01]/[Y0001]') |
fn:ExecutionDate() |
format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]Z') |
The hook fires on the .xslt write:
Same output structure, same values. Two engines, two versions, same result — both verified by the hook automatically.
Step 3: LML compiled to XSLT 3.0
Now the same map authored in the Data Mapper’s LML format — the visual designer’s YAML source that compiles to XSLT 3.0. This is the most complex path because it has dependencies that must be created in the right order.
The prompt
Now create an LML for the same Order-to-Shipment mapping and test it.
The dependency chain
Claude reads the LML file and prepares the next actions
LML maps can reference custom extension functions defined in separate XML files. Before Claude can write the LML, it needs to create these functions. The full write sequence for LML mode is:
-
Launch config — debug configuration pointing at the compiled
.xslt(not the.lml) -
Custom functions —
Artifacts/DataMapper/Extensions/Functions/ShipmentFunctions.xml -
LML file —
Artifacts/MapDefinitions/Order2Shipment_20260327-lml.lml(written last — the compile hook fires on this, usinglml-compile)
The launch config points at Artifacts/Maps/Order2Shipment_20260327-lml.xslt — a file that doesn’t exist yet. It will be generated when the LML compile hook fires. Engine is saxonnet because LML always compiles to XSLT 3.0.
The custom functions
Claude creates three functions in ShipmentFunctions.xml:
<customfunctions>
<function name="prefixShipRef" as="xs:string">
<param name="orderId" as="xs:string"/>
<value-of select="concat('SHP-', $orderId)"/>
</function>
<function name="reformatDate" as="xs:string">
<param name="dateVal" as="xs:date"/>
<value-of select="format-date($dateVal, '[D01]/[M01]/[Y0001]')"/>
</function>
<function name="executionDateTime" as="xs:string">
<value-of select="format-dateTime(...)"/>
</function>
</customfunctions>
No hook fires — this is an .xml file, not .lml or .xslt. But this file has a bug that won’t surface until the LML compiles. executionDateTime has zero parameters.
The LML file
$version: 1
$input: XML
$output: XML
$sourceSchema: OrderSchema.xsd
$targetSchema: ShipmentOutput.xsd
$sourceNamespaces:
ns0: http://schemas.contoso.com/Order
Shipments:
Shipment:
ShipmentRef: prefixShipRef(/ns0:Order/ns0:OrderID)
ShipDate: reformatDate(/ns0:Order/ns0:OrderDate)
Recipient: /ns0:Order/ns0:Customer/ns0:CustomerName
ExecutionDate: executionDateTime()
Lines:
$for(/ns0:Order/ns0:Items/ns0:Item):
$if(is-greater-or-equal(ns0:Quantity, 1)):
Line:
Product: ns0:ProductName
Quantity: ns0:Quantity
Claude writes this file. The LML compile hook fires — calling lml-compile, a dotnet global tool that wraps the Logic Apps SDK’s DataMapTestExecutor.GenerateXslt(). And now the fixing cycle begins.
The fixing cycle
The executionDateTime function has zero parameters — the SDK’s ReadCustomFunctionsDefinitionAsync() hits a NullReferenceException on parameters.Length and silently skips the entire XML file. All three functions become “unrecognized”. Claude adds a dummy parameter, edits the LML, and the hook fires again — second error: is-greater-or-equal isn’t a real LML pseudofunction. Claude switches to xpath("ns0:Quantity >= 1"), edits the LML one more time, and the hook returns clean output.
Three iterations, two bugs, one prompt. The compile hook drove the entire fix cycle — feeding each error back into Claude’s context so it could diagnose and fix without manual intervention.
| Iteration | Error | Root cause | Fix |
|---|---|---|---|
| 1 |
executionDateTime unrecognized |
Zero-param SDK bug — null parameters skips entire XML file |
Add dummy <param>
|
| 2 |
is-greater-or-equal unrecognized |
Not a real LML pseudofunction — UI label vs compiler syntax | Use xpath("ns0:Quantity >= 1")
|
| 3 | Success | — | — |
Three engines, one result
All three implementations now produce matching output from the same input:
| Engine | Version | Date logic | String logic | Filter |
|---|---|---|---|---|
compiled |
XSLT 1.0 | C# DateTime.TryParse
|
C# string.Concat
|
xsl:if test="ord:Quantity >= 1" |
saxonnet |
XSLT 2.0 | format-date(xs:date(...)) |
concat(...) |
xsl:if test="ord:Quantity >= 1" |
saxonnet |
XSLT 3.0 (LML) | ef:reformatDate(...) |
ef:prefixShipRef(...) |
xsl:when test="ns0:Quantity >= 1" |
<!-- All three produce: -->
<ShipmentRef>SHP-ORD-1001</ShipmentRef>
<ShipDate>31/10/2025</ShipDate>
<Recipient>John Doe</Recipient>
The hook verified each automatically at write time. This matters for teams migrating between engines — you can write the same map in XSLT 1.0 for BizTalk and XSLT 3.0 for Logic Apps, and verify both produce identical output in the same session.
What you actually see in VS Code
When the hook fires during a Claude response, the output appears inline in Claude’s context — a PostToolUse system reminder that Claude reads as part of the same turn. You don’t see a separate panel or dialog.
For the XSLT 1.0 clean path:
Claude: [reads Order.xml, ShipmentOutput.xsd, launch.json]
Claude: [writes launch.json entry]
Claude: [writes Order2Shipment_20260327.xslt]
← Hook: PostToolUse:Write — Transform result: <Shipments>...</Shipments>
Claude: "Transform ran, output correct."
For the LML fixing cycle:
Claude: [writes launch.json entry]
Claude: [writes ShipmentFunctions.xml]
Claude: [writes Order2Shipment_20260327-lml.lml]
← Hook: PostToolUse:Write — ERROR: 'executionDateTime' unrecognized
Claude: [edits ShipmentFunctions.xml — adds dummy param]
Claude: [edits .lml — passes dummy argument]
← Hook: PostToolUse:Edit — ERROR: 'is-greater-or-equal' unrecognized
Claude: [edits .lml — switches to xpath()]
← Hook: PostToolUse:Edit — LML compiled + Transform result: <Shipments>...</Shipments>
Claude: "Three iterations, all fixed. Output matches."
From your side, you prompted once. Claude read the schemas, created the files, hit three errors, fixed all three, and came back with a verified transform. The iteration happened between Claude and the hook.
The full picture
Three parts, one system:
Skill — gives Claude the rules before it writes a line. Engine classification, version discipline, write order, debug taxonomy, LML syntax, known SDK bugs. Prevents common mistakes and provides the knowledge to diagnose uncommon ones.
Hooks — close the feedback loop after every write and edit. Compile on LML change using lml-compile (a dotnet global tool that wraps the SDK compiler). Run transform on XSLT change via the Debugger HTTP API. Return results — output or error — to Claude’s context automatically.
The loop — Claude writes → hook runs → Claude reads result → Claude fixes if needed → hook runs again. One prompt from you; the iteration happens between Claude and the hook.
The skill and the hooks are independent — the skill works without the hooks (slower iteration, manual verification), the hooks work without the skill (no domain rules, more trial and error). Together they make a working environment where the AI can do real XSLT work reliably: catch silent namespace bugs before they reach production, fix C# compile errors in the same turn, iterate through LML compiler quirks without you lifting a finger.
The result isn’t “AI-generated XSLT” that you have to review and test manually. It’s XSLT that was already tested — by the same system that wrote it, in the same conversation turn, against real input data.
The XSLT Debugger extension is on the VS Code Marketplace for macOS and Windows.
I built a 98-tool MCP server for managing Meta Ads from Claude Code
I run a marketing agency managing 37+ client ad accounts on Meta. Every day I create campaigns, adjust budgets, fix ad copy, check pixels, rotate creatives. All through the Meta Marketing API.
After months of doing this manually with curl and scripts, I built an MCP server that lets me do everything from Claude Code. Then I open-sourced it.
## What it does
KonQuest Meta Ads MCP gives you 57 tools (free, MIT) for managing Meta Ads directly from Claude Code:
Campaign management – Create, read, update campaigns, ad sets, ads, and creatives. Duplicate campaigns with all their ads in one command.
Multi-asset ads – Create a single ad with video (9:16 + 1:1) and static images (1:1 + 4:5 + 9:16) using asset_feed_spec. No more creating separate ads per format.
Targeting toolkit – Search interests, behaviors, demographics, geo locations. Estimate audience sizes before creating ad sets.
Pixel diagnostics – Check pixel health, verify events are firing, test event sends, diagnose tracking issues on any URL.
Catalog and DPA – Product catalogs, feeds, product sets for dynamic product ads. Create and update product sets with filters.
Setup checker – 42+ automated checks per account: token health, pixel status, page connections, Instagram identity, catalog links.
## How it works in practice
Here’s what managing Meta Ads from Claude Code looks like:
▎ Create a conversion campaign for my skincare store
▎ targeting women 25-45 interested in beauty
Claude reads your vault files (brand voice, ICP profiles, angles),
validates everything, enforces naming conventions, checks your
pixel is firing, and creates:
- Campaign (PAUSED)
- Ad set with targeting
- Ads with creatives
Nothing goes live until you say so.
▎ What’s the ROAS on campaign C5 this week?
Claude pulls insights, breaks down by ad set,
flags underperformers, suggests budget shifts.
▎ Duplicate campaign C3 but change targeting to men 30-50
Claude duplicates the full structure (campaign + ad sets + ads),
applies new targeting, keeps naming conventions consistent.
## Safety first
Every write operation is supervised:
- All ads created PAUSED – nothing goes live without your explicit approval
- Naming enforcement – every object follows your naming convention before the API call happens
- Validation pipeline – compliance, creative specs, tracking, structure, and operational checks run before every write
- Rate limiting – respects Meta API limits with automatic backoff
- Rollback references – every change is logged so you can undo it
- Safety tiers – sandbox, standard, production access levels per account
## The vault system
The server reads local markdown files (your “vault”) for client intelligence:
your-vault/
01_CLIENTS/my-client/
00-profile.md # Account IDs, pixel, page
02-icp-personas.md # Target audience profiles
04-brand-voice.md # Tone, language, style
05-messaging-house.md # Angles, value props
matrix.md # Decision matrix
This means ad copy and targeting suggestions are grounded in actual client data, not generic AI output.
## Installation
bash
pip install konquest-meta-ads-mcp
Add to your .mcp.json:
{
"mcpServers": {
"meta-ads": {
"command": "uv",
"args": ["--directory", "/path/to/meta-ads-mcp", "run", "python", "-m", "meta_ads_mcp"],
"env": {
"META_ACCESS_TOKEN": "your_token_here"
}
}
}
}
Run the setup checker to verify everything works:
> run_setup_check
Open-core model
The 57-tool public package is free and fully functional. You can manage all your Meta Ads campaigns without paying anything.
There's also a premium bundle (41 additional tools, one-time purchase) that adds:
- Optimization engine - automated recommendations for budget shifts, creative rotation, and scaling
- Vault intelligence - generates ad copy grounded in client brand voice and ICP profiles
- Experiment management - A/B test tracking with statistical significance
- Budget governor - prevents overspend with configurable limits
- Greek language QA - Greeklish detection and orthography checks (adaptable to any language)
Premium is completely optional. The free package does the job for most use cases.
Built from production pain
This isn't a weekend project or an API wrapper. It was built from daily production use across 37+ real client accounts with real budgets. Every feature exists because something went wrong
without it:
- Naming enforcement exists because messy account structures made reporting impossible
- The validation pipeline exists because a bad ad set once spent budget on the wrong audience
- Safety tiers exist because a sandbox test accidentally ran on a production account
- Greek QA exists because Greeklish slipped into live ad copy
181 automated tests make sure nothing breaks.
Links
- GitHub: https://github.com/brandu-mos/konquest-meta-ads-mcp
- PyPI: https://pypi.org/project/konquest-meta-ads-mcp/
- Premium bundle: https://farasokster.gumroad.com/l/konquest-meta-ads-mcp-premium
If you manage Meta Ads and use Claude Code, give it a try. Happy to answer questions in the comments.
Hyundai, yeni bir kompakt elektrikli model geliştiriyor [Video]
Hyundai, Hindistan’dan gelen son paylaşımlara göre tamamen elektrikli bir kompakt otomobil modeli üzerinde testler gerçekleştiriyor. Şu anda “HE1” olarak anılan, ancak ticari adının “IONIQ 1” olabileceği iddia edilen model; köşeli bir yapıya sahip ve Hindistan’ın yanı sıra Güney Kore’de de kamuflajlı olarak test ediliyor. Henüz teknik olarak herhangi bir detayın ortaya çıkmadığı araç, doğrudan şehir içi kullanıma yönelik bir seçenek olarak fiyatına bağlı şekilde oldukça yüksek talep görebilir ve Türkiye’ye de getirilebilir.
Hyundai, yeni bir kompakt elektrikli model geliştiriyor [Video] yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.
Tutorial hells led me to JavaScript on Scrimba
(As my 1st writeup on Dev.to i wish to share my experience with the scrimba website)
i turned 37 this march and i’ve had this urge for a career transition into full stack development but initially HTML and CSS went well.. BUT the moment i stepped into JavaScript i ended up in Tutorial hell while going about & around in cycles for over three months while learning absolutely nothing.
feeling hopeless then i turned to Gemini Ai for help in studying JavaScript.. while our conversations were going on mastering the Syntax of JavaScript suddenly out of the Blues Gemini recommended that i take a look at “Scrimba“..
at 1st i thought that this would be another Tutorial rabbit hole, but alas to my amazement the site that was founded by Per Borgen, turned out to be much more than what traditionalists in this field offer.
even though much like its tutoring competitors Scrimba too offers interactive IDE’s and code snippet videos the novel teaching approach that it has adopted within its 9.4 hour JavaScript course amazed me and convinced me to take up a course with them. (at the moment i’m only 9% done in the JavaScript course, and i’m already enjoying their teaching approach)
why did HTML & CSS go well but not JavaScript?
throughout my full stack development study journey i have been trying to learn and the internet and thrown at me a lot of resources. and mind you most of these have only made me hit a brick wall.
all in all i could not find an effective course to teach myself Javascript and that made me rethink whether i should take this Journey of “Full Stack Development”. most of the online programs out there go by so fast that you only manage to rewrite the code that you see on the screen but never improve your muscle memory.
with all due respect the folks out there are doing a wonderful job in terms of teaching the wider world that coding isn’t difficult and the journey could be enjoyed by all, but their teaching methods used to go by a flash and there used to be so many concepts to master in one go. my brain usually does not digest concepts that faster.
most of the tutorials out there are beautifully explained but once you come to the juncture of practically applying JavaScript it becomes really hard and impossible.
The resources on Scrimba
folks would have definitely bumped into well-known tutoring sits like freecodecamp.org and the theodinproject, and Scrimba too adopts a similar approach in transmitting knowledge. all of the sites mentioned in this write up are great and they have their own strengths and merits.
but scrimba focuses more on improving muscle memory rather than teaching and practicing it. this has actually made a huge difference in how i see and feel about Javascript.
and not to forget about the web design of their page. the colors and the style icons that are used to showcase each course is very innovative, simplistic & minimalistic. this helps newbies like myself easily understand where we stand on the course we take and easily help us navigate the site..
Conclusion
for me Scrimba looks promising and at the moment i love everything about this site.
thank you scrimba for not having long lectures and huge documentations to read because the technical words in these usually scare me to death (since i dont have a computer science back ground).
so bottom line take – if you a learner who likes visual appeal and a hands on experience scrimba is surely for you!!
already 9% into the course and i actually feel like i can learn JavaScrip effectively around this time. so for the moment i’m getting things moving guys, lets see how it goes.
Teaching Claude Code to Write XSLT — The Hook Chain
Part 1 covered the XSLT skill — domain knowledge that prevents mistakes upfront. This part covers the runtime side: two PostToolUse hooks that automate compile-and-run so Claude sees the result of its own edits immediately.
Claude writes a file → hooks fire → output (or error) appears in context → Claude fixes and repeats. When the debugger is running and a matching launch config exists, no manual runs are needed. Otherwise the hook reports what’s missing and Claude can guide you.
PostToolUse hooks
Claude Code hooks are shell commands that fire after tool events. PostToolUse fires after every Write or Edit — meaning iterative fixes get immediate feedback on every change.
Configure in .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/generate-xslt-from-lml.sh",
"timeout": 45,
"statusMessage": "Compiling LML + running transform..."
},
{
"type": "command",
"command": "bash .claude/hooks/run-xslt-after-edit.sh",
"timeout": 30,
"statusMessage": "Running XSLT transform..."
}
]
}
]
}
}
Both hooks filter on file extension internally — the first acts only on .lml, the second only on .xslt.
Hook 1: LML compile
When Claude edits a .lml file, the designer isn’t involved — the compiled .xslt would be stale. This hook compiles it and runs the transform in one step.
The compiler: lml-compile
The Logic Apps testing SDK includes DataMapTestExecutor with a GenerateXslt() method. I wrapped it as a .NET global tool:
dotnet tool install -g lml-compile
No running Logic Apps host. No Azurite. Just:
lml-compile input.lml output.xslt
The hook
#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Only act on .lml files
case "$FILE_PATH" in *.lml) ;; *) exit 0 ;; esac
# Derive output path
BASENAME=$(basename "$FILE_PATH" .lml)
MAPS_DIR="$(cd "$(dirname "$FILE_PATH")/.." && pwd)/Maps"
OUT_XSLT="${MAPS_DIR}/${BASENAME}.xslt"
mkdir -p "$MAPS_DIR"
# Compile
set +e
COMPILE_OUTPUT=$(lml-compile "$FILE_PATH" "$OUT_XSLT" 2>&1)
COMPILE_EXIT=$?
set -e
if [ $COMPILE_EXIT -ne 0 ]; then
jq -n --arg err "$COMPILE_OUTPUT" '{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": ("LML compile failed: " + $err)
}
}'
exit 0
fi
# Find workspace and run transform via debugger HTTP API
# (walks up from file to find .vscode/launch.json)
# ... matches launch config, calls POST /run-transform ...
jq -n --arg ctx "$OUTPUT" '{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": ("LML compiled + transform result:n" + $ctx)
}
}'
If compile fails, Claude sees the error. If it succeeds and the debugger is running with a matching launch config, Claude sees the transform output in the same turn. If the debugger isn’t running or no config matches, the hook reports the compile success and what’s missing.
Hook 2: Transform runner
Fires when Claude writes or edits a .xslt file directly (Generate mode).
The key challenge: workspace derivation. In hook context, $(pwd) doesn’t point to the project — it can resolve to /private/tmp. The fix: walk up from the edited file’s path to find .vscode/launch.json:
DIR="$(cd "$(dirname "$EDITED_FILE")" && pwd)"
WORKSPACE=""
while [[ "$DIR" != "/" ]]; do
if [[ -f "$DIR/.vscode/launch.json" ]]; then
WORKSPACE="$DIR"; break
fi
DIR="$(dirname "$DIR")"
done
The hook then reads the matching launch config (resolving ${workspaceFolder}), extracts the input XML and engine, and calls the XSLT Debugger’s HTTP API:
RESULT=$(curl -s -X POST "http://127.0.0.1:$PORT/run-transform"
-H "Content-Type: application/json"
-d "{"stylesheet": "$STYLESHEET", "xml": "$XML",
"engine": "$ENGINE"}")
The result is returned via hookSpecificOutput so Claude sees the transform output in context.
How the hooks interact
Claude writes .xslt (Generate mode):
Claude → Write .xslt → Hook 1 ignores (not .lml) → Hook 2 runs transform → result to Claude
Claude writes .lml (LML mode):
Claude → Write .lml → Hook 1 compiles + runs transform → result to Claude → Hook 2 ignores (not .xslt)
The .xslt file written by lml-compile does not trigger Hook 2 — only files Claude writes directly via Write or Edit trigger hooks.
The feedback loop
Claude writes XSLT → hook runs transform → output in context → error? → Claude edits → hook runs again → correct output
No human in the loop for the fix cycle. Claude writes, sees the result, fixes if needed, sees the result again.
Shell bugs worth knowing
set -e with || true: Using OUTPUT=$(cmd) || true resets $? to 0 — the failure branch is unreachable. Fix: set +e, capture $?, then set -e.
grep -v returns exit 1 when no lines match: Under set -euo pipefail, this kills the hook. Append || true to the grep specifically.
$(pwd) in hook context: Doesn’t inherit the project directory. Always derive workspace from the edited file’s path.
The pattern generalises
The pattern is: skill (domain rules) + hook (verify after every write) = self-correcting AI. Nothing here is XSLT-specific except the domain knowledge and the tools the hooks call.
The same approach works for Terraform (terraform validate + terraform plan after .tf edits), SQL migrations, OpenAPI specs — anything where a write can be verified automatically.
The XSLT Debugger extension is on the VS Code Marketplace for macOS and Windows.









