Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .agents/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
}
13 changes: 13 additions & 0 deletions .agents/hooks/claude-post-tool-use
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Claude Code's str_replace_editor tool uses the argument path rather than file_path to specify the file being edited. The current regex looking for "file_path" will never match, causing the hook to silently fail to detect Dart edits. Additionally, using case-insensitive matching (-i) ensures robustness against any casing variations in the JSON keys.

if printf '%s' "$INPUT" | grep -Eiq '"(file_path|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
13 changes: 13 additions & 0 deletions .agents/hooks/claude-post-tool-use.cmd
Original file line number Diff line number Diff line change
@@ -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
34 changes: 34 additions & 0 deletions .agents/hooks/flag-dart-edit
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using case-insensitive matching (-i) is safer and more robust against different casing (e.g., camelCase targetFile or absolutePath vs PascalCase TargetFile or AbsolutePath). Adding path as a fallback also ensures compatibility with other potential tool schemas.

if printf '%s' "$INPUT" | grep -Eiq '"(TargetFile|AbsolutePath|path)"[^,]*[.]dart'; then

: > "$FLAG" 2>/dev/null || true
fi

echo '{}'
exit 0
15 changes: 15 additions & 0 deletions .agents/hooks/flag-dart-edit.cmd
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions .agents/hooks/flutter-app-runtime
Original file line number Diff line number Diff line change
@@ -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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The temporary directory fallback logic here is inconsistent with .agents/hooks/flag-dart-edit (which uses ${TMPDIR:-${TEMP:-${TMP:-/tmp}}}). If TMPDIR is unset but TEMP or TMP is set, the two scripts will look in different directories, causing the flag file to not be found. Please make them consistent.

TEMP_DIR="${TMPDIR:-${TEMP:-${TMP:-/tmp}}}"
FLAG="${TEMP_DIR}/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
20 changes: 20 additions & 0 deletions .agents/hooks/flutter-app-runtime.cmd
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Claude Code's primary file editing tool is str_replace_editor. The current matcher only matches Edit|Write, which means this hook will never trigger when Claude Code edits files. Please add str_replace_editor to the matcher.

Suggested change
"matcher": "Edit|Write",
"matcher": "str_replace_editor|Edit|Write",

"hooks": [
{
"type": "command",
"command": ".agents/hooks/claude-post-tool-use",
"statusMessage": "flutter-app-runtime: mcp__dart__hot_reload"
}
]
}
]
}
}
Loading