Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
b6dddf934This file is pinned to the selected release. Download the original to open it in your design tool.
import { describe, expect, it } from 'vitest'
import { adaptGoalCommand, adaptSlashCommand, engineSupportsGoal, engineSupportsLoop } from './goalCommand.js'
const ALL_ENGINES = ['claude', 'codex', 'cursor', 'opencode', 'pi', 'hermes', 'commandcode', 'devin', 'muse', 'amp', 'kilo', 'grok'] as const
const NON_LOOP = ['codex', 'cursor', 'opencode', 'pi', 'hermes', 'commandcode', 'devin', 'muse', 'amp', 'kilo', 'grok'] as const
describe('adaptGoalCommand', () => {
it('keeps /goal verbatim for engines with a native /goal (claude, codex)', () => {
for (const engine of ['claude', 'codex'] as const) {
expect(engineSupportsGoal(engine)).toBe(true)
expect(adaptGoalCommand('/goal fix the bug', engine)).toBe('/goal fix the bug')
expect(adaptGoalCommand('/goal', engine)).toBe('/goal')
}
})
it('drops the leading slash for engines without a verified native /goal', () => {
for (const engine of ALL_ENGINES.filter((candidate) => candidate !== 'claude' && candidate !== 'codex')) {
expect(engineSupportsGoal(engine)).toBe(false)
expect(adaptGoalCommand('/goal fix the bug', engine)).toBe('goal fix the bug')
expect(adaptGoalCommand('/goal', engine)).toBe('goal')
expect(adaptGoalCommand('/goal\nmulti line', engine)).toBe('goal\nmulti line')
}
})
it('leaves non-/goal content untouched for every engine', () => {
for (const engine of ALL_ENGINES) {
expect(adaptGoalCommand('just a normal message', engine)).toBe('just a normal message')
expect(adaptGoalCommand('/model gpt-5', engine)).toBe('/model gpt-5')
expect(adaptGoalCommand('/goalkeeper stats', engine)).toBe('/goalkeeper stats') // /goal must be a whole token
expect(adaptGoalCommand('tell me the /goal', engine)).toBe('tell me the /goal') // only a LEADING token is adapted
}
})
})
describe('adaptSlashCommand — /loop', () => {
it('keeps /loop verbatim for claude, the only engine that has one', () => {
expect(engineSupportsLoop('claude')).toBe(true)
expect(adaptSlashCommand('/loop keep fixing tests', 'claude')).toBe('/loop keep fixing tests')
expect(adaptSlashCommand('/loop', 'claude')).toBe('/loop')
})
it('drops the WHOLE token elsewhere — "loop" left behind is not an instruction', () => {
for (const engine of NON_LOOP) {
expect(engineSupportsLoop(engine)).toBe(false)
expect(adaptSlashCommand('/loop keep fixing tests', engine)).toBe('keep fixing tests')
expect(adaptSlashCommand('/loop\nmulti line', engine)).toBe('multi line')
}
})
it('never turns a bare /loop into an empty message', () => {
// An empty submission looks like the turn was sent and then does nothing at all, which is worse
// than a stray word.
for (const engine of NON_LOOP) expect(adaptSlashCommand('/loop', engine)).toBe('loop')
})
it('leaves non-/loop content untouched for every engine', () => {
for (const engine of ALL_ENGINES) {
expect(adaptSlashCommand('/loopback through the proxy', engine)).toBe('/loopback through the proxy')
expect(adaptSlashCommand('run it in a /loop', engine)).toBe('run it in a /loop')
expect(adaptSlashCommand('just a normal message', engine)).toBe('just a normal message')
}
})
it('adapts goal and loop independently on the same engine', () => {
// codex has /goal but not /loop — the two rules must not interfere.
expect(adaptSlashCommand('/goal ship it', 'codex')).toBe('/goal ship it')
expect(adaptSlashCommand('/loop ship it', 'codex')).toBe('ship it')
expect(adaptSlashCommand('/goal ship it', 'cursor')).toBe('goal ship it')
expect(adaptSlashCommand('/loop ship it', 'cursor')).toBe('ship it')
})
})