diff --git a/assets/help/doctor.schema.json b/assets/help/doctor.schema.json new file mode 100644 index 000000000..7e7c5d29a --- /dev/null +++ b/assets/help/doctor.schema.json @@ -0,0 +1,344 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/help/doctor.schema.json", + "title": "Doctor Diagnostic Result", + "description": "JSON schema for oh-my-openagent doctor diagnostic output", + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Check display name" + }, + "status": { + "type": "string", + "enum": [ + "pass", + "fail", + "warn", + "skip" + ], + "description": "Check outcome" + }, + "message": { + "type": "string", + "description": "Result summary message" + }, + "details": { + "description": "Detailed diagnostic lines", + "type": "array", + "items": { + "type": "string" + } + }, + "issues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Short issue title" + }, + "description": { + "type": "string", + "description": "Detailed description of the issue" + }, + "fix": { + "description": "Suggested fix or remediation", + "type": "string" + }, + "affects": { + "description": "Components or areas affected", + "type": "array", + "items": { + "type": "string" + } + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning" + ], + "description": "Severity level of the issue" + } + }, + "required": [ + "title", + "description", + "severity" + ], + "additionalProperties": false, + "ref": "DoctorIssue" + }, + "description": "Issues found by this check" + }, + "duration": { + "description": "Check execution time in milliseconds", + "type": "number" + } + }, + "required": [ + "name", + "status", + "message", + "issues" + ], + "additionalProperties": false, + "ref": "CheckResult" + }, + "description": "All check results" + }, + "systemInfo": { + "type": "object", + "properties": { + "opencodeVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Installed OpenCode version" + }, + "opencodePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Path to OpenCode binary" + }, + "pluginVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "oh-my-openagent plugin version" + }, + "loadedVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Loaded plugin version at runtime" + }, + "bunVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Bun runtime version" + }, + "configPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Path to active config file" + }, + "configValid": { + "type": "boolean", + "description": "Whether the config parses correctly" + }, + "isLocalDev": { + "type": "boolean", + "description": "Whether running in local development mode" + } + }, + "required": [ + "opencodeVersion", + "opencodePath", + "pluginVersion", + "loadedVersion", + "bunVersion", + "configPath", + "configValid", + "isLocalDev" + ], + "additionalProperties": false, + "ref": "SystemInfo", + "description": "System environment information" + }, + "tools": { + "type": "object", + "properties": { + "lspServers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "LSP server identifier" + }, + "extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "File extensions handled" + } + }, + "required": [ + "id", + "extensions" + ], + "additionalProperties": false, + "ref": "LspServerInfo" + }, + "description": "Detected LSP servers" + }, + "astGrepCli": { + "type": "boolean", + "description": "AST-Grep CLI availability" + }, + "astGrepNapi": { + "type": "boolean", + "description": "AST-Grep NAPI availability" + }, + "commentChecker": { + "type": "boolean", + "description": "Comment checker availability" + }, + "ghCli": { + "type": "object", + "properties": { + "installed": { + "type": "boolean", + "description": "Whether GitHub CLI is installed" + }, + "authenticated": { + "type": "boolean", + "description": "Whether GitHub CLI is authenticated" + }, + "username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "GitHub username if authenticated" + } + }, + "required": [ + "installed", + "authenticated", + "username" + ], + "additionalProperties": false, + "ref": "GhCliInfo", + "description": "GitHub CLI status" + }, + "mcpBuiltin": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Built-in MCP server names" + }, + "mcpUser": { + "type": "array", + "items": { + "type": "string" + }, + "description": "User-configured MCP server names" + } + }, + "required": [ + "lspServers", + "astGrepCli", + "astGrepNapi", + "commentChecker", + "ghCli", + "mcpBuiltin", + "mcpUser" + ], + "additionalProperties": false, + "ref": "ToolsSummary", + "description": "Tool and server availability summary" + }, + "summary": { + "type": "object", + "properties": { + "total": { + "type": "number", + "description": "Total number of checks run" + }, + "passed": { + "type": "number", + "description": "Checks that passed" + }, + "failed": { + "type": "number", + "description": "Checks that failed" + }, + "warnings": { + "type": "number", + "description": "Checks with warnings" + }, + "skipped": { + "type": "number", + "description": "Checks that were skipped" + }, + "duration": { + "type": "number", + "description": "Total execution time in milliseconds" + } + }, + "required": [ + "total", + "passed", + "failed", + "warnings", + "skipped", + "duration" + ], + "additionalProperties": false, + "ref": "DoctorSummary", + "description": "Aggregate check statistics" + }, + "exitCode": { + "type": "number", + "description": "Process exit code (0 = success)" + } + }, + "required": [ + "results", + "systemInfo", + "tools", + "summary", + "exitCode" + ], + "additionalProperties": false, + "ref": "DoctorResult" +} \ No newline at end of file diff --git a/script/build-help-schemas.ts b/script/build-help-schemas.ts new file mode 100644 index 000000000..606d75a96 --- /dev/null +++ b/script/build-help-schemas.ts @@ -0,0 +1,52 @@ +#!/usr/bin/env bun +import { z } from "zod" +import { DoctorResultSchema as DoctorSchema } from "../src/help/schema/doctor" + +const SCHEMA_OUTPUT_DIR = "assets/help" + +interface SchemaEntry { + name: string + schema: z.ZodType + title: string + description: string + id: string +} + +async function writeJsonSchema(entry: SchemaEntry): Promise { + const jsonSchema = z.toJSONSchema(entry.schema, { + target: "draft-7", + unrepresentable: "any", + }) as Record + + const output = { + $schema: "http://json-schema.org/draft-07/schema#", + $id: entry.id, + title: entry.title, + description: entry.description, + ...jsonSchema, + } + + const filePath = `${SCHEMA_OUTPUT_DIR}/${entry.name}.schema.json` + await Bun.write(filePath, JSON.stringify(output, null, 2)) + console.log(` ✓ ${entry.name}.schema.json`) +} + +const SCHEMAS: SchemaEntry[] = [ + { + name: "doctor", + schema: DoctorSchema, + title: "Doctor Diagnostic Result", + description: "JSON schema for oh-my-openagent doctor diagnostic output", + id: "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/help/doctor.schema.json", + }, +] + +async function main() { + console.log("Generating Help JSON Schemas...\n") + for (const entry of SCHEMAS) { + await writeJsonSchema(entry) + } + console.log(`\nDone — ${SCHEMAS.length} schema(s) generated in ${SCHEMA_OUTPUT_DIR}/`) +} + +main() diff --git a/src/help/schema/doctor.ts b/src/help/schema/doctor.ts new file mode 100644 index 000000000..9a6dd5799 --- /dev/null +++ b/src/help/schema/doctor.ts @@ -0,0 +1,96 @@ +import { z } from "zod" + +/** + * Help JSON schema for the `doctor` surface. + * Defines the structure of doctor diagnostic output. + */ +export const DoctorIssueSchema = z + .object({ + title: z.string().describe("Short issue title"), + description: z.string().describe("Detailed description of the issue"), + fix: z.string().optional().describe("Suggested fix or remediation"), + affects: z.array(z.string()).optional().describe("Components or areas affected"), + severity: z.enum(["error", "warning"]).describe("Severity level of the issue"), + }) + .meta({ ref: "DoctorIssue" }) + +export const CheckResultSchema = z + .object({ + name: z.string().describe("Check display name"), + status: z.enum(["pass", "fail", "warn", "skip"]).describe("Check outcome"), + message: z.string().describe("Result summary message"), + details: z.array(z.string()).optional().describe("Detailed diagnostic lines"), + issues: z.array(DoctorIssueSchema).describe("Issues found by this check"), + duration: z.number().optional().describe("Check execution time in milliseconds"), + }) + .meta({ ref: "CheckResult" }) + +export const SystemInfoSchema = z + .object({ + opencodeVersion: z.string().nullable().describe("Installed OpenCode version"), + opencodePath: z.string().nullable().describe("Path to OpenCode binary"), + pluginVersion: z.string().nullable().describe("oh-my-openagent plugin version"), + loadedVersion: z.string().nullable().describe("Loaded plugin version at runtime"), + bunVersion: z.string().nullable().describe("Bun runtime version"), + configPath: z.string().nullable().describe("Path to active config file"), + configValid: z.boolean().describe("Whether the config parses correctly"), + isLocalDev: z.boolean().describe("Whether running in local development mode"), + }) + .meta({ ref: "SystemInfo" }) + +export const LspServerInfoSchema = z + .object({ + id: z.string().describe("LSP server identifier"), + extensions: z.array(z.string()).describe("File extensions handled"), + }) + .meta({ ref: "LspServerInfo" }) + +export const GhCliInfoSchema = z + .object({ + installed: z.boolean().describe("Whether GitHub CLI is installed"), + authenticated: z.boolean().describe("Whether GitHub CLI is authenticated"), + username: z.string().nullable().describe("GitHub username if authenticated"), + }) + .meta({ ref: "GhCliInfo" }) + +export const ToolsSummarySchema = z + .object({ + lspServers: z.array(LspServerInfoSchema).describe("Detected LSP servers"), + astGrepCli: z.boolean().describe("AST-Grep CLI availability"), + astGrepNapi: z.boolean().describe("AST-Grep NAPI availability"), + commentChecker: z.boolean().describe("Comment checker availability"), + ghCli: GhCliInfoSchema.describe("GitHub CLI status"), + mcpBuiltin: z.array(z.string()).describe("Built-in MCP server names"), + mcpUser: z.array(z.string()).describe("User-configured MCP server names"), + }) + .meta({ ref: "ToolsSummary" }) + +export const DoctorSummarySchema = z + .object({ + total: z.number().describe("Total number of checks run"), + passed: z.number().describe("Checks that passed"), + failed: z.number().describe("Checks that failed"), + warnings: z.number().describe("Checks with warnings"), + skipped: z.number().describe("Checks that were skipped"), + duration: z.number().describe("Total execution time in milliseconds"), + }) + .meta({ ref: "DoctorSummary" }) + +export const DoctorResultSchema = z + .object({ + results: z.array(CheckResultSchema).describe("All check results"), + systemInfo: SystemInfoSchema.describe("System environment information"), + tools: ToolsSummarySchema.describe("Tool and server availability summary"), + summary: DoctorSummarySchema.describe("Aggregate check statistics"), + exitCode: z.number().describe("Process exit code (0 = success)"), + }) + .meta({ ref: "DoctorResult" }) + +export type DoctorIssue = z.infer +export type CheckResult = z.infer +export type SystemInfo = z.infer +export type LspServerInfo = z.infer +export type GhCliInfo = z.infer +export type ToolsSummary = z.infer +export type DoctorSummary = z.infer +export type DoctorResult = z.infer