codex: support path-backed hooks
Allow path-backed hook bundles to provide both hooks.json and supporting scripts through programs.codex.hooks.
This commit is contained in:
@@ -42,6 +42,7 @@ in
|
||||
configDir = if useXdgDirectories then "${xdgConfigHome}/codex" else ".codex";
|
||||
configFileName = if isTomlConfig then "config.toml" else "config.yaml";
|
||||
skillsDir = "${configDir}/skills";
|
||||
hookScriptDirs = [ "${configDir}/hooks" ] ++ lib.optional useXdgDirectories ".codex/hooks";
|
||||
pluginsMarketplaceName = "home-manager";
|
||||
pluginsDir = "${configDir}/plugins";
|
||||
pluginsCacheDir = "${pluginsDir}/cache";
|
||||
@@ -126,6 +127,9 @@ in
|
||||
mergedSettings =
|
||||
mergedSettingsWithoutMcp
|
||||
// lib.optionalAttrs (mergedMcpServers != { }) { mcp_servers = mergedMcpServers; };
|
||||
hooksArePath = lib.hm.strings.isPathLike cfg.hooks;
|
||||
hooksAreDir = hooksArePath && lib.pathIsDirectory cfg.hooks;
|
||||
hooksJsonSource = if hooksAreDir then cfg.hooks + "/hooks.json" else cfg.hooks;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
warnings = lib.optional hasLegacyProfileSettings ''
|
||||
@@ -166,6 +170,10 @@ in
|
||||
);
|
||||
message = "`programs.codex.rules` attribute values must be files when set to paths";
|
||||
}
|
||||
{
|
||||
assertion = !(hooksAreDir && !builtins.pathExists hooksJsonSource);
|
||||
message = "`programs.codex.hooks` directory must contain a hooks.json file";
|
||||
}
|
||||
];
|
||||
|
||||
home = {
|
||||
@@ -202,9 +210,19 @@ in
|
||||
};
|
||||
};
|
||||
"${configDir}/hooks.json" = lib.mkIf (cfg.hooks != { }) {
|
||||
source = jsonFormat.generate "codex-hooks" { inherit (cfg) hooks; };
|
||||
source =
|
||||
if hooksArePath then
|
||||
hooksJsonSource
|
||||
else
|
||||
jsonFormat.generate "codex-hooks" { inherit (cfg) hooks; };
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs hooksAreDir (
|
||||
lib.genAttrs hookScriptDirs (_: {
|
||||
source = cfg.hooks;
|
||||
recursive = true;
|
||||
})
|
||||
)
|
||||
// lib.listToAttrs [ (mkTextOrPathEntry "${configDir}/AGENTS.md" cfg.context) ]
|
||||
// lib.optionalAttrs (cfg.contextOverride != null) (
|
||||
lib.listToAttrs [ (mkTextOrPathEntry "${configDir}/AGENTS.override.md" cfg.contextOverride) ]
|
||||
|
||||
@@ -125,15 +125,26 @@ in
|
||||
};
|
||||
|
||||
hooks = lib.mkOption {
|
||||
inherit (jsonFormat) type;
|
||||
type = lib.types.either jsonFormat.type lib.types.path;
|
||||
default = { };
|
||||
description = ''
|
||||
Lifecycle hook events written to {file}`CODEX_HOME/hooks.json`.
|
||||
|
||||
This option uses the same event structure as
|
||||
{option}`programs.codex.settings.hooks` and writes it under the
|
||||
This option can either be a hook event attribute set, a path to a
|
||||
complete hooks JSON file, or a path to a hook bundle directory
|
||||
containing {file}`hooks.json` and supporting hook scripts.
|
||||
|
||||
Attribute set values use the same event structure as
|
||||
{option}`programs.codex.settings.hooks` and are written under the
|
||||
top-level `hooks` key expected by Codex's JSON hooks file.
|
||||
|
||||
Directory values install {file}`hooks.json` to
|
||||
{file}`CODEX_HOME/hooks.json` and install the full directory to
|
||||
{file}`CODEX_HOME/hooks` so commands can reference bundled scripts.
|
||||
When {option}`home.preferXdgDirectories` is enabled, the hook
|
||||
directory is also installed to {file}`~/.codex/hooks` for upstream
|
||||
compatibility.
|
||||
|
||||
Hooks can also be configured inline through
|
||||
{option}`programs.codex.settings.hooks`; prefer using only one hook
|
||||
representation per layer.
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
codex-settings-yaml = ./settings-yaml.nix;
|
||||
codex-empty-settings = ./empty-settings.nix;
|
||||
codex-legacy-custom-instructions = ./legacy-custom-instructions.nix;
|
||||
codex-hooks-dir = ./hooks-dir.nix;
|
||||
codex-hooks-dir-xdg = ./hooks-dir-xdg.nix;
|
||||
codex-hooks-file = ./hooks-file.nix;
|
||||
codex-mcp-integration = ./mcp-integration.nix;
|
||||
codex-mcp-integration-with-override = ./mcp-integration-with-override.nix;
|
||||
codex-plugins = ./plugins.nix;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
home.preferXdgDirectories = true;
|
||||
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
hooks = ./hooks-dir;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/codex/hooks.json
|
||||
assertFileContent home-files/.config/codex/hooks.json \
|
||||
${./hooks-dir/hooks.json}
|
||||
|
||||
assertFileExists home-files/.config/codex/hooks/session-start.sh
|
||||
assertFileContent home-files/.config/codex/hooks/session-start.sh \
|
||||
${./hooks-dir/session-start.sh}
|
||||
|
||||
assertFileExists home-files/.codex/hooks/session-start.sh
|
||||
assertFileContent home-files/.codex/hooks/session-start.sh \
|
||||
${./hooks-dir/session-start.sh}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
hooks = ./hooks-dir;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.codex/hooks.json
|
||||
assertFileContent home-files/.codex/hooks.json \
|
||||
${./hooks-dir/hooks.json}
|
||||
|
||||
assertFileExists home-files/.codex/hooks/session-start.sh
|
||||
assertFileContent home-files/.codex/hooks/session-start.sh \
|
||||
${./hooks-dir/session-start.sh}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$HOME/.codex/hooks/session-start.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
printf '%s\n' '{"hookSpecificOutput":{"additionalContext":"Loaded from Home Manager hook bundle"}}'
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
hooks = ./hooks.json;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.codex/hooks.json
|
||||
assertFileContent home-files/.codex/hooks.json \
|
||||
${./hooks.json}
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user