mirror of
https://github.com/nix-community/home-manager.git
synced 2026-09-15 03:09:21 +00:00
223 lines
8.4 KiB
Nix
223 lines
8.4 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.programs.worktrunk;
|
|
tomlFormat = pkgs.formats.toml { };
|
|
wt = lib.getExe cfg.package;
|
|
jq = lib.getExe pkgs.jq;
|
|
|
|
mkClaudeCodeIntegrationEnableOption =
|
|
msg:
|
|
lib.mkEnableOption msg
|
|
// {
|
|
default = cfg.claudeCodeIntegration.enable;
|
|
defaultText = lib.literalExpression "config.programs.worktrunk.claudeCodeIntegration.enable";
|
|
};
|
|
|
|
# Best-effort `wt config state marker` hook entry. `|| true` ensures a failure
|
|
# (e.g. running outside a git worktree) never blocks Claude Code.
|
|
mkMarkerHook = op: {
|
|
type = "command";
|
|
command = "${wt} config state marker ${op} || true";
|
|
};
|
|
in
|
|
{
|
|
meta.maintainers = with lib.maintainers; [
|
|
eveeifyeve
|
|
yzx9
|
|
];
|
|
|
|
options.programs.worktrunk = {
|
|
enable = lib.mkEnableOption "worktrunk";
|
|
|
|
package = lib.mkPackageOption pkgs "worktrunk" { nullable = true; };
|
|
|
|
settings = lib.mkOption {
|
|
inherit (tomlFormat) type;
|
|
default = { };
|
|
example = {
|
|
skip-shell-integration-prompt = true;
|
|
post-start = {
|
|
copy = "wt step copy-ignored";
|
|
};
|
|
};
|
|
description = ''
|
|
Configuration written to `$XDG_CONFIG_HOME/worktrunk/config.toml`.
|
|
'';
|
|
};
|
|
|
|
enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; };
|
|
enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; };
|
|
enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; };
|
|
enableNushellIntegration = lib.hm.shell.mkNushellIntegrationOption { inherit config; };
|
|
|
|
# Claude Code integration.
|
|
claudeCodeIntegration = {
|
|
enable = lib.mkEnableOption "Integrate worktrunk with Claude Code's statusLine, worktree isolation, and skills";
|
|
|
|
# Route Claude Code worktree isolation (`isolation: "worktree"`) through `wt`
|
|
# instead of `git worktree add`, so agent-created worktrees get worktrunk's
|
|
# naming, hooks, and lifecycle. https://worktrunk.dev/claude-code/#worktree-isolation
|
|
worktreeHooks = mkClaudeCodeIntegrationEnableOption "Claude Code WorktreeCreate/WorktreeRemove hooks";
|
|
|
|
# Activity tracking: mirror upstream's state-marker hooks so `wt list` (and
|
|
# the statusline) show 🤖 (working) / 💬 (waiting) per branch, cleared on
|
|
# session end. https://worktrunk.dev/claude-code/#activity-tracking
|
|
activityTrackingHooks = mkClaudeCodeIntegrationEnableOption "Claude Code activity-tracking state markers (🤖/💬 in wt list)";
|
|
|
|
# Install the `/worktrunk` configuration skill (loads when editing worktrunk's
|
|
# config/hooks). Off by default: `settings` already manages
|
|
# `~/.config/worktrunk/config.toml`, and this skill would have the agent edit
|
|
# it directly, which is unsafe and may fail due to insufficient permissions.
|
|
configurationSkill = lib.mkEnableOption ''
|
|
Worktrunk's /worktrunk configuration skill.
|
|
|
|
Warning: this should not be enabled together with `settings`, as the skill
|
|
may attempt to modify the Home Manager-managed Worktrunk configuration file.
|
|
Such edits will fail due to insufficient permissions and may cause the agent
|
|
to attempt unsafe workarounds such as using elevated privileges
|
|
'';
|
|
|
|
# Install the `/wt-switch-create` skill — a slash command that creates a new
|
|
# worktree and switches the session into it.
|
|
switchCreateSkill = mkClaudeCodeIntegrationEnableOption "worktrunk's /wt-switch-create skill";
|
|
|
|
# Opt-in to use `wt` for Claude Code's statusLine, which shows the current worktree and branch.
|
|
statusLine = mkClaudeCodeIntegrationEnableOption "Claude Code statusLine powered by worktrunk";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
home.packages = lib.optional (cfg.package != null) cfg.package;
|
|
|
|
xdg.configFile."worktrunk/config.toml" = lib.mkIf (cfg.settings != { }) {
|
|
source = tomlFormat.generate "config.toml" cfg.settings;
|
|
};
|
|
|
|
# The shell integrations and the Claude Code integration below need the `wt`
|
|
# binary, so they are inert when `package` is set to null.
|
|
programs.bash.initExtra = lib.mkIf (cfg.enableBashIntegration && cfg.package != null) ''
|
|
eval "$(${wt} config shell init bash)"
|
|
'';
|
|
|
|
programs.zsh.initContent = lib.mkIf (cfg.enableZshIntegration && cfg.package != null) ''
|
|
eval "$(${wt} config shell init zsh)"
|
|
'';
|
|
|
|
programs.fish.interactiveShellInit = lib.mkIf (cfg.enableFishIntegration && cfg.package != null) ''
|
|
${wt} config shell init fish | source
|
|
'';
|
|
|
|
programs.nushell = lib.mkIf (cfg.enableNushellIntegration && cfg.package != null) {
|
|
extraConfig = ''
|
|
source ${
|
|
pkgs.runCommand "worktrunk-nushell-config.nu" { } ''
|
|
${wt} config shell init nu > $out
|
|
''
|
|
}
|
|
'';
|
|
};
|
|
|
|
# Claude Code integration: https://worktrunk.dev/claude-code/
|
|
programs.claude-code = lib.mkIf (cfg.package != null && cfg.claudeCodeIntegration.enable) {
|
|
settings = {
|
|
hooks = lib.mkMerge [
|
|
# Route Claude Code worktree create/remove through `wt` (worktree isolation).
|
|
# Mirrors the upstream plugin's hooks, calling `wt`/`jq` by absolute nix path
|
|
# so nothing depends on $PATH or the plugin being installed.
|
|
(lib.mkIf cfg.claudeCodeIntegration.worktreeHooks {
|
|
# stdin: {"name": "<branch>"} → creates a sibling worktree, prints its path.
|
|
WorktreeCreate = [
|
|
{
|
|
hooks = [
|
|
{
|
|
type = "command";
|
|
command = "bash -c 'set -o pipefail; name=$(${jq} -er .name) || exit 1; cd \"\${CLAUDE_PROJECT_DIR:-.}\" || exit 1; ${wt} switch --create \"$name\" --no-cd --format=json | ${jq} -er .path'";
|
|
}
|
|
];
|
|
}
|
|
];
|
|
# stdin: {"worktree_path": "<path>"} → removes the worktree.
|
|
WorktreeRemove = [
|
|
{
|
|
hooks = [
|
|
{
|
|
type = "command";
|
|
command = "bash -c 'p=$(${jq} -er .worktree_path) || exit 1; [ -e \"$p/.git\" ] || exit 0; ${wt} -C \"$p\" remove --foreground \"$p\"'";
|
|
}
|
|
];
|
|
}
|
|
];
|
|
})
|
|
|
|
# Activity tracking — mirrors the upstream plugin's marker hooks
|
|
# (https://github.com/max-sixty/worktrunk/blob/main/plugins/worktrunk/hooks/hooks.json),
|
|
# calling `wt` by absolute nix path. The hook-list type merges with
|
|
# claude-code.nix's own hooks (e.g. the GUI Notification) by concatenation.
|
|
(lib.mkIf cfg.claudeCodeIntegration.activityTrackingHooks {
|
|
# 🤖 the user submitted a prompt → the agent is working.
|
|
UserPromptSubmit = [
|
|
{
|
|
hooks = [ (mkMarkerHook "set 🤖") ];
|
|
}
|
|
];
|
|
# 💬 the agent paused and is waiting for the user.
|
|
Notification = [
|
|
{
|
|
matcher = "";
|
|
hooks = [ (mkMarkerHook "set 💬") ];
|
|
}
|
|
];
|
|
PreToolUse = [
|
|
{
|
|
matcher = "AskUserQuestion";
|
|
hooks = [ (mkMarkerHook "set 💬") ];
|
|
}
|
|
];
|
|
PermissionRequest = [
|
|
{
|
|
matcher = "";
|
|
hooks = [ (mkMarkerHook "set 💬") ];
|
|
}
|
|
];
|
|
Stop = [
|
|
{
|
|
hooks = [ (mkMarkerHook "set 💬") ];
|
|
}
|
|
];
|
|
# Clear the marker when the session ends.
|
|
SessionEnd = [
|
|
{
|
|
matcher = "";
|
|
hooks = [ (mkMarkerHook "clear") ];
|
|
}
|
|
];
|
|
})
|
|
];
|
|
|
|
statusLine = lib.mkIf cfg.claudeCodeIntegration.statusLine {
|
|
type = "command";
|
|
command = "${wt} list statusline --format=claude-code";
|
|
};
|
|
};
|
|
|
|
# Install worktrunk's skills directly into Claude Code (no plugin marketplace).
|
|
# The skill markdown ships inside the nixpkgs `worktrunk` package,
|
|
# version-aligned with the `wt` binary. Each skill has its own enable flag.
|
|
skills = lib.mkMerge [
|
|
(lib.mkIf cfg.claudeCodeIntegration.configurationSkill {
|
|
worktrunk = "${cfg.package}/skills/worktrunk";
|
|
})
|
|
|
|
(lib.mkIf cfg.claudeCodeIntegration.switchCreateSkill {
|
|
wt-switch-create = "${cfg.package}/skills/wt-switch-create";
|
|
})
|
|
];
|
|
};
|
|
};
|
|
}
|