cloud-cost-estimator

7 stars

Best use case

cloud-cost-estimator is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Teams using cloud-cost-estimator should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/cloud-cost-estimator/SKILL.md --create-dirs "https://raw.githubusercontent.com/heldernoid/agentic-build-templates/main/projects/devops-infrastructure/cloud-cost-estimator/skills/cloud-cost-estimator/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/cloud-cost-estimator/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How cloud-cost-estimator Compares

Feature / Agentcloud-cost-estimatorStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

This skill provides specific capabilities for your AI agent. See the About section for full details.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# cloud-cost-estimator Skill

## Overview

cloud-cost-estimator is a pure client-side React SPA that builds multi-provider cloud cost estimates. There is no backend server, no API, and no network requests at runtime. All pricing data is bundled into the compiled JavaScript at build time. Estimates are persisted in `localStorage`.

## Architecture: Client-only SPA

```
src/
  data/
    aws.ts          -- AWS service prices (ServicePrice[])
    gcp.ts          -- GCP service prices (ServicePrice[])
    azure.ts        -- Azure service prices (ServicePrice[])
    index.ts        -- re-exports + metadata (DATA_DATE, version)
  stores/
    estimateStore.ts  -- Zustand: wizard state (step, providers, lineItems)
    savedStore.ts     -- Zustand: persisted saved estimates (localStorage)
  lib/
    calculator.ts   -- computeLineItem, computeTotal, exportCSV, exportJSON
    ulid.ts         -- client-side ULID generation (no dependency)
    shareLink.ts    -- encode/decode estimate to URL hash
  components/
    ProgressSteps.tsx
    ProviderCard.tsx
    ServiceRow.tsx
    LineItemRow.tsx
    CostBar.tsx
    ProviderCompareTable.tsx
  pages/
    EstimatePage.tsx  -- 4-step wizard
    ComparePage.tsx
    SavedPage.tsx
    SettingsPage.tsx
```

## Core Data Types

```typescript
// src/data/types.ts
export interface ServicePrice {
  id: string;              // e.g. "ec2-t3.medium"
  provider: 'aws' | 'gcp' | 'azure';
  name: string;            // display name
  category: 'compute' | 'storage' | 'database' | 'network' | 'other';
  unit: string;            // e.g. "instance", "GB", "GB-month"
  pricePerUnit: number;    // always in USD cents (integer)
  billingModel: 'hourly' | 'monthly' | 'per-gb' | 'per-request' | 'per-gb-month';
  specs?: string;          // e.g. "2 vCPU, 4 GB"
  region?: string;         // default region for this price
  dataDate: string;        // "YYYY-MM-DD"
}

export interface LineItem {
  serviceId: string;
  provider: 'aws' | 'gcp' | 'azure';
  quantity: number;
  monthlyCents: number;    // computed: quantity * normalised monthly price
}

export interface Estimate {
  id: string;              // ULID
  name: string;
  providers: Array<'aws' | 'gcp' | 'azure'>;
  lineItems: LineItem[];
  totalCents: number;
  createdAt: string;       // ISO-8601
  dataDate: string;        // pricing data date used
}
```

## Calculator Logic

```typescript
// src/lib/calculator.ts
export const HOURS_PER_MONTH = 730;

export function computeLineItem(price: ServicePrice, quantity: number): number {
  // returns monthly cost in cents
  switch (price.billingModel) {
    case 'hourly':
      return Math.round(price.pricePerUnit * HOURS_PER_MONTH * quantity);
    case 'monthly':
      return Math.round(price.pricePerUnit * quantity);
    case 'per-gb':
    case 'per-gb-month':
      return Math.round(price.pricePerUnit * quantity);
    case 'per-request':
      return Math.round(price.pricePerUnit * quantity);
    default:
      return Math.round(price.pricePerUnit * quantity);
  }
}

export function computeTotal(lineItems: LineItem[]): number {
  return lineItems.reduce((sum, li) => sum + li.monthlyCents, 0);
}

// Format: 12345 cents -> "$123.45"
export function formatCents(cents: number): string {
  return '$' + (cents / 100).toFixed(2);
}
```

## Zustand Store Shapes

### estimateStore

```typescript
interface EstimateStore {
  step: 1 | 2 | 3 | 4;
  name: string;
  providers: Set<'aws' | 'gcp' | 'azure'>;
  selectedServices: Set<string>;       // serviceId[]
  quantities: Record<string, number>;  // serviceId -> quantity
  lineItems: LineItem[];               // computed on step 3 changes
  // actions
  setStep: (s: 1|2|3|4) => void;
  toggleProvider: (p: string) => void;
  toggleService: (id: string) => void;
  setQuantity: (id: string, qty: number) => void;
  reset: () => void;
}
```

### savedStore

```typescript
interface SavedStore {
  estimates: Estimate[];   // persisted via zustand/middleware/persist
  // actions
  save: (e: Estimate) => void;
  remove: (id: string) => void;
  load: (e: Estimate) => void;  // loads into estimateStore
  exportAll: () => void;        // triggers JSON download
}
// localStorage key: "cce_saved_estimates"
```

## Share Link Encoding

Estimates can be encoded in the URL hash. This allows sharing without a server.

```typescript
// src/lib/shareLink.ts
// Encode: JSON stringify -> btoa -> set window.location.hash
export function encodeEstimate(estimate: Estimate): string;
// Decode: read hash -> atob -> JSON parse
export function decodeEstimate(hash: string): Estimate | null;
```

URL format: `https://host/#share=<base64url-encoded-json>`

No server is contacted. Recipients decode the estimate in the browser.

## Export Formats

### CSV

```
Provider,Service,Category,Quantity,Unit,Unit Price (USD),Monthly (USD)
AWS,EC2 t3.medium,compute,3,instance,$30.37,$91.10
AWS,EC2 t3.large,compute,1,instance,$60.74,$60.74
...
,,,,,,Total,$303.16
```

### JSON

```json
{
  "id": "01HXYZ...",
  "name": "Staging Environment",
  "dataDate": "2026-03-01",
  "exportedAt": "2026-03-20T10:42:00Z",
  "totalMonthlyCents": 30316,
  "totalMonthlyUSD": 303.16,
  "lineItems": [
    {
      "provider": "aws",
      "serviceId": "ec2-t3.medium",
      "name": "EC2 t3.medium",
      "category": "compute",
      "quantity": 3,
      "unit": "instance",
      "unitPriceCents": 3037,
      "monthlyCents": 9110
    }
  ]
}
```

## localStorage Keys

| Key | Contents |
|-----|----------|
| `cce_saved_estimates` | JSON array of `Estimate[]` |
| `cce_settings` | User settings (region, currency, display prefs) |

## Settings

| Setting | Default | Type | Notes |
|---------|---------|------|-------|
| `awsRegion` | `us-east-1` | string | Displayed label only; pricing data is region-specific files |
| `gcpRegion` | `us-central1` | string | As above |
| `azureRegion` | `East US` | string | As above |
| `currency` | `USD` | string | USD only in v1; conversion stubs ready |
| `showAnnual` | `true` | boolean | Show annual projection alongside monthly |
| `showReserved` | `false` | boolean | Show reserved pricing column in catalog |
| `decimalPrecision` | `4` | number | Decimal places for per-unit pricing |
| `warnUnsaved` | `true` | boolean | Confirm dialog when loading over unsaved estimate |

## Provider Compare Logic

`ComparePage` loads all three provider price lists and maps services to equivalents using a `comparisons[]` array in `src/data/comparisons.ts`. Each entry names the equivalent service IDs per provider and a configured quantity.

```typescript
interface Comparison {
  spec: string;        // human label e.g. "2 vCPU / 4 GB general purpose"
  unit: string;        // "per instance, monthly"
  category: ServiceCategory;
  aws?: string;        // serviceId
  gcp?: string;
  azure?: string;
}
```

The cheapest provider per row is highlighted with `.cheapest` CSS class.

## Build and Development

```bash
# install
pnpm install

# dev server (Vite, port 5173)
pnpm dev

# production build
pnpm build

# preview production build
pnpm preview

# type check
pnpm typecheck

# lint
pnpm lint
```

## Vite Config

```typescript
// vite.config.ts
export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist',
    sourcemap: true,
  },
});
```

No environment variables are needed at runtime. The app is fully self-contained after `pnpm build`.

## Deployment

The `dist/` directory produced by `pnpm build` is a static site. Deploy to any static host:

```bash
# Netlify
netlify deploy --prod --dir dist

# Vercel
vercel --prod

# S3 + CloudFront
aws s3 sync dist/ s3://my-bucket/ --delete
aws cloudfront create-invalidation --distribution-id XXXXX --paths "/*"

# nginx
server {
  root /var/www/cloud-cost-estimator/dist;
  try_files $uri $uri/ /index.html;
}
```

## Adding New Pricing Data

Pricing data lives in `src/data/{provider}.ts` as a TypeScript array. To update:

1. Edit the array in the relevant provider file.
2. Update `DATA_DATE` in `src/data/index.ts` to reflect the new pricing date.
3. Run `pnpm build` to bundle updated prices into the JS.

Prices are stored as **integer cents** to avoid floating-point rounding issues. `$0.023/GB` is stored as `pricePerUnit: 2.3` (fractional cents allowed for sub-cent prices; the calculator rounds to integer cents at line-item level).

## Testing Price Accuracy

```bash
# run unit tests for calculator
pnpm test

# Example test assertion (vitest)
expect(computeLineItem({ pricePerUnit: 416, billingModel: 'hourly' }, 3))
  .toBe(910680);  // 3 * $0.0416 * 730hr = $91.07 -> 9107 cents
```

Note: `HOURS_PER_MONTH = 730` is the AWS standard for on-demand monthly estimates.

## Troubleshooting

| Issue | Cause | Fix |
|-------|-------|-----|
| Saved estimates lost | Browser storage cleared | Use Export before clearing |
| Share link too long | Large estimate | URL hash is base64-encoded JSON; works for up to ~50 services |
| Prices seem wrong | Bundled data is outdated | Check `DATA_DATE` in src/data/index.ts; update if needed |
| App blank on load | JS bundle error | Check browser console; run `pnpm build` again |
| Currency not converting | v1 limitation | Only USD supported in v1; conversion planned for v2 |

Related Skills

Skill: Cost Reporting

7
from heldernoid/agentic-build-templates

## Overview

Skill: Uptime Monitoring

7
from heldernoid/agentic-build-templates

## Overview

Skill: Status Page

7
from heldernoid/agentic-build-templates

## Overview

Skill: unit-conversion

7
from heldernoid/agentic-build-templates

## Overview

Skill: recipe-scaler

7
from heldernoid/agentic-build-templates

## Overview

reading-list

7
from heldernoid/agentic-build-templates

Operate the reading-list API to save, manage, tag, search, and export articles.

email-digest

7
from heldernoid/agentic-build-templates

Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.

websocket-realtime

7
from heldernoid/agentic-build-templates

Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".

poll-builder

7
from heldernoid/agentic-build-templates

Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.

Skill: personal-finance

7
from heldernoid/agentic-build-templates

## Overview

Skill: csv-import

7
from heldernoid/agentic-build-templates

## Overview

Skill: Syntax Highlighting

7
from heldernoid/agentic-build-templates

## Purpose