$npx -y skills add KnoxOps/open-devops-skills --skill metrics-collectorPhase 1 Suspect: Discover cloud resources and apply idle detection signals to identify zombie candidates. Prefer cloud monitoring API (CloudWatch/Prometheus/Datadog) for historical metrics. SSH fallback: see prompts/signals-compute.md for in-VM collection rules.
| 1 | ## Input Parameters |
| 2 | |
| 3 | | Name | Type | Required | Description | |
| 4 | |------|------|----------|-------------| |
| 5 | | run_dir | string | Yes | Working directory absolute path for input/output files | |
| 6 | | ssh_key_path | string | No | SSH key path for cloud tags queries and reachability checks | |
| 7 | | task_id | string | Yes | Task ID for progress tracking in scan_episodes.json | |
| 8 | |
| 9 | ## Execution Flow |
| 10 | |
| 11 | ### Task Context |
| 12 | |
| 13 | Before starting execution, initialize `task_context.json`: |
| 14 | |
| 15 | ```json |
| 16 | { |
| 17 | "task_id": "<task_id from input>", |
| 18 | "current_step": 0, |
| 19 | "current_step_id": null, |
| 20 | "status": "running", |
| 21 | "steps": { |
| 22 | "layer_a1_discovery": "pending", |
| 23 | "layer_a2_exclusion": "pending", |
| 24 | "layer_b1_metrics": "pending", |
| 25 | "layer_b2_exclusion": "pending", |
| 26 | "finalize_output": "pending", |
| 27 | "review_signals": "pending" |
| 28 | }, |
| 29 | "updated_at": "<ISO timestamp>" |
| 30 | } |
| 31 | ``` |
| 32 | |
| 33 | Update this file after each step completes. On error, set step status to `"failed"` and overall `status` to `"failed"`. |
| 34 | |
| 35 | ### Step 1: layer_a1_discovery |
| 36 | |
| 37 | **Type:** inline |
| 38 | **Description:** Layer A.1: Discover all resources via cloud CLI and API tools per entity_type |
| 39 | |
| 40 | ## Execution |
| 41 | Follow these instructions: |
| 42 | |
| 43 | Read {run_dir}/intent_detection.json and {run_dir}/connection_config.json. |
| 44 | Discover all cloud resources in scope. For each resource, extract: |
| 45 | resource_id, resource_name, entity_type, resource_type, environment, |
| 46 | tags, provisioned_at, private_ip, status, spec, cost. |
| 47 | For compute resources, also detect virt_type (lxc/kvm/bare-metal/unknown). |
| 48 | Estimate monthly cost from spec: vCPU × $20 + RAM_GB × $3 + disk_GB × $0.10. |
| 49 | |
| 50 | Write {run_dir}/layer_a_raw_resources.json. |
| 51 | Update {run_dir}/scan_episodes.json with scan_status="phase1_layer_a1_completed". |
| 52 | |
| 53 | |
| 54 | ### Progress Tracking |
| 55 | |
| 56 | After completing this step, update `task_context.json`: |
| 57 | - Set `current_step_id` to `"layer_a1_discovery"` |
| 58 | - Set `steps.layer_a1_discovery` to `"completed"` |
| 59 | ### Step 2: layer_a2_exclusion |
| 60 | |
| 61 | **Type:** inline |
| 62 | **Description:** Layer A.2: Apply coarse exclusion rules to Layer A candidates |
| 63 | |
| 64 | ## Execution |
| 65 | Follow these instructions: |
| 66 | |
| 67 | Apply coarse exclusion rules to filter out obviously non-candidate resources. |
| 68 | |
| 69 | **Input**: {run_dir}/layer_a_raw_resources.json |
| 70 | |
| 71 | **Exclusion rules** (deterministic): |
| 72 | 1. New resources: provisioned_at within N days -> exclude, reason="new_resource". |
| 73 | N depends on environment: dev=1 day, staging=3 days, prod=7 days. |
| 74 | If environment is unknown, default to 7 days. |
| 75 | 2. Non-ready status: status in [terminated, deleting, creating] -> exclude, reason="not_ready" |
| 76 | 3. DR/backup tags: tags contain purpose=disaster-recovery or purpose=backup -> exclude, reason="dr_backup_tag" |
| 77 | 4. Recent CI/CD deployment: if the workload has been deployed via the user's CI/CD tool within N days -> exclude, reason="active_cicd". N depends on environment: dev=7, staging=30, prod=90. If no CI/CD tool is configured, skip this rule. For K8s workloads without CI/CD access, check ReplicaSet creation timestamps as a proxy. |
| 78 | |
| 79 | **LLM semantic check** (supplemental, for resources not already excluded): |
| 80 | - Check if resource name contains dr-, backup-, standby- prefix |
| 81 | - Check if tags contain role=standby, usage=backup |
| 82 | - If matched, mark as "potential_dr_backup" but still include in candidates |
| 83 | |
| 84 | |
| 85 | Write the output to the specified output file. |
| 86 | |
| 87 | ## Output |
| 88 | - **Schema:** schemas/layer-a-candidates.schema.json |
| 89 | - **File:** layer_a_candidates.json |
| 90 | |
| 91 | ### Progress Tracking |
| 92 | |
| 93 | After completing this step, update `task_context.json`: |
| 94 | - Set `current_step_id` to `"layer_a2_exclusion"` |
| 95 | - Set `steps.layer_a2_exclusion` to `"completed"` |
| 96 | ### Step 3: layer_b1_metrics |
| 97 | |
| 98 | **Type:** inline |
| 99 | **Description:** Layer B.1: Collect idle/zombie detection signals per resource type |
| 100 | |
| 101 | ## Execution |
| 102 | Follow these instructions: |
| 103 | |
| 104 | Collect idle/zombie detection signals for each resource in {run_dir}/layer_a_candidates.json. |
| 105 | |
| 106 | For signal definitions and per-resource-type thresholds, read prompts/signals-*.md. |
| 107 | |
| 108 | Collect idle detection signals for each Layer A candidate based on its resource_type. |
| 109 | |
| 110 | **Input**: {run_dir}/layer_a_candidates.json |
| 111 | |
| 112 | **Signal rules by resource type** (read the reference file for detailed thresholds and edge cases): |
| 113 | | resource_type | Reference file | |
| 114 | |--------------|---------------| |
| 115 | | compute | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-compute.md` | |
| 116 | | database, cache | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-database.md` | |
| 117 | | storage | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-storage.md` | |
| 118 | | network | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-network.md` | |
| 119 | | k8s_workload | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-k8s-workload.md` | |
| 120 | | k8s_service, k8s_orphan | `$PLUGINS/i |