Skip to content

Commit 11adca6

Browse files
manuvarghese456-collabclaudeDietrich Gebert
authored
fix(opencode): never save review as a level, support /ponytail default (#1103)
`/ponytail review` was saved to the OpenCode flag, so every later chat got a one-line pointer to the review skill instead of the ruleset (the latch fixed for Claude Code in #736). The plugin now accepts only off/lite/full/ultra, and an old saved `review` falls back to the default. `/ponytail default <level>` was read as one unknown level and silently did nothing. It now saves the default via writeDefaultMode, as the Claude hook and pi do. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Dietrich Gebert <dietrichgebert@gmail.com>
1 parent 70d0065 commit 11adca6

2 files changed

Lines changed: 30 additions & 3 deletions

File tree

‎.opencode/plugins/ponytail.mjs‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2323
// The shared instruction builder is CommonJS; bridge to it from this ES module.
2424
const require = createRequire(import.meta.url);
2525
const { getPonytailInstructions } = require('../../hooks/ponytail-instructions');
26-
const { getDefaultMode, normalizePersistedMode } = require('../../hooks/ponytail-config');
26+
const { getDefaultMode, normalizeMode, writeDefaultMode } = require('../../hooks/ponytail-config');
2727
const { parseCommandFile, parseSkillFile } = require('./ponytail-frontmatter.cjs');
2828

2929
// OpenCode has no flag-file convention of its own; keep mode beside its config.
@@ -36,7 +36,8 @@ const statePath = path.join(
3636

3737
function readMode() {
3838
try {
39-
return normalizePersistedMode(fs.readFileSync(statePath, 'utf8').trim()) || getDefaultMode();
39+
// review is a one-shot command, never a level (#736); an old saved review falls back.
40+
return normalizeMode(fs.readFileSync(statePath, 'utf8')) || getDefaultMode();
4041
} catch (e) {
4142
return getDefaultMode();
4243
}
@@ -53,9 +54,16 @@ function writeMode(mode) {
5354
// synchronous store if same-turn switching ever matters.
5455
function persistMode(args) {
5556
const wanted = String(args == null ? '' : args).trim();
57+
// `/ponytail default <level>` saves the default for new sessions, like the Claude hook.
58+
const [first, second] = wanted.toLowerCase().split(/\s+/);
59+
if (first === 'default') {
60+
const saved = writeDefaultMode(second);
61+
if (saved) console.log('ponytail default ' + saved);
62+
return;
63+
}
5664
// Bare /ponytail switches ponytail on, or keeps the level when it already is (#639).
5765
if (!wanted && readMode() !== 'off') return;
58-
const mode = wanted ? normalizePersistedMode(wanted) : (getDefaultMode() === 'off' ? 'full' : getDefaultMode());
66+
const mode = wanted ? normalizeMode(wanted) : (getDefaultMode() === 'off' ? 'full' : getDefaultMode());
5967
if (!mode) return;
6068
writeMode(mode);
6169
console.log('ponytail ' + mode);

‎tests/opencode-plugin.test.js‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,25 @@ test('unsupported /ponytail arguments do not reset the current mode', async () =
265265
assert.equal(fs.readFileSync(statePath, 'utf8'), 'ultra');
266266
});
267267

268+
test('/ponytail review is not a level: the flag keeps the current mode (#736)', async () => {
269+
const hooks = await loadPlugin.server({});
270+
fs.writeFileSync(statePath, 'ultra');
271+
await hooks['command.execute.before']({ command: 'ponytail', arguments: 'review', sessionID: 's' });
272+
assert.equal(fs.readFileSync(statePath, 'utf8'), 'ultra');
273+
// A review saved by an older version falls back to the default instead of latching.
274+
fs.writeFileSync(statePath, 'review');
275+
assert.match((await transform(hooks))[0], /PONYTAIL MODE ACTIVE — level: full/);
276+
});
277+
278+
test('/ponytail default lite saves the default and leaves the session level alone', async () => {
279+
const hooks = await loadPlugin.server({});
280+
fs.writeFileSync(statePath, 'ultra');
281+
await hooks['command.execute.before']({ command: 'ponytail', arguments: 'default lite', sessionID: 's' });
282+
assert.equal(JSON.parse(fs.readFileSync(path.join(tmp, 'ponytail', 'config.json'), 'utf8')).defaultMode, 'lite');
283+
assert.equal(fs.readFileSync(statePath, 'utf8'), 'ultra');
284+
fs.rmSync(path.join(tmp, 'ponytail'), { recursive: true, force: true });
285+
});
286+
268287
test('unrelated commands do not touch the flag', async () => {
269288
try { fs.unlinkSync(statePath); } catch (e) {}
270289
const hooks = await loadPlugin.server({});

0 commit comments

Comments
 (0)