fix(codex): convert /gsd- workflow commands to $gsd- during installation (#1994)
The convertSlashCommandsToCodexSkillMentions function only converted colon-style skill invocations (/gsd:command) but not hyphen-style command references (/gsd-command) used in workflow output templates (Next Up blocks, phase completion messages, etc.). This caused Codex users to see /gsd- prefixed commands instead of $gsd- in chat output. - Add regex to convert /gsd-command → $gsd-command with negative lookbehind to exclude file paths (e.g. bin/gsd-tools.cjs) - Strip /clear references in Codex output (no Codex equivalent) - Add 5 regression tests covering command conversion, path preservation, and /clear removal Co-authored-by: Lakshman <lakshman@lakshman-GG9LQ90J61.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
f8526b5c01
commit
d0ab1d8aaa
@@ -1545,16 +1545,29 @@ function convertClaudeAgentToCodebuddyAgent(content) {
|
||||
}
|
||||
|
||||
function convertSlashCommandsToCodexSkillMentions(content) {
|
||||
// Convert colon-style skill invocations to Codex $ prefix
|
||||
let converted = content.replace(/\/gsd:([a-z0-9-]+)/gi, (_, commandName) => {
|
||||
return `$gsd-${String(commandName).toLowerCase()}`;
|
||||
});
|
||||
converted = converted.replace(/\/gsd-help\b/g, '$gsd-help');
|
||||
// Convert hyphen-style command references (workflow output) to Codex $ prefix.
|
||||
// Negative lookbehind excludes file paths like bin/gsd-tools.cjs where
|
||||
// the slash is preceded by a word char, dot, or another slash.
|
||||
converted = converted.replace(/(?<![a-zA-Z0-9./])\/gsd-([a-z0-9-]+)/gi, (_, commandName) => {
|
||||
return `$gsd-${String(commandName).toLowerCase()}`;
|
||||
});
|
||||
return converted;
|
||||
}
|
||||
|
||||
function convertClaudeToCodexMarkdown(content) {
|
||||
let converted = convertSlashCommandsToCodexSkillMentions(content);
|
||||
converted = converted.replace(/\$ARGUMENTS\b/g, '{{GSD_ARGS}}');
|
||||
// Remove /clear references — Codex has no equivalent command
|
||||
// Handle backtick-wrapped: `\/clear` then: → (removed)
|
||||
converted = converted.replace(/`\/clear`\s*,?\s*then:?\s*\n?/gi, '');
|
||||
// Handle bare: /clear then: → (removed)
|
||||
converted = converted.replace(/\/clear\s*,?\s*then:?\s*\n?/gi, '');
|
||||
// Handle standalone /clear on its own line
|
||||
converted = converted.replace(/^\s*`?\/clear`?\s*$/gm, '');
|
||||
// Path replacement: .claude → .codex (#1430)
|
||||
converted = converted.replace(/\$HOME\/\.claude\//g, '$HOME/.codex/');
|
||||
converted = converted.replace(/~\/\.claude\//g, '~/.codex/');
|
||||
|
||||
@@ -17,6 +17,7 @@ const os = require('os');
|
||||
const {
|
||||
getCodexSkillAdapterHeader,
|
||||
convertClaudeAgentToCodexAgent,
|
||||
convertClaudeCommandToCodexSkill,
|
||||
generateCodexAgentToml,
|
||||
generateCodexConfigBlock,
|
||||
stripGsdFromCodexConfig,
|
||||
@@ -186,6 +187,86 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: resolve"`;
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Codex command prefix conversion ────────────────────────────────────────────
|
||||
|
||||
describe('Codex hyphen-style command prefix conversion', () => {
|
||||
test('converts /gsd-command in workflow output to $gsd-command', () => {
|
||||
const input = `---
|
||||
name: gsd-test
|
||||
description: Test
|
||||
tools: Read
|
||||
---
|
||||
|
||||
/gsd-discuss-phase 1 — gather context
|
||||
/gsd-plan-phase 2 — create plan
|
||||
/gsd-execute-phase 3 — run it`;
|
||||
|
||||
const result = convertClaudeCommandToCodexSkill(input, 'gsd-test');
|
||||
assert.ok(result.includes('$gsd-discuss-phase'), 'converts /gsd-discuss-phase');
|
||||
assert.ok(result.includes('$gsd-plan-phase'), 'converts /gsd-plan-phase');
|
||||
assert.ok(result.includes('$gsd-execute-phase'), 'converts /gsd-execute-phase');
|
||||
assert.ok(!result.includes('/gsd-discuss-phase'), 'no /gsd-discuss-phase remains');
|
||||
});
|
||||
|
||||
test('converts backtick-wrapped /gsd- commands', () => {
|
||||
const input = `---
|
||||
name: gsd-test
|
||||
description: Test
|
||||
tools: Read
|
||||
---
|
||||
|
||||
Run \`/gsd-plan-phase 1\` to plan.`;
|
||||
|
||||
const result = convertClaudeCommandToCodexSkill(input, 'gsd-test');
|
||||
assert.ok(result.includes('$gsd-plan-phase'), 'converts backtick-wrapped command');
|
||||
});
|
||||
|
||||
test('does not convert /gsd- in file paths', () => {
|
||||
const input = `---
|
||||
name: gsd-test
|
||||
description: Test
|
||||
tools: Read
|
||||
---
|
||||
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init`;
|
||||
|
||||
const result = convertClaudeCommandToCodexSkill(input, 'gsd-test');
|
||||
assert.ok(result.includes('gsd-tools.cjs'), 'gsd-tools.cjs preserved in path');
|
||||
assert.ok(!result.includes('$gsd-tools'), 'no $gsd-tools in file path');
|
||||
});
|
||||
|
||||
test('removes /clear then: for Codex', () => {
|
||||
const input = `---
|
||||
name: gsd-test
|
||||
description: Test
|
||||
tools: Read
|
||||
---
|
||||
|
||||
\`/clear\` then:
|
||||
|
||||
\`$gsd-plan-phase 1\``;
|
||||
|
||||
const result = convertClaudeCommandToCodexSkill(input, 'gsd-test');
|
||||
assert.ok(!result.includes('/clear'), 'no /clear remains');
|
||||
assert.ok(result.includes('$gsd-plan-phase'), 'command preserved after /clear removal');
|
||||
});
|
||||
|
||||
test('removes bare /clear then: for Codex', () => {
|
||||
const input = `---
|
||||
name: gsd-test
|
||||
description: Test
|
||||
tools: Read
|
||||
---
|
||||
|
||||
/clear then:
|
||||
/gsd-execute-phase 2`;
|
||||
|
||||
const result = convertClaudeCommandToCodexSkill(input, 'gsd-test');
|
||||
assert.ok(!result.includes('/clear'), 'no /clear remains');
|
||||
assert.ok(result.includes('$gsd-execute-phase'), 'command converted');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── generateCodexAgentToml ─────────────────────────────────────────────────────
|
||||
|
||||
describe('generateCodexAgentToml', () => {
|
||||
|
||||
Reference in New Issue
Block a user