merge: resolve conflicts with main for Codex skills-first support

- bin/install.js: integrate isCommand parameter (Gemini TOML fix) with Codex branch
- package.json: keep v1.20.6 with Codex description
- CHANGELOG.md: merge Codex entries into Unreleased above existing releases
This commit is contained in:
Lex Christopherson
2026-02-23 09:46:48 -06:00
76 changed files with 9358 additions and 7557 deletions

View File

@@ -836,7 +836,7 @@ function copyCommandsAsCodexSkills(srcDir, skillsDir, prefix, pathPrefix, runtim
* @param {string} pathPrefix - Path prefix for file references
* @param {string} runtime - Target runtime ('claude', 'opencode', 'gemini', 'codex')
*/
function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand = false) {
const isOpencode = runtime === 'opencode';
const isCodex = runtime === 'codex';
const dirName = getDirName(runtime);
@@ -854,7 +854,7 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
const destPath = path.join(destDir, entry.name);
if (entry.isDirectory()) {
copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime);
copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime, isCommand);
} else if (entry.name.endsWith('.md')) {
// Replace ~/.claude/ and ./.claude/ with runtime-appropriate paths
let content = fs.readFileSync(srcPath, 'utf8');
@@ -869,12 +869,16 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
content = convertClaudeToOpencodeFrontmatter(content);
fs.writeFileSync(destPath, content);
} else if (runtime === 'gemini') {
// Convert to TOML for Gemini (strip <sub> tags — terminals can't render subscript)
content = stripSubTags(content);
const tomlContent = convertClaudeToGeminiToml(content);
// Replace extension with .toml
const tomlPath = destPath.replace(/\.md$/, '.toml');
fs.writeFileSync(tomlPath, tomlContent);
if (isCommand) {
// Convert to TOML for Gemini (strip <sub> tags — terminals can't render subscript)
content = stripSubTags(content);
const tomlContent = convertClaudeToGeminiToml(content);
// Replace extension with .toml
const tomlPath = destPath.replace(/\.md$/, '.toml');
fs.writeFileSync(tomlPath, tomlContent);
} else {
fs.writeFileSync(destPath, content);
}
} else if (isCodex) {
content = convertClaudeToCodexMarkdown(content);
fs.writeFileSync(destPath, content);
@@ -1067,7 +1071,7 @@ function uninstall(isGlobal, runtime = 'claude') {
// 4. Remove GSD hooks
const hooksDir = path.join(targetDir, 'hooks');
if (fs.existsSync(hooksDir)) {
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh'];
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh', 'gsd-context-monitor.js'];
let hookCount = 0;
for (const hook of gsdHooks) {
const hookPath = path.join(hooksDir, hook);
@@ -1133,10 +1137,32 @@ function uninstall(isGlobal, runtime = 'claude') {
if (settings.hooks.SessionStart.length === 0) {
delete settings.hooks.SessionStart;
}
// Clean up empty hooks object
if (Object.keys(settings.hooks).length === 0) {
delete settings.hooks;
}
// Remove GSD hooks from PostToolUse
if (settings.hooks && settings.hooks.PostToolUse) {
const before = settings.hooks.PostToolUse.length;
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(entry => {
if (entry.hooks && Array.isArray(entry.hooks)) {
const hasGsdHook = entry.hooks.some(h =>
h.command && h.command.includes('gsd-context-monitor')
);
return !hasGsdHook;
}
return true;
});
if (settings.hooks.PostToolUse.length < before) {
settingsModified = true;
console.log(` ${green}✓${reset} Removed context monitor hook from settings`);
}
if (settings.hooks.PostToolUse.length === 0) {
delete settings.hooks.PostToolUse;
}
}
// Clean up empty hooks object
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
delete settings.hooks;
}
if (settingsModified) {
@@ -1601,7 +1627,7 @@ function install(isGlobal, runtime = 'claude') {
const gsdSrc = path.join(src, 'commands', 'gsd');
const gsdDest = path.join(commandsDir, 'gsd');
copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime);
copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime, true);
if (verifyInstalled(gsdDest, 'commands/gsd')) {
console.log(` ${green}✓${reset} Installed commands/gsd`);
} else {
@@ -1746,6 +1772,9 @@ function install(isGlobal, runtime = 'claude') {
const updateCheckCommand = isGlobal
? buildHookCommand(targetDir, 'gsd-check-update.js')
: 'node ' + dirName + '/hooks/gsd-check-update.js';
const contextMonitorCommand = isGlobal
? buildHookCommand(targetDir, 'gsd-context-monitor.js')
: 'node ' + dirName + '/hooks/gsd-context-monitor.js';
// Enable experimental agents for Gemini CLI (required for custom sub-agents)
if (isGemini) {
@@ -1782,6 +1811,27 @@ function install(isGlobal, runtime = 'claude') {
});
console.log(` ${green}✓${reset} Configured update check hook`);
}
// Configure PostToolUse hook for context window monitoring
if (!settings.hooks.PostToolUse) {
settings.hooks.PostToolUse = [];
}
const hasContextMonitorHook = settings.hooks.PostToolUse.some(entry =>
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-context-monitor'))
);
if (!hasContextMonitorHook) {
settings.hooks.PostToolUse.push({
hooks: [
{
type: 'command',
command: contextMonitorCommand
}
]
});
console.log(` ${green}✓${reset} Configured context window monitor hook`);
}
}
return { settingsPath, settings, statuslineCommand, runtime };