npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nivustec/proteus

v1.0.2

Published

Shape-shifting CLI that auto-injects data-testid into React components (JS/TS).

Downloads

5

Readme

Proteus

🌍 Website

Automated data-testid injection for React (JS/TS). Proteus “surfs” your codebase and transforms it on save/commit, enforcing qa_prefixed, unique, and semantic test IDs across your components.

Why Proteus?

  • Consistent, predictable, and unique test IDs
  • Zero-runtime: IDs are injected at build/dev time
  • Works with JSX/TSX and plain JS/TS React projects
  • Safe defaults, with flexible strategies

Features

  • Two strategies only:
    • safe-hash: compact ids like qa_abc123; in maps: qa_abc123_${item.id}
    • functional: semantic ids like qa_products_image_${item.id}
  • Uniqueness enforcement with sibling-indexing for repeated tags
  • Map-awareness: automatically appends key/index for dynamic lists
  • Watch mode (inject on save)
  • Pre-commit integration (inject before commit)
  • Default strategy: functional
  • Default include: React frontend files only (src/**/*.tsx, src/**/*.jsx)

Installation

npm i -D @nivustec/proteus
# or
yarn add -D @nivustec/proteus

For local development of Proteus itself:

# in the Proteus repo
npm run build
npm link

# in your app repo
npm link @nivustec/proteus

Quick Start

  1. Initialize a config in your project root:
proteus init
  1. Inject once:
proteus inject
# or specific files
proteus inject src/components/Button.tsx
  1. Watch mode (auto-inject on save):
proteus inject --watch
  1. Git pre-commit (Husky optional):
proteus setup        # adds a pre-commit hook
git commit -m "feat: ..."  # Proteus injects before commit

Configuration

Create or edit proteus.config.json in your project root.

{
  "injectOnCommit": true,
  "injectOnSave": true,
  "include": ["src/**/*.tsx", "src/**/*.jsx"],
  "exclude": ["node_modules/**", "dist/**"],
  "strategy": "functional",
  "verbose": false,
  "detectReusableComponents": true,
  "autoExcludePatterns": ["**/ui/**", "**/common/**"]
}
  • injectOnCommit: enable pre-commit injection
  • injectOnSave: enable watch mode injection
  • include/exclude: glob patterns for files
  • strategy: safe-hash or functional
  • verbose: enable additional logs in CLI mode
  • detectReusableComponents: auto-detect and skip reusable UI components (default: true)
  • autoExcludePatterns: glob patterns to auto-exclude (e.g., ["**/ui/**", "**/common/**"])

🆕 Reusable Components Detection (v1.1.0)

Proteus now automatically detects reusable components (like Button, Input, Card from UI libraries) and skips injecting data-testid directly in them. Instead, it injects at the usage sites, ensuring unique IDs for each instance.

Detection criteria:

  • File is in a UI/common/shared folder (e.g., /ui/, /common/, /shared/)
  • Uses React.forwardRef or forwardRef<>
  • Has {...props} spread operator

Example:

// src/components/ui/button.tsx (auto-skipped by Proteus)
const Button = React.forwardRef(({ ...props }, ref) => {
  return <button {...props} ref={ref} />; // ✅ No fixed data-testid
});

// src/components/Hero.tsx (Proteus injects here)
<Button onClick={handleClick} data-testid="qa_hero_button_1_abc123">
  Get Started
</Button>
<Button variant="outline" data-testid="qa_hero_button_2_def456">
  View on GitHub
</Button>

Benefits:

  • ✅ No duplicate data-testid across multiple Button instances
  • ✅ Each usage gets a unique, contextual ID
  • ✅ Works with any UI library (shadcn, MUI, Ant Design, etc.)
  • ✅ Zero configuration required (auto-detection enabled by default)

Strategies

functional (semantic, recommended)

Rules (simplified):

  • Prefix: qa_
  • Base: qa_<component>_<role>_[<siblingIndex>]_[<descriptor>]
  • Dynamic lists (map): append key/index as the last segment

Examples:

// Map wrapper element
<div className="item" key={item.id} data-testid={`qa_products_item_${item.id}`} />

// Image inside map
<img data-testid={`qa_products_image_${item.id}`} />

// Text containers inside map
<div data-testid={`qa_products_container_name_${item.id}`}>{item.name}</div>
<div data-testid={`qa_products_container_price_${item.id}`}>{formatCurrency(item.price)}</div>

// Duplicate siblings get indexed
<h1 data-testid={`qa_products_h1_1_${item.id}`}>One</h1>
<h1 data-testid={`qa_products_h1_2_${item.id}`}>Two</h1>

// Outside map, unique suffix may be added to avoid collisions
<div className="header" data-testid="qa_products_header_<uniq>" />

Before/After (functional)

// Before
export function ProductCard({ item }) {
  return (
    <div className="card">
      <img alt={item.name} />
      <h2>{item.name}</h2>
      <button className="add-to-cart">Add</button>
    </div>
  );
}

// After (auto-injected)
export function ProductCard({ item }) {
  return (
    <div className="card" data-testid="qa_productcard_container_<uniq>">
      <img alt={item.name} data-testid="qa_productcard_image_img-item-name_<uniq>" />
      <h2 data-testid="qa_productcard_container_1_item-name_<uniq>">{item.name}</h2>
      <button className="add-to-cart" data-testid="qa_productcard_button_add-to-cart_<uniq>">Add</button>
    </div>
  );
}

safe-hash (compact)

Rules:

  • Prefix: qa_
  • Base: qa_<hash> (stable per location)
  • In maps: append key/index: qa_<hash>_${item.id}

Examples:

// Single element
<div data-testid="qa_ab12cd" />

// Inside map
<div data-testid={`qa_ab12cd_${item.id}`} />

CLI

proteus inject [files...] [options]

Options:
  -c, --config <path>     Path to config file
      --include <paths>   Files to include
      --exclude <paths>   Files to exclude
      --strategy <type>   safe-hash | functional
      --verbose           Enable verbose logs
      --json              Enable machine-readable JSON output
  -w, --watch             Watch mode for development

proteus pre-commit         # injects on staged files (used by Husky)
proteus setup              # configures Husky pre-commit hook
proteus init               # writes default proteus.config.json

Husky (Git hooks):

  • Modern Husky (v7+): if .husky/ exists, proteus setup writes .husky/pre-commit with proteus pre-commit.

  • Legacy fallback: if .husky/ is not detected, proteus setup configures package.json scripts/hooks.

  • Pre-commit processes only staged React files (*.jsx, *.tsx).

  • Watch mode observes the configured include patterns; defaults to src/**/*.tsx and src/**/*.jsx.


Agent-Friendly CLI (MCP Support)

Proteus can be used by AI agents and other automated systems via its machine-readable JSON output. Use the --json flag with the inject command to suppress human-readable logs and receive a structured JSON response.

Usage with --json

proteus inject --json

Example JSON Output

{
  "filesProcessed": 5,
  "totalInjected": 12,
  "errors": 0,
  "errorMessages": []
}

If errors occur, they will be included in the errorMessages array:

{
  "filesProcessed": 1,
  "totalInjected": 0,
  "errors": 1,
  "errorMessages": [
    "Error processing src/components/BrokenComponent.tsx: Parser Error: Unexpected token (1:10)"
  ]
}

Universal Agent (Tool Calling) Support

To enable any AI agent (Gemini, OpenAI, Anthropic, etc.) to interact with Proteus, we provide a universal JSON Schema for the proteus_inject_tool and a dedicated executor function. This approach removes any direct dependency on specific AI SDKs within the Proteus CLI itself.

1. Universal Contract (JSON Schema)

The proteus_inject_tool's function declaration is available as a pure JSON Schema file at the root of the project:

  • proteus-tool-schema.json: This file defines the structure and parameters of the proteus_inject_tool in a standard Open API/JSON Schema format, allowing any agent to understand how to call the tool.

2. Universal Executor

The executeProteusCLI function provides a clean API for executing the Proteus CLI and receiving its output in a machine-readable format.

  • src/tool-executor.ts: This module exports the executeProteusCLI function.

Example Consumer Agent Usage

Here's how a consumer agent (using any AI SDK) would typically integrate with Proteus:

  1. Load the JSON Schema: The agent loads proteus-tool-schema.json to understand the tool's capabilities.
  2. Model Interaction: The agent's model receives a user prompt and, based on the loaded schema, decides to call proteus_inject_tool with specific arguments.
  3. Execute the Tool: The agent then calls the executeProteusCLI function from src/tool-executor.ts with the arguments provided by its model.
  4. Process Output: The agent receives the JSON output from executeProteusCLI and can then use its model to generate a natural language response for the user.
// Example of a consumer agent (conceptual, using a generic AI SDK)
import { readFileSync } from 'fs';
import { executeProteusCLI } from './src/tool-executor.js'; // Adjust path as needed

// 1. Load the Proteus tool schema
const proteusToolSchema = JSON.parse(readFileSync('proteus-tool-schema.json', 'utf-8'));

// Assume your AI SDK has a way to register tools and get model responses
async function runConsumerAgent(userPrompt: string) {
  // --- Conceptual: AI Model decides to call the tool ---
  // In a real scenario, your AI SDK would handle this based on the userPrompt
  // and the registered proteusToolSchema.
  // For demonstration, we'll simulate a tool call from the model.

  const simulatedToolCallArgs = {
    files: ['src/components/MyComponent.tsx'],
    strategy: 'functional',
    json_output: true, // Always true for tool calling
  };

  console.log(`Agent: Simulating tool call to proteus_inject_tool with args:`, simulatedToolCallArgs);

  try {
    // 2. Execute the Proteus CLI via the universal executor
    const toolOutput = await executeProteusCLI(simulatedToolCallArgs);

    console.log(`Agent: Proteus CLI returned:`, toolOutput);

    // --- Conceptual: AI Model processes tool output and generates response ---
    // Your AI model would take toolOutput and generate a natural language response.
    if (toolOutput.errors > 0) {
      console.log(`Agent: I encountered ${toolOutput.errors} errors while injecting test IDs. Please check the logs.`);
    } else {
      console.log(`Agent: Successfully injected ${toolOutput.totalInjected} test IDs into ${toolOutput.filesProcessed} files.`);
    }

  } catch (error) {
    console.error("Agent: Error executing Proteus CLI:", error);
  }
}

// Example usage
runConsumerAgent("Inject test IDs into src/components/MyComponent.tsx using the functional strategy.");

Best Practices

  • Commit generated IDs; they are part of your source (zero runtime)
  • Choose one strategy per repo; we recommend functional
  • Ensure qa_ prefix uniqueness across layers and components
  • Always append a key/index for dynamic maps

Troubleshooting

  1. npx proteus not found / CLI not running
  • Ensure the package is installed in your project
  • For local dev: build + npm link in Proteus repo, then npm link @nivustec/proteus in your app
  1. Duplicate logs in watch mode
  • Proteus debounces its own writes; if you still see duplicates, increase the debounce window in watcher.ts
  1. Pre-commit too verbose
  • Set verbose: false (default). Pre-commit runs in silent mode by design.
  1. Pre-commit not injecting on non-React files
  • By default, only *.jsx and *.tsx staged files are processed. Adjust include if you need other patterns.
  1. IDs are inserted in the wrong place
  • We use AST positions; if you find an edge-case, open an issue with a code sample.
  1. Partial staging lost in pre-commit
  • Current hook re-stages full files after injection. If you rely on partial staging (git add -p), consider disabling injectOnCommit and running proteus inject manually.

License

MIT © Nivustec