prado-os 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.
- prado_os-0.1.0/.agents/skills/prado-os/SKILL.md +49 -0
- prado_os-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- prado_os-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +16 -0
- prado_os-0.1.0/.github/workflows/ci.yml +66 -0
- prado_os-0.1.0/.github/workflows/live-testbed.yml +29 -0
- prado_os-0.1.0/.gitignore +60 -0
- prado_os-0.1.0/AGENTS.md +56 -0
- prado_os-0.1.0/BOOTSTRAP.md +138 -0
- prado_os-0.1.0/CLAUDE.md +21 -0
- prado_os-0.1.0/LICENSE +661 -0
- prado_os-0.1.0/PKG-INFO +263 -0
- prado_os-0.1.0/PROJECT.md +224 -0
- prado_os-0.1.0/README.md +237 -0
- prado_os-0.1.0/USER.md.example +99 -0
- prado_os-0.1.0/assets/banner_clean.html +371 -0
- prado_os-0.1.0/assets/banner_template.html +621 -0
- prado_os-0.1.0/assets/prado-demo.gif +0 -0
- prado_os-0.1.0/assets/screens/01_main_menu.png +0 -0
- prado_os-0.1.0/assets/screens/02_onboarding_wizard.png +0 -0
- prado_os-0.1.0/assets/screens/03_ugr_mail_tui.png +0 -0
- prado_os-0.1.0/assets/screens/04_sync_and_dashboard.png +0 -0
- prado_os-0.1.0/assets/screens/05_doctor_health.png +0 -0
- prado_os-0.1.0/assets/social-preview-1280x640.png +0 -0
- prado_os-0.1.0/assets/social-preview.png +0 -0
- prado_os-0.1.0/assets/ugr_emblem.png +0 -0
- prado_os-0.1.0/assets/ugr_emblem_white.png +0 -0
- prado_os-0.1.0/config.example.yaml +76 -0
- prado_os-0.1.0/docs/README.fr.md +85 -0
- prado_os-0.1.0/docs/README.it.md +85 -0
- prado_os-0.1.0/install.ps1 +339 -0
- prado_os-0.1.0/install.sh +104 -0
- prado_os-0.1.0/pyproject.toml +43 -0
- prado_os-0.1.0/src/prado/__init__.py +9 -0
- prado_os-0.1.0/src/prado/__main__.py +12 -0
- prado_os-0.1.0/src/prado/ai/__init__.py +76 -0
- prado_os-0.1.0/src/prado/ai/base.py +145 -0
- prado_os-0.1.0/src/prado/ai/deterministic.py +168 -0
- prado_os-0.1.0/src/prado/ai/external.py +103 -0
- prado_os-0.1.0/src/prado/ai/ollama.py +110 -0
- prado_os-0.1.0/src/prado/auth/__init__.py +19 -0
- prado_os-0.1.0/src/prado/auth/credentials.py +376 -0
- prado_os-0.1.0/src/prado/auth/mfa.py +361 -0
- prado_os-0.1.0/src/prado/auth/saml.py +230 -0
- prado_os-0.1.0/src/prado/auth/session.py +221 -0
- prado_os-0.1.0/src/prado/cli.py +1489 -0
- prado_os-0.1.0/src/prado/config.py +458 -0
- prado_os-0.1.0/src/prado/core/__init__.py +108 -0
- prado_os-0.1.0/src/prado/core/adoption.py +244 -0
- prado_os-0.1.0/src/prado/core/discovery.py +508 -0
- prado_os-0.1.0/src/prado/core/downloader.py +411 -0
- prado_os-0.1.0/src/prado/core/overwrite.py +217 -0
- prado_os-0.1.0/src/prado/core/parser.py +436 -0
- prado_os-0.1.0/src/prado/core/router.py +286 -0
- prado_os-0.1.0/src/prado/discovery.py +30 -0
- prado_os-0.1.0/src/prado/downloader.py +34 -0
- prado_os-0.1.0/src/prado/exceptions.py +93 -0
- prado_os-0.1.0/src/prado/exporters/__init__.py +59 -0
- prado_os-0.1.0/src/prado/exporters/calendar.py +239 -0
- prado_os-0.1.0/src/prado/exporters/changelog.py +177 -0
- prado_os-0.1.0/src/prado/exporters/dashboard.py +273 -0
- prado_os-0.1.0/src/prado/exporters/gcalendar.py +336 -0
- prado_os-0.1.0/src/prado/i18n.py +302 -0
- prado_os-0.1.0/src/prado/mail/__init__.py +32 -0
- prado_os-0.1.0/src/prado/mail/client.py +817 -0
- prado_os-0.1.0/src/prado/mail/tui.py +584 -0
- prado_os-0.1.0/src/prado/notifications/__init__.py +49 -0
- prado_os-0.1.0/src/prado/notifications/desktop.py +171 -0
- prado_os-0.1.0/src/prado/notifications/dispatcher.py +186 -0
- prado_os-0.1.0/src/prado/notifications/email.py +103 -0
- prado_os-0.1.0/src/prado/notifications/webhooks.py +221 -0
- prado_os-0.1.0/src/prado/onboarding/__init__.py +37 -0
- prado_os-0.1.0/src/prado/onboarding/star.py +241 -0
- prado_os-0.1.0/src/prado/onboarding/wizard.py +609 -0
- prado_os-0.1.0/src/prado/parser.py +26 -0
- prado_os-0.1.0/src/prado/routing.py +26 -0
- prado_os-0.1.0/src/prado/scheduler/__init__.py +41 -0
- prado_os-0.1.0/src/prado/scheduler/daemon.py +596 -0
- prado_os-0.1.0/src/prado/scheduler/doctor.py +594 -0
- prado_os-0.1.0/src/prado/state.py +476 -0
- prado_os-0.1.0/tests/conftest.py +815 -0
- prado_os-0.1.0/tests/e2e/test_adversarial.py +496 -0
- prado_os-0.1.0/tests/e2e/test_adversarial_m2.py +776 -0
- prado_os-0.1.0/tests/e2e/test_cli_json.py +134 -0
- prado_os-0.1.0/tests/e2e/test_cli_onboard.py +109 -0
- prado_os-0.1.0/tests/e2e/test_cli_sync.py +155 -0
- prado_os-0.1.0/tests/e2e/test_live_readonly.py +73 -0
- prado_os-0.1.0/tests/e2e/test_live_real.py +482 -0
- prado_os-0.1.0/tests/e2e/test_tier1_features.py +1480 -0
- prado_os-0.1.0/tests/e2e/test_tier2_boundaries.py +1192 -0
- prado_os-0.1.0/tests/e2e/test_tier3_integration.py +478 -0
- prado_os-0.1.0/tests/e2e/test_tier4_scenarios.py +357 -0
- prado_os-0.1.0/tests/unit/test_adoption.py +122 -0
- prado_os-0.1.0/tests/unit/test_adversarial_isolation_concurrency.py +384 -0
- prado_os-0.1.0/tests/unit/test_adversarial_m3.py +383 -0
- prado_os-0.1.0/tests/unit/test_adversarial_m3_doctor_ai_daemon.py +538 -0
- prado_os-0.1.0/tests/unit/test_adversarial_router_adoption.py +306 -0
- prado_os-0.1.0/tests/unit/test_ai.py +175 -0
- prado_os-0.1.0/tests/unit/test_calendar.py +142 -0
- prado_os-0.1.0/tests/unit/test_cli_notify.py +51 -0
- prado_os-0.1.0/tests/unit/test_config.py +198 -0
- prado_os-0.1.0/tests/unit/test_credentials.py +148 -0
- prado_os-0.1.0/tests/unit/test_daemon.py +216 -0
- prado_os-0.1.0/tests/unit/test_discovery.py +282 -0
- prado_os-0.1.0/tests/unit/test_doctor.py +224 -0
- prado_os-0.1.0/tests/unit/test_downloader.py +228 -0
- prado_os-0.1.0/tests/unit/test_exporters.py +318 -0
- prado_os-0.1.0/tests/unit/test_gcalendar.py +125 -0
- prado_os-0.1.0/tests/unit/test_i18n.py +94 -0
- prado_os-0.1.0/tests/unit/test_m4_cli_onboarding.py +181 -0
- prado_os-0.1.0/tests/unit/test_mail.py +339 -0
- prado_os-0.1.0/tests/unit/test_mfa.py +348 -0
- prado_os-0.1.0/tests/unit/test_notifications.py +310 -0
- prado_os-0.1.0/tests/unit/test_overwrite.py +175 -0
- prado_os-0.1.0/tests/unit/test_parser.py +196 -0
- prado_os-0.1.0/tests/unit/test_router.py +290 -0
- prado_os-0.1.0/tests/unit/test_saml_auth.py +351 -0
- prado_os-0.1.0/tests/unit/test_state.py +181 -0
- prado_os-0.1.0/uv.lock +870 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prado-os
|
|
3
|
+
description: Autonomous installation, 2-phase onboarding, 3-tier SAML/MFA authentication, course auto-discovery, and safe synchronization for Moodle / PRADO UGR via the `prado` CLI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PradoOS Skill (`prado-os`)
|
|
7
|
+
|
|
8
|
+
Use this skill whenever the user asks to install, configure, troubleshoot, or run **PradoOS (`ugr-prado-sync`)** to synchronize their university courses, assignments, and deadlines from PRADO UGR (`pradogrado`, `pradoposgrado`, `prado.ugr.es`) or Moodle.
|
|
9
|
+
|
|
10
|
+
## Core Capabilities & Workflow
|
|
11
|
+
|
|
12
|
+
1. **Language Priority (Spanish Primary, English & Adaptive Multi-Language):**
|
|
13
|
+
- PradoOS serves the Universidad de Granada community: prioritize **Spanish** by default if the user writes in Spanish or references UGR.
|
|
14
|
+
- Detect or ask the student's preferred language (Spanish or English, or conduct onboarding in their native tongue if requested), defaulting to English if unavailable.
|
|
15
|
+
|
|
16
|
+
2. **Zero CLI Jargon to the User:**
|
|
17
|
+
- Speak to the student 100% in natural language. **Never** display CLI flags (`--json`, `--subgroups`) or command strings (`prado daemon install`, `prado star`) in your chat responses—execute them silently in the background.
|
|
18
|
+
|
|
19
|
+
3. **Background Commands for the Agent:**
|
|
20
|
+
```bash
|
|
21
|
+
./install.sh
|
|
22
|
+
.venv/bin/prado doctor --json
|
|
23
|
+
.venv/bin/prado auth login --gui --json
|
|
24
|
+
.venv/bin/prado discover --json
|
|
25
|
+
.venv/bin/prado sync --adopt --json
|
|
26
|
+
.venv/bin/prado gcalendar --json
|
|
27
|
+
.venv/bin/prado daemon install --interval 14400 --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
4. **Security Audit Reassurance & Zero-Terminal GUI Login (`prado auth login --gui --json`):**
|
|
31
|
+
- Before triggering login, inspect `src/prado/auth/credentials.py` and `src/prado/auth/saml.py` and reassure the student that you verified the code locally: their password is stored exclusively in their native OS Keychain and sent only to `idp.ugr.es` (they can also store it directly via OS tools like macOS `/usr/bin/security`, Windows `cmdkey`, or Linux `secret-tool`).
|
|
32
|
+
- Run `.venv/bin/prado auth login --gui --json` directly to open the native OS login dialog on their screen.
|
|
33
|
+
|
|
34
|
+
5. **2-Phase Progressive Disclosure Onboarding:**
|
|
35
|
+
- **Phase 1 (<60s):** Ask only for destination folder (`courses_root`) and sync frequency (e.g. 3x/day or every 4 hours). Apply the **Smart Theory vs Practice** (`smart`) preset automatically for agentic setups.
|
|
36
|
+
- **Phase 2 (Sartorial Academic Concierge & Automation):**
|
|
37
|
+
- **Autonomous Automation:**
|
|
38
|
+
- **Google Antigravity:** Suggest `/automation` or schedule tool (cron `0 8,14,20 * * 1-5`).
|
|
39
|
+
- **Claude Code:** Setup headless cron runner (`claude -p "..." --allowedTools "Bash,Read"`).
|
|
40
|
+
- **OpenAI Codex / Terminal:** Install OS daemon via `prado daemon install --interval 14400 --json`.
|
|
41
|
+
- **Google Calendar:** Run `prado gcalendar --json` to push deadlines with tiered popup alarms.
|
|
42
|
+
- **Sartorial Personalization (`USER.md`):**
|
|
43
|
+
- Offer to configure an **Institutional Email Scraper & Auto-Forwarder (`@correo.ugr.es` / `@go.ugr.es`)**, proposing either: (a) the built-in cross-platform `prado mail` engine (`correo.ugr.es:993` `IMAP4_SSL` + `correo.ugr.es:465` `SMTP_SSL` reusing `CredentialStore` credentials), (b) an MCP email/Gmail connector, or (c) the author's macOS Apple Mail + `osascript` integration.
|
|
44
|
+
- Ask the student for their weekly class timetable to schedule intelligent deep work study blocks between lectures.
|
|
45
|
+
- Ask if they want to merge theoretical and lab portals (e.g. `SO_COMUN` + `SO_E` -> `SO_E/`).
|
|
46
|
+
- Ask for evaluation weights (theory exam, lab project) and academic goals to prioritize dashboard countdowns.
|
|
47
|
+
- Optionally suggest maintaining a chronological activity log (`ACTIVITY_LOG.md`) and/or per-course strategic study guides (`STUDY_GUIDE.md`).
|
|
48
|
+
- Save all custom preferences into `.gitignore`-protected `USER.md` using `USER.md.example` as a model.
|
|
49
|
+
- **GitHub Star Prompt:** On the second completed sync, politely ask once in natural language if they would like you to star `LorSt4r/ugr-prado-sync` on GitHub (`prado star --json`).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 🐛 Reporte de Error / Bug Report
|
|
3
|
+
about: Informa sobre un fallo en la sincronización, inicio de sesión o descarga de archivos
|
|
4
|
+
title: "[BUG] "
|
|
5
|
+
labels: ["bug"]
|
|
6
|
+
assignees: LorSt4r
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### 💻 Entorno y Sistema Operativo
|
|
10
|
+
- **Sistema Operativo:** [ej. macOS Sonoma, Ubuntu 24.04, Windows 11]
|
|
11
|
+
- **Método de Uso:** [Terminal Standalone (`prado sync`) / Asistente de IA (Antigravity, Claude Code, Cursor, Codex)]
|
|
12
|
+
- **Versión de Python:** [ej. 3.12]
|
|
13
|
+
|
|
14
|
+
### 🏛️ Asignatura y Facultad (UGR)
|
|
15
|
+
- **Facultad / Escuela:** [ej. ETSIIT, Derecho, Ciencias, Medicina...]
|
|
16
|
+
- **Nombre de la Asignatura en PRADO:** [ej. Estructura de Datos (Grupo C)]
|
|
17
|
+
- **Tipo de Recurso que falla:** [ej. Descarga de archivo PDF/ZIP, Detección de fecha de entrega, Inicio de sesión MFA]
|
|
18
|
+
|
|
19
|
+
### 📝 Descripción del Problema
|
|
20
|
+
Una explicación clara y concisa de lo que ha fallado.
|
|
21
|
+
|
|
22
|
+
### 📋 Pasos para Reproducir
|
|
23
|
+
1. Ejecutar el comando `prado ...`
|
|
24
|
+
2. Observar el error que aparece en la consola.
|
|
25
|
+
|
|
26
|
+
### 🔍 Salida del Terminal / Captura de Pantalla
|
|
27
|
+
Si es posible, pega la salida del comando o ejecuta `prado doctor --json` y pega el resultado (ocultando cualquier dato personal):
|
|
28
|
+
```text
|
|
29
|
+
(Pega el error aquí)
|
|
30
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 💡 Sugerencia de Funcionalidad / Feature Request
|
|
3
|
+
about: Sugiere una nueva idea o mejora para PradoOS
|
|
4
|
+
title: "[IDEA] "
|
|
5
|
+
labels: ["enhancement"]
|
|
6
|
+
assignees: LorSt4r
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### 🚀 ¿Cuál es tu propuesta o idea?
|
|
10
|
+
Describe de forma clara qué nueva función te gustaría ver en PradoOS.
|
|
11
|
+
|
|
12
|
+
### 🎯 ¿Qué problema o necesidad resuelve?
|
|
13
|
+
Explica cómo te ayudaría en tu día a día en la universidad (ej. integración con otra herramienta, soporte para un tipo especial de archivo en tu facultad, etc.).
|
|
14
|
+
|
|
15
|
+
### 💡 Alternativas o contexto adicional
|
|
16
|
+
Cualquier otro detalle, enlace o captura de pantalla que aporte contexto.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Cross-Platform CI (Linux, macOS, Windows)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test-matrix:
|
|
12
|
+
name: Test on ${{ matrix.os }} (Python ${{ matrix.python-version }})
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
18
|
+
python-version: ["3.12"]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout repository
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Install PradoOS and dev dependencies
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install -e ".[dev]"
|
|
33
|
+
|
|
34
|
+
- name: Run Live Network & Environment Health Check (prado doctor --json)
|
|
35
|
+
run: |
|
|
36
|
+
prado doctor --json || true
|
|
37
|
+
|
|
38
|
+
- name: Verify Self-Bootstrapping Installer on Windows (install.ps1)
|
|
39
|
+
if: runner.os == 'Windows'
|
|
40
|
+
shell: powershell
|
|
41
|
+
env:
|
|
42
|
+
PRADO_NO_INTERACTIVE: "1"
|
|
43
|
+
run: |
|
|
44
|
+
.\install.ps1
|
|
45
|
+
& "$HOME\.local\bin\prado.cmd" --version
|
|
46
|
+
|
|
47
|
+
- name: Verify Self-Bootstrapping Installer on POSIX (install.sh)
|
|
48
|
+
if: runner.os != 'Windows'
|
|
49
|
+
shell: bash
|
|
50
|
+
env:
|
|
51
|
+
PRADO_NO_INTERACTIVE: "1"
|
|
52
|
+
run: |
|
|
53
|
+
bash ./install.sh
|
|
54
|
+
"$HOME/.local/bin/prado" --version
|
|
55
|
+
|
|
56
|
+
- name: Live Zero-Mock OS & UGR Server Integration Suite (test_live_real.py)
|
|
57
|
+
shell: bash
|
|
58
|
+
env:
|
|
59
|
+
PRADO_NO_INTERACTIVE: "1"
|
|
60
|
+
PRADO_LIVE_E2E: "1"
|
|
61
|
+
run: |
|
|
62
|
+
pytest tests/e2e/test_live_real.py -v
|
|
63
|
+
|
|
64
|
+
- name: Run Full Pytest Suite (Unit + Integration + E2E)
|
|
65
|
+
run: |
|
|
66
|
+
pytest -q
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Interactive Cloud Testbed (Live Terminal)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
os:
|
|
7
|
+
description: 'Select OS runner for live interactive test'
|
|
8
|
+
required: true
|
|
9
|
+
default: 'windows-latest'
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- windows-latest
|
|
13
|
+
- ubuntu-latest
|
|
14
|
+
- macos-latest
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
live-terminal:
|
|
18
|
+
name: Live Terminal (${{ inputs.os }})
|
|
19
|
+
runs-on: ${{ inputs.os }}
|
|
20
|
+
timeout-minutes: 60
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Start Interactive Terminal Session (tmate)
|
|
27
|
+
uses: mxschmitt/action-tmate@v3
|
|
28
|
+
with:
|
|
29
|
+
detached: false
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
wheels/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
.installed.cfg
|
|
20
|
+
*.egg
|
|
21
|
+
|
|
22
|
+
# Virtual environments
|
|
23
|
+
.venv/
|
|
24
|
+
venv/
|
|
25
|
+
env/
|
|
26
|
+
ENV/
|
|
27
|
+
|
|
28
|
+
# Sensitive data, credentials, sessions, and state
|
|
29
|
+
.env
|
|
30
|
+
*.cookie
|
|
31
|
+
*.cookies
|
|
32
|
+
.prado_session_cookies.json
|
|
33
|
+
.mail_cache.json
|
|
34
|
+
*.db
|
|
35
|
+
*.sqlite
|
|
36
|
+
*.sqlite3
|
|
37
|
+
session.json
|
|
38
|
+
state.json
|
|
39
|
+
state.db
|
|
40
|
+
credentials.vault
|
|
41
|
+
|
|
42
|
+
# Local configuration overrides (example is committed)
|
|
43
|
+
config.yaml
|
|
44
|
+
!config.example.yaml
|
|
45
|
+
USER.md
|
|
46
|
+
!USER.md.example
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
|
|
49
|
+
# Teamwork agent metadata (MUST be ignored)
|
|
50
|
+
.agents/teamwork/
|
|
51
|
+
|
|
52
|
+
# But KEEP the agent skill tracked:
|
|
53
|
+
!.agents/skills/
|
|
54
|
+
!.agents/skills/prado-os/
|
|
55
|
+
!.agents/skills/prado-os/SKILL.md
|
|
56
|
+
|
|
57
|
+
# Operating System files
|
|
58
|
+
.DS_Store
|
|
59
|
+
Thumbs.db
|
|
60
|
+
Desktop.ini
|
prado_os-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 🧠 AGENTS.md — PradoOS Operational Manifesto & Agent Protocol
|
|
2
|
+
|
|
3
|
+
**Project:** PradoOS (`prado-os`, CLI binary: `prado`)
|
|
4
|
+
**Supported Platforms:** macOS, Linux Desktop (Ubuntu, Fedora, Arch, Debian), Windows 10/11
|
|
5
|
+
**Supported LMS:** PRADO Universidad de Granada (`pradogrado`, `pradoposgrado`, `prado.ugr.es`) & Moodle 4.x
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Purpose & Design Philosophy
|
|
10
|
+
|
|
11
|
+
PradoOS is an open-source academic workflow engine built for two complementary modes of operation:
|
|
12
|
+
1. **Agent-Mediated Mode:** When a student gives this repository URL to an AI coding agent (Antigravity, Claude Code, Codex, Cursor), the agent reads [`BOOTSTRAP.md`](BOOTSTRAP.md) and `.agents/skills/prado-os/SKILL.md` to guide the student through setup and daily academic management.
|
|
13
|
+
2. **100% Free & Deterministic Standalone CLI:** Every feature works deterministically without any paid AI subscription or external API keys via the `prado` command-line interface. Optional BYO-AI providers (`Ollama` local models, `DeepSeek`, `Gemini`, `OpenAI`) can be enabled in `config.yaml` if desired.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Strict Security & Data Isolation Invariants
|
|
18
|
+
|
|
19
|
+
When operating or extending PradoOS, all agents MUST uphold these non-negotiable invariants:
|
|
20
|
+
1. **Strict Separation of Course Data and Credentials:**
|
|
21
|
+
- Configuration (`config.yaml`), session cookies (`session.json`), encrypted fallback vaults (`credentials.vault`), and the SQLite state database (`state.db`) MUST live exclusively inside the OS user configuration directory (`~/.config/prado/` on macOS/Linux with `0700` dir / `0600` file permissions, or `%APPDATA%\prado` on Windows).
|
|
22
|
+
- **Never** write credentials, cookies, or `.db` files inside the student's synced courses folder (`courses_root`), which is frequently backed up to Google Drive, iCloud, OneDrive, or Git.
|
|
23
|
+
2. **Zero Plaintext Passwords in Agent Context:**
|
|
24
|
+
- Never ask a student to paste their institutional password into an AI chat window.
|
|
25
|
+
- Always use interactive TTY input (`prado auth login` or `prado onboard`) or the native OS keyring (`macOS Keychain`, `Windows Credential Manager`, `Linux SecretService / libsecret`).
|
|
26
|
+
3. **4-Quadrant Student Overwrite Protection:**
|
|
27
|
+
- PradoOS tracks the original SHA-256 hash (`original_hash`) of every downloaded file in `state.db`.
|
|
28
|
+
- If a student modifies a local file (`.ipynb`, `.cpp`, `.docx`, `.pdf` annotations) and the professor uploads a new version on PRADO, **never overwrite the student's work**. Save the professor's updated version alongside as `{stem}_server_{YYYYMMDD_HHMMSS}{ext}`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Machine-Readable `--json` CLI Contract
|
|
33
|
+
|
|
34
|
+
Every `prado` subcommand supports `--json` for deterministic agent orchestration:
|
|
35
|
+
|
|
36
|
+
| Command | Purpose | Exit Codes |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `prado onboard --json` | Non-interactive Phase 1 & Phase 2 configuration | `0` OK, `1` Error |
|
|
39
|
+
| `prado auth status --json` | Inspect session validity, 30d MFA trust tokens, and stored credentials | `0` OK, `1` Error |
|
|
40
|
+
| `prado auth login --mfa-code <6-digit> --json` | Complete SAML SSO + Tier 3 MFA verification | `0` OK, `1` Error, `2` MFA Required |
|
|
41
|
+
| `prado discover --json` | Auto-discover enrolled courses via Moodle AJAX + DOM fallback | `0` OK, `1` Error, `2` MFA Required |
|
|
42
|
+
| `prado courses [list\|enable\|disable\|set-preset] --json` | Manage active courses and folder routing preset (`smart` vs `mirror`) | `0` OK, `1` Error |
|
|
43
|
+
| `prado sync [--course ID] [--dry-run] [--adopt] --json` | Synchronize courses, adopt existing files, and export `DASHBOARD.md`, `CHANGELOG.md`, `deadlines.ics` | `0` OK, `1` Error, `2` MFA Required |
|
|
44
|
+
| `prado doctor [--fix] --json` | Execute 9 health probes and optionally repair file permissions | `0` Healthy, `1` Issues Detected |
|
|
45
|
+
| `prado daemon [install\|uninstall\|status] --json` | Manage OS background scheduler (`launchd`, `systemd`, `schtasks`) | `0` OK, `1` Error |
|
|
46
|
+
| `prado mail [tui\|list\|read\|sync\|forward] --json` | Read `@correo.ugr.es` via `correo.ugr.es:993` (`BODY.PEEK[]`) and auto-forward via `correo.ugr.es:465` (`SMTP_SSL`) | `0` OK, `1` Error |
|
|
47
|
+
| `prado star --json` | Star `LorSt4r/ugr-prado-sync` via `gh api` or browser | `0` OK |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 4. Onboarding & User Context
|
|
52
|
+
|
|
53
|
+
- If `USER.md` does not exist in the workspace, follow [`BOOTSTRAP.md`](BOOTSTRAP.md) to onboard the student.
|
|
54
|
+
- During Phase 2 onboarding, proactively offer to set up **institutional email scraping & auto-forwarding (`@correo.ugr.es` / `@go.ugr.es`)** using either the built-in cross-platform `prado mail` engine (`IMAP4_SSL` on `correo.ugr.es:993` + `SMTP_SSL` on `correo.ugr.es:465` reusing `CredentialStore`), an MCP email integration, or the author's original macOS Apple Mail + `osascript` scraper.
|
|
55
|
+
- Optionally suggest (without forcing) maintaining a chronological academic activity log (`ACTIVITY_LOG.md` / `DIARIO_ATTIVITA.md`) and per-course strategic study guides (`STUDY_GUIDE.md`).
|
|
56
|
+
- Copy [`USER.md.example`](USER.md.example) to `USER.md` (which is git-ignored) to persist the student's degree, faculty preset, timetable, email integration preference, and preferred language locally.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# 🎓 PradoOS — AI Agent Bootstrap Playbook (`BOOTSTRAP.md`)
|
|
2
|
+
|
|
3
|
+
Welcome, AI Coding Assistant (**Antigravity**, **Claude Code**, **Cursor**, **Codex**, or **Windsurf**)!
|
|
4
|
+
A student has shared this repository with you so you can autonomously install, configure, and tailor **PradoOS (`prado-os`)** to their operating system (macOS, Windows, or Linux Desktop) and academic faculty.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🌐 Language Priority & Adaptive Onboarding
|
|
9
|
+
|
|
10
|
+
- **Spanish is the primary language of PradoOS and UGR**: By default, greet the student and conduct the onboarding in **Spanish** if they write in Spanish, mention UGR, or have a `@correo.ugr.es` account.
|
|
11
|
+
- **Adaptive Multi-Language Support**:
|
|
12
|
+
1. At the very first turn, greet the student and either detect their language from their initial prompt or ask whether they prefer **Español** or **English** (or their native tongue).
|
|
13
|
+
2. If the student writes in English or requests English, conduct all explanations, questions, and summaries in **English**.
|
|
14
|
+
3. If the student prefers another native language (e.g., Italian, German, French), conduct the onboarding in their native tongue and configure the setup accordingly, defaulting to English if technical terms are clearer.
|
|
15
|
+
- **Golden UX Rule: Zero CLI Jargon to the Student**:
|
|
16
|
+
- **Speak 100% naturally like a personal academic concierge.**
|
|
17
|
+
- **NEVER show CLI command names, flags (`--json`, `--subgroups`, `prado star`, `prado daemon`), or internal code details in your messages to the student.**
|
|
18
|
+
- Run all commands silently in the background and present only clean, human-friendly outcomes and simple choices!
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Phase 1: Under 60 Seconds to First Sync
|
|
23
|
+
|
|
24
|
+
### Step 1 — Detect OS & Install Silently
|
|
25
|
+
1. Run the installer script silently:
|
|
26
|
+
- **macOS / Linux:** `./install.sh`
|
|
27
|
+
- **Windows (PowerShell):** `.\install.ps1`
|
|
28
|
+
2. Verify system readiness in the background:
|
|
29
|
+
```bash
|
|
30
|
+
.venv/bin/prado doctor --json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Step 2 — Language Check & The 2 Essential Questions
|
|
34
|
+
Greet the student warmly in their preferred language (defaulting to Spanish for UGR, or English for international students) and ask only:
|
|
35
|
+
1. **Where would you like to keep your university courses folder?**
|
|
36
|
+
- *ES:* ¿Dónde te gustaría guardar la carpeta de tus asignaturas? (Ej. `~/Documents/UGR_Courses`, o tu carpeta actual si ya tienes apuntes descargados).
|
|
37
|
+
- *EN:* Where would you like to keep your university courses folder? (e.g. `~/Documents/UGR_Courses`, or your existing folder if you already have files).
|
|
38
|
+
2. **How often would you like me to check PRADO for new materials or deadlines?**
|
|
39
|
+
- *ES:* ¿Cada cuánto tiempo deseas que revise automáticamente nuevos materiales o entregas en PRADO? *(Recomendado: 3 veces al día — ej. 08:30, 14:00 y 20:00 — o cada 4 horas)*.
|
|
40
|
+
- *EN:* How often should I check PRADO for new slides, lab tasks, or deadlines? *(Recommended: 3 times a day — e.g. 08:30, 14:00, 20:00 — or every 4 hours)*.
|
|
41
|
+
|
|
42
|
+
*(Note for AI Agents: In this agent-mediated setup, apply the **Smart Theory vs Practice** (`smart`) preset by default (`--preset smart`), because as an AI agent you can intelligently curate files into `Teoria/` and `Practicas/P1..Pn/` while checking syllabus and archiving inactive subgroups. Only use `mirror` if the student specifically requests an exact 1:1 replica of PRADO's layout).*
|
|
43
|
+
|
|
44
|
+
Once they answer, apply their choices in the background:
|
|
45
|
+
```bash
|
|
46
|
+
.venv/bin/prado onboard --courses-root "<PATH>" --preset smart --no-interactive --no-star --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 3 — Security Reassurance & Native OS Login
|
|
50
|
+
1. **Perform a Quick Local Security Audit for the Student:**
|
|
51
|
+
Briefly inspect [`src/prado/auth/credentials.py`](src/prado/auth/credentials.py) and [`src/prado/auth/saml.py`](src/prado/auth/saml.py), then reassure the student in plain language:
|
|
52
|
+
> *"I have verified the source code locally on your computer: your UGR password is saved exclusively inside your operating system's native Keychain (macOS Keychain / Windows Credential Manager / Linux SecretService) and is only ever transmitted directly to the official `idp.ugr.es` server—never to any third party or chat log."*
|
|
53
|
+
2. **Trigger the Native OS Login Window (or Offer Direct OS Keychain Entry):**
|
|
54
|
+
- Run the native OS dialog directly so a system login window pops up on their screen:
|
|
55
|
+
```bash
|
|
56
|
+
.venv/bin/prado auth login --gui --json
|
|
57
|
+
```
|
|
58
|
+
- *(Note for security-conscious users: if a student prefers to save their password directly using their operating system's built-in tool without typing it into the popup, they can also use macOS Keychain Access / `/usr/bin/security add-generic-password -U -s "prado:sso" -a "email@correo.ugr.es" -w`, Windows Credential Manager `cmdkey /generic:prado:sso /user:email@correo.ugr.es /pass`, or Linux `secret-tool store --label="PradoOS" service prado:sso username email@correo.ugr.es`).*
|
|
59
|
+
- If the login requires a 6-digit email verification code (`exit code 2` / `mfa_required`) and the student didn't enter it in the popup, ask them for the 6-digit code sent to their `@correo.ugr.es` email and complete login in the background:
|
|
60
|
+
```bash
|
|
61
|
+
.venv/bin/prado auth login --mfa-code <CODE> --json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 4 — Auto-Discover & Sync Courses
|
|
65
|
+
1. Run discovery and sync (with mid-semester adoption enabled so any pre-existing files are matched by SHA-256 without re-downloading):
|
|
66
|
+
```bash
|
|
67
|
+
.venv/bin/prado sync --adopt --json
|
|
68
|
+
```
|
|
69
|
+
2. Show the student a clean table of their synced courses, how many files were organized, and where their `DASHBOARD.md` and `deadlines.ics` calendar file are located.
|
|
70
|
+
|
|
71
|
+
### Step 5 — Configure Autonomous Agent Automation (Based on Your AI Assistant)
|
|
72
|
+
Based on the student's preferred frequency chosen in Step 2, set up recurring background sync according to your agent environment:
|
|
73
|
+
|
|
74
|
+
1. **In Google Antigravity:**
|
|
75
|
+
- Suggest or trigger the `/automation` slash command or `schedule` tool (e.g., cron `0 8,14,20 * * 1-5` or every 4 hours) to run `.venv/bin/prado sync --adopt --json` in background and alert the student in chat only when new materials or deadlines are detected.
|
|
76
|
+
2. **In Claude Code (`claude` CLI):**
|
|
77
|
+
- Claude Code executes headlessly via `-p` / `--print`.
|
|
78
|
+
- Configure a recurring task or cron script running:
|
|
79
|
+
```bash
|
|
80
|
+
claude -p "Run .venv/bin/prado sync --adopt --json, review DASHBOARD.md and CHANGELOG.md, and alert me only if there are new materials, urgent forum posts, or deliverables in the next 3 days." --allowedTools "Bash,Read"
|
|
81
|
+
```
|
|
82
|
+
3. **In OpenAI Codex / Cursor / Terminal Agents:**
|
|
83
|
+
- Install the native OS background daemon:
|
|
84
|
+
```bash
|
|
85
|
+
.venv/bin/prado daemon install --interval 14400 --json
|
|
86
|
+
```
|
|
87
|
+
*(Installs macOS `launchd`, Linux `systemd --user`, or Windows `schtasks` running 24/7 even when IDE/terminal is closed)*.
|
|
88
|
+
- Save a prompt template in `.agents/prompts/daily-check.md` for the agent.
|
|
89
|
+
4. **Direct Google Calendar API Push:**
|
|
90
|
+
- If the student uses Google Calendar, run:
|
|
91
|
+
```bash
|
|
92
|
+
.venv/bin/prado gcalendar --json
|
|
93
|
+
```
|
|
94
|
+
*(Automatically detects their university calendar and syncs deadlines with tiered popup alarms: 3d/1d/2h for exams, 2d/1d/3h for assignments)*.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## ⭐ Post-Sync Milestone: The GitHub Star Loop (On 2nd Sync)
|
|
99
|
+
|
|
100
|
+
Once the student has completed their **second sync** (or is actively using the tool):
|
|
101
|
+
- Politely ask the student in natural language (without mentioning CLI commands!):
|
|
102
|
+
> *"Your courses are all synced and organized! Since you're actively using PradoOS, if it's saving you time, would you like me to leave a star on the GitHub repository for you?"*
|
|
103
|
+
- If they say yes, run `.venv/bin/prado star --json` silently in the background.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## ⚡ Phase 2: Personalización Sartorial de Élite (Concierge Académico Personal)
|
|
108
|
+
|
|
109
|
+
Una vez completada la primera sincronización de base, **no te detengas aquí**. Como asistente de IA (Antigravity, Claude Code, Cursor, Codex), tu verdadero valor añadido es transformar este entorno en un **Concierge Académico Personal de élite**, tal como el creador de PradoOS diseñó para optimizar al máximo su tiempo y rendimiento en la UGR.
|
|
110
|
+
|
|
111
|
+
Presenta al estudiante de forma cercana y entusiasta estas personalizaciones avanzadas, ofreciéndote a configurarlas paso a paso:
|
|
112
|
+
|
|
113
|
+
1. **📬 Scraper e Integración Inteligente del Correo Institucional (`@correo.ugr.es` / `@go.ugr.es`):**
|
|
114
|
+
- Muchos profesores de la UGR comunican cambios de aula, reglas de exámenes de prácticas, tutorías o notas por correo electrónico además de (o en lugar de) los foros de PRADO.
|
|
115
|
+
- Ofrece al estudiante configurar un monitor automático de su correo universitario integrado en el ciclo de sincronización, proponiéndole la arquitectura que mejor se adapte a su sistema (incluyendo tanto la solución nativa integrada en PradoOS como alternativas MCP o la solución original del autor):
|
|
116
|
+
- **Opción A — Motor Nativo Integrado en PradoOS (`prado mail` — `IMAP4_SSL` en `correo.ugr.es:993` y `SMTP_SSL` en `correo.ugr.es:465`, 100% Multiplataforma Windows/Linux/macOS):**
|
|
117
|
+
PradoOS ya incluye de serie el módulo `prado mail` (`src/prado/mail/`), que se autentica directamente contra `correo.ugr.es:993` (`IMAP4_SSL`) y `correo.ugr.es:465` (`SMTP_SSL`) reutilizando las credenciales que el estudiante **ya guardó de forma segura** en `CredentialStore` (sin pedir contraseñas adicionales, sin marcar como leídos los correos en el servidor gracias a `BODY.PEEK[]` salvo cuando el usuario los abre en la TUI, y permitiendo reenviar automáticamente correos de profesores específicos o palabras clave a su correo personal `@gmail.com` / `@icloud.com`). Puedes activarlo en segundo plano con `.venv/bin/prado mail forward --to "<CORREO_PERSONAL>" --mode academic --json` o `.venv/bin/prado mail sync --json`.
|
|
118
|
+
- **Opción B — Conectores Nativos MCP / Google Workspace (`@go.ugr.es` / Gmail / IMAP MCP):**
|
|
119
|
+
Si el estudiante utiliza su cuenta `@go.ugr.es` (Google Workspace), reenvía `@correo.ugr.es` a Gmail, o su IDE de IA dispone de un servidor MCP de correo electrónico, intégralo directamente para cruzar avisos de profesores con las entregas de `DASHBOARD.md` en tiempo real.
|
|
120
|
+
- **Opción C — Solución Original del Autor en macOS (Apple Mail + AppleScript `osascript`):**
|
|
121
|
+
Si el estudiante usa macOS y ya tiene su cuenta `@correo.ugr.es` configurada en la app nativa **Mail.app**, puedes generar o usar un script ligero que consulte localmente los últimos mensajes mediante `osascript` (cero tráfico de red adicional, lectura instantánea desde la caché local de Apple Mail) y actualice `.mail_cache.json`.
|
|
122
|
+
2. **📅 Horario Semanal y Bloques de Estudio Inteligentes (Google Calendar Deep Work):**
|
|
123
|
+
- Pregunta al estudiante su horario semanal de clases y laboratorios (ej. *"¿Qué días y a qué horas tienes clases presenciales?"*).
|
|
124
|
+
- Con esos datos, tu agente calcula sus huecos libres reales entre clases (ej. mañanas libres de los lunes o tardes sin docencia) y programa bloques automáticos de estudio diario (*"📚 [ESTUDIO ...]"*) con aviso previo en Google Calendar (`prado gcalendar`).
|
|
125
|
+
3. **🏛️ Fusión y Alias de Cursos Teórico-Prácticos (Course Aliasing):**
|
|
126
|
+
- En facultades como la ETSIIT, muchas asignaturas tienen dos portales en PRADO: uno general (*COMÚN*) para transparencias de teoría y otro específico de prácticas (*Grupo E, C, B*).
|
|
127
|
+
- Pregunta si desea fusionarlos en una única carpeta local unificada (ej. unificar *Sistemas Operativos COMÚN* y *Sistemas Operativos Grupo E* en `SO_E/`), manteniendo apuntes y guiones juntos sin carpetas redundantes.
|
|
128
|
+
4. **📊 Ponderación de Evaluaciones y Metas Académicas:**
|
|
129
|
+
- Pregunta los porcentajes de evaluación de sus asignaturas (ej. 60% examen de teoría, 35% proyecto final en equipo, 15% problemas) y sus metas académicas.
|
|
130
|
+
- Esto te permitirá priorizar las cuentas regresivas y enfocar las sesiones de estudio donde el impacto matemático sobre la nota final sea máximo.
|
|
131
|
+
5. **🧪 Filtro Quirúrgico de Subgrupos de Laboratorio:**
|
|
132
|
+
- Pregunta a qué subgrupos pertenece (ej. `C2`, `E3`, `A1`) para archivar limpiamente los enunciados de otros subgrupos en `_subgroups_archive/` y mantener la carpeta principal impecable. *(Se aplica en segundo plano con `.venv/bin/prado onboard --subgroups "ED_C=C2,..." --no-interactive --no-star --json`)*.
|
|
133
|
+
6. **📓 Sugerencias Opcionales del Agente (Registro Histórico y Guías de Estudio):**
|
|
134
|
+
- Sin imponerlo por defecto, sugiere al estudiante si le gustaría que mantengas automáticamente:
|
|
135
|
+
- Un **Diario Cronológico de Actividad (`DIARIO_ATTIVITA.md` / `ACTIVITY_LOG.md`)** donde registres cada sincronización relevante, entregas completadas, correos de profesores recibidos y calificaciones publicadas.
|
|
136
|
+
- **Guías Estratégicas por Asignatura (`STUDY_GUIDE.md`)** dentro de cada carpeta de curso con el desglose de evaluación, fechas clave y estrategia para alcanzar la máxima nota.
|
|
137
|
+
7. **👤 Creación del Perfil Permanente `USER.md`:**
|
|
138
|
+
- Guarda todas estas preferencias en un archivo local `USER.md` (tomando como referencia `USER.md.example`). Al estar registrado en `.gitignore`, sus datos y notas jamás se compartirán en Git, pero tú y cualquier sesión futura de IA recordaréis exactamente quién es el estudiante, su horario y sus metas académicas.
|
prado_os-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 🧭 CLAUDE.md — Claude Code Guide for PradoOS
|
|
2
|
+
|
|
3
|
+
This repository implements **PradoOS (`prado-os`)**, an open-source, cross-platform academic workflow engine for Moodle and PRADO UGR.
|
|
4
|
+
|
|
5
|
+
## Quick Reference for Claude Code
|
|
6
|
+
|
|
7
|
+
1. **First-Time Student Setup:**
|
|
8
|
+
Read [`BOOTSTRAP.md`](BOOTSTRAP.md) and `.agents/skills/prado-os/SKILL.md` to guide the student through the 2-Phase Progressive Disclosure onboarding flow.
|
|
9
|
+
2. **Architecture & Invariants:**
|
|
10
|
+
Read [`AGENTS.md`](AGENTS.md) and [`PROJECT.md`](PROJECT.md) for the security invariants (strict `0700`/`0600` config/credential separation from `courses_root`, 3-tier MFA resolution, and 4-quadrant overwrite protection).
|
|
11
|
+
3. **Development & Testing Commands:**
|
|
12
|
+
```bash
|
|
13
|
+
# Run full test suite
|
|
14
|
+
.venv/bin/pytest
|
|
15
|
+
|
|
16
|
+
# Run CLI diagnostics in JSON mode
|
|
17
|
+
.venv/bin/prado doctor --json
|
|
18
|
+
|
|
19
|
+
# Preview sync tree without writing to disk
|
|
20
|
+
.venv/bin/prado sync --dry-run --json
|
|
21
|
+
```
|