diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a41d3b4..14fac1d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Tsuga toolkit for AI coding agents — two plugins: `telemetry` for OpenTelemetry instrumentation/audits across 9 languages, and `tsuga` for live-platform investigation, dashboards, and the tsuga CLI driver.", - "version": "0.7.4" + "version": "0.7.8" }, "plugins": [ { diff --git a/AGENTS.md b/AGENTS.md index b607815..59912a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,7 +65,7 @@ Do **not** use relative paths like `../../other-plugin/skills/...` — plugins a ## Validation -The repo has no CI. Lint locally before opening a PR: +CI (`.github/workflows/lint.yml`) runs on every PR: manifest JSON validity, `skills` field shape, description ≤ 1024 chars, plugin source paths, stray placeholders. Lint locally before opening a PR: ```bash # Each manifest validates against Claude Code's schema @@ -87,6 +87,13 @@ v_tele=$(jq -r '.version' plugins/telemetry/.claude-plugin/plugin.json) # No stray {{SKILLS_DIR}} grep -rn '{{SKILLS_DIR}}' plugins/ && echo "FAIL: replace with \${CLAUDE_PLUGIN_ROOT}" +# Frontmatter descriptions approaching 1024 chars — CI hard-fails above 1024 (Codex silently +# drops the whole skill there; Claude Code truncates the listing at 1536). Warn early, at 900: +for f in plugins/*/skills/*/SKILL.md; do + len=$(awk -F'description: ' '/^description:/{print length($2); exit}' "$f") + [ "${len:-0}" -gt 900 ] && echo "WARN: $len chars, close to 1024: $f" +done + # No stray top-level skills/ directory [ -d skills ] && echo "FAIL: skills/ should be empty/absent — move into plugins//skills/" ``` diff --git a/README.md b/README.md index b0f112f..5d6de5d 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,10 @@ codex plugin marketplace add tsuga-dev/agent-plugins codex plugin add tsuga@tsuga codex plugin add telemetry@tsuga ``` + +## Ownership & contributions + +Tsuga owns and maintains these plugins — anything generic about operating Tsuga or instrumenting with OpenTelemetry belongs here, and installs with `autoUpdate` receive new versions automatically. + +- **Found a gap or an error?** Open an issue or PR. Field-tested corrections (a workflow the skill should cover, a gotcha it gets wrong) are exactly what we want flowing back upstream. +- **Org-specific conventions** (your naming schemes, internal runbooks, metric families, team structure) don't belong in these skills. Keep them in your own plugin layered on top — skills compose, and yours can reference these by name (e.g. `tsuga-cli`). diff --git a/plugins/telemetry/.claude-plugin/plugin.json b/plugins/telemetry/.claude-plugin/plugin.json index 16fb33a..e24c854 100644 --- a/plugins/telemetry/.claude-plugin/plugin.json +++ b/plugins/telemetry/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "telemetry", "description": "Instrumentation-quality plugin: OpenTelemetry SDK setup for Python, Go, Node.js, Java, .NET, Ruby, PHP, Rust, and C++; Collector configuration; OTTL transformations; semantic conventions; signal-choice advice; instrumentation audits (metrics/traces/logs); smoke-testing; and telemetry-debugging skills. Pair with the `tsuga` plugin for the `tsuga-cli` reference when running audit/debug/smoke-test workflows.", - "version": "0.7.7", + "version": "0.7.8", "author": { "name": "Tsuga Engineering", "email": "engineering@tsuga.com" diff --git a/plugins/tsuga/.claude-plugin/plugin.json b/plugins/tsuga/.claude-plugin/plugin.json index 2952ddc..efb814d 100644 --- a/plugins/tsuga/.claude-plugin/plugin.json +++ b/plugins/tsuga/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "tsuga", "description": "Tsuga platform plugin: the `tsuga` CLI driver (commands, TQL syntax, aggregation bodies, counter math, deep links, cloud/k8s translators) with embedded lookup playbooks for service ownership and reliability review; live-platform investigation skills for service health, errors, latency, and monitor coverage; dashboard building; the incident-investigation orchestrator; and meta-skills for building and validating skill bundles.", - "version": "0.7.7", + "version": "0.7.8", "author": { "name": "Tsuga Engineering", "email": "engineering@tsuga.com" diff --git a/plugins/tsuga/skills/tsuga-cli/SKILL.md b/plugins/tsuga/skills/tsuga-cli/SKILL.md index 9a9425b..0daffd8 100644 --- a/plugins/tsuga/skills/tsuga-cli/SKILL.md +++ b/plugins/tsuga/skills/tsuga-cli/SKILL.md @@ -235,6 +235,8 @@ tsuga metrics list --from -1h # custom time range tsuga metrics get # get metadata for a specific metric ``` +**When the right metric isn't obvious, check existing dashboards before the catalog.** `metrics list` returns every catalogued metric, including series that are no longer (or never were) emitted — name-matching against it is how you end up querying a dead metric and misreading the empty result as "no data exists." Dashboards encode metric + filter + aggregation combinations already validated end-to-end: `tsuga dashboards list`, then `tsuga dashboards get ` and read the widgets' queries. Fall back to the catalog only when no dashboard covers the domain. If a metric you expected to have data comes back empty, treat it as evidence you picked the wrong metric — look for adjacent metric families before concluding the system is silent. + ## Aggregations Scalar (single value) and timeseries queries use JSON body input: @@ -309,6 +311,10 @@ Check `tsuga metrics get ` for `type` + `temporality` first; picking wrong `average` on a counter, no function on a cumulative counter, or `per-second` on a gauge all produce garbage. Custom pipelines may have non-standard temporality — when in doubt, `$knowledge-technology//metrics.csv` (`type` + `post_function` columns) is authoritative. +## Public HTTP API + +When the user is building their own scripts or services against Tsuga (no CLI, no MCP — bulk backfills, metering jobs), read `references/http-api.md` for the verified integration facts: API host, operation-key auth, `/v1` aggregation endpoints, body/envelope shapes, and retention-policy lookback bounds. During skill execution stay CLI-first — never curl the API yourself. + ## Safety - Before running any filter you're constructing, check it doesn't contain field names that look like secrets (`password`, `token`, `api_key`, `secret`). If it does, drop the field; never echo secret material into a tsuga query. diff --git a/plugins/tsuga/skills/tsuga-cli/references/http-api.md b/plugins/tsuga/skills/tsuga-cli/references/http-api.md new file mode 100644 index 0000000..3c3f9b8 --- /dev/null +++ b/plugins/tsuga/skills/tsuga-cli/references/http-api.md @@ -0,0 +1,15 @@ +# Public HTTP API (for user-built scripts) + +For scripts or deployed services that hit Tsuga without the CLI or MCP (bulk backfills, metering jobs). Full OpenAPI reference: `https://app.tsuga.com/swagger`. + +> During skill execution stay CLI-first — never curl the API yourself. This reference exists to hand correct integration facts to users building their own tooling. + +- **Host:** `https://api.tsuga.com` — not `app.tsuga.com`, which is the web UI. +- **Auth:** `Authorization: Bearer `. The key must be an **operation key** with the relevant signal set to **Read** (e.g. Metrics = Read). An ingestion key is the wrong type — it can only send data in, never query it out. The CLI and MCP authenticate with the same key type, so a key that works in `tsuga auth` works over HTTP. +- **Aggregation endpoints** (mirror `tsuga aggregation`): + - `POST /v1/aggregation/multi-query/scalar` + - `POST /v1/aggregation/multi-query/timeseries` — body-level `aggregationWindow` (e.g. `"10s"`, `"1m"`, `"1h"`) +- **Request body:** identical to the CLI aggregation body documented in the skill (`dataSource`, `queries[]`, `timeRange`, `groupBy[]`, optional `formula`, optional `clusterId`). `timeRange.from`/`to` must be **Unix seconds (integers)** — ISO-8601 strings are rejected with `400 FST_ERR_VALIDATION`. +- **Response envelope:** `{"data": , "requestId": "..."}` on success, `{"error": {...}, "requestId": "..."}` on 4XX/5XX. `data` carries the same shape the CLI prints (`results[]` for scalar, `series[].points[]` for timeseries). +- Most CLI commands have a `/v1/...` counterpart (`/v1/logs/search`, `/v1/logs/patterns`, `/v1/traces/search`, `/v1/metrics`, `/v1/monitors`, `/v1/dashboards`, …) — see the OpenAPI reference for the full surface. +- **Lookback is bounded by retention policies**, which are per-signal and resolve global → environment → team (1–3650 days). There is no universal retention constant — check `tsuga retention-policies list` (or Settings → Retention) before assuming how far back a query can go.