trodo-cli 0.2.0 → 0.3.1

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.
package/lib/main.js CHANGED
@@ -9,31 +9,65 @@ const { ApiError } = require('./http');
9
9
 
10
10
  const pkg = require('../package.json');
11
11
 
12
- const HELP = `trodo ${pkg.version} — traces, datasets and agents from the terminal
12
+ const HELP = `trodo ${pkg.version} — traces, evals, datasets, prompts and agents from the terminal
13
13
 
14
14
  trodo login [--url https://api.trodo.ai] [--no-browser] sign in through the app
15
15
  trodo login --key trodo_mk_… or with a key (CI)
16
- trodo whoami | logout
16
+ trodo whoami
17
+ trodo teams the teams this terminal is signed in to
18
+ trodo switch TEAM use another one (login adds teams; --team T for one command)
19
+ trodo logout [TEAM] [--all]
17
20
 
18
- trodo evals list eval names and their steps, for --eval
19
- trodo traces count [filter]
20
- trodo traces export [filter] [--include-trace] [--format trodo|openai|dpo] [--target output|expected] [-o file]
21
- trodo runs list [filter] [--limit 50]
22
- trodo runs get RUN_ID
21
+ Traces
22
+ trodo traces count [filter] how many runs / spans / conversations / verdicts match
23
+ trodo traces stats [filter] [--compare] errors, latency, tokens, cost, feedback, evals, by agent
24
+ trodo traces agents | fields | values FIELD what --agent and --where can name
25
+ trodo traces export [filter] [--include-trace] [--format trodo|openai|dpo] [--target output|expected]
26
+ [-o file | --dir DIR] JSONL, or one JSON file per run / span / conversation
27
+ trodo runs list [filter] [--limit 50] [--offset N]
28
+ trodo runs search TEXT runs whose input, output or spans mention TEXT
29
+ trodo runs get RUN_ID [--tree] the run, spans, messages, feedback, evaluations
30
+ trodo runs tree RUN_ID the span tree
31
+ trodo runs feedback RUN_ID [--thumbs up|down] [--rating 1-5] [--comment TEXT]
32
+ trodo spans list [filter] | search TEXT | get SPAN_ID
33
+ trodo conversations list [filter] [--search TEXT] | get ID [--messages] (alias: threads)
34
+ trodo users get DISTINCT_ID one user's runs, cost, evals and feedback
23
35
 
36
+ Evals
37
+ trodo evals list | get EVAL names, steps (STEP.FIELD), 30-day stats
38
+ trodo evals explain EVAL [--table] what it checks, each step, answers and options, outcomes, filters
39
+ trodo evals steps EVAL [--since 30d] [--table] the paths verdicts took through the steps, with outcomes
40
+ trodo evals verdicts EVAL [--failed|--passed] [--since 7d] [--version N]
41
+ trodo evals verdicts --run ID | --span ID | --conversation ID
42
+ trodo evals verdict VERDICT_ID every step and why
43
+ trodo evals mark VERDICT_ID --right | --wrong --should pass|fail --reason TEXT
44
+ trodo evals queue [--eval EVAL] | review ITEM_ID | grade ITEM_ID CHOICE verdicts waiting on a person
45
+
46
+ Datasets
24
47
  trodo datasets list | get DATASET | versions DATASET
25
- trodo datasets create NAME [--kind runs|spans|conversations|verdicts [filter] [--live] [--include-trace]]
48
+ trodo datasets create NAME [-f rows.jsonl] [--kind runs|spans|conversations|verdicts [filter] [--live] [--include-trace]]
26
49
  trodo datasets add DATASET --kind KIND [filter] [--live] [--include-trace]
27
- trodo datasets append DATASET [-f rows.jsonl] rows, or {run_id}/{span_id}/{conversation_id} lines
50
+ trodo datasets append DATASET [-f rows.jsonl] {input, output?, expected?} or {run_id}/{span_id}/{conversation_id} lines
28
51
  trodo datasets update DATASET [-f updates.jsonl] {id, expected?, output?, metadata?} lines, one version
29
52
  trodo datasets export DATASET [--rows passed|failed|expected|no-expected|marked-wrong|all]
30
53
  [--eval NAME] [--min-score N] [--format trodo|openai|dpo] [--target ...] [--version N] [-o file]
31
54
 
32
- trodo agents list | executions AGENT | execution ID
55
+ Prompts
56
+ trodo prompts list | get PROMPT [--version N | --label L] | versions PROMPT
57
+ trodo prompts pull PROMPT [--label production] [-o prompt.json]
58
+ trodo prompts push PROMPT -f prompt.json [--message "why"] [--label staging]
59
+ trodo prompts label PROMPT VERSION LABEL [--remove]
60
+
61
+ Issues and agents
62
+ trodo issues list [--status open,regression] | get ID | failures ID | close ID | reopen ID
63
+ trodo agents list | get AGENT | executions AGENT | execution ID
33
64
  trodo agents run AGENT [--input JSON] [--wait]
34
65
 
35
- Filter (traces, runs, datasets create/add):
36
- --kind spans|runs|conversations|verdicts (default runs)
66
+ trodo api GET|POST|PATCH|DELETE /path [--param k=v] [--data JSON] any /api/v1 route
67
+ trodo setup claude [--global] install the Trodo skills for Claude Code (or --dir DIR)
68
+
69
+ Filter (traces, runs, spans, conversations, datasets create/add):
70
+ --kind spans|runs|conversations|verdicts (traces and datasets; default runs)
37
71
  --since 24h|7d|30d|3m|today or --from DATE --to DATE (default 7d)
38
72
  --agent NAME repeatable
39
73
  --where "status=error" = != ~ > >= < <= on any Traces property:
@@ -50,25 +84,33 @@ Training data:
50
84
  trodo datasets export "Good refunds" --rows passed --format openai --target output -o train.jsonl
51
85
  firectl dataset create good-refunds train.jsonl
52
86
 
53
- Auth: TRODO_API_KEY / TRODO_API_URL, or \`trodo login\` (~/.trodo/config.json).
87
+ Auth: TRODO_API_KEY / TRODO_API_URL, or \`trodo login\` (~/.trodo/config.json, one key per team).
54
88
  A key is a Trodo API key (Settings → MCP); it acts as the person who made it, on its team, with its scopes.
55
- Output: JSON by default; --table for a table. Exit codes: 0 ok, 1 API error, 2 usage.
89
+ The CLI reads everything and only adds: nothing it does deletes, archives or replaces data.
90
+ Output: JSON by default; --table for a table, --csv for CSV; lists take --all [--max-items N]. Exit codes: 0 ok, 1 API error, 2 usage.
56
91
  `;
57
92
 
58
93
  const COMMANDS = {
59
- login: ['auth', 'login'], whoami: ['auth', 'whoami'], logout: ['auth', 'logout'],
94
+ login: ['auth', 'login'], whoami: ['auth', 'whoami'], logout: ['auth', 'logout'], api: ['api', 'call'], setup: ['setup', 'setup'], teams: ['auth', 'teams'], switch: ['auth', 'switch'],
60
95
  };
96
+ const ALIASES = { threads: 'conversations', thread: 'conversations', run: 'runs', span: 'spans', dataset: 'datasets', prompt: 'prompts', issue: 'issues', agent: 'agents', eval: 'evals', trace: 'traces' };
61
97
  const GROUPS = {
62
- evals: ['list'],
63
- traces: ['count', 'export'],
64
- runs: ['list', 'get'],
65
- datasets: ['list', 'get', 'create', 'add', 'append', 'update', 'export', 'rows', 'versions'],
66
- agents: ['list', 'run', 'executions', 'execution'],
98
+ evals: ['list', 'get', 'explain', 'steps', 'verdicts', 'verdict', 'mark', 'queue', 'review', 'grade'],
99
+ traces: ['count', 'stats', 'export', 'agents', 'fields', 'values'],
100
+ runs: ['list', 'search', 'get', 'tree', 'feedback'],
101
+ spans: ['list', 'search', 'get'],
102
+ conversations: ['list', 'get'],
103
+ datasets: ['list', 'get', 'create', 'add', 'append', 'update', 'export', 'rows', 'download', 'versions'],
104
+ prompts: ['list', 'get', 'versions', 'pull', 'push', 'label'],
105
+ issues: ['list', 'get', 'failures', 'close', 'reopen'],
106
+ agents: ['list', 'get', 'run', 'executions', 'execution'],
107
+ users: ['get'],
67
108
  };
68
109
 
69
110
  async function main(argv) {
70
111
  const { flags, positional } = parse(argv);
71
- const [cmd, sub, ...rest] = positional;
112
+ const [cmd0, sub, ...rest] = positional;
113
+ const cmd = ALIASES[cmd0] || cmd0;
72
114
  // `trodo --version` alone is the CLI's version; `datasets export X --version 3` is a dataset's.
73
115
  if (flags.version === true && !cmd) { process.stdout.write(`${pkg.version}\n`); return 0; }
74
116
  if (!cmd || flags.help || cmd === 'help') { process.stdout.write(HELP); return 0; }
package/lib/output.js CHANGED
@@ -7,6 +7,7 @@
7
7
  const fs = require('fs');
8
8
 
9
9
  function print(data, flags = {}, columns = null) {
10
+ if (flags.csv && Array.isArray(data)) return process.stdout.write(csv(data, columns));
10
11
  if (flags.table && columns && Array.isArray(data)) return process.stdout.write(table(data, columns));
11
12
  const pretty = process.stdout.isTTY && !flags.json;
12
13
  return process.stdout.write(`${JSON.stringify(data, null, pretty ? 2 : 0)}\n`);
@@ -20,6 +21,16 @@ function table(rows, columns) {
20
21
  return `${[line(columns.map(([, label]) => label)), ...rows.map((r) => line(columns.map(([key]) => cell(r[key]))))].join('\n')}\n`;
21
22
  }
22
23
 
24
+ /** RFC 4180 CSV: the table's columns when the command has them, every key otherwise. */
25
+ function csv(rows, columns) {
26
+ const keys = columns ? columns.map(([k]) => k) : [...new Set(rows.flatMap((r) => (r && typeof r === 'object' ? Object.keys(r) : [])))];
27
+ const cell = (v) => {
28
+ const s = v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);
29
+ return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
30
+ };
31
+ return `${[keys.join(','), ...rows.map((r) => keys.map((k) => cell(r[k])).join(','))].join('\n')}\n`;
32
+ }
33
+
23
34
  /** Write lines to --out or stdout; returns { write(obj), close() }. */
24
35
  function sink(out) {
25
36
  const stream = out ? fs.createWriteStream(out) : process.stdout;
@@ -47,4 +58,4 @@ async function readRows(file) {
47
58
  });
48
59
  }
49
60
 
50
- module.exports = { print, table, sink, readRows };
61
+ module.exports = { print, table, csv, sink, readRows };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trodo-cli",
3
- "version": "0.2.0",
4
- "description": "Trodo from the terminal: pull traces by their eval results, build datasets, run labeling agents, export fine-tuning data",
3
+ "version": "0.3.1",
4
+ "description": "Trodo from the terminal: traces, evals, datasets, prompts, issues and agents for people, scripts and coding agents",
5
5
  "bin": {
6
6
  "trodo": "bin/trodo.js"
7
7
  },
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "bin/",
11
11
  "lib/",
12
+ "skills/",
12
13
  "README.md"
13
14
  ],
14
15
  "scripts": {
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: trodo
3
+ version: 1.0.0
4
+ cli_version: ">=0.3.0"
5
+ last_updated: 2026-11-03
6
+ description: >-
7
+ Read and act on an AI agent's production traces in Trodo with the `trodo` CLI:
8
+ find failing runs, read a run's span tree or a whole conversation, see why an
9
+ eval failed step by step, check traffic stats against last week, triage and
10
+ close issues, pull and push prompt versions, give feedback, and turn traces
11
+ into datasets and fine-tuning files. Use for "why did this run fail", "what
12
+ broke since yesterday", "show me the conversation", "which evals fail most",
13
+ "fix this Trodo issue", "update the prompt", "build a dataset from my traces".
14
+ ---
15
+
16
+ # Trodo from the terminal
17
+
18
+ Every command prints JSON (pipe it to `jq`), `--table` for a person, `--csv` for a
19
+ sheet. Exit codes: 0 ok, 1 the API said no, 2 a usage mistake (the message says which).
20
+
21
+ ## Setup
22
+
23
+ ```bash
24
+ npm i -g trodo-cli
25
+ trodo login # opens the app: check the code, choose the team, Connect
26
+ trodo whoami
27
+ ```
28
+
29
+ Without a browser (CI): `TRODO_API_KEY=trodo_mk_…` from Settings → MCP.
30
+
31
+ Everything is per team. `trodo teams` lists the teams this terminal is signed in
32
+ to, `trodo switch TEAM` changes the current one, `--team TEAM` picks one for a
33
+ single command. Check `trodo whoami` before writing anything.
34
+
35
+ ## Understand the evals first
36
+
37
+ ```bash
38
+ trodo evals list --table
39
+ trodo evals explain "EVAL" --table # what it checks, each step, answer options, outcomes, working filters
40
+ ```
41
+
42
+ Read `explain` before writing an `--eval` filter. A decision-model question with
43
+ levels answers a weighted score (1.8 is mostly the top level): use a threshold
44
+ such as `STEP.field<1.5`, never `<2`. A filter matches a unit when any of its
45
+ verdicts matches, so a unit re-scored later can appear under both failed and passed.
46
+
47
+ ## Find what went wrong
48
+
49
+ ```bash
50
+ trodo traces stats --since 24h --compare # errors, latency, cost, evals vs the day before
51
+ trodo runs list --since 24h --where "status=error" --table
52
+ trodo runs list --eval "any:failed" --agent support --table
53
+ trodo issues list --status open,regression --table
54
+ trodo runs search "refund for order 4821" # free text over inputs, outputs and spans
55
+ ```
56
+
57
+ The filter is the Traces page's: `--since 24h|7d|30d`, `--agent NAME`,
58
+ `--where "field op value"` (`= != ~ > >= < <=` on any property:
59
+ `total_cost>0.05`, `span.model~gpt-4o`, `metadata.tier=pro`) and
60
+ `--eval "NAME:COND"` (`failed`, `passed`, `score<0.5`, `reason=CODE`,
61
+ `STEP.FIELD=value`; `trodo evals list` names the evals and their steps).
62
+ `trodo traces agents`, `trodo traces fields` and `trodo traces values FIELD`
63
+ say what `--agent` and `--where` can name.
64
+
65
+ ## Read one
66
+
67
+ ```bash
68
+ trodo runs tree RUN_ID # the span tree: names, models, time, tokens, errors
69
+ trodo runs get RUN_ID # everything: spans, messages, feedback, evaluations
70
+ trodo spans get SPAN_ID # one span's full input and output
71
+ trodo conversations get ID --messages # the thread as user / assistant turns
72
+ trodo evals verdicts --run RUN_ID # every eval's verdict on it
73
+ trodo evals verdict VERDICT_ID # each step the eval took and why
74
+ trodo evals steps EVAL --table # which step decides most failures
75
+ trodo users get DISTINCT_ID # one user's runs, cost, evals and feedback
76
+ ```
77
+
78
+ Read the deepest failing span before deciding the cause: what failed above it is
79
+ what the user saw, not why.
80
+
81
+ ## Fix and verify
82
+
83
+ ```bash
84
+ trodo issues get ID # root cause, evidence, timeline
85
+ trodo issues failures ID # recorded inputs to replay against the fix
86
+ trodo traces count --since 1h --eval "NAME:failed" # after the fix ships: should fall to 0
87
+ trodo issues close ID # a new failure reopens it as a regression
88
+ ```
89
+
90
+ ## Prompts
91
+
92
+ ```bash
93
+ trodo prompts pull support-triage --label production -o prompt.json
94
+ # edit prompt.json
95
+ trodo prompts push support-triage -f prompt.json --message "why" --label staging
96
+ trodo prompts label support-triage 5 production # agents fetching by label switch on their next call
97
+ ```
98
+
99
+ Moving `production` changes what live agents run. Do it only when asked.
100
+
101
+ ## Feedback
102
+
103
+ ```bash
104
+ trodo runs feedback RUN_ID --thumbs down --comment "quoted the wrong plan"
105
+ trodo evals mark VERDICT_ID --wrong --should pass --reason "the refund was within policy"
106
+ trodo evals queue --table ; trodo evals grade ITEM_ID CHOICE
107
+ ```
108
+
109
+ ## Datasets and training data
110
+
111
+ Do the thinking in your own script: export with the filter, process the JSONL
112
+ locally (each line has input, output, and every verdict with each step's
113
+ answer), then write rows back with `datasets create NAME -f rows.jsonl` or
114
+ `datasets update`. Nothing of yours runs on Trodo.
115
+
116
+ ```bash
117
+ trodo traces export --kind spans --eval any:passed --eval any:not-failed --format openai -o sft.jsonl
118
+ trodo traces export --kind runs --since 7d --include-trace --dir ./runs # one JSON file per run
119
+ trodo datasets create "Refund failures" --kind runs --eval "Refund policy:failed" --include-trace
120
+ trodo datasets rows "Refund failures" -o rows.jsonl
121
+ # write better answers: {"id": "...", "expected": "..."} per line
122
+ trodo datasets update "Refund failures" -f updates.jsonl
123
+ trodo datasets export "Refund failures" --rows expected --format openai --target expected -o train.jsonl
124
+ ```
125
+
126
+ `trodo datasets append NAME -f rows.jsonl` takes `{"input": "…", "output": "…", "expected": "…"}`
127
+ lines, or `{"run_id": "…"}` / `{"span_id": "…"}` / `{"conversation_id": "…"}` to copy traffic.
128
+
129
+ ## What the CLI will not do
130
+
131
+ It reads everything and only adds. Nothing deletes, archives, replaces or
132
+ reverts data, and traces cannot be changed. If a task needs that, tell the
133
+ person to do it in the app.
134
+
135
+ ## Anything else
136
+
137
+ `trodo api GET /runs/conversations --param q=alice` calls any `/api/v1` route with the
138
+ signed-in key. `trodo help` lists every command.
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: trodo-datasets
3
+ version: 1.0.0
4
+ cli_version: ">=0.2.0"
5
+ last_updated: 2026-11-03
6
+ description: >-
7
+ Turn an agent's production traces into datasets and fine-tuning files with the
8
+ `trodo` CLI: pick traces by what the team's evals said about them (passed,
9
+ failed, a score, one step's answer), keep whole runs with their span trees,
10
+ process rows locally, write Expected back, and export OpenAI / Fireworks /
11
+ Together chat JSONL or DPO pairs. Use for "build a training set from my
12
+ traces", "export the runs that passed", "label these rows", "make a dataset of
13
+ failures", "fine-tune on what went well", and for running a Trodo labeling agent.
14
+ ---
15
+
16
+ # Trodo datasets from the terminal
17
+
18
+ The team's evals already say which traces went well. A supervised fine-tune
19
+ wants exactly those: their own Output is the target, nothing to label. Rows
20
+ that failed need a better answer first (Expected). Work in that order.
21
+
22
+ ## Setup
23
+
24
+ ```bash
25
+ npm i -g trodo-cli
26
+ trodo login # opens the app: check the code, choose the team, Connect
27
+ trodo whoami
28
+ ```
29
+
30
+ In CI or anywhere without a browser: `TRODO_API_KEY=trodo_mk_…` (Settings → MCP).
31
+ Every command prints JSON; add `--table` to read it yourself.
32
+
33
+ ## 1. Know the evals
34
+
35
+ ```bash
36
+ trodo evals list --table
37
+ ```
38
+
39
+ `--eval "NAME:COND"` takes an eval's name (or any unique part of it, or `any`):
40
+ `failed`, `passed`, `not-failed`, `marked-wrong`, `not-marked-wrong`,
41
+ `marked-right`, `score<0.5`, `reason=CODE`, or one step's answer
42
+ `STEP.FIELD=value` / `STEP.FIELD<3` (STEP is the step's id or the start of its
43
+ name). Several `--eval` flags AND together. "Passed every eval" is
44
+ `--eval any:passed --eval any:not-failed`. People's corrections count: a
45
+ verdict someone marked wrong is read the other way round.
46
+
47
+ ## 2. Look before you pull
48
+
49
+ ```bash
50
+ trodo traces count --kind runs --since 30d --agent support --eval "any:passed" --eval "any:not-failed"
51
+ ```
52
+
53
+ Kinds: `spans` (one model call), `runs` (one task, add `--include-trace` for
54
+ every span), `conversations` (a whole thread), `verdicts` (eval results, for
55
+ training the eval itself). Narrow with `--where "status=error"`,
56
+ `--where "total_cost>0.05"`, `--where "span.model~gpt-4o"`, `--where "metadata.tier=pro"`.
57
+
58
+ ## 3a. Straight to a training file
59
+
60
+ ```bash
61
+ trodo traces export --kind spans --since 30d --eval any:passed --eval any:not-failed \
62
+ --format openai --target output -o sft.jsonl
63
+ ```
64
+
65
+ ## 3b. Or keep it as a dataset (versioned, shared with the team, labelable)
66
+
67
+ ```bash
68
+ trodo datasets create "Refunds that went well" --kind runs --since 30d \
69
+ --agent support --eval "Refund policy:passed" --include-trace [--live]
70
+ trodo datasets export "Refunds that went well" --rows passed --format openai --target output -o train.jsonl
71
+ ```
72
+
73
+ `--live` keeps adding new matches every 15 minutes.
74
+
75
+ ## 4. Process rows locally, write them back
76
+
77
+ ```bash
78
+ trodo datasets export "Refund failures" > rows.jsonl # each line has its "id"
79
+ python label.py < rows.jsonl > updates.jsonl # your logic: {"id", "expected", "metadata"?}
80
+ trodo datasets update "Refund failures" -f updates.jsonl --label derived --name "Expected from label.py"
81
+ ```
82
+
83
+ One call is one dataset version (restorable in the app). `--label derived`
84
+ records that a rule wrote Expected, not a person. Add rows with
85
+ `trodo datasets append NAME -f rows.jsonl`: plain rows `{input, output?, expected?}`
86
+ or references `{run_id}` / `{span_id}` / `{conversation_id}` that Trodo fills in.
87
+
88
+ ## 5. Or let a Trodo agent label in the product
89
+
90
+ The dataset page's **Label** menu (or asking Lucid) makes a labeling agent:
91
+ models that must agree, a model with your rules, a stronger model rewriting
92
+ failures, or two models compared. Run one from here:
93
+
94
+ ```bash
95
+ trodo agents list --table
96
+ trodo agents run "Label Expected where models agree" --wait
97
+ ```
98
+
99
+ ## 6. Train
100
+
101
+ ```bash
102
+ trodo datasets export "Refunds" --rows expected --format openai --target expected -o train.jsonl
103
+ firectl dataset create refunds train.jsonl
104
+ firectl sftj create --base-model accounts/fireworks/models/qwen3-8b --dataset refunds --output-model refunds-v1
105
+ ```
106
+
107
+ DPO: `--format dpo` writes Expected as preferred and Output as rejected, for
108
+ rows that have both and differ.
109
+
110
+ ## Rules
111
+
112
+ - Count first, then pull with a `--limit`. Exports stream; use `-o file`.
113
+ - Never write Expected from a guess. Expected is a person's answer or the
114
+ output of a rule the team agreed on.
115
+ - The key acts as the person who made it, on one team. A 403 `missing_scope`
116
+ means the key needs that scope; make a new one with `trodo login`.