-
Notifications
You must be signed in to change notification settings - Fork 112
Add skill-lint hook for dart_skills_lint #133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
reidbaker
wants to merge
8
commits into
flutter:main
Choose a base branch
from
reidbaker:r-skill-lint-hook
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b5263ef
Add skill-lint hook for dart_skills_lint
reidbaker 486c00f
Run dart format
reidbaker 90126c6
Address review: account for subclass arg overhead in chunking
reidbaker 101d38f
Refactor git hook infrastructure and address PR 133 review feedback
reidbaker aebbfbb
Update best practices and test fundamentals skills
reidbaker a8b3c48
Fix ai made up --directory flag
reidbaker 9db3a5e
Fix non executing skill lint, author skill for adding hooks to repo
reidbaker 5e741f8
Merge remote-tracking branch 'upstream/main' into r-skill-lint-hook
reidbaker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| --- | ||
| name: author-agent-hook | ||
| description: Helps scaffold deterministic script execution triggered by agent lifecycle events (jetski/antigravity hooks) against a minimal set of changes. Make sure to invoke this skill eagerly whenever a user mentions they want to author a hook, automate tasks/scripts on every change, integrate custom scripts/linters into the agent loop, or set up event handlers inside hooks.json, even if they don't explicitly ask for 'hook scaffolding.' | ||
| --- | ||
|
|
||
| # Authoring Agent Lifecycle Hooks (`author-agent-hook`) | ||
|
|
||
| This skill establishes standard, deterministic scaffolding to execute a user-provided script or command during specific agent lifecycle events within the `dart_hooks` repository. | ||
|
|
||
| ## 1. Initial Context Gathering | ||
| Before authoring code, confirm with the user: | ||
| - **Target Script/Command**: The exact path or command string the user wants to execute. | ||
| - **Lifecycle Event Type**: The target event type (`PreToolUse`, `PostToolUse`, `PreInvocation`, `PostInvocation`, or `Stop`). If the user does not specify or does not know the event type, **assume `"Stop"`** by default. | ||
|
|
||
| ## 2. Scaffolding Implementation Details | ||
| Implement the hook functionality by generating the following standard file structure: | ||
|
|
||
| ### A. Executable Runner Script (`bin/agent_<hook_name>.dart`) | ||
| Create a thin entry point script inside the `bin/` directory delegating execution to the shared `runHookMain` utility. | ||
| - **CRITICAL**: Ensure the script contains a proper shebang (`#!/usr/bin/env dart`). | ||
| - **CRITICAL**: Ensure the script file has POSIX executable permissions enabled (`chmod +x`). Without execution bits, the shell will reject execution with `Permission denied` (exit code 126) when triggered via `hooks.json`. | ||
| - **Implementation Pattern**: | ||
| ```dart | ||
| #!/usr/bin/env dart | ||
| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | ||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| import 'dart:io'; | ||
| import 'package:dart_hooks/src/<hook_name>_hook.dart'; | ||
| import 'package:dart_hooks/src/hook_utils.dart'; | ||
|
|
||
| Future<void> main(List<String> args) async { | ||
| await runHookMain( | ||
| args: args, | ||
| logFileName: '<hook_name>.log', | ||
| executeHook: (String source, Future<void> Function(String) logToFile) async { | ||
| final String packageRoot = Directory.current.parent.path; | ||
| final <HookClassName> hook = <HookClassName>(logToFile: logToFile); | ||
| await hook.run( | ||
| args: args, | ||
| currentPath: Directory.current.path, | ||
| packageRoot: packageRoot, | ||
| triggerSource: source, | ||
| ); | ||
| }, | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ### B. Core Hook Subclass (`lib/src/<hook_name>_hook.dart`) | ||
| Implement the custom hook logic by extending `BaseGitHook`. | ||
| - Provide standard overrides for `allowedExtensions`, `hookName`, and `executeCommand`. | ||
| - If the target script needs to process specific filtered paths or directories, override `transformScopedFiles` to map scoped files to the target command arguments. | ||
|
|
||
| ### C. Configuration Registration (`.agents/hooks.json`) | ||
| Register the hook under the user-specified (or defaulted `"Stop"`) event type key inside `.agents/hooks.json`. | ||
| - **Command String Details**: Format the command string exactly as required for direct execution via `sh -c`. | ||
| ```json | ||
| "<hook_name>": { | ||
| "<EventType>": [ | ||
| { | ||
| "type": "command", | ||
| "command": "../bin/agent_<hook_name>.dart --source hook --log", | ||
| "timeout": 120 | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| *(Note: For `Stop` events, handlers use a flat array structure directly under the event key without `matcher` or nested `hooks` wrappers).* | ||
|
|
||
| ## 3. Static Analysis & Testing Hygiene | ||
| Ensure all generated code strictly adheres to repository static analysis standards: | ||
| - **Typing Rules**: Run `dart analyze` to ensure complete absence of info, warning, or error messages. | ||
| - **Unit & Integration Tests**: Author comprehensive test coverage in `test/agent_<hook_name>_test.dart` and `test/agent_<hook_name>_integration_test.dart` verifying behavior via mock process runners and actual temp Git repositories. Verify success using the `run_tests` tool. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #!/usr/bin/env dart | ||
| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | ||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| import 'dart:io'; | ||
| import 'package:dart_hooks/src/hook_utils.dart'; | ||
| import 'package:dart_hooks/src/skill_lint_hook.dart'; | ||
|
|
||
| /// Runs `dart_skills_lint` against any skill whose `SKILL.md` was modified. | ||
| /// Typically invoked automatically by Antigravity via `.agents/hooks.json`. | ||
| /// To run manually, execute from the project root: | ||
| /// `dart tool/dart_hooks/bin/agent_skill_lint.dart` | ||
| Future<void> main(List<String> args) async { | ||
| await runHookMain( | ||
| args: args, | ||
| logFileName: 'skill_lint.log', | ||
| executeHook: (source, logToFile) async { | ||
| final String packageRoot = Directory.current.parent.path; | ||
| final hook = SkillLintHook(logToFile: logToFile); | ||
| await hook.run( | ||
| args: args, | ||
| currentPath: Directory.current.path, | ||
| packageRoot: packageRoot, | ||
| triggerSource: source, | ||
| ); | ||
| }, | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | ||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| import 'dart:io'; | ||
| import 'package:path/path.dart' as p; | ||
| import 'base_git_hook.dart'; | ||
| import 'process_runner.dart'; | ||
|
|
||
| /// Implements a hook that runs `dart_skills_lint` against any skill whose | ||
| /// `SKILL.md` was modified. | ||
| /// | ||
| /// Unlike file-oriented hooks (analyze, format), this hook is skill-directory | ||
| /// oriented: it filters the modified-file list to entries whose basename is | ||
| /// exactly `SKILL.md`, then runs the linter once with each skill's containing | ||
| /// directory passed as a `-s` argument. A single pass; the agent is | ||
| /// responsible for fixing reported errors. | ||
| class SkillLintHook extends BaseGitHook { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this hook interact with a packages dart_skills_lint.yaml especially its ignore.json ? |
||
| /// Creates a [SkillLintHook]. | ||
| SkillLintHook({ | ||
| super.processRunner = const RealProcessRunner(), | ||
| super.fileExists = _defaultFileExists, | ||
| super.printStdout = _defaultPrintStdout, | ||
| required super.logToFile, | ||
| super.onExit = exit, | ||
| }); | ||
|
|
||
| static bool _defaultFileExists(String path) => File(path).existsSync(); | ||
| static void _defaultPrintStdout(String message) => stdout.writeln(message); | ||
|
|
||
| /// Path to the `dart_skills_lint` package, relative to the repository root. | ||
| static const String _lintPackageRelativePath = 'tool/dart_skills_lint'; | ||
|
|
||
| /// CLI entrypoint inside the `dart_skills_lint` package. | ||
| static const String _lintBinRelativePath = 'bin/cli.dart'; | ||
|
|
||
| /// Filters the raw git status modified files by extension (e.g., ['.md']) before | ||
| /// scoping and chunking. | ||
| @override | ||
| List<String> get allowedExtensions => ['.md']; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is this used? |
||
|
|
||
| @override | ||
| String get hookName => 'dart_skills_lint'; | ||
|
|
||
| /// Filters the scoped file list to entries whose basename is case-insensitively | ||
| /// `SKILL.md`, then maps each to its parent directory. Duplicates are | ||
| /// removed and the result is sorted for deterministic command-line output. | ||
| @override | ||
| List<String> transformScopedFiles(List<String> scopedFiles) { | ||
| final skillDirectories = <String>{}; | ||
| for (final file in scopedFiles) { | ||
| if (p.basename(file).toLowerCase() == 'skill.md') { | ||
| skillDirectories.add(p.normalize(p.dirname(file))); | ||
| } | ||
| } | ||
| return skillDirectories.toList()..sort(); | ||
| } | ||
|
|
||
| @override | ||
| Future<ProcessResult> executeCommand(List<String> skillDirectories) { | ||
| final String lintPackageDir = p.join(repoRoot, _lintPackageRelativePath); | ||
| final String lintBinPath = p.join(lintPackageDir, _lintBinRelativePath); | ||
| final args = <String>[ | ||
| 'run', | ||
| lintBinPath, | ||
| for (final dir in skillDirectories) ...['-s', dir], | ||
| ]; | ||
| return processRunner.run('dart', args); | ||
| } | ||
|
reidbaker marked this conversation as resolved.
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.