-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[camera_android_camerax] Setup agent skills camerax #11930
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
Changes from all commits
ee20fa7
26b37ae
d825767
f7f0646
ea27e43
9ac31a2
87cfaf1
acbcc3e
7eabf5f
9e0d9e5
6b68664
0942453
9471107
c4e832f
acc34a5
288e5ec
43be3d1
8d17949
81c1c52
438537c
f64190b
14d7cfc
d7f024e
bc37ae6
16e3f28
83638ff
d93c81e
7c080f9
51ecf16
2271f28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Ignore skills under evaluation. | ||
| skill-creator |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Agent Skills | ||
|
|
||
| This directory contains skills intended for repository maintainers and contributors. Local, checked-in skills are evaluated by `dart_skills_lint` and should be configured to prevent publishing to pub.dev. | ||
|
|
||
| **Note on Remotely Managed Skills:** | ||
| Skills that are remotely defined and managed using `npx skills` should **not** be installed directly into this directory. Instead, they must be installed into the repository root's `third_party/` directory to comply with third-party code policies. Once installed there, they should be symlinked into this directory. | ||
|
|
||
| Please see the `third_party/README.md` file at the root of the repository for specific rules and instructions on adding new remote skills. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| --- | ||
| name: check-readiness | ||
| description: Run this skill to check if the repository is ready for new work. Use this skill whenever the user asks to "check readiness", "see if we are ready to start work", or when starting a new task in the camera_android_camerax package. | ||
| metadata: | ||
| internal: true | ||
| --- | ||
| # Check Readiness | ||
|
|
||
| This skill verifies that the local environment is properly configured and clean before starting new work in the `camera_android_camerax` package. | ||
|
|
||
| ## Instructions | ||
| Run the bundled verification script ([scripts/check.sh](scripts/check.sh)) to perform the automated environment checks: | ||
| ```bash | ||
| bash .agents/skills/check-readiness/scripts/check.sh | ||
|
reidbaker marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| ### Handling the Results | ||
| 1. **If the script succeeds:** Inform the user that the environment is clean, dependencies are resolved, and it is ready for new work. | ||
| 2. **If the script fails:** Explain exactly which check failed (e.g., git is not clean, a symlink is broken, Flutter is missing from PATH) and offer to help resolve it. | ||
|
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. @camsim99 I have some work happening to turn this into dart code but if you are ok with it I would like to land that change independently.
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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| #!/bin/bash | ||
| # Copyright 2013 The Flutter Authors | ||
| # Use of this source code is governed by a BSD-style license that can be | ||
| # found in the LICENSE file. | ||
|
|
||
| # Stop on first error | ||
| set -e | ||
|
|
||
| # Get the directory of this script, then go up to camera_android_camerax root | ||
| SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" | ||
| CAMERAX_DIR="$SCRIPT_DIR/../../../.." | ||
|
|
||
| echo "🔍 Checking if environment is ready for new work..." | ||
|
|
||
| # 1. Check symlinks resolve | ||
| echo "1️⃣ Checking skill symlinks..." | ||
| broken_links=$(find "$CAMERAX_DIR/.agents/skills" -type l ! -exec test -e {} \; -print) | ||
| if [ -n "$broken_links" ]; then | ||
| echo "❌ Error: Found broken symlinks in .agents/skills:" | ||
| echo "$broken_links" | ||
| exit 1 | ||
| fi | ||
| echo "✅ All symlinks resolve correctly." | ||
|
|
||
| # 2. Check git state | ||
| echo "2️⃣ Checking git state..." | ||
| # Check the whole repository git state | ||
| if [ -n "$(git status --porcelain)" ]; then | ||
| echo "❌ Error: Git working directory is not clean. Please commit or stash your changes before starting new work." | ||
| exit 1 | ||
| fi | ||
|
reidbaker marked this conversation as resolved.
|
||
| echo "✅ Git working directory is clean." | ||
|
|
||
| # 3. Check dart and flutter | ||
| echo "3️⃣ Checking Flutter and Dart..." | ||
| if ! command -v flutter &> /dev/null; then | ||
| echo "❌ Error: 'flutter' is not on the PATH." | ||
| exit 1 | ||
| fi | ||
| if ! command -v dart &> /dev/null; then | ||
| echo "❌ Error: 'dart' is not on the PATH." | ||
| exit 1 | ||
| fi | ||
| echo "✅ Flutter and Dart are on the PATH." | ||
|
|
||
| # 4. Check dependencies in camera_android_camerax | ||
| echo "4️⃣ Checking dependencies in camera_android_camerax..." | ||
| cd "$CAMERAX_DIR" | ||
| if ! flutter pub get; then | ||
| echo "❌ Error: Failed to resolve dependencies." | ||
| exit 1 | ||
| fi | ||
| echo "✅ Dependencies are resolved and ready." | ||
|
|
||
| echo "🎉 Environment is fully ready!" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/dart-lang-skills/.agents/skills/dart-add-unit-test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/dart-lang-skills/.agents/skills/dart-collect-coverage |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/dart-lang-skills/.agents/skills/dart-generate-test-mocks |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/dart-lang-skills/.agents/skills/dart-run-static-analysis |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/flutter-skills/.agents/skills/flutter-add-integration-test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/mattpocock-skills/.agents/skills/grill-me |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../../../third_party/skill-repos/obra-superpowers/.agents/skills/receiving-code-review |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| .agents/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| dart_skills_lint: | ||
| rules: | ||
| check-relative-paths: error | ||
| check-trailing-whitespace: error | ||
| directories: | ||
| - path: "skills" | ||
| individual_skills: | ||
| - path: ".agents/skills/check-readiness" | ||
| rules: | ||
| prevent-skills-sh-publishing: error |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Published Skills | ||
|
|
||
| This directory contains AI agent skills that are intended to be published to pub.dev. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // Copyright 2013 The Flutter Authors | ||
| // 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_skills_lint/dart_skills_lint.dart'; | ||
| import 'package:path/path.dart' as p; | ||
|
|
||
| /// A custom lint rule that enforces that all skills tracked in version control | ||
| /// have the `prevent-skills-sh-publishing` rule enabled in `dart_skills_lint.yaml`. | ||
| /// | ||
| /// Third-party skills symlinked from the repository root's `third_party` directory | ||
| /// are exempt. | ||
| class EnforceTrackedSkillsPreventPublishingRule extends SkillRule { | ||
| @override | ||
| String get name => 'enforce-tracked-skills-prevent-publishing'; | ||
|
|
||
| @override | ||
| AnalysisSeverity get severity => AnalysisSeverity.error; | ||
|
|
||
| @override | ||
| Future<List<ValidationError>> validate(SkillContext context) async { | ||
| try { | ||
| final ProcessResult topLevelResult = await Process.run('git', [ | ||
| 'rev-parse', | ||
| '--show-toplevel', | ||
| ]); | ||
| if (topLevelResult.exitCode == 0) { | ||
| final String repoRoot = (topLevelResult.stdout as String).trim(); | ||
| final String resolvedPath = context.directory.resolveSymbolicLinksSync(); | ||
| if (resolvedPath.startsWith('$repoRoot/third_party/')) { | ||
| return []; | ||
| } | ||
| } | ||
| } on FileSystemException { | ||
| // Fallback to normal validation if link resolution fails | ||
| } | ||
|
|
||
| // 2. Check if the skill directory is tracked in git | ||
| final ProcessResult processResult; | ||
| try { | ||
| processResult = await Process.run('git', ['ls-files', context.directory.path]); | ||
| } on ProcessException catch (e) { | ||
| return [ | ||
| ValidationError( | ||
| ruleId: name, | ||
| severity: severity, | ||
| file: 'dart_skills_lint.yaml', | ||
| message: 'Failed to run git to check tracked status. Error: $e', | ||
| ), | ||
| ]; | ||
| } | ||
| if (processResult.exitCode != 0) { | ||
| return []; | ||
| } | ||
| final String output = (processResult.stdout as String).trim(); | ||
| if (output.isEmpty) { | ||
| // Not tracked by git, no enforcement needed | ||
| return []; | ||
| } | ||
|
|
||
| // 3. Check dart_skills_lint.yaml config | ||
| final Configuration config = await ConfigParser.loadConfig(); | ||
| var ruleEnabled = false; | ||
| var pathFound = false; | ||
| AnalysisSeverity? configuredSeverity; | ||
|
|
||
| final String normalizedContextPath = p.canonicalize(context.directory.absolute.path); | ||
| for (final LintTargetConfig skillConfig in config.individualSkillConfigs) { | ||
| final String normalizedConfigPath = p.canonicalize(File(skillConfig.path).absolute.path); | ||
| if (normalizedConfigPath == normalizedContextPath) { | ||
| pathFound = true; | ||
| configuredSeverity = skillConfig.rules['prevent-skills-sh-publishing']; | ||
| if (configuredSeverity == AnalysisSeverity.error) { | ||
| ruleEnabled = true; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!ruleEnabled) { | ||
| return [ | ||
| ValidationError( | ||
| ruleId: name, | ||
| severity: severity, | ||
| file: 'dart_skills_lint.yaml', | ||
| message: _buildErrorMessage( | ||
| relativePath: context.directory.path, | ||
| pathFound: pathFound, | ||
| configuredSeverity: configuredSeverity, | ||
| ), | ||
| ), | ||
| ]; | ||
| } | ||
|
|
||
| return []; | ||
| } | ||
|
|
||
| /// Returns an actionable error message when a skill fails validation. | ||
| /// | ||
| /// The [relativePath] is the path to the skill directory being validated. | ||
| /// If [pathFound] is false, the message indicates that the skill is entirely | ||
| /// missing from `dart_skills_lint.yaml`. If [pathFound] is true but | ||
| /// [configuredSeverity] is null, it indicates the rule is missing. If | ||
| /// [configuredSeverity] is provided, it indicates the rule is set to an | ||
| /// invalid severity. | ||
| /// | ||
| /// The returned string always concludes with the expected YAML snippet. | ||
| String _buildErrorMessage({ | ||
| required String relativePath, | ||
| required bool pathFound, | ||
| required AnalysisSeverity? configuredSeverity, | ||
| }) { | ||
| final buffer = StringBuffer(); | ||
|
|
||
| if (!pathFound) { | ||
| buffer.writeln( | ||
| 'The skill at "$relativePath" is tracked in git, but is missing from dart_skills_lint.yaml.', | ||
| ); | ||
| buffer.writeln( | ||
| 'Please add it under `individual_skills:` with the `prevent-skills-sh-publishing` rule set to `error`:', | ||
| ); | ||
| } else if (configuredSeverity == null) { | ||
| buffer.writeln( | ||
| 'The skill at "$relativePath" is listed in dart_skills_lint.yaml, but the `prevent-skills-sh-publishing` rule is missing.', | ||
| ); | ||
| buffer.writeln('Please add it to the `rules` section for this skill:'); | ||
| } else { | ||
| buffer.writeln( | ||
| 'The skill at "$relativePath" has the `prevent-skills-sh-publishing` rule configured as `${configuredSeverity.name}`.', | ||
| ); | ||
| buffer.writeln( | ||
| 'Tracked skills strictly require this rule to be set to `error` to prevent accidental publishing.', | ||
| ); | ||
| buffer.writeln(); | ||
| buffer.writeln('Please update dart_skills_lint.yaml:'); | ||
| } | ||
|
|
||
| buffer.writeln(); | ||
| buffer.writeln(' individual_skills:'); | ||
| buffer.writeln(' - path: "$relativePath"'); | ||
| buffer.writeln(' rules:'); | ||
| buffer.write(' prevent-skills-sh-publishing: error'); | ||
|
|
||
| return buffer.toString(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // Copyright 2013 The Flutter Authors | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'dart:async'; | ||
|
|
||
| import 'package:dart_skills_lint/dart_skills_lint.dart'; | ||
| import 'package:flutter/foundation.dart'; | ||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:logging/logging.dart'; | ||
|
|
||
| import 'enforce_tracked_skills_prevent_publishing_rule.dart'; | ||
|
|
||
| void main() { | ||
| test('Validate Skills', () async { | ||
| final Level oldLevel = Logger.root.level; | ||
| Logger.root.level = Level.ALL; | ||
| final StreamSubscription<LogRecord> subscription = Logger.root.onRecord.listen((record) { | ||
| debugPrint(record.message); | ||
| }); | ||
|
|
||
| try { | ||
| final Configuration config = await ConfigParser.loadConfig(); | ||
| final bool isValid = await validateSkills( | ||
| config: config, | ||
| customRules: [EnforceTrackedSkillsPreventPublishingRule()], | ||
| ); | ||
| expect(isValid, isTrue, reason: 'Skills validation failed. See above for details.'); | ||
| } finally { | ||
| Logger.root.level = oldLevel; | ||
| await subscription.cancel(); | ||
| } | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Third-Party Agent Skills | ||
|
Collaborator
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. Can we make a
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. done I went with skill-repos because the string "skills" tends to be a magnet for crawlers. |
||
|
|
||
| To comply with third-party code policies, all remotely managed agent skills (installed via `npx skills`) must be isolated in this `third_party/skill-repos/` directory before being symlinked to specific project `.agents/skills` directories. | ||
|
|
||
| ## Rules for Adding New Remote Skills | ||
|
|
||
| When adding a new remote skill, you must follow this exact structure and process: | ||
|
|
||
| 1. **Folder per Repository:** Create a new folder under `third_party/skill-repos/` named uniquely after the GitHub repository the skill originates from (e.g., `third_party/skill-repos/dart-lang-skills`). | ||
|
|
||
| 2. **Installation:** Run the `npx skills` command from *within* that new repository folder. This ensures the `skills-lock.json` file is correctly generated inside the subfolder. All `skills-lock.json` files must be generated by the CLI, not manually crafted. | ||
| ```bash | ||
| cd third_party/skill-repos/<repo-name> | ||
| npx skills add <author/repo> --skill <skill-name> -y | ||
| ``` | ||
|
|
||
| 3. **License Requirement:** You **must** download and retain the original `LICENSE` file from the remote repository and place it directly inside the `third_party/skill-repos/<repo-name>` subfolder. | ||
|
|
||
| 4. **Symlinking:** Once installed, symlink the newly downloaded skill into the appropriate plugin's `.agents/skills` directory using a relative symlink. | ||
| ```bash | ||
| cd packages/<plugin-name>/.agents/skills | ||
| ln -s ../../../../../third_party/skill-repos/<repo-name>/.agents/skills/<skill-name> <skill-name> | ||
| ``` | ||
Uh oh!
There was an error while loading. Please reload this page.