saia_skills 0.1.1 copy "saia_skills: ^0.1.1" to clipboard
saia_skills: ^0.1.1 copied to clipboard

Skill catalog for SAIA agents. Bilingual markdown skills with a read API and no inference engine attached.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:saia_skills/saia_skills.dart';

/// Demo del catálogo.
///
/// Además de mostrar la API, esta app comprueba lo que de verdad se rompía:
/// consumido desde fuera, los assets viven bajo
/// `packages/saia_skills/assets/`, no bajo `assets/`. Si el prefijo se
/// resolviera mal, aquí saldría una lista vacía en vez de un fallo.
void main() => runApp(const SaiaSkillsDemo());

class SaiaSkillsDemo extends StatelessWidget {
  const SaiaSkillsDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'saia_skills',
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFFB07D3A),
        useMaterial3: true,
      ),
      home: const CatalogPage(),
    );
  }
}

class CatalogPage extends StatefulWidget {
  const CatalogPage({super.key});

  @override
  State<CatalogPage> createState() => _CatalogPageState();
}

class _CatalogPageState extends State<CatalogPage> {
  String _idioma = 'es';
  String _consulta = '';

  /// Con búsqueda vacía se pide el catálogo agrupado; con texto, el
  /// resultado plano de `search`. Son las dos formas normales de leerlo.
  Future<Map<String, List<SaiaSkill>>> _cargar() async {
    if (_consulta.trim().isEmpty) {
      return SaiaSkillCatalog.byCategory(preferLanguage: _idioma);
    }
    final encontradas =
        await SaiaSkillCatalog.search(_consulta, preferLanguage: _idioma);
    final out = <String, List<SaiaSkill>>{};
    for (final s in encontradas) {
      out.putIfAbsent(s.category, () => []).add(s);
    }
    return out;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('saia_skills'),
        actions: [
          // El catálogo es bilingüe y `all()` devuelve una entrada por
          // habilidad, no una por idioma.
          Padding(
            padding: const EdgeInsets.only(right: 8),
            child: SegmentedButton<String>(
              segments: const [
                ButtonSegment(value: 'es', label: Text('ES')),
                ButtonSegment(value: 'en', label: Text('EN')),
              ],
              selected: {_idioma},
              onSelectionChanged: (s) => setState(() => _idioma = s.first),
            ),
          ),
        ],
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(64),
          child: Padding(
            padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
            child: TextField(
              decoration: const InputDecoration(
                prefixIcon: Icon(Icons.search),
                hintText: 'Buscar — «negociacion precios» encuentra el id',
                border: OutlineInputBorder(),
                isDense: true,
              ),
              onChanged: (v) => setState(() => _consulta = v),
            ),
          ),
        ),
      ),
      body: FutureBuilder<Map<String, List<SaiaSkill>>>(
        future: _cargar(),
        builder: (context, snap) {
          if (snap.connectionState != ConnectionState.done) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snap.hasError) {
            return _Aviso('No se pudo leer el catálogo: ${snap.error}');
          }

          final porCategoria = snap.data ?? const {};
          if (porCategoria.isEmpty) {
            return const _Aviso('Sin resultados.');
          }

          final categorias = porCategoria.keys.toList()..sort();
          return ListView.builder(
            itemCount: categorias.length,
            itemBuilder: (context, i) {
              final cat = categorias[i];
              final skills = porCategoria[cat]!;
              return ExpansionTile(
                title: Text(cat),
                subtitle: Text('${skills.length}'),
                children: [
                  for (final s in skills)
                    ListTile(
                      title: Text(s.displayName),
                      dense: true,
                      onTap: () => Navigator.of(context).push(
                        MaterialPageRoute<void>(
                          builder: (_) => SkillPage(skill: s),
                        ),
                      ),
                    ),
                ],
              );
            },
          );
        },
      ),
    );
  }
}

/// Contenido de una habilidad, tal cual viene del markdown.
class SkillPage extends StatelessWidget {
  const SkillPage({required this.skill, super.key});

  final SaiaSkill skill;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(skill.displayName)),
      body: FutureBuilder<String>(
        future: skill.load(),
        builder: (context, snap) {
          if (snap.connectionState != ConnectionState.done) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snap.hasError) {
            return _Aviso('No se pudo cargar: ${snap.error}');
          }
          return SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: SelectableText(
              snap.data ?? '',
              style: const TextStyle(fontFamily: 'monospace', height: 1.5),
            ),
          );
        },
      ),
    );
  }
}

class _Aviso extends StatelessWidget {
  const _Aviso(this.texto);

  final String texto;

  @override
  Widget build(BuildContext context) => Center(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Text(texto, textAlign: TextAlign.center),
        ),
      );
}
0
likes
160
points
12
downloads

Documentation

API reference

Publisher

verified publisheropenneom.dev

Weekly Downloads

Skill catalog for SAIA agents. Bilingual markdown skills with a read API and no inference engine attached.

Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on saia_skills