{"id":"ad3e86a6-5e2d-4701-84ba-81f7247ef97b","slug":"finfocus","kind":"skill","name":"finfocus","summary":"FinOps CLI for Pulumi — projected costs, actual spend tracking, budget enforcement, and cost optimization for cloud infrastructure","owner":"rshade","repoUrl":"https://github.com/rshade/finfocus","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":10,"match":null,"fewStars":false,"format":"skill","createdAt":"2025-08-12T10:33:44.000Z","addedAt":"2026-10-21T10:42:02.293Z","logoUrl":"https://avatars.githubusercontent.com/u/131337?u=35257ec6c538765bed5bfb38bb262019375273c8&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","developer-tools"],"tags":["aws","cloud-costs","cloud-infrastructure","cost-estimation","devops","finops","infrastructure-as-code","pulumi"],"runtime":"go","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-21T10:42:11.754Z","sample":false,"url":"https://conduits.dev/tools/finfocus","language":"Go","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# FinFocus\n\n*Cloud cost analysis for Pulumi infrastructure*\n\n[![CI](https://github.com/rshade/finfocus/actions/workflows/ci.yml/badge.svg)](https://github.com/rshade/finfocus/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/rshade/finfocus/graph/badge.svg)](https://codecov.io/gh/rshade/finfocus)\n[![Go Report Card](https://goreportcard.com/badge/github.com/rshade/finfocus)](https://goreportcard.com/report/github.com/rshade/finfocus)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/rshade/finfocus)](https://github.com/rshade/finfocus/blob/main/go.mod)\n[![Release](https://img.shields.io/github/v/release/rshade/finfocus)](https://github.com/rshade/finfocus/releases/latest)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)\n\n**Cloud cost analysis for Pulumi infrastructure** - Calculate projected and actual infrastructure costs without modifying your Pulumi programs.\n\nFinFocus is a CLI tool that analyzes Pulumi infrastructure definitions to provide accurate cost estimates, budget enforcement, and historical cost tracking through a flexible plugin-based architecture.\n\n## Why FinFocus?\n\nCloud cost surprises are the norm. Teams deploy infrastructure with Pulumi but have no visibility into what it will cost until the bill arrives. FinFocus closes that gap:\n\n- **Shift-left on costs** — See projected costs *before* you deploy, directly from `pulumi preview` output\n- **No code changes required** — Works with any existing Pulumi project via JSON export\n- **Budget guardrails** — Enforce spending limits in CI/CD pipelines with non-zero exit codes\n- **Plugin architecture** — Swap pricing sources without changing your workflow\n- **Single dashboard** — Interactive TUI combining actual spend, projected costs, drift analysis, and recommendations\n\n## Key Features\n\n- **🔭 [Unified Overview](docs/src/content/docs/commands/overview.md)**: Interactive dashboard combining actual costs, projected costs, drift analysis, and recommendations in a single view\n- **📊 [Projected Costs](docs/src/content/docs/reference/cli-commands.md#cost-projected)**: Estimate monthly costs before deploying infrastructure\n- **💰 [Budgets & Alerts](docs/src/content/docs/guides/budgets.md)**: Hierarchical budgets (global, provider, tag, type) with CI/CD thresholds\n- **💡 [Recommendations](docs/src/content/docs/guides/recommendations.md)**: Actionable cost optimization insights and savings opportunities\n- **♿ [Accessibility](docs/src/content/docs/guides/accessibility.md)**: High-contrast, plain text, and adaptive terminal UI modes\n- **💰 [Actual Costs](docs/src/content/docs/reference/cli-commands.md#cost-actual)**: Track historical spending with detailed breakdowns\n- **☸️ [Cluster Cost Allocation](docs/src/content/docs/guides/cluster-costs.md)**: Split Kubernetes cluster cost by namespace, controller, pod, node, or label with conservation guarantees\n- **🔌 [Plugin-Based](docs/src/content/docs/plugin-system.md)**: Extensible architecture supporting multiple cost data sources\n- **🧪 [E2E Testing](docs/src/content/docs/testing/e2e-guide.md)**: Comprehensive guide for validating infrastructure costs against real cloud resources\n- **📈 Advanced Analytics**: Resource grouping, filtering, and aggregation\n- **📱 Multiple Formats**: Table, JSON, and NDJSON output options\n- **🔍 Smart Filtering**: Filter by resource type, tags, or custom expressions\n- **🏗️ No Code Changes**: Works with existing Pulumi projects via JSON output\n\n## Quick Start\n\n### 1. Installation\n\n**Install script (recommended)** -- auto-detects OS/architecture and downloads the latest release:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/rshade/finfocus/main/scripts/install.sh | sh\n```\n\n**Build from source:**\n\n```bash\ngit clone https://github.com/rshade/finfocus\ncd finfocus\nmake build\n./bin/finfocus --help\n```\n\n<details>\n<summary>Manual download (pin to a specific version)</summary>\n\n```bash\n# Linux (amd64)\ncurl -L https://github.com/rshade/finfocus/releases/download/v0.3.3/finfocus-v0.3.3-linux-amd64.tar.gz -o finfocus.tar.gz\ntar -xzf finfocus.tar.gz\nchmod +x finfocus\nsudo mv finfocus /usr/local/bin/\n\n# macOS (Apple Silicon)\ncurl -L https://github.com/rshade/finfocus/releases/download/v0.3.3/finfocus-v0.3.3-macos-arm64.tar.gz -o finfocus.tar.gz\ntar -xzf finfocus.tar.gz && chmod +x finfocus && sudo mv finfocus /usr/local/bin/\n\n# macOS (Intel)\ncurl -L https://github.com/rshade/finfocus/releases/download/v0.3.3/finfocus-v0.3.3-macos-amd64.tar.gz -o finfocus.tar.gz\ntar -xzf finfocus.tar.gz && chmod +x finfocus && sudo mv finfocus /usr/local/bin/\n```\n\n```powershell\n# Windows - installs to a user-local directory, no admin rights required\nInvoke-WebRequest -Uri \"https://github.com/rshade/finfocus/releases/download/v0.3.3/finfocus-v0.3.3-windows-amd64.zip\" -OutFile finfocus.zip\nExpand-Archive finfocus.zip -DestinationPath .\n$installDir = \"$env:LocalAppData\\Programs\\finfocus\"\nNew-Item -ItemType Directory -Force -Path $installDir | Out-Null\nMove-Item finfocus.exe \"$installDir\\finfocus.exe\"\n# Add to PATH for the current session (add to $PROFILE for a permanent effect)\n$env:PATH = \"$installDir;$env:PATH\"\n```\n\n</details>\n\n### 2. Generate Pulumi Plan\n\nExport your infrastructure plan to JSON:\n\n```bash\ncd your-pulumi-project\npulumi preview --json > plan.json\n```\n\n### 3. Unified Overview\n\nSee all costs at a glance with the interactive dashboard. From inside any Pulumi\nproject directory, just run finfocus with no arguments:\n\n```bash\nfinfocus\n```\n\nOr invoke the subcommand directly:\n\n```bash\nfinfocus overview\n```\n\nFor CI/CD or when you manage the export step yourself:\n\n```bash\n# Pre-export state and plan\npulumi stack export > state.json\npulumi preview --json > plan.json\n\n# Launch overview with pre-exported files\nfinfocus overview --pulumi-state state.json --pulumi-json plan.json --plain --yes\n```\n\nExample plain text output (`--plain`). Amounts are sample data:\n\n```text\nRESOURCE     TYPE                      STATUS       ACTUAL(MTD)  PROJECTED   DELTA       DRIFT%  RECS  WARN\n--------     ----                      ------       -----------  ---------   -----       ------  ----  ----\nmy-instance  aws:ec2/instance:Inst...  ✓ active     $12.40       $15.00      +$6.20      +18% ⚠  2     drift\nmy-bucket    aws:s3/bucket:Bucket      ✓ active     $0.83        $1.00       -           -       -     -\nmy-db        aws:rds/instance:Inst...  ✓ active     $48.20       $50.00      -$8.40      -15% ⚠  1     drift\n\nSUMMARY      prod                      3 resources  $61.43 USD   $66.00 USD  -$2.20 USD\n```\n\nFull documentation: [Overview command](docs/src/content/docs/commands/overview.md)\n\n### Browser dashboard\n\nRun from the folder containing your Pulumi program:\n\n```bash\nfinfocus --web\n```\n\nFinFocus starts a local server, prints its session URL, and opens your browser.\nUse `--no-browser` on SSH or CI, and `--port 8484` when you need a fixed port.\nThe Overview, Cost, Recommendations, Estimate, and Config views use the same\nGo engine and plugins as the CLI and TUI. Config edits the project or global\n`config.hujson` and does not change cloud resources. Keyboard navigation,\nresource details, cluster expansion, and light/dark themes are available in\nthe browser.\n\nSources follow `finfocus overview`: auto-detect the current Pulumi project and\nstack, or pass `--pulumi-json plan.json` / `--pulumi-state state.json`.\n`--stack`, `--from`, `--to`, `--adapter`, and `--filter` are also accepted.\nThese source flags, `--port`, and `--no-browser` are root-local companions to\n`--web`; they are rejected without it. `--web` is mutually exclusive with `--mcp`.\nTerraform inputs are outside the web UI's current scope.\n\nThe Cost view's group selector and tag filter, and the Recommendations view's\n**Include dismissed** toggle, add browser controls for existing CLI flags.\nThe TUI fixes grouping at launch and has no include-dismissed toggle.\nEstimate changes are temporary what-if calculations; the dashboard cannot\nchange infrastructure or dismiss recommendations. What-If hides known non-billable\nresources by default; **Show all resources** restores them. Unknown billability\nand zero-spend resources remain available. Use **Run preview** for an\non-demand Pulumi preview and restart the session to load fresh actual costs.\n\nKeep the printed URL private: its token opens an authenticated local session.\nThe server binds to loopback, replaces the token in the address bar with an\nHTTP-only session cookie, and checks request origins. Encrypted stacks use a\nmasked browser passphrase prompt with retries. Stop the server with Ctrl+C;\nclosing a browser tab leaves it running.\n\nSee the [web UI guide](docs/src/content/docs/guides/web-ui.md) for launch options,\nstorage behavior, and browser test commands.\n\n### 4. Calculate Costs\n\n**Projected Costs** - Estimate costs before deployment:\n\n```bash\nfinfocus cost projected --pulumi-json plan.json\n```\n\n**Forecast** - Project that monthly cost forward. Plugins report a growth\nmodel. `--growth-rate 0.10` is 10% per month. The chart is plain text, and\n`--output json` returns the same points with timestamps.\n\n```bash\nfinfocus cost forecast --pulumi-json plan.json --growth-type linear --growth-rate 0.10\n```\n\n**Check Budget** - Verify if plan fits within budget:\n\n```bash\n# Configure budget in ~/.finfocus/config.hujson (see Configuration section)\n# Then check projected cost against budget\nfinfocus cost projected --pulumi-json plan.json\n\n# Exit with non-zero code if budget exceeded (CI/CD integration)\nfinfocus cost projected --pulumi-json plan.json --exit-on-threshold\n\n# Custom exit code (default: 1)\nfinfocus cost projected --pulumi-json plan.json --exit-on-threshold --exit-code 2\n\n# Filter budget scope (global, provider:<name>, tag:<key>=<value>, type:<resource-type>)\nfinfocus cost projected --pulumi-json plan.json --budget-scope \"provider:aws\"\n```\n\n**View Recommendations** - Find savings opportunities:\n\n```bash\nfinfocus cost recommendations --pulumi-json plan.json\n```\n\n## Example Output\n\n### Projected Cost Analysis\n\n```bash\n$ finfocus cost projected --pulumi-json examples/plans/aws-simple-plan.json\n\nBudget: $500.00 (75% used)\n[=====================>......] $375.00 / $500.00\n\nRESOURCE                          ADAPTER     MONTHLY   CURRENCY  NOTES\naws:ec2/instance:Instance         aws-spec    $375.00   USD       t3.xlarge\naws:s3/bucket:Bucket             none        $0.00     USD       No pricing info\n```\n\n### Actual Cost Analysis\n\n```bash\n$ finfocus cost actual --pulumi-json plan.json --from 2025-01-01 --group-by type --output json\n{\n  \"summary\": {\n    \"totalMonthly\": 45.67,\n    \"currency\": \"USD\",\n    \"byProvider\": {\"aws\": 45.67},\n    \"byService\": {\"ec2\": 23.45, \"s3\": 12.22, \"rds\": 10.00}\n  },\n  \"resources\": [...]\n}\n```\n\n## Core Concepts\n\n### Resource Analysis Flow\n\n1. **Export** - Generate Pulumi plan JSON with `pulumi preview --json`\n2. **Parse** - Extract resource definitions and properties\n3. **Query** - Fetch cost data via plugins or local specifications\n4. **Aggregate** - Calculate totals with grouping and filtering options\n5. **Output** - Present results in table, JSON, or NDJSON format\n\n### Plugin Architecture\n\nFinFocus uses plugins to fetch cost data from various sources:\n\n- **Cost Plugins**: Query cloud provider APIs (AWS Public Pricing, AWS Cost Explorer, Azure, etc.)\n- **Spec Files**: Local YAML/JSON pricing specifications as fallback\n- **Plugin Discovery**: Automatic detection from `~/.finfocus/plugins/`\n\n## Configuration\n\nFinFocus is configured via `~/.finfocus/config.hujson`. Configuration files use Hujson format (JSON with `//` comments and trailing commas allowed). Legacy `config.yaml` files are automatically migrated to Hujson format on first read.\n\n### Budget Configuration\n\n```yaml\n# yaml-language-server: $schema=https://rshade.github.io/finfocus/schemas/config.json\ncost:\n  budgets:\n    amount: 500.00\n    currency: USD\n    alerts:\n      - threshold: 80\n        type: actual\n      - threshold: 100\n        type: forecasted\n```\n\nSee [Budget Guide](docs/src/content/docs/guides/budgets.md) for full configuration details.\n\n#### Budget Notifications\n\nSend a Slack message or a JSON event to an HTTPS webhook when an alert threshold\nis crossed. Add destinations to an alert in the global config:\n\n```jsonc\n{\n  \"cost\": {\n    \"budgets\": {\n      \"global\": {\n        \"amount\": 500,\n        \"currency\": \"USD\",\n        \"alerts\": [\n          {\n            \"threshold\": 80,\n            \"type\": \"actual\",\n            \"notifications\": [{\"type\": \"slack\", \"url\": \"${FINFOCUS_NOTIFY_SLACK_URL}\"}]\n          }\n        ]\n      }\n    }\n  }\n}\n```\n\nNotifications are opt-in per run (`--notify` or `FINFOCUS_NOTIFY=true`), never\nchange the exit code, and only expand `${FINFOCUS_NOTIFY_*}` variables from the\nglobal config. See [Budget Notifications](docs/src/content/docs/guides/budgets.md#budget-notifications).\n\n### Environment Variables for Secrets\n\nFor sensitive values like API keys and credentials, use environment variables:\n\n```bash\n# AWS credentials (for aws-public and aws-ce plugins)\nexport FINFOCUS_PLUGIN_AWS_ACCESS_KEY_ID=\"your-access-key\"\nexport FINFOCUS_PLUGIN_AWS_SECRET_ACCESS_KEY=\"your-secret-key\"\n\n# Azure credentials (for azure-public plugin)\nexport FINFOCUS_PLUGIN_AZURE_SUBSCRIPTION_ID=\"your-subscription-id\"\n```\n\nThe naming convention is: `FINFOCUS_PLUGIN_<PLUGIN_NAME>_<KEY_NAME>` in uppercase.\n\n### Cost Allocation Tags\n\nFor accurate month-long cost tracking — especially when resources are replaced\nor destroyed mid-month — FinFocus can query cloud billing APIs by **resource\ntags**. This requires your Pulumi-managed resources to carry consistent tags.\n\n> **Why this matters**: When a resource is replaced (e.g., EC2 instance swap),\n> the old cloud ID disappears from Pulumi state. Without tags, FinFocus can\n> only see costs for the *current* resource. With tags like `pulumi:project`\n> on both the old and new resources, billing APIs return the full month's costs.\n>\n> **Important**: Pulumi does **not** automatically tag cloud resources with\n> `pulumi:project` or `pulumi:stack`. These are stack-level metadata only.\n> You must explicitly configure resource tagging using one of the methods below.\n\n<details>\n<summary><strong>AWS — Provider Default Tags</strong></summary>\n\n**Option A: Stack configuration (static values)**\n\n```yaml\n# Pulumi.dev.yaml\nconfig:\n  aws:defaultTags:\n    tags:\n      pulumi:project: my-app\n      pulumi:stack: dev\n      Environment: development\n      CostCenter: \"12345\"\n```\n\n**Option B: Explicit provider in code (dynamic values)**\n\nTypeScript:\n\n```typescript\nconst provider = new aws.Provider(\"tagged\", {\n  defaultTags: {\n    tags: {\n      \"pulumi:project\": pulumi.getProject(),\n      \"pulumi:stack\": pulumi.getStack(),\n      \"Environment\": \"dev\",\n    },\n  },\n});\n\n// Use this provider for all resources\nconst bucket = new aws.s3.Bucket(\"data\", {}, { provider });\n```\n\nGo:\n\n```go\nprovider, _ := aws.NewProvider(ctx, \"tagged\", &aws.ProviderArgs{\n    DefaultTags: &aws.ProviderDefaultTagsArgs{\n        Tags: pulumi.StringMap{\n            \"pulumi:project\": pulumi.String(ctx.Project()),\n            \"pulumi:stack\":   pulumi.String(ctx.Stack()),\n            \"Environment\":    pulumi.String(\"dev\"),\n        },\n    },\n})\n\n// Use this provider for all resources\nbucket, _ := s3.NewBucket(ctx, \"data\", nil, pulumi.Provider(provider))\n```\n\nPython:\n\n```python\nprovider = aws.Provider(\"tagged\", default_tags={\n    \"tags\": {\n        \"pulumi:project\": pulumi.get_project(),\n        \"pulumi:stack\": pulumi.get_stack(),\n        \"Environment\": \"dev\",\n    },\n})\n\n# Use this provider for all resources\nbucket = aws.s3.Bucket(\"data\", opts=pulumi.ResourceOptions(provider=provider))\n```\n\n**After tagging**: Activate `pulumi:project` and `pulumi:stack` as\n[Cost Allocation Tags](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html)\nin the AWS Billing Console. Tags take ~24 hours to appear in Cost Explorer.\n\n</details>\n\n<details>\n<summary><strong>GCP — Provider Default Labels</strong></summary>\n\n> GCP labels must be lowercase with hyphens/underscores only (no colons).\n> Use `pulumi_project` instead of `pulumi:project`.\n\n**Option A: Stack configuration**\n\n```yaml\n# Pulumi.dev.yaml\nconfig:\n  gcp:defaultLabels:\n    pulumi_project: my-app\n    pulumi_stack: dev\n    environment: development\n    cost_center: \"12345\"\n```\n\n**Option B: Explicit provider in code**\n\nTypeScript:\n\n```typescript\nconst provider = new gcp.Provider(\"tagged\", {\n  defaultLabels: {\n    pulumi_project: pulumi.getProject(),\n    pulumi_stack: pulumi.getStack(),\n    environment: \"dev\",\n  },\n});\n```\n\nGo:\n\n```go\nprovider, _ := gcp.NewProvider(ctx, \"tagged\", &gcp.ProviderArgs{\n    DefaultLabels: pulumi.StringMap{\n        \"pulumi_project\": pulumi.String(ctx.Project()),\n        \"pulumi_stack\":   pulumi.String(ctx.Stack()),\n        \"environment\":    pulumi.String(\"dev\"),\n    },\n})\n```\n\nPython:\n\n```python\nprovider = gcp.Provider(\"tagged\", default_labels={\n    \"pulumi_project\": pulumi.get_project(),\n    \"pulumi_stack\": pulumi.get_stack(),\n    \"environment\": \"dev\",\n})\n```\n\n> **Note**: GCP v8.0+ automatically adds the `goog-pulumi-provisioned` label\n> to all resources. Opt out with `gcp:addPulumiAttributionLabel = false`.\n\n</details>\n\n<details>\n<summary><strong>Azure — Stack Transformations</strong></summary>\n\nAzure Native does not have a provider-level `defaultTags` option. Use a\nstack transformation to inject tags into all taggable resources:\n\nTypeScript:\n\n```typescript\npulumi.runtime.registerStackTransformation((args) => {\n  if (args.props[\"tags\"] !== undefined) {\n    args.props[\"tags\"] = {\n      ...args.props[\"tags\"],\n      \"pulumi:project\": pulumi.getProject(),\n      \"pulumi:stack\": pulumi.getStack(),\n      \"Environment\": \"dev\",\n    };\n  }\n  return { props: args.props, opts: args.opts };\n});\n```\n\nGo:\n\n```go\nctx.RegisterStackTransformation(func(args *pulumi.ResourceTransformationArgs) *pulumi.ResourceTransformationResult {\n    if tags, ok := args.Props[\"tags\"]; ok {\n        if tagMap, ok := tags.(pulumi.StringMap); ok {\n            tagMap[\"pulumi:project\"] = pulumi.String(ctx.Project())\n            tagMap[\"pulumi:stack\"] = pulumi.String(ctx.Stack())\n            tagMap[\"Environment\"] = pulumi.String(\"dev\")\n            args.Props[\"tags\"] = tagMap\n        }\n    }\n    return &pulumi.ResourceTransformationResult{Props: args.Props, Opts: args.Opts}\n})\n```\n\nPython:\n\n```python\ndef auto_tags(args: pulumi.ResourceTransformationArgs):\n    if \"tags\" in args.props:\n        args.props[\"tags\"] = {\n            **args.props[\"tags\"],\n            \"pulumi:project\": pulumi.get_project(),\n            \"pulumi:stack\": pulumi.get_stack(),\n            \"Environment\": \"dev\",\n        }\n    return pulumi.ResourceTransformationResult(args.props, args.opts)\n\npulumi.runtime.register_stack_transformation(auto_tags)\n```\n\n</details>\n\n**FinFocus configuration** for tag-based cost queries:\n\n```yaml\n# ~/.finfocus/config.hujson\ncost:\n  allocation:\n    enabled: true\n    tags:\n      - \"pulumi:project\"\n      - \"pulumi:stack\"\n      - \"Environment\"\n      - \"CostCenter\"\n```\n\n### Resource History\n\nFinFocus maintains a resource history database (`~/.finfocus/history/history.db`)\nthat tracks which cloud resource IDs existed over time. This enables accurate\nmonth-long cost queries even when resources are replaced or destroyed mid-month.\n\n**How it works**: Every time FinFocus runs, it snapshots the current state of your\nresources. Over time, this builds a timeline of all cloud IDs each logical resource\nhas had. When you query actual costs for a full month, FinFocus queries billing APIs\nfor *all* historical IDs — not just the current one.\n\n> **Important**: Unlike the cache (`cache.db`), the history database is **not safe\n> to delete**. Deleting it loses the resource identity timeline, reducing cost\n> accuracy for past periods. Back up `~/.finfocus/history/` if you back up your\n> infrastructure configuration.\n\n```yaml\n# ~/.finfocus/config.hujson\ncost:\n  history:\n    enabled: true          # default: true\n    retention_days: 90     # how long to keep entries (default: 90)\n```\n\n## Advanced Usage\n\n### Resource Filtering\n\n```bash\n# Filter by resource type\nfinfocus cost projected --pulumi-json plan.json --filter \"type=aws:ec2/instance\"\n```\n\n### Output Formats\n\n```bash\n# Table format (default)\nfinfocus cost projected --pulumi-json plan.json --output table\n\n# JSON for API integration\nfinfocus cost projected --pulumi-json plan.json --output json\n\n# NDJSON for streaming/pipeline processing\nfinfocus cost projected --pulumi-json plan.json --output ndjson\n```\n\n## Configuration Management\n\nFinFocus provides commands to manage configuration:\n\n```bash\n# Initialize configuration (creates ~/.finfocus/config.hujson)\nfinfocus config init [--force]\n\n# Set configuration values\nfinfocus config set cost.budgets.amount 500.00\nfinfocus config set output.format json\n\n# Get configuration values\nfinfocus config get cost.budgets.amount\n\n# List all configuration\nfinfocus config list [--as json|yaml]\n\n# Validate configuration\nfinfocus config validate [--file path] [--output text|json] [--verbose]\n\n# Inspect effective routing configuration\nfinfocus config routes list [--output table|json]\n\n# Simulate plugin routing for a resource type\nfinfocus config routes test aws:ec2:Instance [region] [--output table|json]\n```\n\n## Multi-Plugin Routing\n\nFinFocus intelligently routes resources to appropriate plugins based on provider, resource patterns, and feature capabilities.\n\n### Automatic Routing (Zero Configuration)\n\nResources automatically route to plugins based on their supported providers:\n\n```bash\n# Install multiple plugins\nfinfocus plugin install aws-public\nfinfocus plugin install gcp-public\n\n# View plugin capabilities\nfinfocus plugin list --verbose\n# NAME        VERSION  PROVIDERS  CAPABILITIES                  SPEC    PATH\n# aws-public  1.0.0    aws        ProjectedCosts, ActualCosts   0.5.5   ~/.finfocus/plugins/aws-public/1.0.0/...\n# gcp-public  1.0.0    gcp        ProjectedCosts, ActualCosts   0.5.5   ~/.finfocus/plugins/gcp-public/1.0.0/...\n\n# Cost calculation automatically routes resources\nfinfocus cost projected --pulumi-json plan.json\n# AWS resources → aws-public\n# GCP resources → gcp-public\n```\n\n### Declarative Routing (Advanced Configuration)\n\nFor advanced control, configure plugin routing in `~/.finfocus/config.hujson`:\n\n```yaml\nrouting:\n  plugins:\n    # Route recommendations to AWS Cost Explorer (higher accuracy)\n    - name: aws-ce\n      features:\n        - Recommendations\n      priority: 20\n      fallback: true\n\n    # Route projected costs to AWS Public (no credentials needed)\n    - name: aws-public\n      features:\n        - ProjectedCosts\n        - ActualCosts\n      priority: 10\n      fallback: true\n\n    # Route EKS resources to specialized plugin (highest priority)\n    - name: eks-costs\n      patterns:\n        - type: glob\n          pattern: \"aws:eks:*\"\n      priority: 30\n```\n\n**Key Features:**\n\n- **Priority-Based Selection**: Higher priority plugins are queried first (default: 0)\n- **Automatic Fallback**: If a plugin fails, automatically try the next priority\n- **Pattern Matching**: Use glob or regex patterns to route specific resource types\n- **Feature Routing**: Assign different plugins for different capabilities\n\n### Validate Routing Configuration\n\n```bash\nfinfocus config validate [--file path] [--output text|json]\n\n# Output (success):\n# Configuration Validation\n# Syntax valid\n# Budget configuration valid\n# Configuration is valid\n```\n\n`--verbose` prints loaded output, plugin, and routing settings after a valid text report.\n\nSee the [Routing Configuration Guide](docs/src/content/docs/guides/routing.md) for detailed examples and troubleshooting.\n\n## Plugin Management\n\n### List & Install Plugins\n\n```bash\n# List the latest version of each installed plugin\nfinfocus plugin list\n\n# List every installed version and mark the one core uses\nfinfocus plugin list --all\n\n# List with detailed capabilities\nfinfocus plugin list --verbose\n\n# Install plugins\nfinfocus plugin install aws-public\nfinfocus plugin install vantage\n\n# Inspect plugin capabilities\nfinfocus plugin inspect aws-public\n\n# Validate plugin installation\nfinfocus plugin validate\n```\n\n### Available Plugins\n\n| Plugin | Status | Description |\n|--------|--------|-------------|\n| `aws-public` | Available | AWS public pricing data |\n| `aws-ce` | In Development | AWS Cost Explorer integration |\n| `azure-public` | In Development | Azure public pricing data |\n| `opencost` | Available | Kubernetes allocation cost from OpenCost or Kubecost |\n| `carbon` | Build from source | Offline EC2 operational carbon estimates alongside monetary pricing. See the [carbon plugin guide](docs/src/content/docs/plugins/carbon.md). |\n| `scorer` | Source install; first public release pending | Opt-in recommendation ranking with Jev (default) or Clef. Uses `SCORER_API_KEY`; hosted backends send recommendation data to TypeSafe AI or Cloudflare. Build with `make install-scorer`. See the [plugin README](https://github.com/rshade/finfocus/tree/main/plugins/scorer) and [scoring guide](docs/src/content/docs/guides/recommendation-scoring.md) |\n\n`jev` stays in the registry so an existing install can be updated, and it is\nmarked deprecated. `finfocus plugin install jev` warns and still installs that\nseparate release. It does not migrate settings. Use `finfocus plugin install\nscorer` for a new setup. Until scorer release assets are published, build it\nwith `make install-scorer`.\n\n## Pulumi Analyzer Integration\n\nFinFocus provides zero-click cost estimation during `pulumi preview` via the Pulumi Analyzer protocol:\n\n```bash\n# Start the analyzer server (prints port to stdout for Pulumi handshake)\nfinfocus analyzer serve [--debug]\n```\n\nWhen integrated with Pulumi, costs are automatically calculated and displayed as advisory diagnostics during preview, without modifying your Pulumi programs. The analyzer uses ADVISORY enforcement and never blocks deployments.\n\n## Debugging\n\nEnable debug output for troubleshooting:\n\n```bash\n# Global debug flag\nfinfocus --debug cost projected --pulumi-json plan.json\n\n# Environment variable\nexport FINFOCUS_LOG_LEVEL=debug\nexport FINFOCUS_LOG_FORMAT=json    # json or console\n```\n\n## Agentic CLI Features\n\nFinFocus supports agent and tooling integration through several features:\n\n### Global Flags\n\nAll commands support these global flags for programmatic integration:\n\n- **`--format json|human`**: Output format selection (JSON for machine consumption, human for terminal)\n- **`--dry-run`**: Preview changes without making them (affects: `plugin install/update/remove`, `analyzer install/uninstall`, `config init/set`, `plugin upgrade`, recommendation operations)\n- **`--yes`**: Skip all confirmation prompts (equivalent to approving interactive operations)\n- **`--idempotency-key`**: Opaque retry-deduplication key for distributed systems\n\n### Machine Discoverability\n\nFinFocus can be introspected by agents and tools:\n\n```bash\n# Emit command schema in AX format (default)\nfinfocus __schema\n\n# Emit command schema in MCP format\nfinfocus __schema --as=mcp\n```\n\n### Use finfocus from an AI assistant (MCP)\n\nFinFocus has a built-in Model Context Protocol (MCP) server. MCP clients such\nas Claude Code and Claude Desktop can call finfocus commands as tools, for\nexample `finfocus-cost-projected` and `finfocus-plugin-list`. Tool calls\nreturn JSON.\n\n```bash\n# Serve over stdio (what MCP clients launch); the two forms are equivalent\nfinfocus --mcp\nfinfocus mcp-server\n\n# Serve over HTTP (loopback-only by default)\nfinfocus mcp-server --transport=http --addr=127.0.0.1:8080\n\n# Serve over HTTP from any address (requires explicit opt-in)\nfinfocus mcp-server --transport=http --addr=0.0.0.0:8080 --allow-non-loopback\n```\n\nRegister it with Claude Code:\n\n```bash\nclaude mcp add finfocus -- finfocus --mcp\n```\n\nOr add it to Claude Desktop's `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"finfocus\": { \"command\": \"/path/to/finfocus\", \"args\": [\"--mcp\"] }\n  }\n}\n```\n\nThe built-in server replaces the standalone `finfocus-mcp` (formerly\n`pulumicost-mcp`) server. See the\n[MCP guide](docs/src/content/docs/guides/mcp.md) for the tool list, output and\nsafety arguments, and the mapping from the old server's tools.\n\n## Documentation\n\nComplete documentation is available in the [docs/](docs/) directory:\n\n- **👤 End Users**: [User Guide](docs/src/content/docs/guides/user-guide.md) - How to install and use FinFocus\n- **💰 Budgets**: [Budget Guide](docs/src/content/docs/guides/budgets.md) - Configure alerts and thresholds\n- **📜 Resource History**: [Resource History Guide](docs/src/content/docs/guides/resource-history.md) - Accurate month-long costs\n- **💡 Recommendations**: [Recommendations Guide](docs/src/content/docs/guides/recommendations.md) - Optimization insights\n- **♿ Accessibility**: [Accessibility Guide](docs/src/content/docs/guides/accessibility.md) - UI configuration\n- **🛠️ Engineers**: [Developer Guide](docs/src/content/docs/guides/developer-guide.md) - How to extend and contribute\n- **🏗️ Architects**: [Architect Guide](docs/src/content/docs/guides/architect-guide.md) - System design and integration\n- **🧪 E2E Testers**: [E2E Testing Guide](docs/src/content/docs/testing/e2e-guide.md) - Setup and execution\n- **💼 Business/CEO**: [Business Value](docs/src/content/docs/guides/business-value.md) - ROI and competitive advantage\n\n**Quick Links:**\n\n- [🚀 5-Minute Quickstart](docs/src/content/docs/getting-started/quickstart.md)\n- [📖 Full Documentation Index](docs/)\n- [🔌 Available Plugins](docs/src/content/docs/plugin-system.md#available-plugins) - AWS Public Pricing and more\n- [🛠️ Plugin Development](docs/src/content/docs/plugins/plugin-development.md)\n- [🏗️ System Architecture](docs/src/content/docs/architecture/system-overview.md)\n- [💬 FAQ & Support](docs/src/content/docs/support/faq.md)\n\n## Contributing\n\nWe welcome contributions! See our development documentation:\n\n- [CONTRIBUTING.md](CONTRIBUTING.md) - Development setup and guidelines\n- [CLAUDE.md](CLAUDE.md) - AI assistant development context\n- [Architecture Documentation](internal/) - Internal package documentation\n\n## License\n\nApache-2.0 - See [LICENSE](LICENSE) for details.\n\n## Agent Skills\n\nFinFocus ships [agent skills](agent-skills/) for AI coding assistants\n(Claude Code, Gemini CLI, etc.) that automate common workflows:\n\n| Skill | Description |\n|-------|-------------|\n| [finfocus-install](agent-skills/finfocus-install/) | Install CLI, detect providers, setup plugins and config |\n| [finfocus-analyzer-setup](agent-skills/finfocus-analyzer-setup/) | Configure Pulumi Analyzer for inline cost estimation |\n| [finfocus-routing](agent-skills/finfocus-routing/) | Configure plugin routing with priority and fallback |\n| [finfocus-budget](agent-skills/finfocus-budget/) | Configure budget thresholds, health, and CI exit codes |\n| [finfocus-diagnose](agent-skills/finfocus-diagnose/) | Debug connectivity, config resolution, cache, and zero cost |\n| [cost-check](agent-skills/cost-check/) | Estimate monthly cost, flag thresholds, write COST_CHECK.md |\n| [cost-drift](agent-skills/cost-drift/) | Compare actual and projected spend, flag drift, write COST_DRIFT.md |\n| [plugin-manage](agent-skills/plugin-manage/) | Install, update, remove, validate, and troubleshoot plugins |\n\nSee [agent-skills/README.md](agent-skills/README.md) for the full list.\n\n## Related Projects\n\n- [finfocus-spec](https://github.com/rshade/finfocus-spec) - Protocol definitions and schemas\n- [finfocus-plugin-aws-public](https://github.com/rshade/finfocus-plugin-aws-public) - AWS public pricing plugin\n- [finfocus-action](https://github.com/rshade/finfocus-action) - GitHub Action that posts cost estimates on pull requests\n- [finfocus-demo](https://github.com/rshade/finfocus-demo) - Live demo of finfocus-action; see [PR #1](https://github.com/rshade/finfocus-demo/pull/1) for the output\n- [agent-skills](https://github.com/rshade/agent-skills) - Generic agent skills; holds stubs that point to the cost skills here\n\n---\n\n**Getting Started**: Try the [examples](examples/) directory for sample Pulumi plans and pricing specifications.\n"}