{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "json-mode",
  "title": "json-mode",
  "description": "Dual-rendering output for CLIs serving humans and agents. Auto-detects TTY vs piped, enforces `stdout`/`stderr` discipline, respects `NO_COLOR`. Never calls `process.exit()`.",
  "dependencies": [
    "picocolors"
  ],
  "registryDependencies": [
    "https://cligentic.railly.dev/r/detect.json"
  ],
  "files": [
    {
      "path": "registry/agent/json-mode.ts",
      "content": "// cligentic block: json-mode\n//\n// Dual-rendering output helpers for CLIs that serve both humans and agents.\n//\n// Design rules:\n//   1. stdout is data. Only structured JSON in --json mode, formatted in human.\n//   2. stderr is logs. Notes, progress, errors.\n//   3. Mode detection is implicit. Piped stdout auto-switches to JSON.\n//   4. Never call process.exit().\n//   5. One call site: emit(value, opts, humanRender?).\n//\n// Usage:\n//   import { detectMode, emit, note, reportError } from \"./agent/json-mode\";\n//\n//   program.option(\"--json\", \"emit JSON for agents\");\n//   program.command(\"list\").action(async (opts) => {\n//     const items = await fetchItems();\n//     emit(items, opts, (data) => {\n//       for (const item of data) console.log(`- ${item.name}`);\n//     });\n//   });\n\nimport pc from \"picocolors\";\nimport {\n  type EmitOptions,\n  type OutputMode,\n  detectMode,\n  shouldColor,\n} from \"../platform/detect.js\";\n\n// Re-export so consumers can import everything from json-mode\nexport { type EmitOptions, type OutputMode as Mode, detectMode, shouldColor };\n\n/**\n * Emits a value to stdout. JSON in json mode, humanRender callback in human.\n * Arrays emit as NDJSON (one object per line) for agent stream-parsing.\n */\nexport function emit<T>(value: T, opts: EmitOptions = {}, humanRender?: (value: T) => void): void {\n  const mode = detectMode(opts);\n\n  if (mode === \"json\") {\n    if (Array.isArray(value)) {\n      for (const item of value) {\n        process.stdout.write(`${JSON.stringify(item)}\\n`);\n      }\n    } else {\n      process.stdout.write(`${JSON.stringify(value)}\\n`);\n    }\n    return;\n  }\n\n  if (humanRender) {\n    humanRender(value);\n    return;\n  }\n\n  process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\n/**\n * Writes a note to stderr. Suppressed in json mode and quiet mode.\n */\nexport function note(message: string, opts: EmitOptions = {}): void {\n  if (opts.json === true) return;\n  if (!process.stdout.isTTY && opts.json !== false) return;\n  if (opts.quiet) return;\n\n  const colored = shouldColor() ? pc.dim(message) : message;\n  process.stderr.write(`${colored}\\n`);\n}\n\n/**\n * Writes a success message. JSON to stdout in json mode, colored to stderr in human.\n */\nexport function emitSuccess(message: string, opts: EmitOptions = {}): void {\n  const mode = detectMode(opts);\n  if (mode === \"json\") {\n    process.stdout.write(`${JSON.stringify({ ok: true, message })}\\n`);\n    return;\n  }\n  const prefix = shouldColor() ? pc.green(\"✓\") : \"✓\";\n  process.stderr.write(`${prefix} ${message}\\n`);\n}\n\n/**\n * Reports an error without exiting. JSON to stdout in json mode, colored\n * to stderr in human. Returns the structured payload for caller inspection.\n */\nexport function reportError(\n  error: string | Error,\n  opts: EmitOptions = {},\n): { ok: false; error: string; stack?: string } {\n  const message = error instanceof Error ? error.message : error;\n  const stack = error instanceof Error ? error.stack : undefined;\n  const payload = { ok: false as const, error: message, ...(stack ? { stack } : {}) };\n\n  const mode = detectMode(opts);\n  if (mode === \"json\") {\n    process.stdout.write(`${JSON.stringify(payload)}\\n`);\n    return payload;\n  }\n\n  const prefix = shouldColor() ? pc.red(\"✗\") : \"✗\";\n  process.stderr.write(`${prefix} ${message}\\n`);\n  if (stack && process.env.DEBUG) {\n    process.stderr.write(`${shouldColor() ? pc.dim(stack) : stack}\\n`);\n  }\n  return payload;\n}\n",
      "type": "registry:file",
      "target": "src/cli/agent/json-mode.ts"
    }
  ],
  "docs": "## Next steps\n\nImport and use in your command handlers:\n\n```ts\nimport { emit, note, reportError, detectMode } from './agent/json-mode';\n\nprogram\n  .option('--json', 'emit JSON for agents')\n  .command('list')\n  .action(async (opts) => {\n    note('Fetching items...', opts);\n    const items = await fetchItems();\n    emit(items, opts, (data) => {\n      for (const item of data) console.log(`- ${item.name}`);\n    });\n  });\n```\n\nIn JSON mode (`--json` or piped stdout): NDJSON to stdout, nothing to stderr.\nIn human mode (TTY): formatted output to stdout, notes to stderr.\n\nPairs with `next-steps` block for agent-first CLIs.",
  "type": "registry:file"
}