@@ -1802,6 +1802,67 @@ func TestClaudeCodeUserPromptHookUsesCurrentMCPServerID(t *testing.T) {
18021802 }
18031803}
18041804
1805+ func TestClaudeCodeUserPromptHookDefersProjectDetectionUntilNeeded (t * testing.T ) {
1806+ data , err := os .ReadFile (filepath .Join (".." , ".." , "plugin" , "claude-code" , "scripts" , "user-prompt-submit.sh" ))
1807+ if err != nil {
1808+ t .Fatalf ("read user prompt hook: %v" , err )
1809+ }
1810+ text := string (data )
1811+
1812+ sessionParse := strings .Index (text , "SESSION_ID=$(echo \" $INPUT\" | jq -r '.session_id // empty')" )
1813+ sessionKeyBranch := strings .Index (text , "if [ -n \" $SESSION_ID\" ]; then" )
1814+ if sessionParse < 0 || sessionKeyBranch < 0 {
1815+ t .Fatalf ("user prompt hook missing expected session parsing/keying structure" )
1816+ }
1817+ if preKey := text [sessionParse :sessionKeyBranch ]; strings .Contains (preKey , "detect_project" ) {
1818+ t .Fatalf ("user prompt hook must not detect project before session_id-first keying" )
1819+ }
1820+
1821+ fallbackDetect := "PROJECT=$(detect_project \" $CWD\" )\n SAFE_PROJECT="
1822+ if ! strings .Contains (text , fallbackDetect ) {
1823+ t .Fatalf ("user prompt hook should detect project only for the no-session_id fallback key" )
1824+ }
1825+
1826+ subsequentMarker := strings .Index (text , "# SUBSEQUENT MESSAGES" )
1827+ if subsequentMarker < 0 {
1828+ t .Fatalf ("user prompt hook missing subsequent-message section" )
1829+ }
1830+ if ! strings .Contains (text [subsequentMarker :], "PROJECT=$(detect_project \" $CWD\" )" ) {
1831+ t .Fatalf ("user prompt hook should detect project for subsequent nudge logic after first-message handling" )
1832+ }
1833+ }
1834+
1835+ func TestClaudeCodeUserPromptSubmitHookTimeout (t * testing.T ) {
1836+ data , err := os .ReadFile (filepath .Join (".." , ".." , "plugin" , "claude-code" , "hooks" , "hooks.json" ))
1837+ if err != nil {
1838+ t .Fatalf ("read Claude Code hooks config: %v" , err )
1839+ }
1840+
1841+ var cfg struct {
1842+ Hooks map [string ][]struct {
1843+ Hooks []struct {
1844+ Command string `json:"command"`
1845+ Timeout int `json:"timeout"`
1846+ } `json:"hooks"`
1847+ } `json:"hooks"`
1848+ }
1849+ if err := json .Unmarshal (data , & cfg ); err != nil {
1850+ t .Fatalf ("parse Claude Code hooks config: %v" , err )
1851+ }
1852+
1853+ entries := cfg .Hooks ["UserPromptSubmit" ]
1854+ if len (entries ) != 1 || len (entries [0 ].Hooks ) != 1 {
1855+ t .Fatalf ("expected one UserPromptSubmit command hook, got %#v" , entries )
1856+ }
1857+ hook := entries [0 ].Hooks [0 ]
1858+ if hook .Command != "${CLAUDE_PLUGIN_ROOT}/scripts/user-prompt-submit.sh" {
1859+ t .Fatalf ("unexpected UserPromptSubmit command %q" , hook .Command )
1860+ }
1861+ if hook .Timeout != 5 {
1862+ t .Fatalf ("UserPromptSubmit timeout = %d, want 5" , hook .Timeout )
1863+ }
1864+ }
1865+
18051866func TestAddClaudeCodeAllowlist (t * testing.T ) {
18061867 t .Run ("creates file from scratch" , func (t * testing.T ) {
18071868 resetSetupSeams (t )
0 commit comments