From 8f8f208319691376db7498a1d1a63aa94c3b4913 Mon Sep 17 00:00:00 2001 From: chcweblogin Date: Fri, 3 Apr 2026 17:19:23 +0530 Subject: [PATCH 1/7] feat(stac_cli): add unit testing infrastructure and core command tests --- .../test/commands/cli_commands_test.dart | 45 +++++++++++++++++++ .../stac_cli/test/utils/file_utils_test.dart | 40 +++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 packages/stac_cli/test/commands/cli_commands_test.dart create mode 100644 packages/stac_cli/test/utils/file_utils_test.dart diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart new file mode 100644 index 00000000..a0955db2 --- /dev/null +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -0,0 +1,45 @@ +import 'package:test/test.dart'; +import 'package:args/command_runner.dart'; +import 'package:stac_cli/src/commands/build_command.dart'; +import 'package:stac_cli/src/commands/init_command.dart'; +import 'package:stac_cli/src/commands/deploy_command.dart'; +import 'package:stac_cli/src/config/env.dart'; + +void main() { + group('CLI Commands', () { + late CommandRunner runner; + + setUp(() { + configureEnvironment({ + 'STAC_BASE_API_URL': 'https://api.test.stac.dev', + 'STAC_GOOGLE_CLIENT_ID': 'test-client-id', + 'STAC_FIREBASE_API_KEY': 'test-api-key', + }); + runner = CommandRunner('stac', 'Stac CLI test runner'); + runner.addCommand(BuildCommand()); + runner.addCommand(InitCommand()); + runner.addCommand(DeployCommand()); + }); + + test('build command has correct name and description', () { + final command = runner.commands['build']; + expect(command, isNotNull); + expect(command!.name, equals('build')); + expect(command.description, isNotEmpty); + }); + + test('init command has correct name and description', () { + final command = runner.commands['init']; + expect(command, isNotNull); + expect(command!.name, equals('init')); + expect(command.description, isNotEmpty); + }); + + test('deploy command has correct name and description', () { + final command = runner.commands['deploy']; + expect(command, isNotNull); + expect(command!.name, equals('deploy')); + expect(command.description, isNotEmpty); + }); + }); +} diff --git a/packages/stac_cli/test/utils/file_utils_test.dart b/packages/stac_cli/test/utils/file_utils_test.dart new file mode 100644 index 00000000..d93e6672 --- /dev/null +++ b/packages/stac_cli/test/utils/file_utils_test.dart @@ -0,0 +1,40 @@ +import 'dart:io'; +import 'package:test/test.dart'; +import 'package:stac_cli/src/utils/file_utils.dart'; +import 'package:path/path.dart' as path; + +void main() { + group('FileUtils', () { + test('homeDirectory returns a string', () { + final home = FileUtils.homeDirectory; + expect(home, isNotEmpty); + }); + + test('configDirectory is a valid path', () { + final config = FileUtils.configDirectory; + expect(config, isNotEmpty); + }); + + test('file operations', () async { + final tempDir = Directory.systemTemp.createTempSync('stac_cli_test'); + final filePath = path.join(tempDir.path, 'test_file.txt'); + + // Test fileExists + expect(await FileUtils.fileExists(filePath), isFalse); + + // Test writeFile and fileExists + await FileUtils.writeFile(filePath, 'hello world'); + expect(await FileUtils.fileExists(filePath), isTrue); + + // Test readFile + final content = await FileUtils.readFile(filePath); + expect(content, 'hello world'); + + // Test deleteFile + await FileUtils.deleteFile(filePath); + expect(await FileUtils.fileExists(filePath), isFalse); + + tempDir.deleteSync(recursive: true); + }); + }); +} From 4db325e6609c34210ba42c702ce22f01ca474341 Mon Sep 17 00:00:00 2001 From: chcweblogin Date: Fri, 3 Apr 2026 17:22:13 +0530 Subject: [PATCH 2/7] docs(test): add descriptive comments and improve reliability of CLI unit tests --- .../test/commands/cli_commands_test.dart | 10 +++-- .../stac_cli/test/utils/file_utils_test.dart | 41 +++++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index a0955db2..97700cf6 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -5,16 +5,20 @@ import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; import 'package:stac_cli/src/config/env.dart'; +/// Test suite for verifying core Stac CLI commands. void main() { group('CLI Commands', () { late CommandRunner runner; setUp(() { + // Initialize environment with mock values to satisfy required checks in services. + // This allows testing command configuration without needing real API keys. configureEnvironment({ 'STAC_BASE_API_URL': 'https://api.test.stac.dev', 'STAC_GOOGLE_CLIENT_ID': 'test-client-id', 'STAC_FIREBASE_API_KEY': 'test-api-key', }); + runner = CommandRunner('stac', 'Stac CLI test runner'); runner.addCommand(BuildCommand()); runner.addCommand(InitCommand()); @@ -23,21 +27,21 @@ void main() { test('build command has correct name and description', () { final command = runner.commands['build']; - expect(command, isNotNull); + expect(command, isNotNull, reason: 'BuildCommand should be registered'); expect(command!.name, equals('build')); expect(command.description, isNotEmpty); }); test('init command has correct name and description', () { final command = runner.commands['init']; - expect(command, isNotNull); + expect(command, isNotNull, reason: 'InitCommand should be registered'); expect(command!.name, equals('init')); expect(command.description, isNotEmpty); }); test('deploy command has correct name and description', () { final command = runner.commands['deploy']; - expect(command, isNotNull); + expect(command, isNotNull, reason: 'DeployCommand should be registered'); expect(command!.name, equals('deploy')); expect(command.description, isNotEmpty); }); diff --git a/packages/stac_cli/test/utils/file_utils_test.dart b/packages/stac_cli/test/utils/file_utils_test.dart index d93e6672..50bc1e28 100644 --- a/packages/stac_cli/test/utils/file_utils_test.dart +++ b/packages/stac_cli/test/utils/file_utils_test.dart @@ -3,38 +3,47 @@ import 'package:test/test.dart'; import 'package:stac_cli/src/utils/file_utils.dart'; import 'package:path/path.dart' as path; +/// Test suite for Stac CLI file utility operations. void main() { group('FileUtils', () { - test('homeDirectory returns a string', () { + // Basic verification of environment-dependent directory getters. + test('homeDirectory returns a non-empty string on this OS', () { final home = FileUtils.homeDirectory; expect(home, isNotEmpty); }); - test('configDirectory is a valid path', () { + test('configDirectory path is generated', () { final config = FileUtils.configDirectory; expect(config, isNotEmpty); }); - test('file operations', () async { + // Integrated test for file system operations using a temporary directory. + test('integrated file operations: create, read, and delete', () async { + // Setup a clean temporary sandbox for this test. final tempDir = Directory.systemTemp.createTempSync('stac_cli_test'); final filePath = path.join(tempDir.path, 'test_file.txt'); - // Test fileExists - expect(await FileUtils.fileExists(filePath), isFalse); + try { + // 1. Initial State: file should not exist. + expect(await FileUtils.fileExists(filePath), isFalse); - // Test writeFile and fileExists - await FileUtils.writeFile(filePath, 'hello world'); - expect(await FileUtils.fileExists(filePath), isTrue); + // 2. Write Operation: create file with content. + await FileUtils.writeFile(filePath, 'hello world'); + expect(await FileUtils.fileExists(filePath), isTrue); - // Test readFile - final content = await FileUtils.readFile(filePath); - expect(content, 'hello world'); + // 3. Read Operation: verify content matches. + final content = await FileUtils.readFile(filePath); + expect(content, equals('hello world')); - // Test deleteFile - await FileUtils.deleteFile(filePath); - expect(await FileUtils.fileExists(filePath), isFalse); - - tempDir.deleteSync(recursive: true); + // 4. Delete Operation: cleanup file. + await FileUtils.deleteFile(filePath); + expect(await FileUtils.fileExists(filePath), isFalse); + } finally { + // Always cleanup the temporary directory logic even if tests fail. + if (tempDir.existsSync()) { + tempDir.deleteSync(recursive: true); + } + } }); }); } From cdda08c7792ec145747beda68986dde80618d889 Mon Sep 17 00:00:00 2001 From: chcweblogin Date: Sun, 5 Apr 2026 12:44:13 +0530 Subject: [PATCH 3/7] style(stac_cli): fix formatting in test files --- packages/stac_cli/test/commands/cli_commands_test.dart | 2 +- packages/stac_cli/test/utils/file_utils_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index 97700cf6..f8c9246b 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -18,7 +18,7 @@ void main() { 'STAC_GOOGLE_CLIENT_ID': 'test-client-id', 'STAC_FIREBASE_API_KEY': 'test-api-key', }); - + runner = CommandRunner('stac', 'Stac CLI test runner'); runner.addCommand(BuildCommand()); runner.addCommand(InitCommand()); diff --git a/packages/stac_cli/test/utils/file_utils_test.dart b/packages/stac_cli/test/utils/file_utils_test.dart index 50bc1e28..76c0cdd1 100644 --- a/packages/stac_cli/test/utils/file_utils_test.dart +++ b/packages/stac_cli/test/utils/file_utils_test.dart @@ -22,7 +22,7 @@ void main() { // Setup a clean temporary sandbox for this test. final tempDir = Directory.systemTemp.createTempSync('stac_cli_test'); final filePath = path.join(tempDir.path, 'test_file.txt'); - + try { // 1. Initial State: file should not exist. expect(await FileUtils.fileExists(filePath), isFalse); From 0e2a6e13b8edad99b284916f9ef27a16a23a8143 Mon Sep 17 00:00:00 2001 From: chcweblogin Date: Fri, 5 Jun 2026 09:31:11 +0530 Subject: [PATCH 4/7] test(cli): add environment teardown and validate home/config directories in tests --- packages/stac_cli/pubspec.lock | 49 +++++++++++++++---- .../test/commands/cli_commands_test.dart | 4 ++ .../stac_cli/test/utils/file_utils_test.dart | 25 +++++++++- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/packages/stac_cli/pubspec.lock b/packages/stac_cli/pubspec.lock index ea1f4bd3..98f343fc 100644 --- a/packages/stac_cli/pubspec.lock +++ b/packages/stac_cli/pubspec.lock @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -241,6 +241,11 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + flutter: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -353,6 +358,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" meta: dependency: transitive description: @@ -449,6 +462,11 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" source_gen: dependency: transitive description: @@ -492,16 +510,18 @@ packages: stac_core: dependency: "direct main" description: - path: "../stac_core" - relative: true - source: path - version: "1.4.0" + name: stac_core + sha256: "855767538be98fb2021ee4d58a85af66e0f40c6317eab94e5a08654c09ce49c0" + url: "https://pub.dev" + source: hosted + version: "1.5.0" stac_logger: - dependency: "direct overridden" + dependency: transitive description: - path: "../stac_logger" - relative: true - source: path + name: stac_logger + sha256: bc3c1cc486d59d2378c1e18bfd9bfa078be564b58d4ae2b3898633c05a02df26 + url: "https://pub.dev" + source: hosted version: "1.1.0" stack_trace: dependency: transitive @@ -583,6 +603,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" vm_service: dependency: transitive description: @@ -649,3 +677,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.10.0 <4.0.0" + flutter: ">=1.17.0" diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index f8c9246b..99b50d2c 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -25,6 +25,10 @@ void main() { runner.addCommand(DeployCommand()); }); + tearDown(() { + configureEnvironment({}); + }); + test('build command has correct name and description', () { final command = runner.commands['build']; expect(command, isNotNull, reason: 'BuildCommand should be registered'); diff --git a/packages/stac_cli/test/utils/file_utils_test.dart b/packages/stac_cli/test/utils/file_utils_test.dart index 76c0cdd1..b7d005e2 100644 --- a/packages/stac_cli/test/utils/file_utils_test.dart +++ b/packages/stac_cli/test/utils/file_utils_test.dart @@ -7,14 +7,35 @@ import 'package:path/path.dart' as path; void main() { group('FileUtils', () { // Basic verification of environment-dependent directory getters. - test('homeDirectory returns a non-empty string on this OS', () { + test('homeDirectory returns a non-empty string on this OS and points to an existing directory', () async { final home = FileUtils.homeDirectory; expect(home, isNotEmpty); + final dir = Directory(home); + expect(await dir.exists(), isTrue, reason: 'Home directory must exist'); + final stat = await dir.stat(); + expect(stat.type, equals(FileSystemEntityType.directory), reason: 'Home directory path must be a directory'); }); - test('configDirectory path is generated', () { + test('configDirectory path is generated and points to a valid directory', () async { final config = FileUtils.configDirectory; expect(config, isNotEmpty); + + final dir = Directory(config); + final originallyExisted = await dir.exists(); + + // Ensure config directory exists (creating it if necessary) + await FileUtils.ensureConfigDirectory(); + + expect(await dir.exists(), isTrue, reason: 'Config directory must exist after ensuring'); + final stat = await dir.stat(); + expect(stat.type, equals(FileSystemEntityType.directory), reason: 'Config directory path must be a directory'); + + // Clean up the created config directory if it didn't exist before the test + if (!originallyExisted && await dir.exists()) { + try { + await dir.delete(recursive: true); + } catch (_) {} + } }); // Integrated test for file system operations using a temporary directory. From 3897043583b39f4d4aac1067e70670005f11dcaa Mon Sep 17 00:00:00 2001 From: chcweblogin Date: Sat, 6 Jun 2026 13:59:17 +0530 Subject: [PATCH 5/7] feat: add Dart/Flutter-native install path for Stac skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add SkillsCommand with 'stac skills add' subcommand - AddCommand fetches repo ZIP, parses skills/catalog.json, and copies skill dirs into .agents/skills/ — no Node/npm required - Prompt users in 'stac init' to optionally install agent skills - Register SkillsCommand in the CLI runner - Add archive: ^4.0.9 dependency for ZIP extraction - Update docs/skills.mdx to show Dart-native path first, keeping npx as an alternative - Add tests for SkillsCommand and AddCommand (7 tests pass) Closes #480 --- docs/skills.mdx | 8 +- packages/stac_cli/bin/stac_cli.dart | 2 + .../lib/src/commands/init_command.dart | 11 ++ .../lib/src/commands/skills/add_command.dart | 147 ++++++++++++++++++ .../lib/src/commands/skills_command.dart | 15 ++ packages/stac_cli/pubspec.lock | 23 ++- packages/stac_cli/pubspec.yaml | 1 + .../test/commands/cli_commands_test.dart | 31 ++++ 8 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 packages/stac_cli/lib/src/commands/skills/add_command.dart create mode 100644 packages/stac_cli/lib/src/commands/skills_command.dart diff --git a/docs/skills.mdx b/docs/skills.mdx index 5fec95c7..6c2b90a1 100644 --- a/docs/skills.mdx +++ b/docs/skills.mdx @@ -7,7 +7,13 @@ Think of Stac Skills as expert assistants that know Stac inside and out. They he ## Installation -Install all Stac skills with one command: +Install all Stac skills natively with the Stac CLI: + +```bash +stac skills add +``` + +Alternatively, you can install them using `npx`: ```bash npx skills add https://github.com/StacDev/stac diff --git a/packages/stac_cli/bin/stac_cli.dart b/packages/stac_cli/bin/stac_cli.dart index aabbf704..ea89ae57 100644 --- a/packages/stac_cli/bin/stac_cli.dart +++ b/packages/stac_cli/bin/stac_cli.dart @@ -9,6 +9,7 @@ import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/project_command.dart'; +import 'package:stac_cli/src/commands/skills_command.dart'; import 'package:stac_cli/src/commands/upgrade_command.dart'; import 'package:stac_cli/src/config/env.dart'; import 'package:stac_cli/src/exceptions/stac_exception.dart'; @@ -67,6 +68,7 @@ void main(List arguments) async { ..addCommand(ProjectCommand()) ..addCommand(BuildCommand()) ..addCommand(DeployCommand()) + ..addCommand(SkillsCommand()) ..addCommand(UpgradeCommand()); // Add global flags diff --git a/packages/stac_cli/lib/src/commands/init_command.dart b/packages/stac_cli/lib/src/commands/init_command.dart index 3b40a19e..09f36df4 100644 --- a/packages/stac_cli/lib/src/commands/init_command.dart +++ b/packages/stac_cli/lib/src/commands/init_command.dart @@ -8,6 +8,7 @@ import '../services/project_service.dart'; import '../utils/console_logger.dart'; import '../utils/file_utils.dart'; import 'base_command.dart'; +import 'skills/add_command.dart'; /// Command for initializing a Stac project from cloud projects class InitCommand extends BaseCommand { @@ -84,6 +85,16 @@ class InitCommand extends BaseCommand { // Create default_stac_options.dart configuration file await _createStacConfigFile(targetDir, project); + // Ask to install skills + final shouldInstallSkills = Confirm( + prompt: 'Install Stac agent skills? (Recommended for AI-assisted development)', + defaultValue: true, + ).interact(); + if (shouldInstallSkills) { + ConsoleLogger.info('Installing skills...'); + await AddCommand().execute(); + } + ConsoleLogger.success('✓ Project initialized successfully!'); ConsoleLogger.info('Next steps:'); ConsoleLogger.info(' 1. Add your Stac widgets definitions to /stac'); diff --git a/packages/stac_cli/lib/src/commands/skills/add_command.dart b/packages/stac_cli/lib/src/commands/skills/add_command.dart new file mode 100644 index 00000000..3ab02b08 --- /dev/null +++ b/packages/stac_cli/lib/src/commands/skills/add_command.dart @@ -0,0 +1,147 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:archive/archive_io.dart'; +import 'package:dio/dio.dart'; +import 'package:path/path.dart' as path; + +import '../../utils/console_logger.dart'; +import '../base_command.dart'; + +/// Command to add Stac AI agent skills +class AddCommand extends BaseCommand { + @override + String get name => 'add'; + + @override + String get description => 'Add Stac AI agent skills to your project'; + + @override + bool get requiresAuth => false; + + AddCommand() { + // Optionally accept a repository URL. + } + + @override + Future execute() async { + String repoUrl = 'https://github.com/StacDev/stac'; + + if (argResults?.rest.isNotEmpty == true) { + repoUrl = argResults!.rest.first; + } + + if (!repoUrl.contains('github.com')) { + ConsoleLogger.error('Currently only github.com URLs are supported.'); + return 1; + } + + // Extract owner/repo + final uri = Uri.parse(repoUrl); + final segments = uri.pathSegments; + if (segments.length < 2) { + ConsoleLogger.error('Invalid GitHub URL format.'); + return 1; + } + + final owner = segments[0]; + final repo = segments[1].replaceAll('.git', ''); + + final zipUrl = 'https://github.com/$owner/$repo/archive/HEAD.zip'; + + ConsoleLogger.info('Fetching skills from $repoUrl...'); + + try { + final dio = Dio(); + final tempDir = await Directory.systemTemp.createTemp('stac_skills_'); + final zipFile = File(path.join(tempDir.path, 'repo.zip')); + + await dio.download(zipUrl, zipFile.path); + + // Extract ZIP + final archive = ZipDecoder().decodeBytes(zipFile.readAsBytesSync()); + final extractDir = Directory(path.join(tempDir.path, 'extracted')); + extractArchiveToDisk(archive, extractDir.path); + + // Find skills/catalog.json + // The extracted folder usually has a root folder named - + final rootDirs = extractDir.listSync().whereType().toList(); + if (rootDirs.isEmpty) { + ConsoleLogger.error('Empty repository archive.'); + return 1; + } + + final repoRoot = rootDirs.first; + ConsoleLogger.info('Extracted root: ${repoRoot.path}'); + + final catalogFile = File(path.join(repoRoot.path, 'skills', 'catalog.json')); + ConsoleLogger.info('Looking for catalog at: ${catalogFile.path}'); + + if (!await catalogFile.exists()) { + ConsoleLogger.error('skills/catalog.json not found in repository.'); + + ConsoleLogger.info('Contents of extracted:'); + for (var e in extractDir.listSync(recursive: true)) { + ConsoleLogger.info(e.path); + } + + return 1; + } + + // Parse catalog.json + final catalogContent = await catalogFile.readAsString(); + final List catalog = jsonDecode(catalogContent); + + final targetAgentsDir = Directory(path.join(Directory.current.path, '.agents', 'skills')); + if (!await targetAgentsDir.exists()) { + await targetAgentsDir.create(recursive: true); + } + + int installedCount = 0; + for (final skill in catalog) { + final skillName = skill['name']; + final skillPath = skill['path']; + + if (skillName == null || skillPath == null) continue; + + final sourceSkillDir = Directory(path.join(repoRoot.path, skillPath)); + if (!await sourceSkillDir.exists()) { + ConsoleLogger.warning('Skill directory $skillPath not found, skipping.'); + continue; + } + + final targetSkillDir = Directory(path.join(targetAgentsDir.path, skillName)); + if (await targetSkillDir.exists()) { + await targetSkillDir.delete(recursive: true); + } + await targetSkillDir.create(recursive: true); + + // Copy directory contents + await _copyDirectory(sourceSkillDir, targetSkillDir); + ConsoleLogger.success('✓ $skillName (copied)'); + installedCount++; + } + + ConsoleLogger.success('Installed $installedCount skills to .agents/skills'); + + // Cleanup + await tempDir.delete(recursive: true); + return 0; + } catch (e) { + ConsoleLogger.error('Failed to install skills: $e'); + return 1; + } + } + + Future _copyDirectory(Directory source, Directory destination) async { + await for (var entity in source.list(recursive: false)) { + if (entity is Directory) { + var newDirectory = Directory(path.join(destination.path, path.basename(entity.path))); + await newDirectory.create(); + await _copyDirectory(entity.absolute, newDirectory); + } else if (entity is File) { + await entity.copy(path.join(destination.path, path.basename(entity.path))); + } + } + } +} diff --git a/packages/stac_cli/lib/src/commands/skills_command.dart b/packages/stac_cli/lib/src/commands/skills_command.dart new file mode 100644 index 00000000..caabefbf --- /dev/null +++ b/packages/stac_cli/lib/src/commands/skills_command.dart @@ -0,0 +1,15 @@ +import 'package:args/command_runner.dart'; +import 'skills/add_command.dart'; + +/// Command for managing Stac AI agent skills +class SkillsCommand extends Command { + @override + String get name => 'skills'; + + @override + String get description => 'Manage Stac AI agent skills'; + + SkillsCommand() { + addSubcommand(AddCommand()); + } +} diff --git a/packages/stac_cli/pubspec.lock b/packages/stac_cli/pubspec.lock index abef6e2b..a4d3c1c4 100644 --- a/packages/stac_cli/pubspec.lock +++ b/packages/stac_cli/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" + archive: + dependency: "direct main" + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.dev" + source: hosted + version: "4.0.9" args: dependency: "direct main" description: @@ -414,6 +422,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.2" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" pub_semver: dependency: transitive description: @@ -510,9 +526,10 @@ packages: stac_core: dependency: "direct main" description: - path: "../stac_core" - relative: true - source: path + name: stac_core + sha256: "855767538be98fb2021ee4d58a85af66e0f40c6317eab94e5a08654c09ce49c0" + url: "https://pub.dev" + source: hosted version: "1.5.0" stac_logger: dependency: transitive diff --git a/packages/stac_cli/pubspec.yaml b/packages/stac_cli/pubspec.yaml index 89c523dd..1e17fb04 100644 --- a/packages/stac_cli/pubspec.yaml +++ b/packages/stac_cli/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: json_annotation: ^4.11.0 dotenv: ^4.2.0 cryptography: ^2.9.0 + archive: ^4.0.9 # Executables that can be run globally executables: diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index 99b50d2c..7f8b2a68 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -3,6 +3,8 @@ import 'package:args/command_runner.dart'; import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; +import 'package:stac_cli/src/commands/skills_command.dart'; +import 'package:stac_cli/src/commands/skills/add_command.dart'; import 'package:stac_cli/src/config/env.dart'; /// Test suite for verifying core Stac CLI commands. @@ -23,6 +25,7 @@ void main() { runner.addCommand(BuildCommand()); runner.addCommand(InitCommand()); runner.addCommand(DeployCommand()); + runner.addCommand(SkillsCommand()); }); tearDown(() { @@ -49,5 +52,33 @@ void main() { expect(command!.name, equals('deploy')); expect(command.description, isNotEmpty); }); + + test('skills command has correct name and description', () { + final command = runner.commands['skills']; + expect(command, isNotNull, reason: 'SkillsCommand should be registered'); + expect(command!.name, equals('skills')); + expect(command.description, isNotEmpty); + }); + + test('skills add subcommand is registered', () { + final skillsCommand = runner.commands['skills'] as SkillsCommand; + expect(skillsCommand.subcommands['add'], isNotNull, + reason: 'AddCommand should be registered as a subcommand of skills'); + expect(skillsCommand.subcommands['add']!.name, equals('add')); + expect(skillsCommand.subcommands['add']!.description, isNotEmpty); + }); + }); + + group('AddCommand', () { + test('has correct name and description', () { + final cmd = AddCommand(); + expect(cmd.name, equals('add')); + expect(cmd.description, isNotEmpty); + }); + + test('does not require auth', () { + final cmd = AddCommand(); + expect(cmd.requiresAuth, isFalse); + }); }); } From 0df44b91a0bcaf5286d092f7c0b5a3fdee64cec4 Mon Sep 17 00:00:00 2001 From: Pratikdate Date: Sat, 6 Jun 2026 14:39:49 +0530 Subject: [PATCH 6/7] Revert "feat: add Dart/Flutter-native install path for Stac skills" This reverts commit 3897043583b39f4d4aac1067e70670005f11dcaa. --- docs/skills.mdx | 8 +- packages/stac_cli/bin/stac_cli.dart | 2 - .../lib/src/commands/init_command.dart | 11 -- .../lib/src/commands/skills/add_command.dart | 147 ------------------ .../lib/src/commands/skills_command.dart | 15 -- packages/stac_cli/pubspec.lock | 23 +-- packages/stac_cli/pubspec.yaml | 1 - .../test/commands/cli_commands_test.dart | 31 ---- 8 files changed, 4 insertions(+), 234 deletions(-) delete mode 100644 packages/stac_cli/lib/src/commands/skills/add_command.dart delete mode 100644 packages/stac_cli/lib/src/commands/skills_command.dart diff --git a/docs/skills.mdx b/docs/skills.mdx index 6c2b90a1..5fec95c7 100644 --- a/docs/skills.mdx +++ b/docs/skills.mdx @@ -7,13 +7,7 @@ Think of Stac Skills as expert assistants that know Stac inside and out. They he ## Installation -Install all Stac skills natively with the Stac CLI: - -```bash -stac skills add -``` - -Alternatively, you can install them using `npx`: +Install all Stac skills with one command: ```bash npx skills add https://github.com/StacDev/stac diff --git a/packages/stac_cli/bin/stac_cli.dart b/packages/stac_cli/bin/stac_cli.dart index ea89ae57..aabbf704 100644 --- a/packages/stac_cli/bin/stac_cli.dart +++ b/packages/stac_cli/bin/stac_cli.dart @@ -9,7 +9,6 @@ import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/project_command.dart'; -import 'package:stac_cli/src/commands/skills_command.dart'; import 'package:stac_cli/src/commands/upgrade_command.dart'; import 'package:stac_cli/src/config/env.dart'; import 'package:stac_cli/src/exceptions/stac_exception.dart'; @@ -68,7 +67,6 @@ void main(List arguments) async { ..addCommand(ProjectCommand()) ..addCommand(BuildCommand()) ..addCommand(DeployCommand()) - ..addCommand(SkillsCommand()) ..addCommand(UpgradeCommand()); // Add global flags diff --git a/packages/stac_cli/lib/src/commands/init_command.dart b/packages/stac_cli/lib/src/commands/init_command.dart index 09f36df4..3b40a19e 100644 --- a/packages/stac_cli/lib/src/commands/init_command.dart +++ b/packages/stac_cli/lib/src/commands/init_command.dart @@ -8,7 +8,6 @@ import '../services/project_service.dart'; import '../utils/console_logger.dart'; import '../utils/file_utils.dart'; import 'base_command.dart'; -import 'skills/add_command.dart'; /// Command for initializing a Stac project from cloud projects class InitCommand extends BaseCommand { @@ -85,16 +84,6 @@ class InitCommand extends BaseCommand { // Create default_stac_options.dart configuration file await _createStacConfigFile(targetDir, project); - // Ask to install skills - final shouldInstallSkills = Confirm( - prompt: 'Install Stac agent skills? (Recommended for AI-assisted development)', - defaultValue: true, - ).interact(); - if (shouldInstallSkills) { - ConsoleLogger.info('Installing skills...'); - await AddCommand().execute(); - } - ConsoleLogger.success('✓ Project initialized successfully!'); ConsoleLogger.info('Next steps:'); ConsoleLogger.info(' 1. Add your Stac widgets definitions to /stac'); diff --git a/packages/stac_cli/lib/src/commands/skills/add_command.dart b/packages/stac_cli/lib/src/commands/skills/add_command.dart deleted file mode 100644 index 3ab02b08..00000000 --- a/packages/stac_cli/lib/src/commands/skills/add_command.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:archive/archive_io.dart'; -import 'package:dio/dio.dart'; -import 'package:path/path.dart' as path; - -import '../../utils/console_logger.dart'; -import '../base_command.dart'; - -/// Command to add Stac AI agent skills -class AddCommand extends BaseCommand { - @override - String get name => 'add'; - - @override - String get description => 'Add Stac AI agent skills to your project'; - - @override - bool get requiresAuth => false; - - AddCommand() { - // Optionally accept a repository URL. - } - - @override - Future execute() async { - String repoUrl = 'https://github.com/StacDev/stac'; - - if (argResults?.rest.isNotEmpty == true) { - repoUrl = argResults!.rest.first; - } - - if (!repoUrl.contains('github.com')) { - ConsoleLogger.error('Currently only github.com URLs are supported.'); - return 1; - } - - // Extract owner/repo - final uri = Uri.parse(repoUrl); - final segments = uri.pathSegments; - if (segments.length < 2) { - ConsoleLogger.error('Invalid GitHub URL format.'); - return 1; - } - - final owner = segments[0]; - final repo = segments[1].replaceAll('.git', ''); - - final zipUrl = 'https://github.com/$owner/$repo/archive/HEAD.zip'; - - ConsoleLogger.info('Fetching skills from $repoUrl...'); - - try { - final dio = Dio(); - final tempDir = await Directory.systemTemp.createTemp('stac_skills_'); - final zipFile = File(path.join(tempDir.path, 'repo.zip')); - - await dio.download(zipUrl, zipFile.path); - - // Extract ZIP - final archive = ZipDecoder().decodeBytes(zipFile.readAsBytesSync()); - final extractDir = Directory(path.join(tempDir.path, 'extracted')); - extractArchiveToDisk(archive, extractDir.path); - - // Find skills/catalog.json - // The extracted folder usually has a root folder named - - final rootDirs = extractDir.listSync().whereType().toList(); - if (rootDirs.isEmpty) { - ConsoleLogger.error('Empty repository archive.'); - return 1; - } - - final repoRoot = rootDirs.first; - ConsoleLogger.info('Extracted root: ${repoRoot.path}'); - - final catalogFile = File(path.join(repoRoot.path, 'skills', 'catalog.json')); - ConsoleLogger.info('Looking for catalog at: ${catalogFile.path}'); - - if (!await catalogFile.exists()) { - ConsoleLogger.error('skills/catalog.json not found in repository.'); - - ConsoleLogger.info('Contents of extracted:'); - for (var e in extractDir.listSync(recursive: true)) { - ConsoleLogger.info(e.path); - } - - return 1; - } - - // Parse catalog.json - final catalogContent = await catalogFile.readAsString(); - final List catalog = jsonDecode(catalogContent); - - final targetAgentsDir = Directory(path.join(Directory.current.path, '.agents', 'skills')); - if (!await targetAgentsDir.exists()) { - await targetAgentsDir.create(recursive: true); - } - - int installedCount = 0; - for (final skill in catalog) { - final skillName = skill['name']; - final skillPath = skill['path']; - - if (skillName == null || skillPath == null) continue; - - final sourceSkillDir = Directory(path.join(repoRoot.path, skillPath)); - if (!await sourceSkillDir.exists()) { - ConsoleLogger.warning('Skill directory $skillPath not found, skipping.'); - continue; - } - - final targetSkillDir = Directory(path.join(targetAgentsDir.path, skillName)); - if (await targetSkillDir.exists()) { - await targetSkillDir.delete(recursive: true); - } - await targetSkillDir.create(recursive: true); - - // Copy directory contents - await _copyDirectory(sourceSkillDir, targetSkillDir); - ConsoleLogger.success('✓ $skillName (copied)'); - installedCount++; - } - - ConsoleLogger.success('Installed $installedCount skills to .agents/skills'); - - // Cleanup - await tempDir.delete(recursive: true); - return 0; - } catch (e) { - ConsoleLogger.error('Failed to install skills: $e'); - return 1; - } - } - - Future _copyDirectory(Directory source, Directory destination) async { - await for (var entity in source.list(recursive: false)) { - if (entity is Directory) { - var newDirectory = Directory(path.join(destination.path, path.basename(entity.path))); - await newDirectory.create(); - await _copyDirectory(entity.absolute, newDirectory); - } else if (entity is File) { - await entity.copy(path.join(destination.path, path.basename(entity.path))); - } - } - } -} diff --git a/packages/stac_cli/lib/src/commands/skills_command.dart b/packages/stac_cli/lib/src/commands/skills_command.dart deleted file mode 100644 index caabefbf..00000000 --- a/packages/stac_cli/lib/src/commands/skills_command.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:args/command_runner.dart'; -import 'skills/add_command.dart'; - -/// Command for managing Stac AI agent skills -class SkillsCommand extends Command { - @override - String get name => 'skills'; - - @override - String get description => 'Manage Stac AI agent skills'; - - SkillsCommand() { - addSubcommand(AddCommand()); - } -} diff --git a/packages/stac_cli/pubspec.lock b/packages/stac_cli/pubspec.lock index a4d3c1c4..abef6e2b 100644 --- a/packages/stac_cli/pubspec.lock +++ b/packages/stac_cli/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" - archive: - dependency: "direct main" - description: - name: archive - sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff - url: "https://pub.dev" - source: hosted - version: "4.0.9" args: dependency: "direct main" description: @@ -422,14 +414,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.2" - posix: - dependency: transitive - description: - name: posix - sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" - url: "https://pub.dev" - source: hosted - version: "6.5.0" pub_semver: dependency: transitive description: @@ -526,10 +510,9 @@ packages: stac_core: dependency: "direct main" description: - name: stac_core - sha256: "855767538be98fb2021ee4d58a85af66e0f40c6317eab94e5a08654c09ce49c0" - url: "https://pub.dev" - source: hosted + path: "../stac_core" + relative: true + source: path version: "1.5.0" stac_logger: dependency: transitive diff --git a/packages/stac_cli/pubspec.yaml b/packages/stac_cli/pubspec.yaml index 1e17fb04..89c523dd 100644 --- a/packages/stac_cli/pubspec.yaml +++ b/packages/stac_cli/pubspec.yaml @@ -18,7 +18,6 @@ dependencies: json_annotation: ^4.11.0 dotenv: ^4.2.0 cryptography: ^2.9.0 - archive: ^4.0.9 # Executables that can be run globally executables: diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index 7f8b2a68..99b50d2c 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -3,8 +3,6 @@ import 'package:args/command_runner.dart'; import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; -import 'package:stac_cli/src/commands/skills_command.dart'; -import 'package:stac_cli/src/commands/skills/add_command.dart'; import 'package:stac_cli/src/config/env.dart'; /// Test suite for verifying core Stac CLI commands. @@ -25,7 +23,6 @@ void main() { runner.addCommand(BuildCommand()); runner.addCommand(InitCommand()); runner.addCommand(DeployCommand()); - runner.addCommand(SkillsCommand()); }); tearDown(() { @@ -52,33 +49,5 @@ void main() { expect(command!.name, equals('deploy')); expect(command.description, isNotEmpty); }); - - test('skills command has correct name and description', () { - final command = runner.commands['skills']; - expect(command, isNotNull, reason: 'SkillsCommand should be registered'); - expect(command!.name, equals('skills')); - expect(command.description, isNotEmpty); - }); - - test('skills add subcommand is registered', () { - final skillsCommand = runner.commands['skills'] as SkillsCommand; - expect(skillsCommand.subcommands['add'], isNotNull, - reason: 'AddCommand should be registered as a subcommand of skills'); - expect(skillsCommand.subcommands['add']!.name, equals('add')); - expect(skillsCommand.subcommands['add']!.description, isNotEmpty); - }); - }); - - group('AddCommand', () { - test('has correct name and description', () { - final cmd = AddCommand(); - expect(cmd.name, equals('add')); - expect(cmd.description, isNotEmpty); - }); - - test('does not require auth', () { - final cmd = AddCommand(); - expect(cmd.requiresAuth, isFalse); - }); }); } From 03501b8ef2291195dbf76437903c9e0ea2445265 Mon Sep 17 00:00:00 2001 From: Pratikdate Date: Sat, 6 Jun 2026 14:39:49 +0530 Subject: [PATCH 7/7] revert: temporary native-skills install changes from dev branch Reverting the accidental push of the native-skills feature to the dev branch. This reverts commit 3897043583b39f4d4aac1067e70670005f11dcaa. --- docs/skills.mdx | 8 +- packages/stac_cli/bin/stac_cli.dart | 2 - .../lib/src/commands/init_command.dart | 11 -- .../lib/src/commands/skills/add_command.dart | 147 ------------------ .../lib/src/commands/skills_command.dart | 15 -- packages/stac_cli/pubspec.lock | 23 +-- packages/stac_cli/pubspec.yaml | 1 - .../test/commands/cli_commands_test.dart | 31 ---- 8 files changed, 4 insertions(+), 234 deletions(-) delete mode 100644 packages/stac_cli/lib/src/commands/skills/add_command.dart delete mode 100644 packages/stac_cli/lib/src/commands/skills_command.dart diff --git a/docs/skills.mdx b/docs/skills.mdx index 6c2b90a1..5fec95c7 100644 --- a/docs/skills.mdx +++ b/docs/skills.mdx @@ -7,13 +7,7 @@ Think of Stac Skills as expert assistants that know Stac inside and out. They he ## Installation -Install all Stac skills natively with the Stac CLI: - -```bash -stac skills add -``` - -Alternatively, you can install them using `npx`: +Install all Stac skills with one command: ```bash npx skills add https://github.com/StacDev/stac diff --git a/packages/stac_cli/bin/stac_cli.dart b/packages/stac_cli/bin/stac_cli.dart index ea89ae57..aabbf704 100644 --- a/packages/stac_cli/bin/stac_cli.dart +++ b/packages/stac_cli/bin/stac_cli.dart @@ -9,7 +9,6 @@ import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/project_command.dart'; -import 'package:stac_cli/src/commands/skills_command.dart'; import 'package:stac_cli/src/commands/upgrade_command.dart'; import 'package:stac_cli/src/config/env.dart'; import 'package:stac_cli/src/exceptions/stac_exception.dart'; @@ -68,7 +67,6 @@ void main(List arguments) async { ..addCommand(ProjectCommand()) ..addCommand(BuildCommand()) ..addCommand(DeployCommand()) - ..addCommand(SkillsCommand()) ..addCommand(UpgradeCommand()); // Add global flags diff --git a/packages/stac_cli/lib/src/commands/init_command.dart b/packages/stac_cli/lib/src/commands/init_command.dart index 09f36df4..3b40a19e 100644 --- a/packages/stac_cli/lib/src/commands/init_command.dart +++ b/packages/stac_cli/lib/src/commands/init_command.dart @@ -8,7 +8,6 @@ import '../services/project_service.dart'; import '../utils/console_logger.dart'; import '../utils/file_utils.dart'; import 'base_command.dart'; -import 'skills/add_command.dart'; /// Command for initializing a Stac project from cloud projects class InitCommand extends BaseCommand { @@ -85,16 +84,6 @@ class InitCommand extends BaseCommand { // Create default_stac_options.dart configuration file await _createStacConfigFile(targetDir, project); - // Ask to install skills - final shouldInstallSkills = Confirm( - prompt: 'Install Stac agent skills? (Recommended for AI-assisted development)', - defaultValue: true, - ).interact(); - if (shouldInstallSkills) { - ConsoleLogger.info('Installing skills...'); - await AddCommand().execute(); - } - ConsoleLogger.success('✓ Project initialized successfully!'); ConsoleLogger.info('Next steps:'); ConsoleLogger.info(' 1. Add your Stac widgets definitions to /stac'); diff --git a/packages/stac_cli/lib/src/commands/skills/add_command.dart b/packages/stac_cli/lib/src/commands/skills/add_command.dart deleted file mode 100644 index 3ab02b08..00000000 --- a/packages/stac_cli/lib/src/commands/skills/add_command.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:archive/archive_io.dart'; -import 'package:dio/dio.dart'; -import 'package:path/path.dart' as path; - -import '../../utils/console_logger.dart'; -import '../base_command.dart'; - -/// Command to add Stac AI agent skills -class AddCommand extends BaseCommand { - @override - String get name => 'add'; - - @override - String get description => 'Add Stac AI agent skills to your project'; - - @override - bool get requiresAuth => false; - - AddCommand() { - // Optionally accept a repository URL. - } - - @override - Future execute() async { - String repoUrl = 'https://github.com/StacDev/stac'; - - if (argResults?.rest.isNotEmpty == true) { - repoUrl = argResults!.rest.first; - } - - if (!repoUrl.contains('github.com')) { - ConsoleLogger.error('Currently only github.com URLs are supported.'); - return 1; - } - - // Extract owner/repo - final uri = Uri.parse(repoUrl); - final segments = uri.pathSegments; - if (segments.length < 2) { - ConsoleLogger.error('Invalid GitHub URL format.'); - return 1; - } - - final owner = segments[0]; - final repo = segments[1].replaceAll('.git', ''); - - final zipUrl = 'https://github.com/$owner/$repo/archive/HEAD.zip'; - - ConsoleLogger.info('Fetching skills from $repoUrl...'); - - try { - final dio = Dio(); - final tempDir = await Directory.systemTemp.createTemp('stac_skills_'); - final zipFile = File(path.join(tempDir.path, 'repo.zip')); - - await dio.download(zipUrl, zipFile.path); - - // Extract ZIP - final archive = ZipDecoder().decodeBytes(zipFile.readAsBytesSync()); - final extractDir = Directory(path.join(tempDir.path, 'extracted')); - extractArchiveToDisk(archive, extractDir.path); - - // Find skills/catalog.json - // The extracted folder usually has a root folder named - - final rootDirs = extractDir.listSync().whereType().toList(); - if (rootDirs.isEmpty) { - ConsoleLogger.error('Empty repository archive.'); - return 1; - } - - final repoRoot = rootDirs.first; - ConsoleLogger.info('Extracted root: ${repoRoot.path}'); - - final catalogFile = File(path.join(repoRoot.path, 'skills', 'catalog.json')); - ConsoleLogger.info('Looking for catalog at: ${catalogFile.path}'); - - if (!await catalogFile.exists()) { - ConsoleLogger.error('skills/catalog.json not found in repository.'); - - ConsoleLogger.info('Contents of extracted:'); - for (var e in extractDir.listSync(recursive: true)) { - ConsoleLogger.info(e.path); - } - - return 1; - } - - // Parse catalog.json - final catalogContent = await catalogFile.readAsString(); - final List catalog = jsonDecode(catalogContent); - - final targetAgentsDir = Directory(path.join(Directory.current.path, '.agents', 'skills')); - if (!await targetAgentsDir.exists()) { - await targetAgentsDir.create(recursive: true); - } - - int installedCount = 0; - for (final skill in catalog) { - final skillName = skill['name']; - final skillPath = skill['path']; - - if (skillName == null || skillPath == null) continue; - - final sourceSkillDir = Directory(path.join(repoRoot.path, skillPath)); - if (!await sourceSkillDir.exists()) { - ConsoleLogger.warning('Skill directory $skillPath not found, skipping.'); - continue; - } - - final targetSkillDir = Directory(path.join(targetAgentsDir.path, skillName)); - if (await targetSkillDir.exists()) { - await targetSkillDir.delete(recursive: true); - } - await targetSkillDir.create(recursive: true); - - // Copy directory contents - await _copyDirectory(sourceSkillDir, targetSkillDir); - ConsoleLogger.success('✓ $skillName (copied)'); - installedCount++; - } - - ConsoleLogger.success('Installed $installedCount skills to .agents/skills'); - - // Cleanup - await tempDir.delete(recursive: true); - return 0; - } catch (e) { - ConsoleLogger.error('Failed to install skills: $e'); - return 1; - } - } - - Future _copyDirectory(Directory source, Directory destination) async { - await for (var entity in source.list(recursive: false)) { - if (entity is Directory) { - var newDirectory = Directory(path.join(destination.path, path.basename(entity.path))); - await newDirectory.create(); - await _copyDirectory(entity.absolute, newDirectory); - } else if (entity is File) { - await entity.copy(path.join(destination.path, path.basename(entity.path))); - } - } - } -} diff --git a/packages/stac_cli/lib/src/commands/skills_command.dart b/packages/stac_cli/lib/src/commands/skills_command.dart deleted file mode 100644 index caabefbf..00000000 --- a/packages/stac_cli/lib/src/commands/skills_command.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:args/command_runner.dart'; -import 'skills/add_command.dart'; - -/// Command for managing Stac AI agent skills -class SkillsCommand extends Command { - @override - String get name => 'skills'; - - @override - String get description => 'Manage Stac AI agent skills'; - - SkillsCommand() { - addSubcommand(AddCommand()); - } -} diff --git a/packages/stac_cli/pubspec.lock b/packages/stac_cli/pubspec.lock index a4d3c1c4..abef6e2b 100644 --- a/packages/stac_cli/pubspec.lock +++ b/packages/stac_cli/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" - archive: - dependency: "direct main" - description: - name: archive - sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff - url: "https://pub.dev" - source: hosted - version: "4.0.9" args: dependency: "direct main" description: @@ -422,14 +414,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.2" - posix: - dependency: transitive - description: - name: posix - sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" - url: "https://pub.dev" - source: hosted - version: "6.5.0" pub_semver: dependency: transitive description: @@ -526,10 +510,9 @@ packages: stac_core: dependency: "direct main" description: - name: stac_core - sha256: "855767538be98fb2021ee4d58a85af66e0f40c6317eab94e5a08654c09ce49c0" - url: "https://pub.dev" - source: hosted + path: "../stac_core" + relative: true + source: path version: "1.5.0" stac_logger: dependency: transitive diff --git a/packages/stac_cli/pubspec.yaml b/packages/stac_cli/pubspec.yaml index 1e17fb04..89c523dd 100644 --- a/packages/stac_cli/pubspec.yaml +++ b/packages/stac_cli/pubspec.yaml @@ -18,7 +18,6 @@ dependencies: json_annotation: ^4.11.0 dotenv: ^4.2.0 cryptography: ^2.9.0 - archive: ^4.0.9 # Executables that can be run globally executables: diff --git a/packages/stac_cli/test/commands/cli_commands_test.dart b/packages/stac_cli/test/commands/cli_commands_test.dart index 7f8b2a68..99b50d2c 100644 --- a/packages/stac_cli/test/commands/cli_commands_test.dart +++ b/packages/stac_cli/test/commands/cli_commands_test.dart @@ -3,8 +3,6 @@ import 'package:args/command_runner.dart'; import 'package:stac_cli/src/commands/build_command.dart'; import 'package:stac_cli/src/commands/init_command.dart'; import 'package:stac_cli/src/commands/deploy_command.dart'; -import 'package:stac_cli/src/commands/skills_command.dart'; -import 'package:stac_cli/src/commands/skills/add_command.dart'; import 'package:stac_cli/src/config/env.dart'; /// Test suite for verifying core Stac CLI commands. @@ -25,7 +23,6 @@ void main() { runner.addCommand(BuildCommand()); runner.addCommand(InitCommand()); runner.addCommand(DeployCommand()); - runner.addCommand(SkillsCommand()); }); tearDown(() { @@ -52,33 +49,5 @@ void main() { expect(command!.name, equals('deploy')); expect(command.description, isNotEmpty); }); - - test('skills command has correct name and description', () { - final command = runner.commands['skills']; - expect(command, isNotNull, reason: 'SkillsCommand should be registered'); - expect(command!.name, equals('skills')); - expect(command.description, isNotEmpty); - }); - - test('skills add subcommand is registered', () { - final skillsCommand = runner.commands['skills'] as SkillsCommand; - expect(skillsCommand.subcommands['add'], isNotNull, - reason: 'AddCommand should be registered as a subcommand of skills'); - expect(skillsCommand.subcommands['add']!.name, equals('add')); - expect(skillsCommand.subcommands['add']!.description, isNotEmpty); - }); - }); - - group('AddCommand', () { - test('has correct name and description', () { - final cmd = AddCommand(); - expect(cmd.name, equals('add')); - expect(cmd.description, isNotEmpty); - }); - - test('does not require auth', () { - final cmd = AddCommand(); - expect(cmd.requiresAuth, isFalse); - }); }); }