Nano Banana Pro
Agent skill for nano-banana-pro
**Shared utilities library for SideQuest marketplace plugins** - Type-safe, well-tested foundation modules for plugin development.
Sign in to like and favorite skills
Shared utilities library for SideQuest marketplace plugins - Type-safe, well-tested foundation modules for plugin development.
Testing:
bun test before committing changes*.test.ts alongside source filesCode Quality:
Module Independence:
src/ should be self-containedType: Internal workspace package | Package Name:
@sidequest/core
Language: TypeScript (strict mode) | Runtime: Bun | Test Framework: Bun test
core/ ├── src/ │ ├── validate/ # Plugin validation engine (12 validators) │ │ ├── validators/ # Individual validator modules │ │ │ ├── agents-md.ts # Validates AGENTS.md syntax │ │ │ ├── commands-md.ts # Validates slash command docs │ │ │ ├── hooks-json.ts # Validates hooks configuration │ │ │ ├── marketplace-json.ts # Validates marketplace metadata │ │ │ ├── mcp-json.ts # Validates MCP server config │ │ │ ├── mcp-tool-naming.ts # Validates tool naming conventions │ │ │ ├── plugin-json.ts # Validates plugin metadata │ │ │ ├── plugin-structure.ts # Validates directory structure │ │ │ ├── skill-md.ts # Validates skill documentation │ │ │ └── README.md # Validator documentation │ │ ├── cli.ts # Validation CLI entry point │ │ ├── reporter.ts # Formats validation results │ │ ├── runner.ts # Orchestrates validation runs │ │ └── types.ts # Validation type definitions │ ├── cli/ # CLI argument parsing utilities │ ├── compression/ # Gzip compression helpers │ ├── formatters/ # Terminal output formatters │ ├── fs/ # File system utilities │ ├── git/ # Git operations │ ├── glob/ # File pattern matching │ ├── hash/ # File hashing utilities │ ├── hooks/ # Plugin hook utilities │ ├── html/ # HTML generation utilities │ ├── llm/ # LLM integration (Claude, Ollama) │ ├── logging/ # Structured logging with correlation IDs │ ├── mcp/ # Simplified MCP server API (mcpez fork) │ ├── password/ # Password validation │ ├── slo/ # SLO tracking with burn rate analysis │ ├── spawn/ # Process spawning utilities │ ├── streams/ # Stream processing utilities │ ├── terminal/ # Terminal utilities (colors, formatting) │ ├── utils/ # General utilities │ └── validation/ # Input validation (identifiers, numbers, names) ├── package.json # Package metadata with subpath exports └── tsconfig.json # TypeScript configuration (extends root)
bun typecheck # TypeScript type checking (tsc --noEmit) bun test # Run all tests (Bun test native) bun test <path> # Run specific test file or directory
src/validate/)The validation engine ensures plugin quality across the marketplace.
Core Files:
types.ts - ValidationIssue, ValidationResult, ValidationSeverityrunner.ts - Runs validators against plugin directoriesreporter.ts - Formats validation results for CLI outputcli.ts - CLI entry point for validation commands12 Validators: (see
src/validate/validators/README.md)
| Validator | Purpose | Key Rules |
|---|---|---|
| Validates AGENTS.md structure | Proper headings, content format |
| Validates slash command docs | Required sections, examples |
| Validates hooks configuration | Valid events, command structure |
| Validates marketplace metadata | Schema compliance, plugin refs |
| Validates MCP server config | Server definitions, paths |
| Validates tool naming | |
| Validates plugin metadata | Schema, required fields |
| Validates directory layout | Required files, structure |
| Validates skill documentation | Proper sections, examples |
Usage Pattern:
import { validateHooksJson } from "@sidequest/core/validate/validators"; const issues = await validateHooksJson("/path/to/plugin/hooks/hooks.json"); if (issues.length > 0) { // Handle validation failures }
src/mcp/)Simplified MCP server API forked from mcpez by John Lindquist.
Why This Exists: The official
@modelcontextprotocol/sdk is powerful but verbose. This module provides a declarative, function-based API with 90% less boilerplate.
Key Features:
Before (official SDK - ~40 lines):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "my-server", version: "1.0.0" }); server.registerTool(/* ... */); const transport = new StdioServerTransport(); await server.connect(transport);
After (this module - ~5 lines):
import { tool, z } from "@sidequest/core/mcp"; tool("greet", { description: "Greet someone", inputSchema: { name: z.string() }, }, async ({ name }) => ({ content: [{ type: "text", text: `Hello ${name}!` }] }));
src/mcp-response/)High-level wrapper for MCP tool handlers that reduces boilerplate from ~25 lines to ~5 lines per tool.
Why This Exists: Even with the simplified
@sidequest/core/mcp API, tool handlers still need ~25 lines of repetitive code for correlation IDs, logging, error handling, and response formatting. The wrapToolHandler function automates all of this.
Key Features:
Before (low-level API - ~25 lines per tool):
import { tool, z } from "@sidequest/core/mcp"; import { parseResponseFormat, respondText, respondError, ResponseFormat } from "@sidequest/core/mcp-response"; tool("para_config", { inputSchema: { response_format: z.enum(["markdown", "json"]).optional() } }, async (args) => { const cid = createCorrelationId(); const startTime = Date.now(); log({ cid, tool: "para_config", event: "request" }); try { const config = loadConfig(); const format = parseResponseFormat(args.response_format); log({ cid, tool: "para_config", event: "response", success: true, durationMs: Date.now() - startTime }); return respondText(format, JSON.stringify(config)); } catch (error) { log({ cid, tool: "para_config", durationMs: Date.now() - startTime, success: false, error }); return respondError(format, error); } });
After (wrapToolHandler - ~5 lines per tool):
import { tool, z } from "@sidequest/core/mcp"; import { wrapToolHandler } from "@sidequest/core/mcp-response"; tool("para_config", { inputSchema: { response_format: z.enum(["markdown", "json"]).optional() } }, wrapToolHandler( async (args, format) => { const config = loadConfig(); return config; // Wrapper handles formatting }, { toolName: "para_config", logger: myLogger, createCid: () => randomUUID() } ));
Error Categorization: The wrapper automatically categorizes errors for better observability:
src/llm/)Utilities for calling LLMs (Claude headless CLI, Ollama API) with structured extraction.
Key Capabilities:
Modules:
model-router.ts - Route calls to Claude/Ollamaprompt-builder.ts - Build structured promptsconstraints.ts - Field constraints for extractionresponse-parser.ts - Parse LLM responsestypes.ts - Shared typesUsage:
import { callModel, buildStructuredPrompt } from "@sidequest/core/llm"; const prompt = buildStructuredPrompt({ objective: "Extract metadata", constraints: { /* ... */ }, examples: [/* ... */] }); const result = await callModel(prompt, { model: "claude-sonnet-4" });
src/logging/)Structured logging with correlation IDs for request tracing.
Features:
Files:
factory.ts - Logger creationcorrelation.ts - Correlation ID managementmetrics.ts - Performance metricsconfig.ts - Logger configurationsrc/validation/)Input validation for identifiers, numbers, and names.
Why This Exists: Common validation patterns (kebab-case IDs, priority ranges, area names) were duplicated across plugins. This module consolidates them with comprehensive tests and security hardening.
Modules:
identifiers.ts - Kebab-case and camelCase validation (validateClassifierId, validateFieldName, validateTemplateName)numbers.ts - Bounded numeric ranges (validatePriority 0-100, validateWeight 0.0-1.0)names.ts - Human-readable names (validateAreaName, validateDisplayName)Usage:
import { validateClassifierId, validatePriority } from "@sidequest/core/validation"; const id = validateClassifierId('medical-bill'); // ✅ OK validateClassifierId('Medical-Bill'); // ❌ Error: must be kebab-case const priority = validatePriority(75); // ✅ OK validatePriority(150); // ❌ Error: must be 0-100
src/terminal/)Terminal output formatting with color support.
Capabilities:
src/fs/)Enhanced file system operations with safety checks.
Features:
src/glob/)src/slo/)Service Level Objective tracking with error budgets and burn rate analysis.
Features:
Files:
types.ts - SLO types and definitionstracker.ts - Main SLO tracking logicpersistence.ts - JSONL-based event storageUsage:
import { createSLOTracker } from "@sidequest/core/slo"; const tracker = createSLOTracker({ definitions: { api_latency: { name: "API Latency", target: 0.95, threshold: 1000, unit: "ms", window: "24h", errorBudget: 0.05 } } }); tracker.recordEvent("api_latency", false, 850); const result = await tracker.checkBreach("api_latency", 1100);
| Module | Purpose |
|---|---|
| CLI argument parsing and validation |
| Gzip compression/decompression |
| Output formatting utilities |
| Git operations (status, commit, etc.) |
| File hashing (MD5, SHA) |
| Plugin hook utilities |
| HTML generation and templating |
| MCP tool handler wrapper (reduces boilerplate by ~20 lines per tool) |
| Password validation rules |
| Process spawning with stdio capture |
| Stream processing utilities |
| General-purpose utilities |
| Input validation (identifiers, numbers, names) |
The package.json uses subpath exports for explicit module boundaries:
{ "exports": { "./cli": "./src/cli/index.ts", "./compression": "./src/compression/index.ts", "./formatters": "./src/formatters/index.ts", "./fs": "./src/fs/index.ts", "./git": "./src/git/index.ts", "./glob": "./src/glob/index.ts", "./hash": "./src/hash/index.ts", "./hooks": "./src/hooks/index.ts", "./html": "./src/html/index.ts", "./llm": "./src/llm/index.ts", "./logging": "./src/logging/index.ts", "./mcp": "./src/mcp/index.ts", "./password": "./src/password/index.ts", "./slo": "./src/slo/index.ts", "./spawn": "./src/spawn/index.ts", "./streams": "./src/streams/index.ts", "./terminal": "./src/terminal/index.ts", "./utils": "./src/utils/index.ts", "./validation": "./src/validation/index.ts", "./validate/types": "./src/validate/types.ts", "./validate/runner": "./src/validate/runner.ts", "./validate/reporter": "./src/validate/reporter.ts", "./validate/validators": "./src/validate/validators/index.ts" } }
Usage in plugins:
import { validateHooksJson } from "@sidequest/core/validate/validators"; import { tool, z } from "@sidequest/core/mcp"; import { callModel } from "@sidequest/core/llm"; import { logger } from "@sidequest/core/logging"; import { createSLOTracker } from "@sidequest/core/slo"; import { validateClassifierId, validatePriority } from "@sidequest/core/validation";
TypeScript:
Testing:
*.test.ts alongside source filesimport { describe, expect, test } from "bun:test"File Naming:
index.ts for module entry points*.test.ts for testsDocumentation:
src/my-module/src/my-module/index.tssrc/my-module/index.test.ts"./my-module": "./src/my-module/index.ts"
bun test src/my-module/bun typechecksrc/validate/validators/my-validator.tsexport async function validateMyThing(options: ValidatorOptions): Promise<ValidationIssue[]>src/validate/validators/my-validator.test.tssrc/validate/validators/index.tssrc/validate/validators/README.mdbun test src/validate/validators/my-validator.test.tsValidator Template:
import type { ValidationIssue, ValidatorOptions } from "../types.ts"; export async function validateMyThing( options: ValidatorOptions ): Promise<ValidationIssue[]> { const issues: ValidationIssue[] = []; // Validation logic here if (somethingWrong) { issues.push({ ruleId: "my-thing/rule-id", message: "Description of the problem", severity: "error", file: options.pluginRoot, suggestion: "How to fix it" }); } return issues; }
Production:
@logtape/logtape - Structured logging framework@logtape/file - File logger for LogTape@modelcontextprotocol/sdk - Official MCP SDK (used by mcp module)zod - Schema validation (used by mcp module)Development:
bun-types - Bun runtime typestypescript - TypeScript compilerRun all tests:
bun test
Run specific module tests:
bun test src/validate/ bun test src/mcp/ bun test src/llm/
Run single test file:
bun test src/validate/validators/hooks-json.test.ts
Test coverage areas:
// Validation errors issues.push({ ruleId: "module/specific-error", message: "Clear description", severity: "error", file: filePath, line: lineNumber, // optional suggestion: "How to fix" // optional }); // Async operations try { const result = await riskyOperation(); return result; } catch (error) { logger.error("Operation failed", { error }); throw new Error(`Failed to do thing: ${error.message}`); }
import { logger, withCorrelationId } from "@sidequest/core/logging"; await withCorrelationId(async () => { logger.info("Starting operation"); // ... work ... logger.info("Operation complete"); });
import { tool, z } from "@sidequest/core/mcp"; tool("my_tool", { description: "What this tool does", inputSchema: { query: z.string(), response_format: z.enum(["markdown", "json"]).default("markdown") } }, async ({ query, response_format }) => { try { const result = await doWork(query); const text = response_format === "json" ? JSON.stringify(result) : formatMarkdown(result); return { content: [{ type: "text", text }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: error.message, isError: true }) }], isError: true }; } });
TypeScript errors:
bun typecheck
Test failures:
bun test --watch # Watch mode for debugging
Module not found: Check package.json exports match your import path
Circular dependencies: Use Kit tools to trace:
kit_deps or kit_blast
From PROJECT_INDEX.json:
src/validate/validators/| Resource | Location |
|---|---|
| Validator Documentation | |
| MCP Documentation | (inline docs) |
| Parent Project CLAUDE.md | |
| Root TypeScript Config | |
Using core utilities in a plugin:
"@sidequest/core": "workspace:*" in plugin package.jsonimport { tool } from "@sidequest/core/mcp"bun installContributing to core:
cd coresrc/*.test.ts filesbun testbun typecheckFor detailed plugin development guide, see parent @../PLUGIN_DEV_GUIDE.md