AgentSkillsCN

Node Patterns

Node模式

SKILL.md

Node.js Patterns

Patterns for the cases where Node is unavoidable: hook scripts, CLI tooling, build automation.

Hook Scripts

Hook scripts receive JSON on stdin and must write to stdout/stderr. This is the primary Node use case in this config.

javascript
#!/usr/bin/env node

// Pattern: stdin JSON processor for Claude Code hooks
let data = "";
process.stdin.on("data", (chunk) => (data += chunk));
process.stdin.on("end", () => {
  try {
    const input = JSON.parse(data);
    const toolName = input.tool_name || "";
    const command = input.tool_input?.command || "";

    // Your hook logic here
    if (/dangerous-pattern/.test(command)) {
      process.stderr.write("[Hook] Warning message for the user\n");
    }
  } catch {
    // Silently pass through on parse errors
  }
  process.stdout.write(data);
});

CLI Tool Structure

code
bin/tool-name.mjs           # Entry point with shebang
lib/
  config.mjs                # Config loading (env vars, flags)
  commands/                  # One file per subcommand
  utils.mjs                 # Shared utilities
package.json                # "type": "module", "bin" field

Error Handling

javascript
// Wrap errors with context using Error.cause
async function fetchConfig(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return await response.json();
  } catch (err) {
    throw new Error(`fetch config from ${url}`, { cause: err });
  }
}

Process Management

javascript
import { spawn } from "node:child_process";

function runCommand(cmd, args, { timeout = 30_000 } = {}) {
  return new Promise((resolve, reject) => {
    const proc = spawn(cmd, args, { stdio: "pipe", timeout });
    let stdout = "";
    let stderr = "";
    proc.stdout.on("data", (d) => (stdout += d));
    proc.stderr.on("data", (d) => (stderr += d));
    proc.on("close", (code) => {
      if (code === 0) resolve({ stdout, stderr });
      else reject(new Error(`${cmd} exited with ${code}: ${stderr}`));
    });
    proc.on("error", reject);
  });
}

Configuration

javascript
// Config precedence: flags > config file > .env > env vars
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";

async function loadConfig() {
  const defaults = { port: 3000, logLevel: "info" };
  const envOverrides = {
    port: process.env.PORT ? parseInt(process.env.PORT, 10) : undefined,
    logLevel: process.env.LOG_LEVEL,
  };
  // Remove undefined values
  const env = Object.fromEntries(
    Object.entries(envOverrides).filter(([, v]) => v !== undefined)
  );
  return { ...defaults, ...env };
}

When to Use Node vs. Bash

Use CasePreferenceReason
Hook scripts (JSON processing)NodeBash JSON parsing is fragile
Simple file operationsBashFewer moving parts
CLI with subcommandsGoBetter binary distribution
Build automationBashShell pipelines are native
Complex async orchestrationNodeAsync/await beats background jobs
One-off scriptsBashNo dependency overhead