Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JANUS — Claude Code plugin

CI

Janus, the Roman god of gates, has two faces looking in opposite directions — and so does this pipeline. One face looks downstream, where an OpenShift consultant works: the exact RHEL/OCP builds customers run, their crashes, upgrades, and CVEs. The other looks upstream, where the fix belongs: the kernel, Kubernetes, and KubeVirt communities. Dig deep enough into Linux and the two views meet at the same root cause.

An OpenShift / RHEL / CNV research & investigation pipeline for Claude Code — case types range from kernel crash forensics to upgrade/compatibility analysis, CVE impact assessment, and operator/component behavior investigation. JANUS uses Claude Code itself as the orchestrator: the lead session composes a pipeline of small agent stages per case type, gates the dynamic ones, and hands a ranked-hypothesis report to a human. It lays evidence and hypotheses on the table — the human makes the final call.

Every case feeds both faces: synthesize hands the consultant a ranked-hypothesis report grounded in the exact builds involved, and when the root cause turns out to be everyone's problem, upstream-adviser drafts the contribution proposal to carry it home.

Architecture

How a case flows through the team — the lead session orchestrates, small agent stages investigate in parallel, and the human holds every gate that matters:

flowchart TD
    human(["Human"]) -->|"question / artifact"| intake

    intake["1 · Intake — classify the case, write case.yaml"] --> fanout
    fanout["2 · Fan-out — launch stages in parallel"]

    subgraph stages["Static stages — each writes cases/<id>/findings/<stage>.md"]
        ds["doc-search<br/>okp · mslearn · aws"]
        st["source-trace<br/>casket (optional)"]
        ca["crash-analyze<br/>drgn · gdb"]
        ia["iac-author<br/>terraform · ansible<br/>writes IaC, runs nothing"]
    end

    lv["lab-verify — DYNAMIC<br/>disposable lab, never production"]

    fanout --> ds & st & ca & ia
    ia -->|"cases/&lt;id&gt;/iac/"| lv
    fanout -.->|"only after the human approves<br/>APPROVE_&lt;id&gt;.md"| lv

    ds & st & ca & ia --> fanin
    lv -.-> fanin
    fanin["3&nbsp;· Fan-in — read findings, spot gaps"]

    follow["github-trace · jira-trace<br/>(conditional, read-only)"]
    fanin -.->|"a stage surfaced a<br/>PR / Jira key"| follow
    follow -.->|"findings/&lt;stage&gt;.md"| fanin

    fanin --> syn["synthesize — cross-reference all findings"]
    syn --> report[("results/report.md<br/>ranked hypotheses · Confidence + Basis + refs")]
    report --> qc["4&nbsp;· Quality check<br/>chain verify · urlcheck · quotecheck · versioncheck · linkcheck · prosecheck (ja) · two judgment gates (C1/C2)"]
    qc -.->|"send-back, by sub-code"| syn
    qc -->|"handoff"| human2(["Human — final call, writes verdict.md"])
Loading

And how JANUS improves itself — two periodic agents sit outside the pipeline and feed a human-gated review queue:

flowchart LR
    case["Completed case"] -->|"new failure pattern,<br/>human-approved"| lessons
    lessons[("janus-lessons<br/>project-local staging")] -->|"injected into<br/>stage briefs"| next["Next case"]

    verdicts[("verdict.md × 10<br/>or weekly")] --> si["self-improver<br/>metrics: hit rate, escalation<br/>precision/recall, calibration"]
    lessons -->|"recurs across ≥2 cases"| si
    report[("results/report.md<br/>high-confidence upstream defect")] --> ua["upstream-adviser<br/>advisory only — never<br/>opens issues/PRs itself"]

    si -->|"IMPROVE_&lt;date&gt;.md"| rq[("review-queue/")]
    ua -->|"contribution drafts"| rq
    rq -->|"human approves"| plugin["Plugin agent catalogs<br/>failure & reusable patterns,<br/>process changes"]
Loading

What's in the plugin

.claude-plugin/marketplace.json      # marketplace listing → plugins/janus
plugins/janus/
  .claude-plugin/plugin.json         # plugin manifest
  skills/janus/SKILL.md              # /janus — pipeline driver
  skills/janus/scripts/chain.py      # per-case evidence hash ledger (seal/verify/lock)
  skills/janus/scripts/urlcheck.py   # reference-URL liveness check (backs gate C1/url)
  skills/janus/scripts/quotecheck.py # verbatim-quote fidelity check (backs gate C2/quote)
  skills/janus/scripts/versioncheck.py # version-provenance check (backs gate C2/version)
  skills/janus/scripts/linkcheck.py  # evidence links resolve to a real file/anchor (backs C1/link)
  skills/janus/scripts/prosecheck.py # ja report prose via textlint (backs gate C2/prose)
  skills/janus/scripts/textlintrc.json # ja-technical-writing config for the above
  skills/deck/                       # report → branded .pptx/PDF
  skills/okp-doc-search/             # okp-mcp research know-how (queries, doc_id rules)
  hooks/                             # secret-safety + evidence-lock (PreToolUse denies) + evidence-chain (PostToolUse auto-seal)
  agents/                            # 10 agents (patterns inlined into each)
    doc-search  source-trace  github-trace  jira-trace  crash-analyze
    iac-author  lab-verify  synthesize  self-improver  upstream-adviser
scripts/validate.py                  # repo consistency checks (CI-friendly, stdlib-only)
scripts/selftest.py                  # offline self-tests for chain.py / urlcheck.py / quotecheck.py / versioncheck.py / linkcheck.py / prosecheck.py / hooks
.github/workflows/ci.yml             # runs both on every push / PR

The pipeline: { doc-search, source-trace, crash-analyze, iac-author | [approve] lab-verify } | synthesize — eight composable stages connected by a universal findings/*.md format (github-trace and jira-trace join conditionally when another stage surfaces an upstream PR/issue or a Jira ticket), plus two periodic agents. Reusable investigation patterns (drgn triage, CVE tracing, refuting an a-priori hypothesis, goroutine-leak repro, etc.) are inlined into each agent so they travel with the plugin.

Install

This repo is itself a Claude Code plugin marketplace (.claude-plugin/marketplace.json). Inside a Claude Code session:

/plugin marketplace add nogunix/janus   # register straight from GitHub
/plugin install janus@janus             # install the plugin (plugin@marketplace)

Or from a local clone (useful when editing the plugin):

git clone https://github.com/nogunix/janus.git ~/janus
/plugin marketplace add ~/janus     # register the local marketplace
/plugin install janus@janus         # install the plugin (plugin@marketplace)

When editing the plugin, run python3 scripts/validate.py before committing — it checks manifest schema, skill/agent frontmatter, hook-script paths, and SKILL.md ↔ agents/ stage sync.

Submitted to Anthropic's community marketplace for review; once approved it will also be installable via /plugin marketplace add anthropics/claude-plugins-communityjanus@claude-community. Until then, use the direct-from-GitHub install above — it already tracks the latest release.

Restart Claude Code so the skills and agents load, then verify:

  • /pluginjanus shows as installed and enabled
  • /janus appears in the skill list; the ten janus:* agents appear in the Agent tool list

Day-to-day maintenance:

/plugin marketplace update janus    # re-read the local clone after edits
/plugin uninstall janus@janus       # remove the plugin
/plugin marketplace remove janus    # remove the marketplace entry

Working method (model-agnostic quality)

Investigation quality is enforced by explicit discipline, not by the model in the seat:

  • Evidence-basis labels — every finding carries Basis: VERIFIED | REASONED | ASSUMED (tool output observed vs. inferred from reading vs. carried in) alongside its confidence, and a label is only promoted by new evidence.
  • Named acceptance gates — the lead checks each report against two judgment gates: C1 GROUNDING (references, public URLs, no speculation language, basis integrity) and C2 COMPLETENESS & FIDELITY (completeness, verbatim artifact names, verbatim evidence quotes). Failures go back to synthesize by sub-code (C1/basis, C2/quote-absent, …); a HIGH hypothesis needs at least one VERIFIED finding behind it. Three of these checks are mechanical — reference liveness, quote fidelity, and the evidence chain (see Integrity checks below).
  • Causation gate — crash-analyze may not record a crash cause without "X causes Y because Z" where X and Y are observations from this vmcore; correlation without a mechanism caps at MEDIUM.
  • Failure-pattern catalogs — agents carry symptom → wrong move → correct move entries seeded from real cases (e.g. a search timeout means "reduce scope", never "report negative").
  • Lessons loop — project-specific lessons are banked (with human approval) in .claude/skills/janus-lessons/SKILL.md, which plugin updates never overwrite; the lead injects relevant entries into stage briefs, and recurring ones get promoted into the plugin's own catalogs via the self-improver review queue.

Integrity checks (mechanical, before any human-level gate)

Four stdlib-only scripts turn "trust the report" into "check the report." All run at handoff; a failure sends the report back rather than shipping it.

Evidence chain — scripts/chain.py. Each case carries an append-only hash ledger, cases/<id>/chain.jsonl. Every record holds the sha256 of one evidence file plus the previous record's hash — the same linked-hash idea as a blockchain. It makes edits visible, never impossible: a legitimate revision (a report sent back to synthesize, an updated finding) appends a new record and the ledger keeps the full history; an edit that bypasses sealing breaks verification.

$ python3 scripts/chain.py verify cases/<id>
FAIL: TAMPER: results/report.md changed after last seal

Sealing is mostly automatic — a PostToolUse hook (hooks/evidence-chain.py) seals every write into the evidence set (case.yaml, findings, report, audit logs, verdict) — and the lead also seals explicitly before synthesis and at close (covering shell-written files the hook can't see). Deleting a record to cover tracks fails too: the broken hash link exposes the gap. The upshot is that the audit trail behind a claim can't be quietly rewritten after the fact, and the human verdicts self-improver's metrics stand on stay ground truth.

The chain detects rewrites; chain.py lock prevents the accident in the first place. When the lead closes fan-in it drops the write bits on the fact base (case.yaml, findings/*.md, audit/*), and a PreToolUse hook (hooks/evidence-lock.py) denies tracked writes to locked files with an explanation instead of a bare permission error — so a stage can no longer clobber another stage's findings mid-flight. chain.py unlock is the lead's explicit escape hatch for a legitimate revision (unlock → edit → re-seal → lock).

Quote fidelity — scripts/quotecheck.py. The telephone-game failure the lock can't catch: findings survive intact on disk while a fact mutates as synthesize copies it into the report — "reproduced" softens into "may reproduce", a version number drifts. The report is a legitimately new file, so no hash ledger notices. Instead, the report carries its load-bearing facts as attributed verbatim quotes (> … / > — findings/<stage>.md), and quotecheck verifies each one appears word-for-word in the file it cites — backing gate C2/quote:

$ python3 scripts/quotecheck.py cases/<id>/results/report.md
FAIL: report.md:12: quote not found verbatim in findings/doc-search.md: "…"

Reference liveness — scripts/urlcheck.py. Backs gate C1/url by curl-checking every reference URL in the report. A fabricated citation (the classic LLM failure) dies as a 404 instead of a footnote nobody clicked:

$ python3 scripts/urlcheck.py cases/<id>/results/report.md
FAIL: https://access.redhat.com/errata/RHSA-2099:9999/ (404)

The check is deliberately honest about what it can't prove. A portal that 302-redirects a missing path into an SSO login flow (returns 200) is classified gated, not live — existence unconfirmable without authenticating, so it's flagged for a human rather than passed or failed. 401/403/429 fold into the same class. A fully-unreachable network downgrades to a notice and passes, so air-gapped okp-mcp installs stay usable.

Version provenance — scripts/versioncheck.py. The drift the quote check can't see: a fact observed at one product version reworded into a claim about another. Backs gate C2/version. The one hard FAIL is a source location cited with no version anywhere in its Ref (no NVR, casket path, or commit) — which version was read is unrecoverable. The rest are warnings the lead judges: a Detail/Ref pair crossed within one product family (Detail says 4.16, Ref pins 4.18), or — against the version_scope a case may declare — a finding or report version in that family but off-scope. Family-anchoring keeps kernel 5.14, image tags, and RPM releases from drowning the OCP-minor signal:

$ python3 scripts/versioncheck.py cases/<id>
FAIL: findings/source-trace.md F2: source location cited with no version pin
warning: results/report.md: version 4.19 asserted but backed by no finding

Evidence links — scripts/linkcheck.py. The report is meant to be read by clicking: each claim links straight to the finding behind it, so a reviewer lands on the evidence instead of grepping for it.

H1 rests on [F3](../findings/crash-analyze.md#f3-sigsegv-in-qemu-kvm)
and the [lab trace](../audit/lab-1.log).

That affordance creates a new way to lie, which is why the check exists. urlcheck.py only sees http(s)://, so a relative link to a finding that was never written — or to an #anchor no heading produces — renders as an ordinary blue link and resolves to nothing. Backs gate C1/link:

$ python3 scripts/linkcheck.py cases/<id>/results/report.md
FAIL: report.md:8: no such anchor in findings/crash-analyze.md: #f9-does-not-exist
FAIL: report.md:9: evidence file does not exist: findings/source-trace.md

Anchors are matched the way GitHub and VS Code generate them (lowercase, punctuation dropped rather than hyphenated, duplicates suffixed -1), plus explicit <a id="…">. Links inside fenced code blocks are ignored, and a target outside the case directory is a FAIL. Unlike urlcheck this never fails open — local resolution is deterministic and offline, so a broken link is always a real defect.

Japanese prose quality — scripts/prosecheck.py. The other four ask whether the report is true; none ask whether it is readable. For a report_language: ja case this wraps textlint with the ja-technical-writing preset and backs gate C2/prose:

$ python3 scripts/prosecheck.py cases/<id>
FAIL: report.md:7:47 [ja-technical-writing/no-mix-dearu-desumasu] "である"調 であるべき箇所に "ですます"調
FAIL: report.md:11:3 [ja-technical-writing/no-hankaku-kana] Disallow to use 半角カタカナ: "カタナ"

Two boundaries make it safe. It never rewrites — no --fix, because that would mutate prose chain.py has sealed and quotecheck.py cross-checks, and would break the rule that the lead never patches the report itself; violations go back to synthesize like any other gate. And ja-no-weak-phrase is switched off on purpose: it flags hedging, but in JANUS a LOW-confidence hypothesis is supposed to read as uncertain. Forcing assertive prose would make the report overclaim — precisely what the Confidence/Basis labels exist to prevent.

This is the only check that shells out to a non-stdlib tool, so it fails open in every direction — English case, textlint not installed, preset missing, no report yet — with a notice and exit 0. A notice means not checked, never passed; the script refuses to print OK for a run that did not happen. Quoted evidence, code blocks, tables and headings are excluded from linting (textlint-filter-rule-node-types), though note that the preset's own rules already skip those node types — the filter is a backstop for rules added later, such as prh for terminology, which do match inside blockquotes.

All six scripts and the lock hook have offline self-tests (scripts/selftest.py) exercising tamper detection, ledger-edit detection, lock/deny/unlock, quote-mutation detection, the gated-vs-dead URL split, version-provenance drift, evidence-link resolution, and prosecheck's fail-open paths (including the regression that once printed OK when textlint was absent); .github/workflows/ci.yml runs them with validate.py on every push and PR — no npm install needed, because the prose check degrades to a notice there.

See CHANGELOG.md for version history.

Usage

Invoke /janus with a question or an artifact. The lead classifies the case, shows you the pipeline it intends to run, fans the stages out on approval, and hands you a ranked-hypothesis report at cases/<id>/results/report.md.

CVE impact assessment (needs okp-mcp):

/janus Does CVE-2024-1086 affect OpenShift 4.16 worker nodes?

{ doc-search, source-trace } | synthesize — errata/KB sweep plus the actual code path, cross-referenced into ranked hypotheses. A well-supported "not affected, and here is why" is a valid outcome.

Kernel crash forensics (needs drgn):

/janus Analyze the vmcore under cases/2026-07-11-node-panic/artifacts/,
kernel 5.14.0-570.el9. The node panicked during a VM live migration.

→ adds crash-analyze: drgn triage (crashed thread, dmesg, task states), then up to 5 observe → hypothesize → probe rounds. Every probe and its output lands in cases/<id>/audit/ — the report's claims point at them.

Upgrade / cross-version compatibility:

/janus What changed between OCP 4.18 and 4.20 that could break VMs
using SCSI-3 persistent reservations over multipath?

→ version-diff investigation across layers (kernel, RHEL userspace, CNV). When a stage surfaces a KubeVirt PR or an RHEL-NNNNN ticket it cannot open, the lead launches github-trace / jira-trace follow-ups at fan-in. For ARO cases, mslearn covers the Azure layer; for ROSA cases, the AWS MCP servers cover the AWS layer.

Japanese prompts work the same way — the skill triggers on phrases like 「vmcoreを解析」「OOM調査」「アップグレード互換性を調査」「CVEの影響評価」.

Reproducible lab as Infrastructure-as-Code (needs terraform / ansible):

/janus Build the ARO 4.16 lab needed to reproduce this multipath
hypothesis — Terraform + Ansible, ready for review.

→ adds iac-author: it looks every provider argument and version pin up in the Terraform registry rather than recalling it, checks the sharp-edged values before writing them (instance-type allowlist, GPU AZ availability, node disk ≥ 3× model size, image tags that actually exist), and writes fmt-ed, validate-d, ansible-lint-clean code into cases/<id>/iac/ with documented variables and no credentials. Anything it could not confirm is left un-defaulted with a TODO(iac-author) at the exact line — a hole you can see beats a plausible guess that survives review.

Every finding in the report carries Confidence + Basis (VERIFIED / REASONED / ASSUMED) + a reference a human can open — a CVE/errata URL, a source permalink, or a drgn audit log. Live-cluster verification (lab-verify) is only ever proposed: it runs on a disposable lab, and only after you approve review-queue/APPROVE_<id>.md.

Authoring that IaC and applying it are deliberately different stages. iac-author is static and autonomous, because writing a .tf changes no infrastructure; lab-verify is the only stage that runs it, behind the approval gate, as explicit shell commands recorded in cases/<id>/audit/. The ansible MCP's executing tools (ansible_navigator, ade_setup_environment) are granted to no agent, and the terraform grants are enumerated rather than wildcarded so that enabling that server's enterprise tools can never quietly hand an agent apply_run. scripts/validate.py fails the build if either rule is broken.

MCP dependencies (environment-specific)

The plugin does not bundle MCP config — server paths are machine-specific. Register each server yourself (claude mcp add …) before running an investigation, then confirm claude mcp list shows ✔ Connected — a tool being advertised isn't the same as the server being reachable.

okp-mcp — Red Hat docs / CVE / errata / KB

Bridges to the official Offline Knowledge Portal (OKP) Solr index. Requires a Red Hat account (registry.redhat.io access + an OKP access key from https://access.redhat.com/offline/access/). The bridge server itself is public OSS: https://github.com/rhel-lightspeed/okp-mcp.

podman login registry.redhat.io        # needs a Red Hat account
# build the okp-mcp bridge image per github.com/rhel-lightspeed/okp-mcp
podman play kube okp-pod.yaml          # manifest below
claude mcp add --transport http okp-mcp http://localhost:8000/mcp --scope user

okp-pod.yaml — Solr + bridge in one pod:

apiVersion: v1
kind: Pod
metadata:
  name: okp-mcp
spec:
  containers:
    - name: redhat-okp
      image: registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest
      ports:
        - containerPort: 8983
          hostPort: 8983
      env:
        - name: ACCESS_KEY
          value: "<your-okp-access-key>"
        - name: SOLR_JETTY_HOST
          value: "0.0.0.0"
      volumeMounts:
        - name: redhat-okp-data
          mountPath: /opt/solr/server/solr/portal/data
    - name: okp-mcp
      image: localhost/okp-mcp:latest
      ports:
        - containerPort: 8000
          hostPort: 8000
      env:
        - name: MCP_SOLR_URL
          value: "http://localhost:8983"
  volumes:
    - name: redhat-okp-data
      persistentVolumeClaim:
        claimName: redhat-okp-data
  restartPolicy: Always

mslearn — Microsoft Learn docs for the ARO/Azure layer

Public remote server, no auth, used by doc-search. Official server docs: https://github.com/MicrosoftDocs/mcp.

claude mcp add --transport http mslearn https://learn.microsoft.com/api/mcp

aws-docs / aws-knowledge / aws-support — AWS docs for the ROSA/AWS layer

The AWS mirror of mslearn, used by doc-search for ROSA (Red Hat OpenShift Service on AWS) and the AWS services beneath it. All optional, from awslabs/mcp; doc-search uses whichever are connected and skips the rest.

  • aws-knowledge — hosted, read-only, no auth; cross-searches AWS docs / blogs / What's New / API references:
    claude mcp add --transport http aws-knowledge https://knowledge-mcp.global.api.aws
  • aws-docs — read-only, no credentials; runs via uvx:
    claude mcp add aws-docs -- uvx awslabs.aws-documentation-mcp-server@latest
  • aws-support — needs AWS credentials + a Business/Enterprise support plan. Only its read-only describe_* tools are granted to doc-search (the case create / reply / resolve tools are deliberately withheld — JANUS never mutates a support case):
    claude mcp add aws-support --env AWS_PROFILE=<profile> --env AWS_REGION=us-east-1 \
      -- uvx awslabs.aws-support-mcp-server@latest

slack — optional, bring your own workspace

doc-search can supplement official docs with your team's Slack discussions. It calls the tools of redhat-community-ai-tools/slack-mcp (search_messages, get_thread, get_channel_history, list_joined_channels, …), which runs locally via Podman/Docker (quay.io/redhat-ai-tools/slack-mcp) or its one-shot setup script. Register it with SLACK_MCP_READ_ONLY=1 so state-mutating tools (post/react/join) error out — JANUS only reads. Point it at a workspace you are authorized to search. Slack hits are supplementary evidence only — findings attribute them as [slack] #channel, YYYY-MM-DD and never rest a conclusion on them alone. Without it, doc-search simply skips the Slack angle.

mcp-atlassian — optional, Jira ticket deep-dive

Used by the conditional jira-trace stage when another stage surfaces a Jira ticket key (e.g. Red Hat Jira RHEL-NNNNN / OCPBUGS-NNNNN). Public OSS: https://github.com/sooperset/mcp-atlassian. Register it with READ_ONLY_MODE=true — that disables every write tool at server level and is the safety boundary that keeps jira-trace observation-only (JANUS never creates, edits, comments on, or transitions tickets):

claude mcp add mcp-atlassian -s user \
  --env JIRA_URL=https://issues.redhat.com \
  --env JIRA_PERSONAL_TOKEN=<your-PAT> \
  --env READ_ONLY_MODE=true \
  -- uvx mcp-atlassian

For Jira Cloud, use JIRA_USERNAME + JIRA_API_TOKEN instead of the personal access token. Without this server, jira-trace is skipped and ticket references stay in the report as gaps.

drgn — vmcore static analysis

Public OSS: https://github.com/walac/drgn-mcp, an MCP wrapper around the drgn programmable kernel debugger. Run it sandboxed — network-cut, read-only, unprivileged — since upstream has no built-in sandboxing:

git clone https://github.com/walac/drgn-mcp.git
cd drgn-mcp && python3 -m venv .venv && .venv/bin/pip install -e .
claude mcp add drgn -s user -- "$(pwd)/.venv/bin/python" -m drgn_mcp.server

github — upstream PR/issue/commit lookup

Official hosted read-only endpoint of github/github-mcp-server — no server to run yourself. The /readonly path serves only read tools, which is the safety boundary here. Used by github-trace and upstream-adviser:

claude mcp add --transport http github https://api.githubcopilot.com/mcp/readonly

linux — read-only RHEL node/VM diagnostics

Public OSS: https://github.com/rhel-lightspeed/linux-mcp-server, local or over SSH. Used by lab-verify. Register with LINUX_MCP_TOOLSET=fixed (lowercase) so the arbitrary-script run_script toolset stays disabled — that's the safety boundary that keeps lab-verify read-only:

pip install git+https://github.com/rhel-lightspeed/linux-mcp-server.git
claude mcp add linux -s user --env LINUX_MCP_TOOLSET=fixed -- linux-mcp-server

terraform — provider/module/policy lookup for IaC authoring

HashiCorp's official terraform-mcp-server, used by iac-author to write HCL against the current registry schema instead of from memory. Its default tool set is read-only registry lookup:

podman run -d -p 8090:8080 --name terraform-mcp \
  hashicorp/terraform-mcp-server:latest
claude mcp add --transport http terraform http://localhost:8090/mcp

Only the enumerated registry tools are granted. Enabling the server's enterprise tools with a Terraform token adds create_run / apply_run, which apply real infrastructure — JANUS never grants mcp__terraform__* as a wildcard for exactly that reason.

ansible — scaffolding, lint, and best practices for IaC authoring

The ansible-dev-tools MCP server, used by iac-author:

pip install ansible-dev-tools
adt mcp --sse --port 8081        # see the ansible-dev-tools docs for current flags
claude mcp add --transport sse ansible http://localhost:8081/sse

Only the authoring subset is grantedansible_lint, ansible_content_best_practices, zen_of_ansible, create_ansible_projects, define_and_build_execution_env, adt_check_env, ade_environment_info. Two of this server's tools are deliberately granted to no agent: ansible_navigator runs playbooks against real targets, and ade_setup_environment runs the host package manager. Playbook execution belongs to lab-verify, post-approval, as an explicit ansible-playbook command in the audit trail.

Optional tooling — textlint (Japanese reports only)

Not an MCP server, and not required: scripts/prosecheck.py shells out to textlint only for report_language: ja cases, and degrades to a notice whenever it is absent. Install it if you hand Japanese reports to customers:

npm install -g textlint \
  textlint-rule-preset-ja-technical-writing \
  textlint-filter-rule-node-types

The config ships with the plugin at skills/janus/scripts/textlintrc.json and is passed explicitly with -c, so it never collides with a .textlintrc of your own. prosecheck.py itself stays stdlib-only, CI needs no npm install, and npx is invoked with --no-install so nothing is ever downloaded mid-investigation.

Safety

Read-only. Dead-artifact analysis (vmcore) is autonomous-safe; live-target work (lab provisioning, dynamic tracing) requires explicit human approval and a disposable lab, never production. Never spoofs guardrails; the final root-cause call is the human's.

License

MIT License - see the LICENSE file for details.

This is my personal project. It is created and maintained in my personal capacity, and has no relation to my employer's business or confidential information.

About

OpenShift/RHEL/CNV research & investigation pipeline for Claude Code — crash forensics, upgrade/compat analysis, CVE impact, component behavior.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages