smartbiblia 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ wheels/
6
+ *.egg-info
7
+ **/*.lock
8
+
9
+ # Jupyter Notebook checkpoints
10
+ .ipynb_checkpoints
11
+
12
+ # VS Code settings
13
+ .vscode/
14
+
15
+ # Pytest cache
16
+ .pytest_cache/
17
+
18
+ # Virtual environments
19
+ .venv
20
+
21
+ # Environment files (all locations)
22
+ .env
23
+ **/.env
24
+ .env.*
25
+ **/.env.*
26
+
27
+ # Private
28
+ outputs-examples/
29
+ **/CHANGELOG.json
30
+ **/*.zip
31
+ **/DEPLOYMENT.md
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: smartbiblia
3
+ Version: 0.1.0
4
+ Summary: CLI to install smartbiblia skills and MCP servers into your agent workspace
5
+ Project-URL: Repository, https://github.com/smartbiblia-solutions/agentic-stack
6
+ License: MIT
7
+ Keywords: bibliography,claude,codex,hermes,library,mcp,openclaw,research,skills
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: httpx>=0.27
10
+ Requires-Dist: rich>=13
11
+ Requires-Dist: typer>=0.12
12
+ Description-Content-Type: text/markdown
13
+
14
+ # smartbiblia
15
+
16
+ CLI d'installation des skills [smartbiblia](https://github.com/smartbiblia-solutions/agentic-stack)
17
+ dans un workspace agent.
18
+
19
+ > Pour les MCP servers (OpenAlex, Sudoc SRU, Primo), voir les READMEs dans `mcp/` —
20
+ > ils s'installent via `git clone` ou `uv run <url>` directement, sans passer par cette CLI.
21
+
22
+ ## Installation
23
+
24
+ Aucune installation permanente requise — utilise [`uvx`](https://docs.astral.sh/uv/) pour lancer directement depuis PyPI :
25
+
26
+ ```bash
27
+ uvx smartbiblia list
28
+ ```
29
+
30
+ Ou installe globalement :
31
+
32
+ ```bash
33
+ uv tool install smartbiblia
34
+ # puis :
35
+ smartbiblia list
36
+ ```
37
+
38
+ ## Commandes
39
+
40
+ ### Lister les skills disponibles
41
+
42
+ ```bash
43
+ smartbiblia list
44
+ smartbiblia list --tag french
45
+ smartbiblia list --tag open-access
46
+ ```
47
+
48
+ ### Consulter le détail d'une skill
49
+
50
+ ```bash
51
+ smartbiblia info idref
52
+ smartbiblia info synthesize
53
+ ```
54
+
55
+ ### Installer une skill
56
+
57
+ ```bash
58
+ # Installe dans ./skills/idref/
59
+ smartbiblia add idref
60
+
61
+ # Dossier personnalisé
62
+ smartbiblia add sudoc --dest ./mon-projet/skills
63
+ ```
64
+
65
+ ### Mettre à jour une skill installée
66
+
67
+ ```bash
68
+ smartbiblia update idref
69
+ smartbiblia update synthesize
70
+ ```
71
+
72
+ ## Développement local
73
+
74
+ ```bash
75
+ cd cli/
76
+ uv sync
77
+ uv run smartbiblia list
78
+ ```
79
+
80
+ ### Publier sur PyPI
81
+
82
+ ```bash
83
+ uv build
84
+ uv publish
85
+ ```
86
+
87
+ > **Note :** mettre à jour `GITHUB_REPO` dans `installer.py` si le repo est renommé.
@@ -0,0 +1,74 @@
1
+ # smartbiblia
2
+
3
+ CLI d'installation des skills [smartbiblia](https://github.com/smartbiblia-solutions/agentic-stack)
4
+ dans un workspace agent.
5
+
6
+ > Pour les MCP servers (OpenAlex, Sudoc SRU, Primo), voir les READMEs dans `mcp/` —
7
+ > ils s'installent via `git clone` ou `uv run <url>` directement, sans passer par cette CLI.
8
+
9
+ ## Installation
10
+
11
+ Aucune installation permanente requise — utilise [`uvx`](https://docs.astral.sh/uv/) pour lancer directement depuis PyPI :
12
+
13
+ ```bash
14
+ uvx smartbiblia list
15
+ ```
16
+
17
+ Ou installe globalement :
18
+
19
+ ```bash
20
+ uv tool install smartbiblia
21
+ # puis :
22
+ smartbiblia list
23
+ ```
24
+
25
+ ## Commandes
26
+
27
+ ### Lister les skills disponibles
28
+
29
+ ```bash
30
+ smartbiblia list
31
+ smartbiblia list --tag french
32
+ smartbiblia list --tag open-access
33
+ ```
34
+
35
+ ### Consulter le détail d'une skill
36
+
37
+ ```bash
38
+ smartbiblia info idref
39
+ smartbiblia info synthesize
40
+ ```
41
+
42
+ ### Installer une skill
43
+
44
+ ```bash
45
+ # Installe dans ./skills/idref/
46
+ smartbiblia add idref
47
+
48
+ # Dossier personnalisé
49
+ smartbiblia add sudoc --dest ./mon-projet/skills
50
+ ```
51
+
52
+ ### Mettre à jour une skill installée
53
+
54
+ ```bash
55
+ smartbiblia update idref
56
+ smartbiblia update synthesize
57
+ ```
58
+
59
+ ## Développement local
60
+
61
+ ```bash
62
+ cd cli/
63
+ uv sync
64
+ uv run smartbiblia list
65
+ ```
66
+
67
+ ### Publier sur PyPI
68
+
69
+ ```bash
70
+ uv build
71
+ uv publish
72
+ ```
73
+
74
+ > **Note :** mettre à jour `GITHUB_REPO` dans `installer.py` si le repo est renommé.
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "smartbiblia"
7
+ version = "0.1.0"
8
+ description = "CLI to install smartbiblia skills and MCP servers into your agent workspace"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ keywords = ["claude", "codex", "openclaw", "hermes", "skills", "mcp", "bibliography", "library", "research"]
13
+ dependencies = [
14
+ "typer>=0.12",
15
+ "httpx>=0.27",
16
+ "rich>=13",
17
+ ]
18
+
19
+ [project.scripts]
20
+ smartbiblia = "smartbiblia.cli:app"
21
+
22
+ [project.urls]
23
+ Repository = "https://github.com/smartbiblia-solutions/agentic-stack"
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["src/smartbiblia"]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,51 @@
1
+ # Catalogue des skills disponibles.
2
+ # Ce fichier est lu directement depuis GitHub par la CLI.
3
+ # Mettre à jour ce fichier lors de l'ajout d'une nouvelle skill.
4
+
5
+ [skills.resolve-idref]
6
+ description = "Alignement automatique de noms de personnes vers des PPNs IdRef avec score de confiance"
7
+ path = "skills/resolve-authorities-idref"
8
+ maturity = "experimental"
9
+ tags = ["authority", "french", "abes", "idref", "alignment"]
10
+
11
+ [skills.sudoc]
12
+ description = "Recherche dans le catalogue collectif SUDOC (livres, thèses, périodiques, manuscrits)"
13
+ path = "skills/search-records-sudoc"
14
+ maturity = "stable"
15
+ tags = ["catalog", "french", "unimarc", "abes", "sudoc"]
16
+
17
+ [skills.hal]
18
+ description = "Recherche dans HAL, dépôt ouvert français (articles, thèses, rapports)"
19
+ path = "skills/search-records-hal"
20
+ maturity = "stable"
21
+ tags = ["repository", "french", "open-access", "hal"]
22
+
23
+ [skills.openalex]
24
+ description = "Recherche dans OpenAlex, la plus grande base bibliographique mondiale en accès ouvert"
25
+ path = "skills/search-works-openalex"
26
+ maturity = "stable"
27
+ tags = ["global", "open-access", "openalex", "doi"]
28
+
29
+ [skills.generate-queries]
30
+ description = "Génération de stratégies de recherche documentaire à partir d'une question de recherche (8-15 requêtes bilingues)"
31
+ path = "skills/generate-search-queries"
32
+ maturity = "stable"
33
+ tags = ["search-strategy", "bilingual", "queries"]
34
+
35
+ [skills.synthesize]
36
+ description = "Synthèse de littérature académique (screening PRISMA, résumé, appréciation qualité, synthèse thématique/chronologique)"
37
+ path = "skills/synthesize-literature"
38
+ maturity = "stable"
39
+ tags = ["synthesis", "review", "prisma", "literature"]
40
+
41
+ [skills.convert-unimarc]
42
+ description = "Conversion de notices UNIMARC entre formats XML, JSON et ISO 2709 binaire"
43
+ path = "skills/convert-records-unimarc"
44
+ maturity = "stable"
45
+ tags = ["unimarc", "marc", "conversion", "format"]
46
+
47
+ [skills.dmp]
48
+ description = "Rédaction de Plans de Gestion de Données (PGD/DMP) selon les principes FAIR"
49
+ path = "skills/write-data-management-plan"
50
+ maturity = "experimental"
51
+ tags = ["dmp", "fair", "open-science", "research"]
@@ -0,0 +1,142 @@
1
+ """
2
+ smartbiblia — CLI d'installation de skills pour agents.
3
+
4
+ Usage:
5
+ smartbiblia list [--tag <tag>]
6
+ smartbiblia add <name> [--dest <path>] [--force]
7
+ smartbiblia info <name>
8
+ smartbiblia update <name> [--dest <path>]
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from pathlib import Path
14
+ from typing import Annotated, Optional
15
+
16
+ import tomllib
17
+ import typer
18
+ from rich import print
19
+ from rich.table import Table
20
+ from rich.console import Console
21
+
22
+ from .installer import fetch_catalog_raw, fetch_path
23
+
24
+ app = typer.Typer(
25
+ name="smartbiblia",
26
+ help="Installe les skills smartbiblia dans ton workspace agent.",
27
+ no_args_is_help=True,
28
+ )
29
+ console = Console()
30
+
31
+
32
+ def _load_catalog() -> dict:
33
+ try:
34
+ raw = fetch_catalog_raw()
35
+ return tomllib.loads(raw)
36
+ except Exception as exc:
37
+ print(f"[red]Impossible de charger le catalogue : {exc}[/red]")
38
+ raise typer.Exit(1)
39
+
40
+
41
+ def _resolve(catalog: dict, name: str) -> dict:
42
+ meta = catalog.get("skills", {}).get(name)
43
+ if not meta:
44
+ print(f"[red]Skill '{name}' introuvable dans le catalogue.[/red]")
45
+ print(f"[dim]Utilisez [bold]smartbiblia list[/bold] pour voir les skills disponibles.[/dim]")
46
+ raise typer.Exit(1)
47
+ return meta
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # list
52
+ # ---------------------------------------------------------------------------
53
+
54
+ @app.command("list")
55
+ def list_cmd(
56
+ tag: Annotated[Optional[str], typer.Option("--tag", "-t", help="Filtrer par tag")] = None,
57
+ ):
58
+ """Liste les skills disponibles."""
59
+ catalog = _load_catalog()
60
+
61
+ table = Table(title="smartbiblia — skills disponibles", show_lines=False, highlight=True)
62
+ table.add_column("Nom", style="cyan bold", no_wrap=True)
63
+ table.add_column("Maturité")
64
+ table.add_column("Description")
65
+
66
+ for name, meta in catalog.get("skills", {}).items():
67
+ if tag and tag not in meta.get("tags", []):
68
+ continue
69
+ maturity = meta.get("maturity", "")
70
+ color = {"stable": "green", "beta": "yellow", "experimental": "red"}.get(maturity, "white")
71
+ table.add_row(
72
+ name,
73
+ f"[{color}]{maturity}[/{color}]",
74
+ meta.get("description", ""),
75
+ )
76
+
77
+ console.print(table)
78
+
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # info
82
+ # ---------------------------------------------------------------------------
83
+
84
+ @app.command()
85
+ def info(
86
+ name: Annotated[str, typer.Argument(help="Nom de la skill")],
87
+ ):
88
+ """Affiche les détails d'une skill."""
89
+ catalog = _load_catalog()
90
+ meta = _resolve(catalog, name)
91
+
92
+ print(f"\n[bold cyan]{name}[/bold cyan]")
93
+ print(f"[bold]Description :[/bold] {meta.get('description', '')}")
94
+ print(f"[bold]Maturité :[/bold] {meta.get('maturity', '')}")
95
+ print(f"[bold]Tags :[/bold] {', '.join(meta.get('tags', []))}")
96
+ print(f"[bold]Chemin repo :[/bold] {meta.get('path', '')}")
97
+ print()
98
+
99
+
100
+ # ---------------------------------------------------------------------------
101
+ # add
102
+ # ---------------------------------------------------------------------------
103
+
104
+ @app.command()
105
+ def add(
106
+ name: Annotated[str, typer.Argument(help="Nom de la skill (ex: idref, sudoc, synthesize)")],
107
+ dest: Annotated[Optional[Path], typer.Option("--dest", "-d", help="Dossier de destination (défaut: ./skills/)")] = None,
108
+ force: Annotated[bool, typer.Option("--force", "-f", help="Écraser sans confirmation")] = False,
109
+ ):
110
+ """Installe une skill dans le workspace courant."""
111
+ catalog = _load_catalog()
112
+ meta = _resolve(catalog, name)
113
+
114
+ target: Path = (dest or Path("skills")) / name
115
+
116
+ if target.exists() and not force:
117
+ overwrite = typer.confirm(f"{target} existe déjà. Écraser ?", default=False)
118
+ if not overwrite:
119
+ raise typer.Exit(0)
120
+
121
+ with console.status(f"Téléchargement de la skill [cyan]{name}[/cyan]…"):
122
+ try:
123
+ fetch_path(meta["path"], target)
124
+ except Exception as exc:
125
+ print(f"[red]Erreur lors du téléchargement : {exc}[/red]")
126
+ raise typer.Exit(1)
127
+
128
+ print(f"[green]✓ Skill [bold]{name}[/bold] installée dans [bold]{target}[/bold][/green]")
129
+
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # update
133
+ # ---------------------------------------------------------------------------
134
+
135
+ @app.command()
136
+ def update(
137
+ name: Annotated[str, typer.Argument(help="Nom de la skill à mettre à jour")],
138
+ dest: Annotated[Optional[Path], typer.Option("--dest", "-d")] = None,
139
+ ):
140
+ """Met à jour une skill déjà installée (réinstalle depuis GitHub)."""
141
+ add(name=name, dest=dest, force=True)
142
+ print(f"[dim]Mise à jour terminée.[/dim]")
@@ -0,0 +1,76 @@
1
+ """
2
+ Télécharge un sous-dossier du repo GitHub et l'installe localement.
3
+ Utilise l'API tarball de GitHub — pas besoin de git ni de clone complet.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import io
9
+ import os
10
+ import tarfile
11
+ from pathlib import Path
12
+
13
+ import httpx
14
+
15
+ GITHUB_ORG = "smartbiblia-solutions"
16
+ GITHUB_REPO = "agentic-stack" # à mettre à jour si le repo est renommé
17
+ BRANCH = "main"
18
+
19
+ _TARBALL_URL = (
20
+ f"https://api.github.com/repos/{GITHUB_ORG}/{GITHUB_REPO}/tarball/{BRANCH}"
21
+ )
22
+ _CATALOG_URL = (
23
+ f"https://raw.githubusercontent.com/{GITHUB_ORG}/{GITHUB_REPO}"
24
+ f"/{BRANCH}/cli/src/smartbiblia/catalog.toml"
25
+ )
26
+
27
+
28
+ def _auth_headers() -> dict[str, str]:
29
+ token = os.environ.get("SMARTBIBLIA_GITHUB_TOKEN")
30
+ if token:
31
+ return {"Authorization": f"Bearer {token}"}
32
+ return {}
33
+
34
+
35
+ def fetch_catalog_raw() -> str:
36
+ """Retourne le contenu brut du catalog.toml depuis GitHub."""
37
+ resp = httpx.get(_CATALOG_URL, headers=_auth_headers(), timeout=10, follow_redirects=True)
38
+ resp.raise_for_status()
39
+ return resp.text
40
+
41
+
42
+ def fetch_path(remote_path: str, dest: Path) -> None:
43
+ """
44
+ Télécharge le tarball du repo et extrait uniquement le sous-dossier
45
+ `remote_path` vers `dest`.
46
+
47
+ Le tarball GitHub préfixe tous les chemins par "<org>-<repo>-<sha>/",
48
+ ce préfixe est retiré à l'extraction.
49
+ """
50
+ resp = httpx.get(
51
+ _TARBALL_URL,
52
+ headers=_auth_headers(),
53
+ timeout=60,
54
+ follow_redirects=True,
55
+ )
56
+ resp.raise_for_status()
57
+
58
+ with tarfile.open(fileobj=io.BytesIO(resp.content), mode="r:gz") as tar:
59
+ root_prefix = tar.getnames()[0].split("/")[0] + "/"
60
+ target_prefix = root_prefix + remote_path.strip("/") + "/"
61
+
62
+ members = [m for m in tar.getmembers() if m.name.startswith(target_prefix)]
63
+
64
+ if not members:
65
+ raise FileNotFoundError(
66
+ f"Chemin '{remote_path}' introuvable dans le tarball GitHub. "
67
+ "Vérifiez que le nom correspond bien à un dossier du repo."
68
+ )
69
+
70
+ dest.mkdir(parents=True, exist_ok=True)
71
+
72
+ for member in members:
73
+ member.name = member.name[len(target_prefix):]
74
+ if not member.name:
75
+ continue
76
+ tar.extract(member, path=dest, filter="data")