feat: Add JSONC support for oh-my-opencode config files (#275)

Uses Microsoft's jsonc-parser package for reliable JSONC parsing:
- oh-my-opencode.jsonc (preferred) or oh-my-opencode.json
- Supports line comments (//), block comments (/* */), and trailing commas
- Better error reporting with line/column positions

Core changes:
- Added jsonc-parser dependency (Microsoft's VS Code parser)
- Shared JSONC utilities (parseJsonc, parseJsoncSafe, readJsoncFile, detectConfigFile)
- Main plugin config loader uses detectConfigFile for .jsonc priority
- CLI config manager supports JSONC parsing

Comprehensive test suite with 18 tests for JSONC parsing.

Fixes #265

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
Sisyphus
2025-12-28 00:45:17 +09:00
committed by GitHub
parent c4c0d82f97
commit b2c2c6eab7
8 changed files with 381 additions and 88 deletions

View File

@@ -47,7 +47,7 @@ import { builtinTools, createCallOmoAgent, createBackgroundTools, createLookAt,
import { BackgroundManager } from "./features/background-agent";
import { createBuiltinMcps } from "./mcp";
import { OhMyOpenCodeConfigSchema, type OhMyOpenCodeConfig, type HookName } from "./config";
import { log, deepMerge, getUserConfigDir, addConfigLoadError } from "./shared";
import { log, deepMerge, getUserConfigDir, addConfigLoadError, parseJsonc, detectConfigFile } from "./shared";
import { PLAN_SYSTEM_PROMPT, PLAN_PERMISSION } from "./agents/plan-prompt";
import * as fs from "fs";
import * as path from "path";
@@ -119,7 +119,7 @@ function loadConfigFromPath(configPath: string, ctx: any): OhMyOpenCodeConfig |
try {
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, "utf-8");
const rawConfig = JSON.parse(content);
const rawConfig = parseJsonc<Record<string, unknown>>(content);
migrateConfigFile(configPath, rawConfig);
@@ -201,19 +201,15 @@ function mergeConfigs(
}
function loadPluginConfig(directory: string, ctx: any): OhMyOpenCodeConfig {
// User-level config path (OS-specific)
const userConfigPath = path.join(
getUserConfigDir(),
"opencode",
"oh-my-opencode.json"
);
// User-level config path (OS-specific) - prefer .jsonc over .json
const userBasePath = path.join(getUserConfigDir(), "opencode", "oh-my-opencode");
const userDetected = detectConfigFile(userBasePath);
const userConfigPath = userDetected.format !== "none" ? userDetected.path : userBasePath + ".json";
// Project-level config path
const projectConfigPath = path.join(
directory,
".opencode",
"oh-my-opencode.json"
);
// Project-level config path - prefer .jsonc over .json
const projectBasePath = path.join(directory, ".opencode", "oh-my-opencode");
const projectDetected = detectConfigFile(projectBasePath);
const projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : projectBasePath + ".json";
// Load user config first (base)
let config: OhMyOpenCodeConfig = loadConfigFromPath(userConfigPath, ctx) ?? {};