diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ce07977..ea62c3b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -63,3 +63,31 @@ jobs: echo "::error::Found {{SKILLS_DIR}} in plugins/. This is the npx-skills install-time placeholder and is NOT substituted by Claude Code. Use \${CLAUDE_PLUGIN_ROOT}/skills//... instead." exit 1 fi + + - name: Validate skills index.json (OpenCode) + run: | + set -e + INDEX=plugins/tsuga/skills/index.json + jq empty "$INDEX" + + # Every skill dir must have an entry in index.json + fail=0 + for d in plugins/tsuga/skills/*/; do + name=$(basename "$d") + [ "$name" = "index.json" ] && continue + if ! jq -e ".skills[] | select(.name == \"$name\")" "$INDEX" >/dev/null 2>&1; then + echo "::error file=$INDEX::skill '$name' missing from index.json" + fail=1 + fi + done + + # Every file referenced in index.json must exist on disk + jq -r '.skills[] | .name as $s | .files[] | "\($s)/\(.)"' "$INDEX" | \ + while IFS= read -r f; do + if [ ! -f "plugins/tsuga/skills/$f" ]; then + echo "::error file=$INDEX::index.json references missing file: $f" + fail=1 + fi + done + + exit $fail diff --git a/AGENTS.md b/AGENTS.md index 91ad156..7af0bb8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,7 @@ This repo distributes **one plugin**, `tsuga`, from a single marketplace using t .claude-plugin/marketplace.json ← marketplace listing; each plugin points to its own subdir via source plugins/tsuga/ .claude-plugin/plugin.json ← per-plugin manifest + skills/index.json ← OpenCode skills index (lists every skill + its files) skills//SKILL.md ← skills auto-discovered by Claude Code / Codex ``` @@ -20,7 +21,8 @@ Notes: ## When changing a skill 1. Edit `plugins/tsuga/skills//SKILL.md` (or files under its `references/`). -2. **Bump versions in lockstep.** Two places: +2. **Update `plugins/tsuga/skills/index.json`** — if files were added or removed, update the `files[]` array for that skill. +3. **Bump versions in lockstep.** Two places: - `.claude-plugin/marketplace.json` → `metadata.version` - `plugins/tsuga/.claude-plugin/plugin.json` → `version` @@ -34,7 +36,8 @@ Notes: ## When adding a new skill 1. Create `plugins/tsuga/skills//SKILL.md` with proper frontmatter (`name:`, `description:`). -2. Use existing naming patterns: +2. **Add the skill to `plugins/tsuga/skills/index.json`** — add an entry with `name`, `description`, and `files[]` listing every file in the skill directory (use relative paths, e.g. `references/foo.md`). +3. Use existing naming patterns: - `otel-*` for SDK, Collector, and code-facing OpenTelemetry guidance. - `tsuga-*` for live Tsuga workflows, dashboards, audits, telemetry debug workflows, and CLI-backed tasks. - Unprefixed names for platform-agnostic advisory skills. @@ -91,6 +94,20 @@ done # No stray top-level skills/ directory [ -d skills ] && echo "FAIL: skills/ should be empty/absent — move into plugins/tsuga/skills/" + +# index.json is valid JSON and every skill dir has an entry +jq empty plugins/tsuga/skills/index.json +for d in plugins/tsuga/skills/*/; do + name=$(basename "$d") + jq -e ".skills[] | select(.name == \"$name\")" plugins/tsuga/skills/index.json >/dev/null 2>&1 || \ + echo "FAIL: $name missing from index.json" +done + +# Every file in index.json files[] exists on disk +jq -r '.skills[] | .name as $s | .files[] | "\($s)/\(.)"' plugins/tsuga/skills/index.json | \ + while IFS= read -r f; do + [ -f "plugins/tsuga/skills/$f" ] || echo "FAIL: index.json references missing file: $f" + done ``` ## Release @@ -112,6 +129,7 @@ claude plugin tag ./plugins/tsuga - Don't add a `skills[]` array to marketplace.json. Skills auto-discover from the plugin's subdir. - Don't restore standalone telemetry packaging. - Don't rename a skill folder without grepping the repo for cross-references (`tsuga-cli`, `otel-instrumentation`, etc.). +- Don't let `plugins/tsuga/skills/index.json` drift — every skill directory must have a matching entry, and `files[]` must list every file on disk. ## Skill authoring rules diff --git a/README.md b/README.md index 0c31b39..6a8ea8f 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,26 @@ codex plugin marketplace add tsuga-dev/agent-plugins codex plugin add tsuga@tsuga ``` +### OpenCode + +> [!IMPORTANT] +This has only been tested with opencode v1, as v2 is still in beta. + +Add the skills URL to your `opencode.json`: + +```json +{ + "$schema": "https://opencode.ai/config.json", + "skills": { + "urls": ["https://raw.githubusercontent.com/tsuga-dev/agent-plugins/main/plugins/tsuga/skills/"] + } +} +``` + +You can replace `main` with any valid SHA of the repository to pin the skills if you want to opt-out of "auto update". + +The trailing slash is required. OpenCode fetches `index.json` from that URL, discovers all skills, and downloads them to `~/.cache/opencode/skills/`. + ## Ownership & contributions Tsuga owns and maintains this plugin — anything generic about operating Tsuga or instrumenting with OpenTelemetry belongs here, and installs with `autoUpdate` receive new versions automatically. diff --git a/plugins/tsuga/skills/index.json b/plugins/tsuga/skills/index.json new file mode 100644 index 0000000..d9ebe33 --- /dev/null +++ b/plugins/tsuga/skills/index.json @@ -0,0 +1,277 @@ +{ + "skills": [ + { + "name": "build-incident-history", + "description": "One-shot procedure for turning a raw incident dump into a populated incident-history archive.", + "files": [ + "RECOMMENDED_PROMPT.md", + "SKILL.md", + "references/INPUT_LAYOUT.md", + "references/LESSONS.md", + "references/PROCEDURE.md", + "references/SUBAGENT_PROMPT.md", + "references/SUMMARY_TEMPLATE.md", + "references/VERIFICATION.md" + ] + }, + { + "name": "build-knowledge-company", + "description": "One-shot procedure for turning a live Tsuga account + codebase list into the knowledge-company tree.", + "files": [ + "RECOMMENDED_PROMPT.md", + "SKILL.md", + "references/CLI_TRANSLATION.md", + "references/INPUT_LAYOUT.md", + "references/LESSONS.md", + "references/PROCEDURE.md", + "references/SERVICE_KNOWLEDGE_TEMPLATE.md", + "references/SKILL_TEMPLATE.md", + "references/SUBAGENT_PROMPT.md", + "references/TEAM_KNOWLEDGE_TEMPLATE.md", + "references/VERIFICATION.md" + ] + }, + { + "name": "check-skill-health", + "description": "Use when linting Tsuga skill bundles after editing runtime skills, generated archives, or references.", + "files": [ + "SKILL.md", + "references/CHECKLIST.md", + "references/RULES.md", + "scripts/check-forbidden-tokens.sh", + "scripts/check-frontmatter.sh", + "scripts/check-incident-history.sh", + "scripts/check-knowledge-company.sh", + "scripts/check-skill-length.sh", + "scripts/lint-all.sh", + "scripts/sample-execute-commands.sh" + ] + }, + { + "name": "gh", + "description": "GitHub CLI for inspecting workflow runs, PRs, commits, releases, and deployments.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "incident-investigation", + "description": "Primary entry point for active-incident investigation, post-incident RCA, or recurring-degradation triage.", + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/branch-change-correlation.md", + "references/branch-telemetry-sweep.md", + "references/case-manifest.md", + "references/investigation-record.md", + "references/tsuga-rules.md", + "references/playbooks/auth-tls.md", + "references/playbooks/data-quality.md", + "references/playbooks/database.md", + "references/playbooks/deploy-drift.md", + "references/playbooks/kubernetes.md", + "references/playbooks/queue-backpressure.md" + ] + }, + { + "name": "knowledge-technology", + "description": "Per-technology reference bundles with metric names, incident shapes, derived signals, and log patterns.", + "files": [ + "SKILL.md", + "references/apache/README.md", + "references/apache/metrics.csv", + "references/apache/queries.md", + "references/aws-api-gateway/README.md", + "references/aws-api-gateway/metrics.csv", + "references/aws-api-gateway/queries.md", + "references/aws-docdb/README.md", + "references/aws-docdb/metrics.csv", + "references/aws-docdb/queries.md", + "references/aws-dynamodb/README.md", + "references/aws-dynamodb/metrics.csv", + "references/aws-dynamodb/queries.md", + "references/aws-ecs/README.md", + "references/aws-ecs/metrics.csv", + "references/aws-ecs/queries.md", + "references/aws-efs/README.md", + "references/aws-efs/metrics.csv", + "references/aws-efs/queries.md", + "references/aws-elasticache/README.md", + "references/aws-elasticache/metrics.csv", + "references/aws-elasticache/queries.md", + "references/aws-elb/README.md", + "references/aws-elb/metrics.csv", + "references/aws-elb/queries.md", + "references/aws-eventbridge/README.md", + "references/aws-eventbridge/metrics.csv", + "references/aws-eventbridge/queries.md", + "references/aws-firehose/README.md", + "references/aws-firehose/metrics.csv", + "references/aws-firehose/queries.md", + "references/aws-lambda/README.md", + "references/aws-lambda/metrics.csv", + "references/aws-lambda/queries.md", + "references/aws-nat-gateway/README.md", + "references/aws-nat-gateway/metrics.csv", + "references/aws-nat-gateway/queries.md", + "references/aws-privatelink/README.md", + "references/aws-privatelink/metrics.csv", + "references/aws-privatelink/queries.md", + "references/aws-rds/README.md", + "references/aws-rds/metrics.csv", + "references/aws-rds/queries.md", + "references/aws-sqs/README.md", + "references/aws-sqs/metrics.csv", + "references/aws-sqs/queries.md", + "references/caddy/README.md", + "references/caddy/metrics.csv", + "references/caddy/queries.md", + "references/cassandra/README.md", + "references/cassandra/metrics.csv", + "references/cassandra/queries.md", + "references/envoy/README.md", + "references/envoy/metrics.csv", + "references/envoy/queries.md", + "references/gcp-pubsub/README.md", + "references/gcp-pubsub/metrics.csv", + "references/gcp-pubsub/queries.md", + "references/gcp-storage/README.md", + "references/gcp-storage/metrics.csv", + "references/gcp-storage/queries.md", + "references/haproxy/README.md", + "references/haproxy/metrics.csv", + "references/haproxy/queries.md", + "references/istio/README.md", + "references/istio/metrics.csv", + "references/istio/queries.md", + "references/jvm/README.md", + "references/jvm/metrics.csv", + "references/jvm/queries.md", + "references/kafka/README.md", + "references/kafka/metrics.csv", + "references/kafka/queries.md", + "references/kubernetes/README.md", + "references/kubernetes/metrics.csv", + "references/kubernetes/queries.md", + "references/kubernetes/runbooks.md", + "references/litespeed/README.md", + "references/litespeed/metrics.csv", + "references/litespeed/queries.md", + "references/mysql/README.md", + "references/mysql/metrics.csv", + "references/mysql/queries.md", + "references/nginx/README.md", + "references/nginx/metrics.csv", + "references/nginx/queries.md", + "references/nvidia-gpu/README.md", + "references/nvidia-gpu/metrics.csv", + "references/nvidia-gpu/queries.md", + "references/openai/README.md", + "references/openai/metrics.csv", + "references/openai/queries.md", + "references/otel-collector/README.md", + "references/otel-collector/metrics.csv", + "references/otel-collector/queries.md", + "references/postgres/README.md", + "references/postgres/metrics.csv", + "references/postgres/queries.md", + "references/quickwit/README.md", + "references/quickwit/metrics.csv", + "references/quickwit/queries.md", + "references/rabbitmq/README.md", + "references/rabbitmq/metrics.csv", + "references/rabbitmq/queries.md", + "references/redis/README.md", + "references/redis/metrics.csv", + "references/redis/queries.md" + ] + }, + { + "name": "otel-collector", + "description": "Use when Collector YAML, Helm values, OTLP exporters, receiver binding, pipeline topology, or OTTL expressions are involved.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "otel-instrumentation", + "description": "Use when adding, fixing, generating, or auditing OpenTelemetry app SDK setup across nine languages.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "signal-choice-advisor", + "description": "Use when choosing an OpenTelemetry signal, instrument, or telemetry name.", + "files": [ + "SKILL.md", + "references/signal-choice.md" + ] + }, + { + "name": "tsuga-analyze-trace-latency", + "description": "Use when asked about slow requests, high latency, latency spikes, p95/p99 trace duration, or slow spans.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "tsuga-audit-monitor-coverage", + "description": "Use when asked to check monitor coverage, services without monitors, alerting gaps, or notification routing.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "tsuga-audit-telemetry-quality", + "description": "Use when auditing telemetry quality for logs, metrics, traces, or resource identity.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "tsuga-build-dashboard", + "description": "Use when asked to create, update, validate, or review a Tsuga dashboard or its widgets.", + "files": [ + "SKILL.md", + "references/layout-rules.md", + "references/widget-reference.md" + ] + }, + { + "name": "tsuga-cli", + "description": "Use when a task involves Tsuga CLI commands, TQL search, aggregation bodies, or service ownership.", + "files": [ + "SKILL.md", + "references/app-deep-links.md", + "references/aws-translator.md", + "references/azure-translator.md", + "references/gcp-translator.md", + "references/kubectl-translator.md", + "references/playbooks/find-owner-and-context.md", + "references/playbooks/reliability-review.md" + ] + }, + { + "name": "tsuga-debug-telemetry-ingestion", + "description": "Use when telemetry is missing, sparse, delayed, or not visible in Tsuga.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "tsuga-investigate-errors", + "description": "Use when asked about service errors, error spikes, exception patterns, or anomalous error volume.", + "files": [ + "SKILL.md" + ] + }, + { + "name": "tsuga-investigate-service-health", + "description": "Use when investigating active incidents, service health checks, degraded service reports, or error spikes.", + "files": [ + "SKILL.md" + ] + } + ] +}