fix: prevent installer from deleting opencode.json on parse errors (#475)

The installer was using JSON.parse() which fails on JSONC (JSON with
Comments) files. OpenCode supports JSONC via jsonc-parser, so users
may have comments or trailing commas in their config.

Previously, parse failures would reset config to {} and overwrite the
user's file, causing data loss.

Changes:
- Add parseJsonc() to handle comments, trailing commas, and BOM
- On parse failure, skip permission config entirely instead of
  overwriting user's file
- Show helpful error message with reason

Fixes #474

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
TÂCHES
2026-02-08 01:14:50 -06:00
committed by GitHub
parent 6c537373c6
commit 6cf4a4e3f6

View File

@@ -965,6 +965,67 @@ function uninstall(isGlobal, runtime = 'claude') {
`);
}
/**
* Parse JSONC (JSON with Comments) by stripping comments and trailing commas.
* OpenCode supports JSONC format via jsonc-parser, so users may have comments.
* This is a lightweight inline parser to avoid adding dependencies.
*/
function parseJsonc(content) {
// Strip BOM if present
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
// Remove single-line and block comments while preserving strings
let result = '';
let inString = false;
let i = 0;
while (i < content.length) {
const char = content[i];
const next = content[i + 1];
if (inString) {
result += char;
// Handle escape sequences
if (char === '\\' && i + 1 < content.length) {
result += next;
i += 2;
continue;
}
if (char === '"') {
inString = false;
}
i++;
} else {
if (char === '"') {
inString = true;
result += char;
i++;
} else if (char === '/' && next === '/') {
// Skip single-line comment until end of line
while (i < content.length && content[i] !== '\n') {
i++;
}
} else if (char === '/' && next === '*') {
// Skip block comment
i += 2;
while (i < content.length - 1 && !(content[i] === '*' && content[i + 1] === '/')) {
i++;
}
i += 2; // Skip closing */
} else {
result += char;
i++;
}
}
}
// Remove trailing commas before } or ]
result = result.replace(/,(\s*[}\]])/g, '$1');
return JSON.parse(result);
}
/**
* Configure OpenCode permissions to allow reading GSD reference docs
* This prevents permission prompts when GSD accesses the get-shit-done directory
@@ -981,10 +1042,14 @@ function configureOpencodePermissions() {
let config = {};
if (fs.existsSync(configPath)) {
try {
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const content = fs.readFileSync(configPath, 'utf8');
config = parseJsonc(content);
} catch (e) {
// Invalid JSON - start fresh but warn user
console.log(` ${yellow}⚠${reset} opencode.json had invalid JSON, recreating`);
// Cannot parse - DO NOT overwrite user's config
console.log(` ${yellow}⚠${reset} Could not parse opencode.json - skipping permission config`);
console.log(` ${dim}Reason: ${e.message}${reset}`);
console.log(` ${dim}Your config was NOT modified. Fix the syntax manually if needed.${reset}`);
return;
}
}