Files
dotfiles/nix-shared/home-manager/codex-generated-skills.nix

286 lines
8.8 KiB
Nix

{
config,
lib,
pkgs,
...
}: let
cfg = config.myModules.codexGeneratedSkills;
oos = config.lib.file.mkOutOfStoreSymlink;
in {
options.myModules.codexGeneratedSkills = {
enable = lib.mkEnableOption "Codex home setup";
codexHome = lib.mkOption {
type = lib.types.str;
default = "${config.home.homeDirectory}/.codex";
description = "Codex home directory.";
};
worktreeCodexDir = lib.mkOption {
type = lib.types.str;
default = "${config.home.homeDirectory}/dotfiles/dotfiles/codex";
description = "Codex dotfiles directory in the live worktree.";
};
localConfig = lib.mkOption {
type = lib.types.str;
default = "${cfg.codexHome}/config.local.toml";
description = "Host-local Codex config fragment appended after the shared config.";
};
generatedStateConfig = lib.mkOption {
type = lib.types.str;
default = "${cfg.codexHome}/config.local-state.toml";
description = "Codex-owned host-local config sections harvested from config.toml.";
};
skillsDir = lib.mkOption {
type = lib.types.str;
default = "${cfg.codexHome}/skills";
description = "Codex skills directory.";
};
primaryRuntimeDir = lib.mkOption {
type = lib.types.str;
default = "${config.home.homeDirectory}/.cache/codex-runtimes/codex-primary-runtime";
description = "Codex primary runtime cache directory.";
};
};
config = lib.mkIf cfg.enable {
home.file = {
".codex/.gitignore" = {
force = true;
source = oos "${cfg.worktreeCodexDir}/.gitignore";
};
".codex/AGENTS.md" = {
force = true;
source = oos "${cfg.worktreeCodexDir}/AGENTS.md";
};
};
home.activation.prepareCodexDirectory = lib.hm.dag.entryBefore ["checkLinkTargets"] ''
codex_home=${lib.escapeShellArg cfg.codexHome}
skills_dir=${lib.escapeShellArg cfg.skillsDir}
if [ -L "$codex_home" ]; then
rm -f "$codex_home"
mkdir -p "$codex_home"
elif [ ! -e "$codex_home" ]; then
mkdir -p "$codex_home"
elif [ ! -d "$codex_home" ]; then
echo "Skipping Codex setup because $codex_home is not a directory" >&2
exit 1
fi
if [ -L "$skills_dir" ]; then
tmp_skills="$(mktemp -d "''${TMPDIR:-/tmp}/codex-skills.XXXXXX")"
trap 'rm -rf "$tmp_skills"' EXIT
for generated_dir in .system codex-primary-runtime; do
if [ -d "$skills_dir/$generated_dir" ]; then
cp -R "$skills_dir/$generated_dir" "$tmp_skills/$generated_dir"
fi
done
rm -f "$skills_dir"
mkdir -p "$skills_dir"
for generated_dir in "$tmp_skills"/*; do
if [ -e "$generated_dir" ]; then
mv "$generated_dir" "$skills_dir/"
fi
done
elif [ ! -e "$skills_dir" ]; then
mkdir -p "$skills_dir"
elif [ ! -d "$skills_dir" ]; then
echo "Skipping Codex skills setup because $skills_dir is not a directory" >&2
exit 1
fi
'';
home.activation.generateCodexConfig = lib.hm.dag.entryAfter ["writeBoundary"] ''
codex_home=${lib.escapeShellArg cfg.codexHome}
base=${lib.escapeShellArg "${cfg.worktreeCodexDir}/config.toml"}
local_config=${lib.escapeShellArg cfg.localConfig}
local_state_config=${lib.escapeShellArg cfg.generatedStateConfig}
target="$codex_home/config.toml"
begin_marker="# BEGIN AUTO-GENERATED CODEX MACHINE STATE"
end_marker="# END AUTO-GENERATED CODEX MACHINE STATE"
rejected_project_prefixes=${lib.escapeShellArg (
if pkgs.stdenv.isDarwin
then "/home/ /run/"
else "/Users/ /Volumes/"
)}
if [ ! -r "$base" ]; then
echo "Missing shared Codex config at $base" >&2
exit 1
fi
mkdir -p "$codex_home"
if [ -r "$target" ]; then
local_state="$(mktemp "$codex_home/config.local-state.XXXXXX")"
trap 'rm -f "$tmp" "$local_state"' EXIT
${lib.getExe pkgs.gawk} \
-v begin_marker="$begin_marker" \
-v end_marker="$end_marker" \
-v rejected_prefixes="$rejected_project_prefixes" '
FNR == NR {
if ($0 ~ /^\[[^]]+\]$/) {
base_sections[$0] = 1
}
next
}
function rejected_project(section, path, count, parts, i) {
if (section !~ /^\[projects\."/) {
return 0
}
path = section
sub(/^\[projects\."/ , "", path)
sub(/"\]$/, "", path)
count = split(rejected_prefixes, parts, /[[:space:]]+/)
for (i = 1; i <= count; i++) {
if (parts[i] != "" && index(path, parts[i]) == 1) {
return 1
}
}
return 0
}
function flush_block() {
if (section != "" && !(section in base_sections) && !rejected_project(section)) {
if (printed) {
print ""
}
printf "%s", block
printed = 1
}
}
$0 == begin_marker || $0 == end_marker {
next
}
/^\[[^]]+\]$/ {
flush_block()
section = $0
block = $0 "\n"
next
}
section != "" {
block = block $0 "\n"
}
END {
flush_block()
}
' "$base" "$target" \
| ${lib.getExe pkgs.perl} -0pe 's/\n{3,}/\n\n/g' \
> "$local_state"
if [ -s "$local_state" ]; then
chmod 600 "$local_state"
mv -f "$local_state" "$local_state_config"
else
rm -f "$local_state" "$local_state_config"
fi
fi
tmp="$(mktemp "$codex_home/config.toml.XXXXXX")"
trap 'rm -f "$tmp"' EXIT
chmod 600 "$tmp"
cat "$base" > "$tmp"
if [ -r "$local_config" ]; then
printf '\n' >> "$tmp"
cat "$local_config" >> "$tmp"
fi
if [ -r "$local_state_config" ]; then
printf '\n%s\n' "$begin_marker" >> "$tmp"
cat "$local_state_config" >> "$tmp"
printf '%s\n' "$end_marker" >> "$tmp"
fi
if [ -e "$target" ] && cmp -s "$tmp" "$target"; then
rm -f "$tmp"
else
mv -f "$tmp" "$target"
fi
'';
home.activation.linkCodexDotfileSkills = lib.hm.dag.entryAfter ["writeBoundary"] ''
skills_dir=${lib.escapeShellArg cfg.skillsDir}
worktree_skills=${lib.escapeShellArg "${cfg.worktreeCodexDir}/skills"}
if [ ! -d "$worktree_skills" ]; then
echo "Skipping Codex dotfile skills setup because $worktree_skills is not a directory" >&2
exit 1
fi
mkdir -p "$skills_dir"
for skill in "$worktree_skills"/*; do
[ -d "$skill" ] || continue
[ -r "$skill/SKILL.md" ] || continue
name="$(basename "$skill")"
case "$name" in
.system|codex-primary-runtime) continue ;;
esac
target="$skills_dir/$name"
if [ -L "$target" ] || [ ! -e "$target" ]; then
ln -sfn "$skill" "$target"
elif [ ! -d "$target" ]; then
echo "Skipping Codex skill $name because $target exists and is not a directory" >&2
fi
done
'';
home.activation.setupCodexGeneratedSkills = lib.hm.dag.entryAfter ["linkCodexDotfileSkills"] ''
codex_home=${lib.escapeShellArg cfg.codexHome}
skills_dir=${lib.escapeShellArg cfg.skillsDir}
runtime_skills_root=${lib.escapeShellArg "${cfg.primaryRuntimeDir}/skills/skills"}
mkdir -p "$skills_dir"
if [ -x ${lib.escapeShellArg "${pkgs.codex}/bin/codex"} ]; then
if ! CODEX_HOME="$codex_home" ${lib.escapeShellArg "${pkgs.codex}/bin/codex"} debug prompt-input "bootstrap generated Codex skills" >/dev/null; then
echo "setupCodexGeneratedSkills: codex failed to bootstrap .system skills" >&2
fi
else
echo "setupCodexGeneratedSkills: codex binary not found; skipping .system skill bootstrap" >&2
fi
if [ -d "$runtime_skills_root" ]; then
tmp_dir="$(mktemp -d "''${TMPDIR:-/tmp}/codex-primary-runtime.XXXXXX")"
trap 'rm -rf "$tmp_dir"' EXIT
mkdir -p "$tmp_dir/codex-primary-runtime"
for skill_name in slides spreadsheets; do
if [ -d "$runtime_skills_root/$skill_name" ]; then
cp -R "$runtime_skills_root/$skill_name" "$tmp_dir/codex-primary-runtime/$skill_name"
fi
done
if [ "$(find "$tmp_dir/codex-primary-runtime" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" != "0" ]; then
rm -rf "$skills_dir/codex-primary-runtime"
mv "$tmp_dir/codex-primary-runtime" "$skills_dir/codex-primary-runtime"
fi
else
echo "setupCodexGeneratedSkills: runtime skills not found at $runtime_skills_root; skipping codex-primary-runtime" >&2
fi
'';
};
}