Skip to content

fix: support composable client hooks - #786

Merged
1jehuang merged 1 commit into
masterfrom
fix/issue-759-client-hooks
Aug 4, 2026
Merged

fix: support composable client hooks#786
1jehuang merged 1 commit into
masterfrom
fix/issue-759-client-hooks

Conversation

@1jehuang

@1jehuang 1jehuang commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • accept lifecycle hook commands as either the existing scalar string or an ordered array
  • execute observer and gate commands independently
  • carry each initiating client's terminal identity through shared-server session start, resume, clear, turn, and cleanup paths
  • replace stale server terminal variables before launching hooks so concurrent panes stay isolated

Root cause

Hook configuration stored only one string, forcing integrations to replace or wrap an existing command. Hook processes also inherited the long-lived server's startup environment rather than the client snapshot already sent in Subscribe, so later panes could be attributed to the wrong terminal.

Validation

  • cargo check -p jcode-config-types -p jcode-terminal-launch -p jcode-base -p jcode-app-core
  • cargo test -p jcode-base hooks -- --nocapture
  • cargo test -p jcode-terminal-launch client_terminal_env_replaces_inherited_identity_and_exports_aliases
  • cargo test -p jcode-base hooks_config_defaults_and_parses_from_toml
  • cargo test -p jcode-base test_env_override_lifecycle_hooks
  • cargo fmt --all

Addresses #759 without closing it from triage.

--- — Jcode agent (automated triage), on behalf of @1jehuang

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR expands lifecycle hook support and carries client terminal identity into shared-server hook execution. The main changes are:

  • Hook config now accepts either a scalar command or an ordered command array.
  • Observer and pre_tool gate hooks now run each configured command independently.
  • Hook processes receive the initiating client’s terminal environment instead of the server’s stale startup environment.
  • Session start, resume, clear, turn processing, and cleanup paths now run under client-scoped hook environment context.

Confidence Score: 4/5

Mostly safe, with one contained bug in initial connection terminal-env metadata.

Hook parsing and execution changes are localized and covered by tests. The first Subscribe terminal snapshot is not copied into ClientConnectionInfo, so spawn/focus paths can use empty terminal identity data on the normal connection path.

Files Needing Attention: crates/jcode-app-core/src/server/client_lifecycle.rs

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex inspected the handle_client initial subscribe path and added a focused Tokio test named initial_subscribe_stores_terminal_env_in_client_connection_info to drive a real Subscribe request with non-empty terminal identity values and inspect ClientConnectionInfo.terminal_env.
  • T-Rex saved the repro source as the Repro: generated focused Rust test source for initial Subscribe terminal_env storage artifact and attempted to run the focused cargo test, but the run timed out due to the build-lock and budget limits, preventing capture of a full failure log.
  • T-Rex validated the contract checks and found the package check completed successfully, the hooks and config tests passed, and the terminal identity test passed, while noting that cargo-fmt is not installed for the stable toolchain.
  • T-Rex planned to re-run the focused cargo test after clearing potential stale cargo processes and to capture the completed failure output under the appropriate repro artifacts.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
crates/jcode-app-core/src/server/client_lifecycle.rs Adds task-local client terminal env scoping around session lifecycle and turn-processing paths; initial connection metadata still stores an empty terminal env.
crates/jcode-base/src/hooks.rs Introduces ordered hook command execution and task-local client terminal env application for hook processes.
crates/jcode-config-types/src/lib.rs Adds HookCommands with scalar/array serde support and converts hook config fields to use it.
crates/jcode-terminal-launch/src/lib.rs Adds helper to replace stale terminal identity env vars and export client-prefixed aliases.
crates/jcode-base/src/config/env_overrides.rs Updates lifecycle hook env overrides to accept scalar commands or TOML-style command arrays.
crates/jcode-base/src/config_tests.rs Covers hook command array parsing and env override behavior for lifecycle hooks.
crates/jcode-base/src/config.rs Re-exports the new HookCommands config type alongside existing config types.
crates/jcode-base/src/terminal_launch.rs Re-exports apply_client_terminal_env for hook process setup.

Sequence Diagram

sequenceDiagram
participant Client
participant Server as handle_client
participant Agent
participant Hooks
participant Cmd as Hook command(s)

Client->>Server: Subscribe(working_dir, terminal_env)
Server->>Server: "active_terminal_env = terminal_env"
Server->>Hooks: with_client_terminal_env(active_terminal_env)
Hooks->>Agent: create/resume/clear/process session
Agent->>Hooks: dispatch_observer / run_pre_tool_gate
Hooks->>Cmd: "apply_client_terminal_env + JCODE_HOOK_* env"
loop configured commands
    Hooks->>Cmd: spawn observer or await gate
end
Cmd-->>Hooks: exit status (gate only)
Hooks-->>Agent: allow/block decision
Agent-->>Server: events/result
Server-->>Client: ServerEvent stream
Loading

Comments Outside Diff (1)

  1. crates/jcode-app-core/src/server/client_lifecycle.rs, line 540 (link)

    P1 Store initial terminal env
    active_terminal_env is populated from the first Subscribe, but this connection record still stores terminal_env: Vec::new() until a later Subscribe request updates it. On the normal initial-subscribe path, shared-server spawn/focus code that reads ClientConnectionInfo.terminal_env keeps seeing an empty snapshot, so hooks launched for that connection lose the initiating pane identity.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: crates/jcode-app-core/src/server/client_lifecycle.rs
    Line: 540
    
    Comment:
    **Store initial terminal env**
    `active_terminal_env` is populated from the first `Subscribe`, but this connection record still stores `terminal_env: Vec::new()` until a later `Subscribe` request updates it. On the normal initial-subscribe path, shared-server spawn/focus code that reads `ClientConnectionInfo.terminal_env` keeps seeing an empty snapshot, so hooks launched for that connection lose the initiating pane identity.
    
    
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Prompt To Fix All With AI
### Issue 1
crates/jcode-app-core/src/server/client_lifecycle.rs:540
**Store initial terminal env**
`active_terminal_env` is populated from the first `Subscribe`, but this connection record still stores `terminal_env: Vec::new()` until a later `Subscribe` request updates it. On the normal initial-subscribe path, shared-server spawn/focus code that reads `ClientConnectionInfo.terminal_env` keeps seeing an empty snapshot, so hooks launched for that connection lose the initiating pane identity.

```suggestion
                terminal_env: active_terminal_env.clone(),
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix: support composable client hooks (fi..." | Re-trigger Greptile

@1jehuang
1jehuang merged commit 1df9f99 into master Aug 4, 2026
8 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant