diff --git a/.github/release/build-power.js b/.github/release/build-power.js
new file mode 100644
index 0000000..b3fa867
--- /dev/null
+++ b/.github/release/build-power.js
@@ -0,0 +1,177 @@
+'use strict'
+
+/**
+ * POWER.md generator (Kiro Power).
+ *
+ * Single source of truth is skills/terraform-skill/SKILL.md. This emits a
+ * repo-root POWER.md so the same skill installs as a Kiro Power ("Add power
+ * from GitHub"). Pure, deterministic, idempotent.
+ *
+ * POWER.md is a GENERATED, CI-owned artifact - same contract as
+ * .codex-plugin/plugin.json: never hand-edit. The release pre-commit hook
+ * regenerates and stages it so the tag carries an in-sync tree.
+ *
+ * displayName + keywords are reused from .codex-plugin/plugin.json (one
+ * curated source). references/ files are NOT moved; only relative links are
+ * rewritten so they resolve from repo root.
+ *
+ * Node built-ins only (fs, path). No npm deps. No YAML lib: the SKILL.md
+ * frontmatter is a fixed, simple shape parsed line by line.
+ *
+ * Usage:
+ * node .github/release/build-power.js # write POWER.md
+ * node .github/release/build-power.js --check # exit 1 if out of sync
+ */
+
+const fs = require('fs')
+const path = require('path')
+
+const SKILL_REL = 'skills/terraform-skill/SKILL.md'
+const CODEX_REL = '.codex-plugin/plugin.json'
+const POWER_REL = 'POWER.md'
+const SENTINEL_REL = 'version.json'
+const MCP_SERVER = 'terraform-mcp-server'
+
+function repoRoot() {
+ const root = process.env.GITHUB_WORKSPACE || process.cwd()
+ if (!fs.existsSync(path.join(root, SENTINEL_REL))) {
+ throw new Error(
+ `build-power: sentinel ${SENTINEL_REL} not found in repo root ` +
+ `${root}; refusing to run (wrong working directory?)`
+ )
+ }
+ return root
+}
+
+function splitFrontmatter(src) {
+ // parts[0] == '' (before first ---), [1] == frontmatter, [2] == body
+ const parts = src.split('---')
+ if (parts.length < 3 || parts[0].trim() !== '') {
+ throw new Error(`build-power: ${SKILL_REL} has no leading --- frontmatter`)
+ }
+ return { fm: parts[1], body: parts.slice(2).join('---') }
+}
+
+// The SKILL.md frontmatter is a fixed shape: top-level name/description/
+// license, then a metadata: block with 2-space-indented author/version.
+function parseFrontmatter(fm) {
+ const out = {}
+ for (const raw of fm.split('\n')) {
+ const line = raw.replace(/\r$/, '')
+ let m
+ if ((m = line.match(/^name:\s*(.+?)\s*$/))) out.name = m[1]
+ else if ((m = line.match(/^description:\s*(.+?)\s*$/)))
+ out.description = m[1]
+ else if ((m = line.match(/^\s+author:\s*(.+?)\s*$/))) out.author = m[1]
+ else if ((m = line.match(/^\s+version:\s*(.+?)\s*$/))) out.version = m[1]
+ }
+ for (const k of ['name', 'description', 'author', 'version']) {
+ if (!out[k]) {
+ throw new Error(`build-power: ${SKILL_REL} frontmatter missing ${k}`)
+ }
+ }
+ return out
+}
+
+function yamlDoubleQuoted(s) {
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'
+}
+
+function buildPower(root) {
+ const skillSrc = fs.readFileSync(path.join(root, SKILL_REL), 'utf8')
+ const { fm, body } = splitFrontmatter(skillSrc)
+ const meta = parseFrontmatter(fm)
+
+ const codex = JSON.parse(fs.readFileSync(path.join(root, CODEX_REL), 'utf8'))
+ const displayName =
+ (codex.interface && codex.interface.displayName) || meta.name
+ const keywords = Array.isArray(codex.keywords) ? codex.keywords : []
+ if (!keywords.length) {
+ throw new Error(`build-power: ${CODEX_REL} has no keywords`)
+ }
+
+ // references/ files stay in place; rewrite links so they resolve from root.
+ const rewritten = body
+ .replace(/\]\(references\//g, '](skills/terraform-skill/references/')
+ .replace(/^\n+/, '')
+ .replace(/\s*$/, '')
+
+ // Quote free-text scalars + every keyword so a future value containing a
+ // YAML-sensitive char (:, #, [, etc.) cannot break frontmatter parsing.
+ // version stays unquoted: a CI-controlled multi-dot semver is always a
+ // YAML string and the validate.yml semver check reads it directly.
+ const front = [
+ '---',
+ `name: ${yamlDoubleQuoted(meta.name)}`,
+ `displayName: ${yamlDoubleQuoted(displayName)}`,
+ `description: ${yamlDoubleQuoted(meta.description)}`,
+ `keywords: [${keywords.map((k) => yamlDoubleQuoted(k)).join(', ')}]`,
+ `author: ${yamlDoubleQuoted(meta.author)}`,
+ `version: ${meta.version}`,
+ '---',
+ ].join('\n')
+
+ const banner =
+ ''
+
+ const mcpTrailer = [
+ '## MCP Tools (Kiro)',
+ '',
+ 'This Power optionally bundles the HashiCorp official',
+ '`' +
+ MCP_SERVER +
+ '` (see `mcp.json`) for read-only Terraform Registry and',
+ 'provider/module documentation lookups. Kiro registers it under the',
+ 'Powers section of `~/.kiro/settings/mcp.json` on install. The guidance',
+ 'above works without it; with it, registry/schema lookups are exact',
+ 'instead of recalled.',
+ '',
+ 'The image uses the floating `latest` tag. Docker caches it on first run',
+ 'and does not auto-update; run `docker pull ' +
+ 'hashicorp/terraform-mcp-server:latest` to refresh, or pin a specific',
+ 'tag in `~/.kiro/settings/mcp.json` if a new release misbehaves.',
+ ].join('\n')
+
+ return `${front}\n\n${banner}\n\n${rewritten}\n\n${mcpTrailer}\n`
+}
+
+function main() {
+ const root = repoRoot()
+ const check = process.argv.includes('--check')
+ const generated = buildPower(root)
+ const dest = path.join(root, POWER_REL)
+ const current = fs.existsSync(dest) ? fs.readFileSync(dest, 'utf8') : null
+
+ if (check) {
+ if (current !== generated) {
+ console.error(
+ `build-power: ${POWER_REL} is out of sync with ${SKILL_REL}. ` +
+ `Run: node .github/release/build-power.js`
+ )
+ process.exit(1)
+ }
+ console.log(`build-power: ${POWER_REL} in sync`)
+ return
+ }
+
+ if (current === generated) {
+ console.log(`build-power: ${POWER_REL} already up to date`)
+ return
+ }
+ fs.writeFileSync(dest, generated)
+ console.log(`build-power: wrote ${POWER_REL}`)
+}
+
+if (require.main === module) {
+ try {
+ main()
+ } catch (err) {
+ console.error(`build-power: ${err && err.message ? err.message : err}`)
+ process.exit(1)
+ }
+}
+
+module.exports = { buildPower }
diff --git a/.github/release/pre-commit.js b/.github/release/pre-commit.js
index 97d5665..0b6b8cc 100644
--- a/.github/release/pre-commit.js
+++ b/.github/release/pre-commit.js
@@ -22,9 +22,11 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
+const { buildPower } = require('./build-power')
const SKILL_REL = 'skills/terraform-skill/SKILL.md'
const MANIFEST_REL = '.codex-plugin/plugin.json'
+const POWER_REL = 'POWER.md'
const SENTINEL_REL = 'version.json'
function repoRoot() {
@@ -86,6 +88,14 @@ function updateCodexManifest(root, version) {
return MANIFEST_REL
}
+// Regenerate the Kiro POWER.md from the (already version-synced) SKILL.md so
+// the release commit and tag carry an in-sync tree, same as the codex
+// manifest. Must run AFTER updateSkillVersion + updateCodexManifest.
+function updatePowerFile(root) {
+ fs.writeFileSync(path.join(root, POWER_REL), buildPower(root))
+ return POWER_REL
+}
+
async function preCommit(props) {
const version = props && props.version
if (!version || typeof version !== 'string') {
@@ -105,9 +115,12 @@ async function preCommit(props) {
console.log(`pre-commit: ${MANIFEST_REL} absent; skipped`)
}
+ const power = updatePowerFile(root)
+ console.log(`pre-commit: regenerated ${power} -> ${version}`)
+
// The action stages only version-file + changelog. Stage ours so
// they are in the release commit and the tag.
- const toStage = [skill]
+ const toStage = [skill, power]
if (manifest) toStage.push(manifest)
execSync(`git add ${toStage.join(' ')}`, { cwd: root, stdio: 'inherit' })
} catch (err) {
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 148e6b3..bcccdfa 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -6,6 +6,8 @@ on:
- 'skills/**'
- '.claude-plugin/**'
- '.codex-plugin/**'
+ - 'POWER.md'
+ - 'mcp.json'
- '.github/workflows/**'
- '.github/release/**'
push:
@@ -14,6 +16,8 @@ on:
- 'skills/**'
- '.claude-plugin/**'
- '.codex-plugin/**'
+ - 'POWER.md'
+ - 'mcp.json'
- '.github/workflows/**'
- '.github/release/**'
workflow_dispatch:
@@ -140,6 +144,49 @@ jobs:
print(f"✅ Codex manifest version in sync ({manifest_version})")
EOF
+ - name: Check POWER.md (Kiro) Sync
+ run: |
+ set -e
+ echo "🔍 Regenerating POWER.md and checking it is committed in sync..."
+ node .github/release/build-power.js --check
+ python3 << 'EOF'
+ import json, re, sys
+ import yaml
+
+ content = open('POWER.md').read()
+ if not content.startswith('---'):
+ print("❌ ERROR: POWER.md has no frontmatter")
+ sys.exit(1)
+ fm = yaml.safe_load(content.split('---', 2)[1])
+
+ required = {'name', 'displayName', 'description', 'keywords',
+ 'author', 'version'}
+ missing = required - set(fm.keys())
+ if missing:
+ print(f"❌ ERROR: POWER.md frontmatter missing {missing}")
+ sys.exit(1)
+ if not isinstance(fm['keywords'], list) or not fm['keywords']:
+ print("❌ ERROR: POWER.md keywords must be a non-empty list")
+ sys.exit(1)
+ if not re.match(r'^\d+\.\d+\.\d+$', str(fm['version'])):
+ print(f"❌ ERROR: POWER.md version not semver: {fm['version']!r}")
+ sys.exit(1)
+
+ mcp = json.load(open('mcp.json'))
+ servers = list((mcp.get('mcpServers') or {}).keys())
+ if not servers:
+ print("❌ ERROR: mcp.json has no mcpServers")
+ sys.exit(1)
+ for s in servers:
+ if s not in content:
+ print(f"❌ ERROR: mcp.json server {s!r} not referenced "
+ f"in POWER.md")
+ sys.exit(1)
+
+ print(f"✅ POWER.md in sync (v{fm['version']}, "
+ f"{len(fm['keywords'])} keywords, MCP: {', '.join(servers)})")
+ EOF
+
- name: Check File Size
run: |
LINES=$(wc -l < skills/terraform-skill/SKILL.md)
diff --git a/CLAUDE.md b/CLAUDE.md
index fe39f6a..3d28b60 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -10,13 +10,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
A **Claude Code skill** - executable documentation that Claude loads to provide Terraform/OpenTofu expertise. It encodes terraform-best-practices.com patterns into Claude's context as version-controlled AI instructions.
+It also ships as a **Kiro Power**: the repo-root `POWER.md` (+ optional
+`mcp.json`) is generated from `SKILL.md`, so the skill content is shared, not
+forked.
+
## Repository Structure
```
terraform-skill/
├── skills/
│ └── terraform-skill/ # Skill autodiscovered by Claude Code plugin system
-│ ├── SKILL.md # Core skill file (~299 lines)
+│ ├── SKILL.md # Core skill file (~299 lines) — single source of truth
│ └── references/ # Reference files loaded on demand
│ ├── ci-cd-workflows.md
│ ├── code-intelligence-lsp.md
@@ -26,15 +30,28 @@ terraform-skill/
│ ├── security-compliance.md
│ ├── state-management.md
│ └── testing-frameworks.md
+├── POWER.md # GENERATED Kiro Power (from SKILL.md) — CI-owned, never hand-edit
+├── mcp.json # Optional Kiro MCP (read-only terraform-mcp-server)
├── tests/ # Baseline scenarios and rationalization tracking
│ ├── baseline-scenarios.md
│ ├── compliance-verification.md
│ └── rationalization-table.md
-└── .github/workflows/
- ├── validate.yml # PR validation (frontmatter, size, links, lint)
- └── automated-release.yml # Auto-release on master push via conventional commits
+└── .github/
+ ├── release/
+ │ ├── pre-commit.js # Release version-sync hook (SKILL.md/codex/POWER.md)
+ │ └── build-power.js # POWER.md generator (`--check` for CI)
+ └── workflows/
+ ├── validate.yml # PR validation (frontmatter, POWER.md sync, size, links, lint)
+ └── automated-release.yml # Auto-release on master push via conventional commits
```
+`POWER.md` and `.codex-plugin/plugin.json` are **generated, CI-owned
+artifacts** synced from `SKILL.md` by `.github/release/build-power.js` and the
+release `pre-commit.js` hook. Never hand-edit them — `validate.yml` runs
+`build-power.js --check` and fails the PR if `POWER.md` drifts. Edit
+`SKILL.md`/`references/` and regenerate with
+`node .github/release/build-power.js`.
+
## Development Workflow
**This is documentation, not code.** No build, no compiled tests.
@@ -90,6 +107,8 @@ The release workflow automatically:
- Syncs `skills/terraform-skill/SKILL.md` YAML frontmatter
`metadata.version` (the single version source; the canonical version is the
git tag managed by the release pipeline)
+- Syncs `.codex-plugin/plugin.json` and regenerates `POWER.md` from
+ `SKILL.md`, staging both into the release commit + tag
**Never manually edit version numbers** - the CI handles this.
diff --git a/POWER.md b/POWER.md
new file mode 100644
index 0000000..edcc6c1
--- /dev/null
+++ b/POWER.md
@@ -0,0 +1,316 @@
+---
+name: "terraform-skill"
+displayName: "Terraform Skill"
+description: "Use when writing, reviewing, or debugging Terraform/OpenTofu modules, tests, CI, scans, or state ops - diagnoses failure mode (identity churn, secrets, blast radius, CI drift, state corruption) with version-aware guards."
+keywords: ["ci-cd", "iac", "infrastructure-as-code", "modules", "opentofu", "security-scanning", "state-management", "terraform", "testing"]
+author: "Anton Babenko"
+version: 1.14.0
+---
+
+
+
+# Terraform Skill for Claude
+
+Diagnose-first guidance for Terraform and OpenTofu. Core file is a workflow; depth lives in references loaded on demand.
+
+## Response Contract
+
+Every Terraform/OpenTofu response must include:
+
+1. **Assumptions & version floor** — runtime (`terraform` or `tofu`), exact version, providers, state backend, execution path (local/CI/Cloud/Atlantis), environment criticality. State assumptions explicitly if the user did not provide them.
+2. **Risk category addressed** — one or more of: identity churn, secret exposure, blast radius, CI drift, compliance gaps, state corruption, provider upgrade risk, testing blind spots.
+3. **Chosen remediation & tradeoffs** — what was chosen, what was traded off, why.
+4. **Validation plan** — exact commands (`fmt -check`, `validate`, `plan -out`, policy check) tailored to runtime and risk tier.
+5. **Rollback notes** — for any destructive or state-mutating change: how to undo, what evidence to keep.
+
+Never recommend direct production apply without a reviewed plan artifact and approval.
+
+## Workflow
+
+1. **Capture execution context** — runtime+version, provider(s), backend, execution path, environment criticality.
+2. **Diagnose failure mode(s)** using the routing table below. If intent spans categories, load both references.
+3. **Load only the matching reference file(s)** — do not preload depth the task does not need.
+4. **Propose fix with risk controls** — why this addresses the mode, what could still go wrong, guardrails (tests/approvals/rollback).
+5. **Generate artifacts** — HCL, migration blocks (`moved`, `import`), CI changes, policy rules.
+6. **Validate before finalizing** — run validation commands tailored to risk tier.
+7. **Emit the Response Contract** at the end.
+
+## Diagnose Before You Generate
+
+| Failure category | Symptoms | Primary references |
+|------------------|----------|--------------------|
+| **Identity churn** | Resource addresses shift after refactor, `count` index churn, missing `moved` blocks | [Code Patterns: count vs for_each](skills/terraform-skill/references/code-patterns.md#count-vs-for_each-deep-dive), [Code Patterns: moved blocks](skills/terraform-skill/references/code-patterns.md#moved-blocks-terraform-11), [Code Patterns: LLM mistakes](skills/terraform-skill/references/code-patterns.md#llm-mistake-checklist--code-patterns) |
+| **Secret exposure** | Secrets in defaults, state, logs, CI artifacts | [Security & Compliance](skills/terraform-skill/references/security-compliance.md), [Code Patterns: write-only](skills/terraform-skill/references/code-patterns.md#write-only-arguments-terraform-111), [State Management](skills/terraform-skill/references/state-management.md) |
+| **Blast radius** | Oversized stacks, shared prod/non-prod state, unsafe applies | [State Management](skills/terraform-skill/references/state-management.md), [Module Patterns](skills/terraform-skill/references/module-patterns.md) |
+| **CI drift** | Local plan ≠ CI plan, apply without reviewed artifact, unpinned versions | [CI/CD Workflows](skills/terraform-skill/references/ci-cd-workflows.md), [Code Patterns: versions](skills/terraform-skill/references/code-patterns.md#version-management) |
+| **Compliance gaps** | Missing policy stage, no approval model, no evidence retention | [Security & Compliance](skills/terraform-skill/references/security-compliance.md), [CI/CD Workflows](skills/terraform-skill/references/ci-cd-workflows.md) |
+| **Testing blind spots** | Plan-only validation of computed values, set-type indexing, mock/real confusion | [Testing Frameworks](skills/terraform-skill/references/testing-frameworks.md) |
+| **State corruption / recovery** | Stuck lock, backend migration, drift reconciliation | [State Management](skills/terraform-skill/references/state-management.md) |
+| **Provider upgrade risk** | Breaking-change provider bump, unpinned modules | [Code Patterns: versions](skills/terraform-skill/references/code-patterns.md#version-management), [Module Patterns](skills/terraform-skill/references/module-patterns.md) |
+| **Provider lifecycle** | Removing a provider with resources still in state, orphaned resources, `removed` block usage | [State Management: Provider Removal](skills/terraform-skill/references/state-management.md#provider-removal) |
+| **Navigation / safe-rename blind spots** | Cannot locate symbol defs/refs semantically, value-symbol rename done as blind text replace, grep-only refactor missing refs, hallucinated `rg` shim | [Code Intelligence](skills/terraform-skill/references/code-intelligence-lsp.md#terraform-ls-capability-matrix) |
+
+## When to Use This Skill
+
+**Activate when:** creating or reviewing Terraform/OpenTofu configurations or modules, setting up or debugging tests, structuring multi-environment deployments, implementing IaC CI/CD, choosing module patterns or state organization, configuring or migrating remote state backends.
+
+**Don't use for:** basic HCL syntax questions Claude already knows, provider API reference (link to docs), cloud-platform questions unrelated to Terraform/OpenTofu.
+
+## Core Principles
+
+### Module Hierarchy
+
+| Type | When to Use | Scope |
+|------|-------------|-------|
+| **Resource module** | Single logical group of connected resources | VPC + subnets, SG + rules |
+| **Infrastructure module** | Collection of resource modules for a purpose | Multiple resource modules in one region/account |
+| **Composition** | Complete infrastructure | Spans multiple regions/accounts |
+
+Flow: resource → resource module → infrastructure module → composition.
+
+### Directory Layout
+
+```
+environments/ # prod/ staging/ dev/ — per-env configurations
+modules/ # networking/ compute/ data/ — reusable modules
+examples/ # minimal/ complete/ — docs + integration fixtures
+```
+
+Separate **environments** from **modules**. Use `examples/` as both documentation and test fixtures. Keep modules small and single-responsibility.
+
+See [Module Patterns](skills/terraform-skill/references/module-patterns.md) for architecture principles, naming conventions, variable/output contracts.
+
+### Naming Conventions (summary)
+
+- Descriptive resource names (`aws_instance.web_server`, not `aws_instance.main`)
+- Reserve `this` for genuine singleton resources only
+- Prefix variables with context (`vpc_cidr_block`, not `cidr`)
+- Standard files: `main.tf`, `variables.tf`, `outputs.tf`, `versions.tf`
+
+See [Module Patterns: Variable Naming](skills/terraform-skill/references/module-patterns.md) and [Code Patterns: Block Ordering](skills/terraform-skill/references/code-patterns.md#block-ordering--structure) for examples.
+
+### Block Ordering (summary)
+
+Resource blocks: `count`/`for_each` first → arguments → `tags` → `depends_on` → `lifecycle`.
+Variable blocks: `description` → `type` → `default` → `validation` → `nullable` → `sensitive`.
+
+See [Code Patterns: Block Ordering & Structure](skills/terraform-skill/references/code-patterns.md#block-ordering--structure) for the full rules and examples.
+
+## Testing Strategy
+
+### Decision Matrix: Which Testing Approach?
+
+| Situation | Approach | Tools | Cost |
+|-----------|----------|-------|------|
+| Quick syntax check | Static analysis | `validate`, `fmt` | Free |
+| Pre-commit validation | Static + lint | `validate`, `tflint`, `trivy`, `checkov` | Free |
+| Terraform 1.6+, simple logic | Native test framework | `terraform test` | Free-Low |
+| Pre-1.6, or Go expertise | Integration testing | Terratest | Low-Med |
+| Security/compliance focus | Policy as code | OPA, Sentinel | Free |
+| Cost-sensitive workflow | Mock providers (1.7+) | Native tests + mocks | Free |
+| Multi-cloud, complex | Full integration | Terratest + real infra | Med-High |
+
+### Native Test Rules (1.6+)
+
+Before writing test code: validate resource schemas via Terraform MCP so assertions target real attributes.
+
+- `command = plan` — fast, for input-derived values only
+- `command = apply` — required for **computed values** (ARNs, generated names) and **set-type nested blocks**
+- Set-type blocks cannot be indexed with `[0]` — use `for` expressions or materialize via `command = apply`
+- Common set types: S3 encryption rules, lifecycle transitions, IAM policy statements
+
+See [Testing Frameworks](skills/terraform-skill/references/testing-frameworks.md) for static-analysis pipelines, native-test patterns, Terratest integration, mock providers, and the full LLM-mistake checklist.
+
+## Count vs For_Each — Quick Rule
+
+| Scenario | Use | Why |
+|----------|-----|-----|
+| Boolean condition (create / don't) | `count = condition ? 1 : 0` | Optional singleton toggle |
+| Items may be reordered or removed | `for_each = toset(list)` | Stable resource addresses |
+| Reference by key | `for_each = map` | Named access |
+| Multiple named resources | `for_each` | Better identity stability |
+
+**Never** use list index as long-lived identity — removing a middle element reshuffles every address after it. For the decision matrix, safe migration playbook, `moved` block patterns, and known-at-plan failure cases, see [Code Patterns: count vs for_each](skills/terraform-skill/references/code-patterns.md#count-vs-for_each-deep-dive).
+
+## Locals for Dependency Management
+
+Using `try()` in a local to prefer a conditional resource's attribute over its parent is a specialized but high-value pattern — it forces correct deletion order without explicit `depends_on`. Common use: VPC + secondary CIDR associations + subnets.
+
+See [Code Patterns: Locals for Dependency Management](skills/terraform-skill/references/code-patterns.md#locals-for-dependency-management) for the full pattern and worked example.
+
+## Module Development
+
+Standard layout:
+
+```
+my-module/
+├── README.md # Usage documentation
+├── main.tf # Primary resources
+├── variables.tf # Typed inputs with descriptions
+├── outputs.tf # Output values
+├── versions.tf # required_version + required_providers
+├── examples/
+│ ├── minimal/
+│ └── complete/
+└── tests/
+ └── module_test.tftest.hcl # or Go for Terratest
+```
+
+**Variable contracts**: always `description`, always explicit `type`, use `validation` for complex constraints, use `sensitive = true` for secrets, prefer `optional()` with typed defaults (1.3+) over untyped `map(any)`.
+
+**Output contracts**: always `description`, mark sensitive outputs, expose stable subsets (not whole provider objects).
+
+See [Module Patterns](skills/terraform-skill/references/module-patterns.md) for the full contract patterns, module release checklist, and LLM-mistake checklist.
+
+## CI/CD
+
+Pipeline stages: **validate** → **test** → **plan** → **apply** (with environment protection).
+
+Cost control: mock providers on PR validation, real-cloud integration only on main or scheduled, tag test resources, auto-cleanup.
+
+Drift prevention: pin runtime and providers, commit `.terraform.lock.hcl`, apply the **reviewed plan artifact** from the plan stage (do not re-run `plan` inside the apply job), run policy/security stage on every path to apply.
+
+See [CI/CD Workflows](skills/terraform-skill/references/ci-cd-workflows.md) for GitHub Actions, GitLab CI, and Atlantis templates plus the LLM-mistake checklist.
+
+## Security & Compliance
+
+**Essential checks:**
+
+```bash
+trivy config .
+checkov -d .
+```
+
+**Don't:** store secrets in variables or `.tfvars`, use default VPC, skip encryption, open security groups to `0.0.0.0/0`, use inline `ingress`/`egress` blocks in `aws_security_group`.
+
+**Do:** source secrets from AWS Secrets Manager / Parameter Store or use `write_only` arguments on 1.11+, create dedicated VPCs, enforce encryption at rest and TLS, least-privilege SGs, use separate `aws_vpc_security_group_{ingress,egress}_rule` resources (AWS provider v5+).
+
+Marking a variable `sensitive = true` masks display only — the value still lives in state. Use `write_only` / `*_wo` on 1.11+, or keep secret material out of Terraform entirely via runtime lookups.
+
+See [Security & Compliance](skills/terraform-skill/references/security-compliance.md) for trivy/checkov pipelines, state-file hardening, compliance mappings, and the LLM-mistake checklist.
+
+## State Management
+
+**Never use local state in teams or production.** Remote backends provide automatic locking, encryption, versioning, audit logging, and safe collaboration.
+
+### Minimum Viable Backend (AWS S3, 1.10+)
+
+```hcl
+terraform {
+ backend "s3" {
+ bucket = "my-terraform-state"
+ key = "prod/vpc/terraform.tfstate"
+ region = "us-east-1"
+ encrypt = true
+ use_lockfile = true # Native S3 locking, 1.10+
+ }
+}
+```
+
+On Terraform < 1.10, use `dynamodb_table = "terraform-state-lock"` instead of `use_lockfile`. Azure Storage, GCS, and Terraform Cloud all offer built-in locking — see the State Management reference for syntax.
+
+### State Organization
+
+| Pattern | Use When | Example Path |
+|---------|----------|--------------|
+| **Per environment** | Different teams per env | `prod/terraform.tfstate`, `staging/...` |
+| **Per component** | Independent lifecycles | `prod/vpc/`, `prod/eks/`, `prod/rds/` |
+| **Hybrid** (recommended) | Both benefits | `prod/networking/`, `prod/compute/`, `staging/networking/` |
+
+Split state when: different teams, different update cadences, or >500 resources. Combine when: tightly coupled resources, <100 resources, same lifecycle.
+
+See [State Management](skills/terraform-skill/references/state-management.md) for locking, migration, multi-team isolation, disaster recovery, and the LLM-mistake checklist.
+
+## Version Management
+
+| Component | Strategy | Example |
+|-----------|----------|---------|
+| Terraform runtime | Pin minor | `required_version = "~> 1.9"` |
+| Providers | Pin major | `version = "~> 5.0"` |
+| Modules (prod) | Pin exact | `version = "5.1.2"` |
+| Modules (dev) | Allow patch | `version = "~> 5.1"` |
+
+Commit `.terraform.lock.hcl` intentionally. Keep provider/runtime upgrades in a separate PR from functional changes. See [Code Patterns: Version Management](skills/terraform-skill/references/code-patterns.md#version-management) for constraint syntax and upgrade workflow.
+
+## Modern Terraform Features (1.0+)
+
+| Feature | Min version | Common use |
+|---------|-------------|------------|
+| `try()` | 0.13+ | Safe fallbacks, replaces `element(concat())` |
+| `nullable = false` | 1.1+ | Prevent `null` silently overriding defaults |
+| `moved` blocks | 1.1+ | Refactor without destroy/recreate |
+| `optional()` with defaults | 1.3+ | Typed object attributes |
+| `import` blocks | 1.5+ | Declarative imports, reviewable in VCS |
+| `check` blocks | 1.5+ | Runtime assertions |
+| Native `terraform test` | 1.6+ | Built-in test framework |
+| Mock providers | 1.7+ | Cost-free unit testing |
+| `removed` blocks | 1.7+ | Declarative resource removal |
+| Provider-defined functions | 1.8+ | Provider-specific transformations (requires provider to declare functions) |
+| Cross-variable validation | 1.9+ | Reference other `var.*` in `validation` blocks |
+| `write_only` arguments | 1.11+ | Secrets never stored in state |
+| S3 native lock-file | 1.10+ | State locking without DynamoDB |
+
+Before emitting a feature, verify the runtime floor. See [Code Patterns: Feature Guard Table](skills/terraform-skill/references/code-patterns.md#feature-guard-table--version-floor--common-llm-errors) for the full table with common LLM error patterns per feature.
+
+## Runtime-Specific Guidance
+
+- **Terraform 1.0-1.5 / OpenTofu 1.0-1.5**: Terratest for integration, static analysis + plan validation only (no native tests).
+- **1.6+**: native `terraform test` / `tofu test` available — migrate simple unit tests, keep Terratest for complex integration.
+- **1.7+**: mock providers cut test cost — mock for unit tests, real runs for final integration.
+- **1.10+**: S3 native lock-file (`use_lockfile`) is the correct default for new configurations — DynamoDB locking is no longer required.
+- **1.11+**: `write_only` arguments for secret handling keep credentials out of state.
+- **Terraform vs OpenTofu**: both supported. For licensing, governance, and feature delta, see [Quick Reference: Terraform vs OpenTofu](skills/terraform-skill/references/quick-reference.md#terraform-vs-opentofu-comparison).
+
+## Code Intelligence (terraform-ls)
+
+Semantic navigation for HCL. terraform-ls is optional; without it every row below degrades to a disclosed `rg` + Read fallback.
+
+Self-contained terraform-ls layer of a generic code-intelligence discipline - apply the rows below directly. Recommended companion: the `code-intelligence` plugin (same `antonbabenko/agent-plugins` marketplace) carries the generic discipline (position anchoring, degradation gate, disclosure format, anti-phantom-shim) and ships `/code-intelligence:doctor` for readiness. If it is installed, defer to its generic protocol; this skill stays fully self-contained without it.
+
+| Goal | Use | Tradeoff |
+|------|-----|----------|
+| Find definition / all references | terraform-ls `goToDefinition` / `findReferences` | Needs `init` + a position anchor |
+| Rename value symbol (var/local/output/provider alias) | Manual: `findReferences` -> per-file fresh Read -> edit -> `validate` | No rename provider |
+| Rename resource/module address | `moved` block + `plan` shows 0 destroy | Text rename forces destroy/recreate |
+| Exact text / known name / `.tfvars` / non-HCL | `rg` + Read | No semantic scope |
+
+✅ Supported: `goToDefinition`, `findReferences`, `documentSymbol`, `hover`, `workspaceSymbol`.
+❌ Unsupported: `goToImplementation`, call hierarchy, rename provider. Do not call these then report their absence as a finding.
+
+- ✅ Prereq: local `terraform`/`tofu` on PATH, `terraform init` run; cold start may need one retry.
+- ✅ LSP calls are position-anchored (`file:line:character`) - anchor with `rg` first, never symbol-name-only.
+- ❌ Do not claim "LSP broken, using rg" until the [Degradation Gate](skills/terraform-skill/references/code-intelligence-lsp.md#degradation-gate) passes; disclose any tool substitution on the first line.
+
+Depth: [Code Intelligence](skills/terraform-skill/references/code-intelligence-lsp.md#terraform-ls-capability-matrix).
+
+## Reference Files
+
+Progressive disclosure — essentials here, depth on demand:
+
+- [Testing Frameworks](skills/terraform-skill/references/testing-frameworks.md) — static analysis, native tests, Terratest, mock providers
+- [Module Patterns](skills/terraform-skill/references/module-patterns.md) — structure, variable/output contracts, `terraform_remote_state` rules, release checklist
+- [CI/CD Workflows](skills/terraform-skill/references/ci-cd-workflows.md) — GitHub Actions, GitLab CI, Atlantis, cost control
+- [Security & Compliance](skills/terraform-skill/references/security-compliance.md) — trivy/checkov, secrets handling, compliance mappings
+- [State Management](skills/terraform-skill/references/state-management.md) — backends, locking, migration, multi-team, recovery
+- [Code Patterns](skills/terraform-skill/references/code-patterns.md) — block ordering, `count`/`for_each` deep dive, modern features, version management, locals
+- [Code Intelligence](skills/terraform-skill/references/code-intelligence-lsp.md) - terraform-ls capabilities, position-anchored calls, manual rename, degradation gate
+- [Quick Reference](skills/terraform-skill/references/quick-reference.md) — command cheat sheets, flowcharts, troubleshooting
+
+## License
+
+Apache License 2.0. See LICENSE for full terms.
+
+**Copyright © 2026 Anton Babenko**
+
+## MCP Tools (Kiro)
+
+This Power optionally bundles the HashiCorp official
+`terraform-mcp-server` (see `mcp.json`) for read-only Terraform Registry and
+provider/module documentation lookups. Kiro registers it under the
+Powers section of `~/.kiro/settings/mcp.json` on install. The guidance
+above works without it; with it, registry/schema lookups are exact
+instead of recalled.
+
+The image uses the floating `latest` tag. Docker caches it on first run
+and does not auto-update; run `docker pull hashicorp/terraform-mcp-server:latest` to refresh, or pin a specific
+tag in `~/.kiro/settings/mcp.json` if a new release misbehaves.
diff --git a/README.md b/README.md
index eff37cd..c359070 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,26 @@ Update with `cd ~/.antigravity/skills/terraform-skill && git pull`.
+
+Kiro
+
+This repo is also a [Kiro Power](https://kiro.dev/docs/powers/) (root
+`POWER.md` + optional `mcp.json`). In Kiro: **Powers panel → "Add power from
+GitHub"**, then paste:
+
+```text
+https://github.com/antonbabenko/terraform-skill
+```
+
+Kiro activates the power on keyword match (e.g. "terraform", "opentofu",
+"state", "modules"). Installing it also registers the optional read-only
+HashiCorp `terraform-mcp-server` (from `mcp.json`) under the Powers section of
+`~/.kiro/settings/mcp.json` — the guidance works without it. `POWER.md` is
+generated from `skills/terraform-skill/SKILL.md`; the skill content is shared,
+not duplicated.
+
+
+
Manual (symlink local clone)
diff --git a/mcp.json b/mcp.json
new file mode 100644
index 0000000..6425ff6
--- /dev/null
+++ b/mcp.json
@@ -0,0 +1,11 @@
+{
+ "mcpServers": {
+ "terraform-mcp-server": {
+ "command": "docker",
+ "args": ["run", "-i", "--rm", "hashicorp/terraform-mcp-server:latest"],
+ "env": {},
+ "disabled": false,
+ "autoApprove": []
+ }
+ }
+}