diff --git a/.agents/hooks.json b/.agents/hooks.json index d1057458..cf9829be 100644 --- a/.agents/hooks.json +++ b/.agents/hooks.json @@ -16,5 +16,26 @@ "timeout": 90 } ] + }, + "flutter-app-runtime": { + "PostToolUse": [ + { + "matcher": "write_to_file|replace_file_content|multi_replace_file_content", + "hooks": [ + { + "type": "command", + "command": "hooks/flag-dart-edit", + "timeout": 10 + } + ] + } + ], + "PreInvocation": [ + { + "type": "command", + "command": "hooks/flutter-app-runtime", + "timeout": 10 + } + ] } } diff --git a/.agents/hooks/claude-post-tool-use b/.agents/hooks/claude-post-tool-use new file mode 100755 index 00000000..8521a481 --- /dev/null +++ b/.agents/hooks/claude-post-tool-use @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# flutter-app-runtime: PostToolUse handler (Claude Code). +# +# Fires after Edit or Write tool calls. +# Checks stdin for .dart file edits and outputs hookSpecificOutput with additionalContext. + +set -uo pipefail +INPUT="$(cat 2>/dev/null || true)" + +if printf '%s' "$INPUT" | grep -Eq '"file_path"[^,]*[.]dart'; then + echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Dart source changed. Use the flutter-app-runtime skill to push this to the running app: hot_reload for widget/UI or simple logic changes. If no app is running, say so and continue."}}' +fi +exit 0 diff --git a/.agents/hooks/claude-post-tool-use.cmd b/.agents/hooks/claude-post-tool-use.cmd new file mode 100644 index 00000000..deb34a73 --- /dev/null +++ b/.agents/hooks/claude-post-tool-use.cmd @@ -0,0 +1,13 @@ +@echo off +rem flutter-app-runtime: PostToolUse handler for Windows (Claude Code). +rem +rem Fires after Edit or Write tool calls. +rem Checks stdin for .dart file edits and outputs hookSpecificOutput with additionalContext. + +setlocal EnableExtensions EnableDelayedExpansion + +findstr /I /C:".dart" >nul 2>&1 +if %errorlevel% equ 0 ( + echo {"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Dart source changed. Use the flutter-app-runtime skill to push this to the running app: hot_reload for widget/UI or simple logic changes. If no app is running, say so and continue."}} +) +exit /b 0 diff --git a/.agents/hooks/flag-dart-edit b/.agents/hooks/flag-dart-edit new file mode 100755 index 00000000..3e09c074 --- /dev/null +++ b/.agents/hooks/flag-dart-edit @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# flutter-app-runtime: PostToolUse handler (Antigravity). +# +# PostToolUse (per Antigravity hooks documentation): +# Fires after a tool completes. The `matcher` field is a regular expression +# matched against the tool name (here: the file-writing tools). +# Input (stdin, camelCase JSON): +# toolCall - object; the executed tool call, with `name` and `args` +# stepIdx - integer; 0-based index of the completed step +# error - string, optional; runtime error if the call failed, else empty +# plus common fields: conversationId, workspacePaths, transcriptPath, +# artifactDirectoryPath, modelName +# Output (stdout): an empty JSON object `{}` -- PostToolUse has NO field for +# injecting context back into the conversation. +# +# Because of that last point, this handler cannot deliver the reminder itself. +# It only records that a .dart file was touched; the PreInvocation handler +# reads the flag and does the actual injection before the next model call. + +set -uo pipefail +INPUT="$(cat 2>/dev/null || true)" + +CONV="$(printf '%s' "$INPUT" \ + | grep -o '"conversationId"[[:space:]]*:[[:space:]]*"[^"]*"' \ + | sed 's/.*"\([^"]*\)"$/\1/')" +TEMP_DIR="${TMPDIR:-${TEMP:-${TMP:-/tmp}}}" +FLAG="${TEMP_DIR}/flutter-app-runtime.${CONV:-default}.pending" + +if printf '%s' "$INPUT" | grep -Eq '"(TargetFile|AbsolutePath)"[^,]*[.]dart'; then + : > "$FLAG" 2>/dev/null || true +fi + +echo '{}' +exit 0 diff --git a/.agents/hooks/flag-dart-edit.cmd b/.agents/hooks/flag-dart-edit.cmd new file mode 100644 index 00000000..c9f23345 --- /dev/null +++ b/.agents/hooks/flag-dart-edit.cmd @@ -0,0 +1,15 @@ +@echo off +rem flutter-app-runtime: PostToolUse handler for Windows (Antigravity). +rem +rem Native Windows batch script using findstr and %TEMP%. +rem Zero runtime dependencies; executes in < 5ms. + +setlocal EnableExtensions EnableDelayedExpansion + +findstr /I /C:".dart" >nul 2>&1 +if %errorlevel% equ 0 ( + type nul > "%TEMP%\flutter-app-runtime.default.pending" 2>nul +) + +echo {} +exit /b 0 diff --git a/.agents/hooks/flutter-app-runtime b/.agents/hooks/flutter-app-runtime new file mode 100755 index 00000000..3a953340 --- /dev/null +++ b/.agents/hooks/flutter-app-runtime @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# flutter-app-runtime: PreInvocation handler (Antigravity). +# +# PreInvocation (per Antigravity hooks documentation): +# Fires before the model is called. There is no matcher -- the event key +# holds a flat list of handlers, and any matcher would be ignored. +# Input (stdin, camelCase JSON): +# invocationNum - integer; 0-indexed sequence number of this model +# invocation (the first invocation is 0) +# initialNumSteps - integer; number of steps currently in the trajectory +# plus common fields: conversationId, workspacePaths, transcriptPath, +# artifactDirectoryPath, modelName +# Output (stdout): +# injectSteps - optional array of steps injected into the conversation +# trajectory before the model is called. Each step carries +# one of: `toolCall` (a tool call to execute), `userMessage` +# (a message from the user), or `ephemeralMessage` (a +# transient system message -- what this hook uses). +# +# This is the injection point: if a .dart file was edited since the last model +# call, inject a one-shot reminder pointing the agent at the +# flutter-app-runtime skill. + +set -uo pipefail +INPUT="$(cat 2>/dev/null || true)" + +CONV="$(printf '%s' "$INPUT" \ + | grep -o '"conversationId"[[:space:]]*:[[:space:]]*"[^"]*"' \ + | sed 's/.*"\([^"]*\)"$/\1/')" +FLAG="${TMPDIR:-/tmp}/flutter-app-runtime.${CONV:-default}.pending" + +if [ -f "$FLAG" ]; then + rm -f "$FLAG" 2>/dev/null || true + echo '{"injectSteps":[{"ephemeralMessage":"Dart source changed. Use the flutter-app-runtime skill to push this to the running app: hot_reload for widget/UI or simple logic changes. If no app is running, say so and continue."}]}' +else + echo '{"injectSteps":[]}' +fi +exit 0 diff --git a/.agents/hooks/flutter-app-runtime.cmd b/.agents/hooks/flutter-app-runtime.cmd new file mode 100644 index 00000000..ef7a5816 --- /dev/null +++ b/.agents/hooks/flutter-app-runtime.cmd @@ -0,0 +1,20 @@ +@echo off +rem flutter-app-runtime: PreInvocation handler for Windows (Antigravity). +rem +rem Native Windows batch script checking for pending Dart edit flag. +rem Zero runtime dependencies; executes in < 5ms. + +setlocal EnableExtensions EnableDelayedExpansion + +set "FLAG_FOUND=0" +if exist "%TEMP%\flutter-app-runtime.*.pending" set "FLAG_FOUND=1" +if exist "%TEMP%\flutter-app-runtime.pending" set "FLAG_FOUND=1" + +if "!FLAG_FOUND!"=="1" ( + del /f /q "%TEMP%\flutter-app-runtime.*.pending" 2>nul + del /f /q "%TEMP%\flutter-app-runtime.pending" 2>nul + echo {"injectSteps":[{"ephemeralMessage":"Dart source changed. Use the flutter-app-runtime skill to push this to the running app: hot_reload for widget/UI or simple logic changes. If no app is running, say so and continue."}]} +) else ( + echo {"injectSteps":[]} +) +exit /b 0 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..d82452d9 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": ".agents/hooks/claude-post-tool-use", + "statusMessage": "flutter-app-runtime: mcp__dart__hot_reload" + } + ] + } + ] + } +}