metergraph-cli 0.2.0-preview.3 → 0.2.0-preview.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: metergraph-iterate
3
+ description: Refine and rerun a Metergraph model-swap analysis after the person has inspected the report in the app - turn their findings into a specific change (eval revision, cases, candidates, traffic or route settings), start a new approved run linked to the previous one, and compare the two honestly. Use when someone returns with "the grader is wrong on these cases", "drop that model", "rerun it", "what changed since last run", or stage 6 of the metergraph-model-swap loop.
4
+ ---
5
+
6
+ # Refine and rerun
7
+
8
+ A rerun is a new run with one deliberate change. The earlier run, report and
9
+ eval revision stay as they are.
10
+
11
+ ## 0. Confirm the connection
12
+
13
+ If this conversation has not already confirmed the workspace, call
14
+ `metergraph_get_workspace_context`, then `metergraph_get_capabilities`, before
15
+ anything else. Say which workspace you are connected to in one line, and plan
16
+ only with the tools the capabilities advertise.
17
+
18
+ ## 1. Recover where the loop is
19
+
20
+ 1. Read the loop record (in the conversation or `.metergraph/model-swap.md`). If
21
+ there is none, `metergraph_list_analysis_runs` and `metergraph_get_report`
22
+ on the latest run for this workload, and rebuild it.
23
+ 2. Confirm the workload's `selection_reference` still exists with
24
+ `metergraph_list_classified_workloads`. A newer classification may have
25
+ replaced it; say so and pick the matching workload by its traces, not by its
26
+ name alone.
27
+
28
+ ## 2. Turn the finding into one change
29
+
30
+ Ask what the person saw in the app, then map it:
31
+
32
+ | Finding | Change | Skill |
33
+ | --- | --- | --- |
34
+ | A grade is wrong on some cases | Fix the check or rubric criterion; save a new eval revision; add the cases as reference cases | `metergraph-evals` |
35
+ | The bar is missing something important | Add a check or criterion, test it on the preview, save a new revision | `metergraph-evals` |
36
+ | A candidate is clearly out, or one is missing | Change candidate selection on the Opportunities page | `metergraph-candidates` |
37
+ | Too few samples, or `insufficient_samples` | More production traffic for this workload, then rerun | `metergraph-workloads` |
38
+ | Traces were skipped | Replay opt-out or residency on a route; the person changes it in the app | `metergraph-candidates` |
39
+ | The winner looks right | No rerun. Plan the code change and a canary instead | — |
40
+
41
+ Change one thing per rerun when you can, so the comparison means something.
42
+ If several must change, list them all in the confirmation.
43
+
44
+ ## 3. Rerun only with a new yes
45
+
46
+ A rerun spends money and sends captured prompts to providers again. Get the
47
+ same explicit confirmation as the first run (see `metergraph-analyze`), plus
48
+ what changed. If the request already names the change and approves a budget and
49
+ time, that is the confirmation: restate it, make the change (for an eval fix,
50
+ save the new revision first, as in `metergraph-evals`), then start. Then call `metergraph_start_analysis` with:
51
+
52
+ - a **new** `request_key`, for example `rerun-<pattern_id>-<yyyymmdd>-<n>`;
53
+ - the same `pinned_workload`, unless the workload itself changed;
54
+ - `rerun_of_run_id` set to the previous run ID;
55
+ - `reference_binding_id` when reference cases were bound.
56
+
57
+ Poll and read the report as in `metergraph-analyze` and `metergraph-report`.
58
+
59
+ ## 4. Compare honestly
60
+
61
+ Show the two runs side by side only for what is comparable:
62
+
63
+ ```
64
+ run <a> (<date>) run <b> (<date>)
65
+ eval <revision a> <revision b>
66
+ samples <n> <n>
67
+ <candidate> <grade, cost ratio> <grade, cost ratio>
68
+ ```
69
+
70
+ If the eval revision, the sample or the candidate set changed, say that a
71
+ different grade may come from the change, not from the model. Then give the
72
+ decision line from `metergraph-report` with the new report link, and update the
73
+ loop record.
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: metergraph-model-swap
3
+ description: Run the Metergraph model-swap loop from a coding agent - choose a workload, choose traces and candidate models, define or refine the eval, start the analysis, summarize the report with its app link, then refine and rerun. Use when someone asks whether a workload could use a cheaper or better model, asks to plan or run a model swap analysis, asks "what should my largest workload run on", or comes back after inspecting a Metergraph report. Use the metergraph skill first when Metergraph is not connected.
4
+ ---
5
+
6
+ # Model-swap loop
7
+
8
+ This skill is the map. Each stage has its own skill; load it when you reach the
9
+ stage. The person stays in the coding agent for choosing, defining and
10
+ rerunning, and goes to the Metergraph app to read the detailed report and
11
+ inspect evidence.
12
+
13
+ ```
14
+ choose workload → choose traces and models → define or refine eval
15
+ → run analysis → summary + report link → (person inspects evidence in the app)
16
+ → refine and rerun ↺
17
+ ```
18
+
19
+ | Stage | Skill | Ends with |
20
+ | --- | --- | --- |
21
+ | 1. Choose workload | `metergraph-workloads` | One classified workload, with its `selection_reference` and why it was picked |
22
+ | 2. Choose traces and models | `metergraph-candidates` | A run plan: environment, window, candidate models, route settings that must change |
23
+ | 3. Define or refine the eval | `metergraph-evals` | A saved eval revision bound to the workload, or a written rubric the person enters in the app |
24
+ | 4. Run the analysis | `metergraph-analyze` | A run ID, polled to a final state |
25
+ | 5. Summarize | `metergraph-report` | A short summary and the report link |
26
+ | 6. Refine and rerun | `metergraph-iterate` | What changed and a new, separately approved run |
27
+
28
+ ## Start every session the same way
29
+
30
+ 1. Call `metergraph_get_workspace_context`, then `metergraph_get_capabilities`.
31
+ Say which workspace and deployment you are connected to in one line. Stop if
32
+ it is not the one the person means.
33
+ 2. Call `metergraph_list_analysis_runs` with a small limit. If a run already
34
+ exists for this workload, the person is probably on stage 6: read its report
35
+ first instead of starting over, and give the report link
36
+ (`<origin>/#analysis?report=<analysis_id>`, as in `metergraph-report`).
37
+ 3. Write down the tools that are advertised. Plan only with those. When a stage
38
+ needs a tool that is not advertised, say which one, keep the selection you
39
+ have, and hand that step to the app page named in the stage skill. Never
40
+ invent a tool, call a dashboard API with an agent key, or drive the app in a
41
+ browser unless the person asks for that specific change.
42
+
43
+ ## Rules that hold across stages
44
+
45
+ - **Workloads are not routes.** Routes are labels on traffic. An analysis runs
46
+ on a classified workload, identified by `source_run_id`, `pattern_id` and
47
+ `pattern_set_version`. Do not rank or plan on routes and then discover this at
48
+ the start step.
49
+ - **Check the data before planning.** A workspace created today, data that all
50
+ arrived in one batch, fewer than 10 samples, or totals that disagree between
51
+ tools and the app all change the plan. Say so first, with the numbers.
52
+ - **One decision at a time.** End each turn with the single decision you need
53
+ from the person, not a list of everything that could be configured.
54
+ - **Spending and data egress need a yes.** Starting an analysis sends copies of
55
+ captured prompts to other model providers and costs money. Stage 4 has the
56
+ exact confirmation to get. A connected key, a configured provider key or an
57
+ earlier yes for a different run is not approval.
58
+ - **Settings are the person's.** Replay opt-out, data residency, candidate
59
+ models, cost and latency limits and the size floor are changed in the app.
60
+ Explain the change and give the page; do not make it for them.
61
+ - **Keep answers short.** Lead with the answer or the blocker. Tables only for
62
+ three or more rows. Give confidence once, where it matters.
63
+
64
+ ## Keep a loop record
65
+
66
+ Keep these values in the conversation, and, if you can write files in the
67
+ person's project and they agree, in `.metergraph/model-swap.md`. They are not
68
+ secret. They let a later session pick up at stage 6.
69
+
70
+ ```
71
+ workspace: <name> (<workspace_id>) at <origin>
72
+ workload: <display_name> source_run_id=<…> pattern_id=<…> pattern_set_version=<…>
73
+ environment: <production|…> window: <from>–<to>
74
+ candidates: <model ids>
75
+ eval: <suite or rubric revision id, or "rubric in chat, not saved">
76
+ runs: <run_id> (<status>) → report <analysis_id> <link>
77
+ decision: <switch | no switch | more testing> because <reason>
78
+ next: <the one next step>
79
+ ```
80
+
81
+ Never put keys, `.env` values or captured prompt or response text in it.
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: metergraph-onboarding
3
+ description: Guide a coding agent from an empty local or hosted MeterGraph workspace to a verified application trace, then hand off to workload and eval selection.
4
+ ---
5
+
6
+ # Set up MeterGraph through the first usable trace
7
+
8
+ Confirm the user's deployment choice, application repository, target workspace,
9
+ coding client and whether they want SDK instrumentation or an existing supported
10
+ import. Use the actual MeterGraph origin the user selected. Do not guess an
11
+ origin from a credential. Record client/version, application revision, setup
12
+ start time and fixture source. Keep demo, synthetic, import and application
13
+ traffic distinct.
14
+
15
+ ## Establish the deployment and workspace
16
+
17
+ For hosted MeterGraph, connect with OAuth. No operator-issued key is needed.
18
+ Add the hosted endpoint as an HTTP MCP server with no credential or headers,
19
+ for example `claude mcp add --transport http metergraph
20
+ https://app.metergraph.dev/v1/agent/mcp`. Then use the client's own
21
+ authentication step (in Claude Code, `/mcp`, choose `metergraph`, then
22
+ Authenticate). The human completes browser sign-in and consent in the browser,
23
+ in the workspace they intend to use, and confirms the workspace shown on the
24
+ consent page. The consent page names the requested role; a client following
25
+ the server's challenge requests Metadata. If the client cannot
26
+ complete OAuth, or the user needs retained content or replay, describe the
27
+ Keys-page scoped connection as a manual alternative and obtain the user's
28
+ choice. Never silently fall back to a key.
29
+
30
+ For the first application trace, check the installed Metergraph CLI's version
31
+ and help before naming commands. `metergraph-cli@0.1.0` has `doctor` and project
32
+ skill installation; a later installed version may offer `login`, `setup` and
33
+ `verify`. Use only commands present in that installation. Otherwise follow
34
+ https://www.metergraph.dev/docs/start/first-trace/ and its manual setup steps.
35
+ Do not infer that a server endpoint or source-only CLI command has shipped.
36
+
37
+ For customer-local MeterGraph, use the released bundle and its included
38
+ README, signed manifest and verification files. The human obtains the current
39
+ pull-only registry credential; registry access is not yet universally self-serve.
40
+ Use password-stdin and the local secret store for registry login. Verify the
41
+ archive before running its scripts. Configure the bundle's local settings,
42
+ start with `./bin/start`, and inspect `./bin/status` and the reported readiness.
43
+ Use the bundle's printed loopback URL. Record bundle version and image digests.
44
+ The human sets their local admin/database passwords and signs in. Reuse the
45
+ bundle's startup/recovery instructions; do not replace them with an ad-hoc
46
+ Compose stack. First boot may ingest a demo trace, which is synthetic evidence.
47
+
48
+ ## Connect the coding client
49
+
50
+ For hosted OAuth, the connection is the URL-only server added above. For a
51
+ keyed or customer-local connection, reuse the Keys page's client-specific
52
+ instructions, documented in
53
+ https://www.metergraph.dev/docs/guides/agent-access/. The endpoint is
54
+ `<origin>/v1/agent/mcp`; the stdio bridge is `metergraph-mcp` with
55
+ `METERGRAPH_URL` and `METERGRAPH_AGENT_TOKEN` in its environment. Verify
56
+ `tools/list`, then `metergraph_get_workspace_context` and
57
+ `metergraph_get_capabilities`. Record the selected origin separately: `managed` identifies the hosted profile
58
+ and does not distinguish staging from production. Match the returned workspace ID and deployment
59
+ to the user's selection before reading traces. Capabilities and required scopes
60
+ from this connection are authoritative. Do not infer write authority from
61
+ `agent:read` or provider authority from a healthy MCP connection.
62
+
63
+ Keep these credentials separate:
64
+
65
+ | Credential | Destination and purpose | Human step |
66
+ | --- | --- | --- |
67
+ | Registry pull credential | Image registry only | Obtain/refresh through the supported distribution channel |
68
+ | Ingest credential | Application SDK or supported import connector | Create an ingest key for the selected workspace |
69
+ | Agent Access credential | Coding client's supported OAuth/secret configuration | Sign in/consent or create a scoped connection |
70
+ | Model-provider credential | Deployment's supported runner/provider configuration | Set the provider key and approve paid execution |
71
+
72
+ Never ask for secrets in chat, commit them, put them in a shared MCP file or
73
+ save them in verification evidence. A read connection is sufficient for this
74
+ onboarding verification. Replay, eval previews and analyses make separate
75
+ authorization and cost decisions.
76
+
77
+ ## Instrument or import application traffic
78
+
79
+ Reuse the appropriate language/provider instructions and the repository prompt
80
+ at https://www.metergraph.dev/docs/agents/instrument-a-repo/#verify-one-application-call, or a supported
81
+ connector under https://www.metergraph.dev/docs/import/overview/. Inspect the real
82
+ application entry points first. Exclude tests, fixtures, mock libraries, demos
83
+ and validation jobs from production traffic claims. Keep provider base URLs,
84
+ gateway credentials and application behavior intact. Review instrumentation
85
+ changes with the user, including calls that could not be captured.
86
+
87
+ Use the selected deployment's ingestion destination and the ingest credential,
88
+ not its MCP URL or Agent Access key. Use that deployment's SDK/connector setup
89
+ instructions to choose the complete ingest URL. Set capture/retention as the
90
+ workspace owner intends; metadata-only telemetry does not prove retained
91
+ inputs/outputs needed for eval replay. Do not silently enable content capture.
92
+ Avoid treating received HTTP acceptance as worker processing completion.
93
+
94
+ With user authorization, trigger one normal application request/job and record
95
+ its application-owned trace ID and time. For an import, record the supported
96
+ connector, source trace identity and imported time window. Do not create an LLM
97
+ connection, paid eval rule or provider invocation just to manufacture proof.
98
+
99
+ ## Verify and hand off
100
+
101
+ 1. Read bounded `metergraph_get_ingestion_health` if available. Preserve pending,
102
+ processed and failed states and receive/process timestamps. Counts apply to
103
+ a workspace window; they do not identify the batch that contains one trace.
104
+ 2. Read bounded `metergraph_query_traces` in the same workspace/time window and
105
+ locate the exact application/import trace ID. Continue bounded pagination
106
+ when advertised. An empty page is a setup state, not proof of no ingestion.
107
+ 3. If the user permits a content-class read and `trace_content` is available,
108
+ inspect one `metergraph_get_trace` for retained usable input/output and capture
109
+ warnings. Record availability and omissions, never the content in setup logs.
110
+ 4. When advertised, use classified-workload and model-readiness tools to explain
111
+ available cohorts, capture eligibility, supported checks and missing provider
112
+ credentials. A configured credential is not proof of a successful provider
113
+ call. Missing capability is an explicit handoff blocker.
114
+ 5. Return workspace/profile, bundle/app/client revisions, source label, exact
115
+ trace ID/time window, ingestion and capture evidence, warnings, human steps
116
+ still needed and the next eval selection action. A synthetic demonstration
117
+ does not meet real application or hosted pilot acceptance.
118
+
119
+ Verify the named trace through the connected client's advertised MCP tools,
120
+ with an explicit source label and the same workspace/deployment before and
121
+ after any permitted content read. The older trace-debug contract has no
122
+ workspace envelope, so confirm its tenant-bound connection separately. Trace
123
+ discovery is not analysis readiness. Keep retained content out of setup reports.
124
+
125
+ ## Recovery
126
+
127
+ | State | Next action |
128
+ | --- | --- |
129
+ | Signup, sign-in or OAuth consent required | Hand the browser step to the human, then reconnect and verify workspace identity |
130
+ | Registry access expired/denied | Refresh the pull credential through the supported distribution channel, repeat registry login, retain local data |
131
+ | Local startup or bootstrap failure | Inspect `bin/status` and the bundle's named logs/readiness checks; follow its recovery instructions |
132
+ | Runner unavailable | Use the bundle's runner health instructions; do not claim MCP health proves analysis readiness |
133
+ | Empty data | Check application environment, correct ingest destination/key, flush/import window and worker status; exclude demos/mocks |
134
+ | Received but pending/failed | Inspect ingestion failure classes and the existing retry/recovery flow before changing instrumentation |
135
+ | Missing/retained content unavailable | Explain capture policy, retention and omission; collect a new permitted application trace |
136
+ | Agent credential revoked or insufficient scope | Reconnect/re-authorize through the workspace owner's supported path; do not use an ingest key |
137
+ | Provider key absent or candidate unsupported | Complete the runner/provider setup or choose an available candidate; defer paid execution |
138
+ | API capability unavailable | State the unsupported deployment boundary and the specific supported next step |
139
+
140
+ Do not run destructive `down --volumes` as recovery. The bundle warns that raw
141
+ payload persistence differs from database persistence; prefer its stop/start
142
+ procedure and preserve existing data. Refine and rerun evals only through the
143
+ separately authorized workflow. Packaging this skill belongs to the distribution
144
+ workflow; this source does not assert that plugin OAuth/configuration or hosted
145
+ onboarding has already shipped.
146
+
147
+ Use the public skill's current client/runtime/deployment table at
148
+ https://www.metergraph.dev/SKILL.md. An unimplemented cloud/OAuth client path
149
+ is blocked even if server code exists. Query an exact SDK trace ID, or resolve a
150
+ provider request ID only when that argument is advertised. Ambiguous request IDs
151
+ need confirmation from the application; never select the first match. Preserve
152
+ checkpoint source run, pattern and pattern-set identities in workload queries and
153
+ cursor continuation. Missing/fallback IDs and expired records leave incomplete
154
+ evidence. Read classification availability separately from failed analysis and
155
+ report availability. Discovery must not launch or spend on classification or analysis.
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: metergraph-report
3
+ description: Summarize a finished Metergraph analysis in a few lines with the link to its report in the app, say whether the evidence supports a switch, no switch or more testing, and tell the person exactly what to inspect in the app. Use after an analysis completes, when someone asks "what did the analysis find", "should we switch", "give me the report link", or stage 5 of the metergraph-model-swap loop.
4
+ ---
5
+
6
+ # Summarize the report and hand off to the app
7
+
8
+ The coding agent gives the answer and the link. The app is where the person
9
+ reads the full report and inspects case-level evidence.
10
+
11
+ ## 0. Confirm the connection
12
+
13
+ If this conversation has not already confirmed the workspace, call
14
+ `metergraph_get_workspace_context`, then `metergraph_get_capabilities`, before
15
+ anything else. Say which workspace you are connected to in one line, and plan
16
+ only with the tools the capabilities advertise.
17
+
18
+ ## 1. Read it
19
+
20
+ 1. `metergraph_get_analysis_run` with the run ID. Continue only when the status
21
+ is final. If `report.available` is false on a completed run, the report is
22
+ missing: say so and give the run's page (below).
23
+ 2. `metergraph_get_report` with `report.analysis_id`. Read the `outcome`, each
24
+ workload's `state`, `sample_count` and `reason`, each `model_swap`
25
+ opportunity (`recommendation_status`, `quality_vs_reference`,
26
+ `safe_switch_rate`, `cost_ratio`, `quality_grade_counts`,
27
+ `evaluation_gate`, `savings_usd`), `spend_projection`,
28
+ `analysis_generation_cost` and every warning.
29
+ 3. Read evidence with `metergraph_get_report_evidence` only if the person asks
30
+ about specific cases and the key has the Debug role. It returns captured
31
+ content.
32
+
33
+ ## 2. Write the summary
34
+
35
+ At most eight lines, in this order:
36
+
37
+ ```
38
+ <Switch | Don't switch | Test more>: <workload> from <current model> to <candidate>.
39
+ Quality: <quality_vs_reference>, <safe_switch_rate> of cases safe to switch, graded on <n> samples against <eval revision or "default graders">.
40
+ Cost: <cost_ratio>× per call; about $<savings_usd>/month projected from <observed_days> days of traffic.
41
+ Runner-up: <candidate> — <one reason it lost>.
42
+ Caveats: <sample size, short window, partial coverage, failed candidates, no eval bound>.
43
+ Report: <link>
44
+ Inspect: <the one thing to look at in the app>
45
+ ```
46
+
47
+ Rules:
48
+
49
+ - Every number comes from the report. Say "projected" for `spend_projection`
50
+ figures; a projection from a few days of traffic is not observed savings.
51
+ - Keep quality failures, evaluator or execution failures and missing coverage
52
+ apart. A candidate with missing results has not passed.
53
+ - If no eval was bound, say the grades came from default graders, not from the
54
+ person's own quality bar, and point to `metergraph-evals`.
55
+ - `no_opportunities` is a real answer: the candidates did not beat the current
56
+ model within the bar. `no_eligible_workloads` with `insufficient_samples`
57
+ needs more traffic, not a rerun.
58
+ - If the person's code shows where this workload's model is set, name the file
59
+ and line and suggest a canary experiment. Do not change code without a
60
+ separate instruction.
61
+
62
+ ## 3. Links
63
+
64
+ Use a link a tool returned (`metergraph_links`, `links`) exactly as given.
65
+ Otherwise build it from the origin of the Metergraph server the agent is
66
+ connected to (hosted is `https://app.metergraph.dev`; the customer-local bundle
67
+ is normally `http://127.0.0.1:8080`) and these app routes:
68
+
69
+ | Page | Route |
70
+ | --- | --- |
71
+ | Report | `<origin>/#analysis?report=<analysis_id>` |
72
+ | One workload's results in a report | `<origin>/#analysis/<analysis_id>/workloads/<workload_id>/model_evaluation` |
73
+ | Workload evidence | `<origin>/#workloads?pattern=<workload_id>&report=<analysis_id>` |
74
+ | Eval definition used | `<origin>/#analysis/<analysis_id>/workloads/<workload_id>/eval_definition` |
75
+ | All analyses and runs | `<origin>/#opportunities` |
76
+
77
+ Say that a built link was built, not returned, in a few words. If the person
78
+ says it does not open, give the Opportunities page instead.
79
+
80
+ ## 4. What to inspect in the app
81
+
82
+ Name one or two concrete things, from what the report shows:
83
+
84
+ - cases where the candidate was graded worse than the current model, to judge
85
+ whether the grader or the candidate is wrong;
86
+ - a check with a surprising pass rate, which may be a bad check;
87
+ - the cost of the cases that drive most of the spend.
88
+
89
+ Then ask the person to come back with what they found: grades they disagree
90
+ with, cases that should be added, or a candidate to drop. That is the input to
91
+ `metergraph-iterate`.
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: metergraph-workloads
3
+ description: Choose which Metergraph workload to analyze - list classified workloads, rank them by spend with the source of each number stated, flag anomalies and suspect data, check readiness, and hand off when classification has not run. Use for "what is my largest workload", "list my workloads", "which workload should we optimize", or stage 1 of the metergraph-model-swap loop.
4
+ ---
5
+
6
+ # Choose a workload
7
+
8
+ The goal is one classified workload, its exact `selection_reference`, and a
9
+ one-sentence reason it was chosen.
10
+
11
+ ## 0. Confirm the connection
12
+
13
+ If this conversation has not already confirmed the workspace, call
14
+ `metergraph_get_workspace_context`, then `metergraph_get_capabilities`, before
15
+ anything else. Say which workspace you are connected to in one line, and plan
16
+ only with the tools the capabilities advertise.
17
+
18
+ ## 1. Read what exists
19
+
20
+ 1. `metergraph_list_classified_workloads` with a small limit (10). Each item
21
+ has a `display_name`, `classified_sample_count` and a `selection_reference`
22
+ of `source_run_id`, `pattern_id` and `pattern_set_version`. Keep the
23
+ reference exactly as returned. The name is a label.
24
+ 2. `metergraph_list_routes` and `metergraph_get_usage` with `days: 30`. These
25
+ give cost, calls, tokens, latency and errors per route per UTC day, plus
26
+ each route's `constraints` and `replay_eligible_calls`.
27
+ 3. `metergraph_list_reports` with a small limit. If a recent report exists,
28
+ `metergraph_get_report` gives each workload's `spend_projection`.
29
+
30
+ ## 2. Rank by spend, and say where each number came from
31
+
32
+ There are two kinds of spend number. Do not mix them in one column.
33
+
34
+ | Number | Source | Meaning |
35
+ | --- | --- | --- |
36
+ | Observed cost | `metergraph_get_usage` | What was ingested in the window. Per route, not per workload. |
37
+ | Projected spend | a report's `spend_projection` (`daily_rate_usd`, `projected_monthly_cost_usd`, `population`) | An extrapolation the analysis made. Per workload. |
38
+
39
+ The app's pages can show projected numbers. If the person quotes figures from
40
+ the app that do not match what you read, say which source each one is before
41
+ ranking, and ask which one they mean. Never rank on classified sample counts:
42
+ they are the sample, not the traffic.
43
+
44
+ Rank on a typical day, not the total. Compute each route's median daily cost
45
+ over the window. If one day or one hour dominates a route's total, call it out
46
+ as a spike and investigate it with `metergraph_list_incidents` and
47
+ `metergraph_query_traces` (status and error counts) before treating that route
48
+ as large. A runaway loop or retry storm is a bug to fix, not a model to swap.
49
+
50
+ Present at most the top five, one line each: workload, current model(s), typical
51
+ daily cost, share of typical spend, and one reason it is or is not a good
52
+ candidate. Opus-class models on extraction, classification or short-output
53
+ work are the usual best candidates. Workloads already on small models, or
54
+ under about 5% of spend, rarely repay an analysis.
55
+
56
+ ## 3. Check the data is real
57
+
58
+ Say so before planning when any of these hold, with the numbers:
59
+
60
+ - the workspace or all its data is only hours old, or arrived in one ingest
61
+ batch (`metergraph_get_ingestion_health`);
62
+ - incidents or usage disagree with each other for the same day;
63
+ - traces are labeled synthetic or demo, or a route mixes staging and
64
+ production. Ask which environment the analysis should use.
65
+
66
+ An analysis of seeded data proves the pipeline works and says nothing about
67
+ real costs.
68
+
69
+ ## 4. Check readiness of the chosen workload
70
+
71
+ `metergraph_get_workload_readiness` with the selection reference and the
72
+ chosen environment. Report `blocking_reasons`, the counts and the
73
+ `retained_window` in one or two lines. An analysis needs at least **10
74
+ samples**. Below that it completes with `no_eligible_workloads`; the fix is
75
+ more traffic, not a rerun. `stale_checkpoint` means the classification is
76
+ older than the current window.
77
+
78
+ ## 5. When nothing is classified yet
79
+
80
+ An empty list with `classification_pending` or `no_traces` means classification
81
+ has not run. No agent tool starts classification on its own. Tell the person:
82
+
83
+ 1. Open the **Opportunities** page in the Metergraph app and choose **Find
84
+ opportunities**.
85
+ 2. Choose to pick a specific workload rather than the automatic option. That
86
+ run classifies the traffic and then waits for them to choose a workload,
87
+ before any candidate model is called.
88
+ 3. The automatic option classifies and then immediately analyzes the largest
89
+ workloads with the saved candidate models, which sends captured prompts to
90
+ those providers and spends money. Point this out before they click.
91
+
92
+ When they are back, list classified workloads again and continue. Do not click
93
+ through the app yourself unless the person asks you to and has approved what
94
+ the button will do.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metergraph-cli",
3
- "version": "0.2.0-preview.3",
3
+ "version": "0.2.0-preview.5",
4
4
  "description": "Preview Metergraph command line tool with a read-only connection probe, a project skill installer, Metadata-only sign in and bounded workspace Metadata reads",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,8 @@
14
14
  "src/*.js",
15
15
  "assets/skill/SKILL.md",
16
16
  "assets/skill/manifest.json",
17
+ "assets/skills/manifest.json",
18
+ "assets/skills/*/SKILL.md",
17
19
  "LICENSE",
18
20
  "README.md"
19
21
  ],
package/src/args.js CHANGED
@@ -26,13 +26,15 @@ import { parseOrigin } from "./origin.js";
26
26
  import { isCursor, isSafeFilter } from "./read-contract.js";
27
27
 
28
28
  export const READ_COMMANDS = Object.freeze(["status", "context", "capabilities", "usage", "routes", "traces"]);
29
- const COMMANDS = new Set(["doctor", "help", "skill", "login", "logout", "setup", "verify", ...READ_COMMANDS]);
30
- const HELP_TOPICS = new Set(["doctor", "skill", "login", "logout", "setup", "verify", ...READ_COMMANDS]);
29
+ const COMMANDS = new Set(["doctor", "help", "skill", "skills", "login", "logout", "setup", "verify", ...READ_COMMANDS]);
30
+ const HELP_TOPICS = new Set(["doctor", "skill", "skills", "login", "logout", "setup", "verify", ...READ_COMMANDS]);
31
31
  const SKILL_ACTIONS = new Set(["install", "update"]);
32
+ const SKILLS_ACTIONS = new Set(["install", "update", "list"]);
32
33
  const READ_BASE = ["--project", "--config-dir", "--timeout-ms"];
33
34
  const OPTIONS = {
34
35
  doctor: new Set(["--url", "--timeout-ms"]),
35
36
  skill: new Set(["--client", "--runtime", "--project"]),
37
+ skills: new Set(["--client", "--runtime", "--project"]),
36
38
  login: new Set(["--runtime", "--url", "--workspace", "--project", "--config-dir", "--timeout-ms"]),
37
39
  setup: new Set(["--runtime", "--url", "--workspace", "--project", "--config-dir", "--env-file", "--client", "--timeout-ms", "--deployment", "--agent-token-file", "--repository"]),
38
40
  logout: new Set(["--project", "--config-dir"]),
@@ -86,6 +88,8 @@ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
86
88
  // { ok: true, command: "version", json }
87
89
  // { ok: true, command: "doctor", origin, timeoutMs, json }
88
90
  // { ok: true, command: "skill", action, client, runtime, project, json }
91
+ // { ok: true, command: "skills", action, client, runtime, project, json }
92
+ // (client and runtime are null for list)
89
93
  // { ok: true, command: "login", runtime, origin, workspace, project,
90
94
  // configDir, timeoutMs, signup, noBrowser, reconnect, json }
91
95
  // { ok: true, command: "logout", project, configDir, json }
@@ -113,7 +117,9 @@ export function parseArgs(argv) {
113
117
 
114
118
  const fail = (code, message, outcome = "invalid_input") => ({
115
119
  ok: false,
116
- command: command === "skill" && action !== null ? `skill ${action}` : command ?? (version ? "version" : null),
120
+ command: (command === "skill" || command === "skills") && action !== null
121
+ ? `${command} ${action}`
122
+ : command ?? (version ? "version" : null),
117
123
  json,
118
124
  code,
119
125
  message,
@@ -142,6 +148,10 @@ export function parseArgs(argv) {
142
148
  action = arg;
143
149
  continue;
144
150
  }
151
+ if (command === "skills" && action === null && SKILLS_ACTIONS.has(arg)) {
152
+ action = arg;
153
+ continue;
154
+ }
145
155
 
146
156
  if (READ_COMMANDS.includes(command)) {
147
157
  const equals = arg.indexOf("=");
@@ -192,6 +202,12 @@ export function parseArgs(argv) {
192
202
  `Unknown skill subcommand at argument ${position}. Use install or update.`,
193
203
  );
194
204
  }
205
+ if (command === "skills" && action === null && !arg.startsWith("-")) {
206
+ return fail(
207
+ "unknown_subcommand",
208
+ `Unknown skills subcommand at argument ${position}. Use install, update or list.`,
209
+ );
210
+ }
195
211
  return fail(
196
212
  "unknown_argument",
197
213
  `Unrecognized argument at position ${position}. Run "metergraph --help" for usage.`,
@@ -203,6 +219,7 @@ export function parseArgs(argv) {
203
219
  return { ok: true, command: "help", topic, json };
204
220
  }
205
221
  if (command === "skill") return parseSkill(action, values, json, fail);
222
+ if (command === "skills") return parseSkills(action, values, json, fail);
206
223
  if (command === "login") return parseLogin(values, flags, json, fail);
207
224
  if (command === "setup") return parseSetup(values, flags, json, fail);
208
225
  if (command === "verify") return parseVerify(values, flags, json, fail);
@@ -293,6 +310,29 @@ function parseSkill(action, values, json, fail) {
293
310
  return { ok: true, command: "skill", action, client, runtime, project: project ?? null, json };
294
311
  }
295
312
 
313
+ // skills install and update take the same options as skill. list is read
314
+ // only and takes just --project.
315
+ function parseSkills(action, values, json, fail) {
316
+ if (action === null) {
317
+ return fail(
318
+ "missing_subcommand",
319
+ 'skills requires a subcommand: install, update or list. Run "metergraph help skills" for usage.',
320
+ );
321
+ }
322
+ if (action === "list") {
323
+ if (values["--client"] !== undefined || values["--runtime"] !== undefined) {
324
+ return fail("unexpected_option", "skills list takes only --project. It reports every client.");
325
+ }
326
+ const project = values["--project"];
327
+ if (project !== undefined && (project === "" || project.includes("\0"))) {
328
+ return fail("invalid_project", "--project must name an existing directory.");
329
+ }
330
+ return { ok: true, command: "skills", action, client: null, runtime: null, project: project ?? null, json };
331
+ }
332
+ const parsed = parseSkill(action, values, json, fail);
333
+ return parsed.ok ? { ...parsed, command: "skills" } : parsed;
334
+ }
335
+
296
336
  const INVALID_URL =
297
337
  "--url must be a bare https origin such as https://metergraph.example.com, " +
298
338
  "or an http origin on localhost, 127.0.0.1 or [::1]. " +
package/src/cli.js CHANGED
@@ -10,12 +10,13 @@ import {
10
10
  helpData,
11
11
  helpText,
12
12
  skillText,
13
+ skillsText,
13
14
  toJsonLine,
14
15
  versionData,
15
16
  } from "./output.js";
16
17
  import { runRead } from "./read.js";
17
18
  import { readMessage, readText } from "./read-output.js";
18
- import { runSkill } from "./skill.js";
19
+ import { listSkillPack, runSkill, runSkillPack } from "./skill.js";
19
20
  import { runSetup } from "./setup.js";
20
21
  import { containsKnownCredential, preflightNonHostedSetup } from "./setup-deployment.js";
21
22
  import { VERSION } from "./constants.js";
@@ -151,6 +152,20 @@ async function run(argv, { stdout, stderr }) {
151
152
  return result;
152
153
  }
153
154
 
155
+ if (parsed.command === "skills") {
156
+ const { outcome, reason, message, data } =
157
+ parsed.action === "list" ? listSkillPack(parsed) : runSkillPack(parsed);
158
+ const result = envelope({
159
+ command: `skills ${parsed.action}`,
160
+ outcome,
161
+ reason,
162
+ data,
163
+ message: outcome === "ok" ? null : message,
164
+ });
165
+ stdout.write(parsed.json ? toJsonLine(result) : skillsText(result, message));
166
+ return result;
167
+ }
168
+
154
169
  const { outcome, reason, report } = await runDoctor({
155
170
  origin: parsed.origin,
156
171
  timeoutMs: parsed.timeoutMs,