¶ciel.cli — Interfaz de línea de comandos
Aplicación Typer que expone el comando ciel. El punto de entrada registrado
en PyPI es ciel.cli.main:app.
¶
ciel.cli
Ciel CLI application and command modules.
¶
app = typer.Typer(name='ciel', help='Ciel Agent Framework CLI', no_args_is_help=True)
module-attribute
¶
REPO_ROOT = Path(__file__).resolve().parents[2]
module-attribute
¶
__version__ = _pkg_version('ciel')
module-attribute
¶
app = typer.Typer(name='ciel', help='Ciel Agent Framework CLI', no_args_is_help=True)
module-attribute
¶
console = Console()
module-attribute
¶
ChatMessage
dataclass
Source code in src/ciel/runtime/tools.py
¶
text() -> str
Extract plain text from content, tolerant to multimodal parts.
strcontent is returned verbatim.listcontent concatenates thetextof every part whose type is"text"but drops images/audio/video, so consumers (CLI, compression,AgentResponse.text) see only readable text.
Source code in src/ciel/runtime/tools.py
¶
ChatRequest
dataclass
Source code in src/ciel/runtime/tools.py
¶
DefaultAgentRuntime
Concrete runtime wiring provider + tool execution with tracing.
Source code in src/ciel/runtime/__init__.py
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | |
¶
run_agent_loop(*, request: ChatRequest, tenant_id: Optional[str] = None, toolset: Optional[str] = None, limit: int = 32) -> AgentRuntimeResult
async
Run the agentic tool loop (multi-turn ReAct when tools are present).
The loop issues up to limit model completions. After each completion
that requests tool calls, the requested tools are dispatched and their
results are appended as messages; the model is then called again. The
loop stops when (a) the model returns no tool calls (finish_reason
stop), (b) limit turns are exhausted, or (c) there are no tools
registered in the request.
Backward compatibility: when limit <= 1 or request.tools is
empty, exactly one completion is produced and no tool messages are
appended (the historical single-step behaviour). Every tool result from
every turn is preserved in loop_results so the facade can surface
the full tool_results list.
Source code in src/ciel/runtime/__init__.py
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | |
¶
stream_tokens(*, request: ChatRequest, tenant_id: Optional[str] = None, toolset: Optional[str] = None) -> AsyncIterator[str]
async
Stream incremental assistant tokens from the provider.
Calls provider.stream (real SSE streaming) and re-emits the
partial content of each incremental :class:ChatResponse as it
arrives, so callers see the answer grow token by token.
Source code in src/ciel/runtime/__init__.py
¶
DefaultToolDispatcher
Dispatch tool requests to a configured ToolProvider.
Source code in src/ciel/runtime/__init__.py
¶
ToolProvider
dataclass
Concrete tool provider used by the runtime's tool dispatcher.
Source code in src/ciel/runtime/__init__.py
¶
ToolRegistry
Source code in src/ciel/runtime/tools.py
¶
_EchoProvider
Proveedor offline: devuelve echo:<prompt> sin red.
Source code in src/ciel/cli/main.py
¶
_build_runtime()
Construye un runtime para la CLI.
Usa el proveedor remoto si CIEL_PROVIDER_URL está configurado, o un
proveedor echo offline en caso contrario (para smoke tests sin red).
Source code in src/ciel/cli/main.py
¶
_callback(ctx: typer.Context, verbose: bool = typer.Option(False, '--verbose', '-v', help='Log extended')) -> None
¶
_load_board_group()
¶
_load_chat_group()
¶
_load_cost_group()
¶
_load_eval_group()
¶
_load_flow_group()
¶
_load_graph_group()
¶
_load_loop_group()
¶
_load_rbac_group()
¶
_load_root_group()
¶
_load_skills_group()
¶
_load_studio_group()
¶
_load_swarm_group()
¶
_ok(value: str) -> str
¶
_run_stream(runtime, request: ChatRequest, tenant: Optional[str]) -> None
Imprime tokens en tiempo real usando runtime.stream_tokens.
stream_tokens re-emite el contenido creciente del assistant, así que
aquí solo se imprime el delta respecto al fragmento anterior para evitar
duplicados. Si no hay fragmentos, se imprime un aviso.
Source code in src/ciel/cli/main.py
¶
chat(message: str = typer.Argument(..., help='Prompt a enviar al agente'), app_name: str = typer.Option('default', '--app', '-a', help='App name to chat with'), quiet: bool = typer.Option(False, '-q', '--quiet', help='Minimal output mode'), stream: bool = typer.Option(False, '--stream', help='Streaming en tiempo real (SSE por consola)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id (default $CIEL_TENANT)')) -> None
Envía un prompt al runtime y muestra la respuesta.
Con --stream se imprimen los fragmentos incrementalmente en tiempo real.
Si el proveedor no implementa streaming, hace fallback a un único chunk con
el texto completo.
Source code in src/ciel/cli/main.py
¶
checkpoints(session_root: Path = typer.Option(..., '--session-root', exists=True, file_okay=False, help='Directory containing session state')) -> None
Source code in src/ciel/cli/main.py
¶
compression(target_dir: Path = typer.Option(..., '--target-dir', exists=True, file_okay=False, help='Project root for context loading'), max_chars: int = typer.Option(20000, '--max-chars', help='Context render budget in characters')) -> None
Source code in src/ciel/cli/main.py
¶
doctor() -> None
Source code in src/ciel/cli/main.py
¶
info() -> None
¶
init(path: Path = typer.Argument(Path('.'), help='Target directory (created if missing)'), force: bool = typer.Option(False, '--force', help='Overwrite existing files')) -> None
Scaffold a new Ciel project (pyproject + agent + ciel.yaml). Offline-safe.
Source code in src/ciel/cli/main.py
¶
observe(otel_endpoint: str | None = typer.Option(None, '--otel-endpoint', help='OTLP collector endpoint')) -> None
Initialize a tracing session and report OpenTelemetry observability status.
Useful as a smoke check of the observability stack: prints whether OTel is available, the active exporter, and the number of spans emitted so far.
Source code in src/ciel/cli/main.py
¶
run(agent: str = typer.Option(..., '--agent', '-a', help='Agent module or path'), input_file: Path | None = typer.Option(None, '--input', '-i', help='Input file')) -> None
Source code in src/ciel/cli/main.py
¶
serve(host: str = typer.Option('0.0.0.0', '--host', help='Bind host'), port: int = typer.Option(8080, '--port', '-p', help='Bind port'), tenant: str | None = typer.Option(None, '--tenant', help='Default tenant id (else $CIEL_TENANT)'), otel: bool = typer.Option(False, '--otel', help='Enable OpenTelemetry tracing (in-memory or OTLP endpoint)'), otel_endpoint: str | None = typer.Option(None, '--otel-endpoint', help='OTLP collector endpoint (else in-memory exporter)')) -> None
Run the composed Ciel gateway (control + MCP host + webhook) via uvicorn.
Source code in src/ciel/cli/main.py
¶
_ = (tempfile, Path)
module-attribute
¶
console = Console()
module-attribute
¶
root_app = typer.Typer(name='root', help='Run the root agent (ADK sub_agents, offline-safe)')
module-attribute
¶
MemoryStore
Bases: SqliteStateBackend
Alias retrocompatible a :class:SqliteStateBackend (SQLite en disco).
MemoryStore es SQLite en disco desde F5 — el nombre engaña. Para
F15 se refactorizó para heredar de StateBackend de modo que cualquier
store que hoy recibe un MemoryStore puede recibir también un
PostgresStateBackend compartido sin cambios de API.
Se sigue construyendo con la misma firma: MemoryStore(db_path).
Source code in src/ciel/runtime/memory.py
¶
RootAgent
Builder declarativo: add_specialist + set_router; luego compile.
El router decide a qué specialist enrutar la petición. Si devuelve
None, el root_handler (opcional) la atiende localmente.
Source code in src/ciel/orchestration/root.py
¶
RootRunner
Ejecuta el root agent: enruta la petición y ejecuta el specialist (o root).
Source code in src/ciel/orchestration/root.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
¶
route(prompt: str, *, session_id: Optional[str] = None, session_store: Optional[SessionStore] = None, tenant_id: Optional[str] = None) -> RootState
async
Enruta y ejecuta la petición, manteniendo session state por tenant.
Si se pasa session_id (+ session_store/tenant_id), el estado
del agente se reconstruye con el historial de turnos previos y el turno
resultante se persiste para la próxima invocación (estilo ADK session
state entre turnos). OFFLINE-SAFE: no requiere red ni proveedor.
Source code in src/ciel/orchestration/root.py
¶
RootState
dataclass
Estado compartido del root agent: prompt, ruta elegida y resultado.
history acumula los turnos previos de la session (estilo ADK session
state) para que el agente recuerde contexto entre invocaciones.
Source code in src/ciel/orchestration/root.py
¶
SessionStore
Estado de session durable por tenant, sobre MemoryStore.
Claves internas (todas namespaced con session: para no colisionar con
otros módulos que usan el mismo MemoryStore):
session:<sid>:turns-> {"version", "turns": [turn, ...]}session:<sid>:state-> {key: value} (estado arbitrario)session:<sid>:board_links-> {"links": [board_task_id, ...]}session_index:<tenant>-> {"sessions": [sid, ...]} (índice)
Source code in src/ciel/orchestration/session.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
¶
append_turn(*, tenant_id: Optional[str], session_id: str, turn: Dict[str, Any]) -> None
Añade un turno al historial de la session y lo registra en el índice.
Source code in src/ciel/orchestration/session.py
¶
history(*, tenant_id: Optional[str], session_id: str) -> List[Dict[str, Any]]
Recupera el historial de turnos (vacío si la session no existe).
Source code in src/ciel/orchestration/session.py
¶
link_board_task(*, tenant_id: Optional[str], session_id: str, board_task_id: str) -> None
Vincula una tarea del board (mismo tenant) con la session.
Source code in src/ciel/orchestration/session.py
¶
list_sessions(*, tenant_id: Optional[str]) -> List[str]
¶
Specialist
dataclass
Agente especialista enrutado por el root agent (ADK sub_agent).
Source code in src/ciel/orchestration/root.py
¶
Supervisor
Source code in src/ciel/orchestration/supervisor.py
¶
_build_demo_agent() -> RootAgent
Root agent de DEMOSTRACIÓN EN MEMORIA con 2 specialists + root handler.
Source code in src/ciel/cli/root.py
¶
_db_handler(state: RootState) -> str
¶
_demo_router(prompt: str)
Source code in src/ciel/cli/root.py
¶
_net_handler(state: RootState) -> str
¶
_resolve_session_store(db: Optional[str], tenant: Optional[str])
Resuelve un SessionStore sobre MemoryStore (o None si no hay --db).
¶
_root_handler(state: RootState) -> str
¶
route(prompt: str = typer.Argument(..., help='Petición a enrutar por el root agent'), db: Optional[str] = typer.Option(None, '--db', help='Ruta del MemoryStore SQLite para session state persistente'), session_id: Optional[str] = typer.Option(None, '--session-id', help='Id de session (mantiene historial entre turnos)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id (multitenancy nativo)')) -> None
Route a prompt through the demo root agent (offline, ADK sub_agents).
Con --db + --session-id el agente recuerda turnos previos entre
invocaciones (session state persistente por tenant, estilo ADK). Sin red ni
proveedor.
Source code in src/ciel/cli/root.py
¶
__all__ = ['chat_app', 'group']
module-attribute
¶
chat_app = typer.Typer(name='chat', help='Run group chats (AutoGen-style, offline-safe)')
module-attribute
¶
console = Console()
module-attribute
¶
Agent
dataclass
Participante conversable del group chat.
responder recibe el GroupChatState y devuelve el texto de su
réplica (o una corutina). Si terminate_if recibe el texto y devuelve
True, ese mensaje finaliza la conversación.
Source code in src/ciel/orchestration/chat.py
¶
GroupChat
Configuración del group chat (participantes + estrategia de turnos).
Source code in src/ciel/orchestration/chat.py
¶
GroupChatManager
Ejecuta el diálogo: selecciona hablante, recolecta réplica, evalúa
terminación. Montado SOBRE Supervisor para heredar retry/timeout/budget
por participante (cada réplica es un worker del supervisor).
Source code in src/ciel/orchestration/chat.py
¶
GroupChatState
dataclass
Estado compartido del group chat: transcripto + metadata por ronda.
Source code in src/ciel/orchestration/chat.py
¶
Supervisor
Source code in src/ciel/orchestration/supervisor.py
¶
_build_demo_chat(max_rounds: int) -> GroupChat
Group chat de DEMOSTRACION EN MEMORIA con 3 agentes locales.
No usa red ni proveedor; cada responder opera sobre state.transcript
(OFFLINE-SAFE). El revisor emite TERMINATE cuando ya existe propuesta,
implementacion y pruebas -> el chat converge deterministicamente.
Source code in src/ciel/cli/chat.py
¶
_coder(state: GroupChatState) -> str
¶
_print_transcript(state: GroupChatState, title: str = 'Group chat') -> None
Imprime con Rich el transcripto (role/content/round) y un Panel resumen.
Source code in src/ciel/cli/chat.py
¶
_reviewer(state: GroupChatState) -> str
Revisor: propone en su primera ronda; cuando coder y tester ya reaccionaron a la propuesta, aprueba y emite TERMINATE (converge).
Source code in src/ciel/cli/chat.py
¶
_tester(state: GroupChatState) -> str
¶
group(message: str = typer.Option('Tarea: disenar feature de login', '--message', '-m', help='Mensaje inicial del usuario que dispara el chat'), rounds: int = typer.Option(12, '--rounds', '-r', help='Numero maximo de rondas antes de forzar fin'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id (para trazabilidad)')) -> None
Run an offline 3-agent group chat that converges with TERMINATE.
Source code in src/ciel/cli/chat.py
¶
DEFAULT_DB_NAME = 'ciel_loop.sqlite3'
module-attribute
¶
__all__ = ['loop_app', 'run', 'resume']
module-attribute
¶
console = Console()
module-attribute
¶
loop_app = typer.Typer(name='loop', help='Autonomous event-loop agent (offline-safe)')
module-attribute
¶
AutonomousAgent
Agente autónomo que descompone un objetivo en tareas y las ejecuta.
OFFLINE-SAFE: por defecto el planner es un handler local que parte el
objetivo en una sola tarea (o N tareas si se provee plan explícito);
cada tarea se ejecuta con un handler local sobre task.payload (no red).
Source code in src/ciel/orchestration/agent.py
¶
run_goal(goal: str, handler: TaskHandler, *, plan: Optional[Sequence[str]] = None) -> List[Task]
async
Descompone goal en tareas (plan o una sola) y las ejecuta.
Cada tarea completada persiste un turno en SessionStore si está
disponible (ADK session_state entre ejecuciones del agente).
Source code in src/ciel/orchestration/agent.py
¶
EventLoop
Bucle de eventos durable que ejecuta una Task con reintentos.
Montado sobre Supervisor (retry/timeout/budget por worker). Si el
handler falla, el loop aplica backoff exponencial (capped) y reintenta
hasta max_attempts. Tras cada intento persiste un checkpoint en
MemoryStore; resume rehidrata y continúa.
OFFLINE-SAFE: el handler por defecto (y los demos) son funciones locales.
Source code in src/ciel/orchestration/agent.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | |
¶
resume(*, run_id: str, handler: TaskHandler) -> Task
async
Reanuda un loop interrumpido desde su último checkpoint.
Rehidrata la Task persistida y continúa los reintentos. Si la tarea
ya estaba en succeeded/failed al interrumpirse, la devuelve tal
cual (idempotente). Cumple el criterio Fase 6: tras reinicio continúa y
completa.
Source code in src/ciel/orchestration/agent.py
¶
run(task: Task, handler: TaskHandler, *, run_id: Optional[str] = None) -> Task
async
Ejecuta task con reintentos exponenciales hasta completar o agotar.
Source code in src/ciel/orchestration/agent.py
¶
EventLoopCheckpointStore
Persiste el estado del EventLoop sobre MemoryStore.
Clave: loop:<run_id> (namespaced para no colisionar con otros módulos).
Guarda el Task en curso + el estado del loop (intent, estado global).
Source code in src/ciel/orchestration/agent.py
¶
MemoryStore
Bases: SqliteStateBackend
Alias retrocompatible a :class:SqliteStateBackend (SQLite en disco).
MemoryStore es SQLite en disco desde F5 — el nombre engaña. Para
F15 se refactorizó para heredar de StateBackend de modo que cualquier
store que hoy recibe un MemoryStore puede recibir también un
PostgresStateBackend compartido sin cambios de API.
Se sigue construyendo con la misma firma: MemoryStore(db_path).
Source code in src/ciel/runtime/memory.py
¶
Supervisor
Source code in src/ciel/orchestration/supervisor.py
¶
Task
dataclass
Unidad de trabajo autónomo durable (estilo ADK/AutoGen task).
status: pending | running | succeeded | failed.
attempts se incrementa por cada ejecución del handler.
Source code in src/ciel/orchestration/agent.py
¶
_demo_handler(task: Task)
Handler OFFLINE: devuelve echo del objetivo, sin red ni proveedor.
¶
_print_task(task: Task, title: str = 'Task') -> None
Source code in src/ciel/cli/loop.py
¶
resolve_db_path(db_flag: Optional[Path]) -> str
Resuelve la ruta del loop SQLite.
Prioridad: --db > variable de entorno CIEL_LOOP_DB > archivo por
defecto en el directorio actual.
Source code in src/ciel/cli/loop.py
¶
resume(run_id: str = typer.Option(..., '--run-id', help='Run id to resume'), db: Path = typer.Option(..., '--db', help='Same SQLite db used in run'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id'), session: Optional[str] = typer.Option(None, '--session-id', help='Session id')) -> None
Resume an interrupted autonomous loop from its last checkpoint (requires --db).
Source code in src/ciel/cli/loop.py
¶
run(goal: str = typer.Argument(..., help='Goal for the autonomous agent'), run_id: Optional[str] = typer.Option(None, '--run-id', help='Explicit run id (for later resume)'), db: Optional[Path] = typer.Option(None, '--db', help='SQLite db for the checkpointer (or CIEL_LOOP_DB)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id'), session: Optional[str] = typer.Option(None, '--session-id', help='Session id (for session state)')) -> None
Run an autonomous agent over a goal offline (no network, no provider).
With --db the run is persisted via a checkpointer so it can be resumed
later with the resume command (e.g. after a process restart).
Source code in src/ciel/cli/loop.py
¶
DEFAULT_DB_NAME = 'ciel_graph.sqlite3'
module-attribute
¶
__all__ = ['graph_app', 'demo', 'run', 'resume']
module-attribute
¶
console = Console()
module-attribute
¶
graph_app = typer.Typer(name='graph', help='Build and run explicit state graphs (offline-safe)')
module-attribute
¶
AgentSpec
dataclass
Source code in src/ciel/orchestration/__init__.py
¶
GraphCheckpointStore
Persistencia de checkpoints del grafo sobre MemoryStore.
Mismo patrón que ciel.runtime.checkpoints.CheckpointStore: usa la
clave (tenant_id, session_id, key) ya con multitenancy nativo.
Source code in src/ciel/orchestration/graph.py
¶
GraphState
dataclass
Estado mutable compartido entre nodos (estilo state machine LangGraph).
Source code in src/ciel/orchestration/graph.py
¶
MemoryStore
Bases: SqliteStateBackend
Alias retrocompatible a :class:SqliteStateBackend (SQLite en disco).
MemoryStore es SQLite en disco desde F5 — el nombre engaña. Para
F15 se refactorizó para heredar de StateBackend de modo que cualquier
store que hoy recibe un MemoryStore puede recibir también un
PostgresStateBackend compartido sin cambios de API.
Se sigue construyendo con la misma firma: MemoryStore(db_path).
Source code in src/ciel/runtime/memory.py
¶
StateGraph
Grafo de estado explícito, cíclico/condicional, con checkpoint.
add_noderegistra un nodo ejecutable.add_edgeregistra una transición incondicional.add_conditional_edgesregistra transiciones con guarda booleana.set_entry_point/set_finish_pointdefinen inicio y fin.
Source code in src/ciel/orchestration/graph.py
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
¶
_build_demo_graph() -> StateGraph
Grafo de DEMOSTRACIÓN EN MEMORIA: entry -> plan -> execute -> finish.
No usa red ni proveedor; cada nodo escribe en state.data y devuelve un
payload. Pensado para smoke tests offline (igual que ciel chat con
_EchoProvider).
Source code in src/ciel/cli/graph.py
¶
_build_spec_graph(spec: AgentSpec) -> StateGraph
Construye un grafo pipeline lineal a partir de un AgentSpec.
entry = steps[0].id, aristas en orden, finish = último paso.
Cada nodo guarda en state.data[f"__out__{step.id}"] un dict con el
id/kind/prompt del paso.
Source code in src/ciel/cli/graph.py
¶
_print_state(state: GraphState, title: str = 'Graph') -> None
Imprime con Rich el estado resultante del grafo.
Source code in src/ciel/cli/graph.py
¶
demo() -> None
Run an in-memory demo graph offline (no network, no provider).
Source code in src/ciel/cli/graph.py
¶
resolve_db_path(db_flag: Optional[Path]) -> str
Resuelve la ruta del grafo SQLite.
Prioridad: --db > variable de entorno CIEL_GRAPH_DB > archivo por
defecto en el directorio actual.
Source code in src/ciel/cli/graph.py
¶
resume(run_id: str = typer.Option(..., '--run-id', help='Run id to resume'), db: Path = typer.Option(..., '--db', help='Same SQLite db used in run'), spec: Optional[Path] = typer.Option(None, '--spec', '-s', help='YAML AgentSpec used in run (else demo graph)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id')) -> None
Resume an interrupted graph from its last checkpoint.
Source code in src/ciel/cli/graph.py
¶
run(spec: Optional[Path] = typer.Option(None, '--spec', '-s', help='YAML AgentSpec (name, topology, steps[{id,kind,tool,prompt,depends_on}])'), run_id: Optional[str] = typer.Option(None, '--run-id', help='Explicit run id (for later resume)'), db: Optional[Path] = typer.Option(None, '--db', help='SQLite db for the checkpointer (or CIEL_GRAPH_DB)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id')) -> None
Run a graph. With --spec builds a linear pipeline; otherwise runs the demo.
Source code in src/ciel/cli/graph.py
¶
DEFAULT_DB_NAME = 'ciel_flow.sqlite3'
module-attribute
¶
__all__ = ['flow_app', 'run', 'resume']
module-attribute
¶
console = Console()
module-attribute
¶
flow_app = typer.Typer(name='flow', help='Build and run event-driven flows (offline-safe)')
module-attribute
¶
Flow
Constructor declarativo de flows event-driven.
API: add_start, add_listen, add_router; luego compile.
Los IDs de paso se derivan del __name__ de la función salvo que se
pasen explícitamente.
Source code in src/ciel/orchestration/flows.py
¶
add_branch(fn: StepFn, step_id: Optional[str] = None) -> 'Flow'
Paso rama activado EXCLUSIVAMENTE por un router (no tiene fuente de datos).
Source code in src/ciel/orchestration/flows.py
¶
FlowCheckpointStore
Persistencia de checkpoints de flow sobre MemoryStore (multitenancy nativo).
Source code in src/ciel/orchestration/flows.py
¶
FlowRunner
Ejecuta el flow paso a paso (event-driven) con checkpoint + resume.
Source code in src/ciel/orchestration/flows.py
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | |
¶
resume(*, run_id: str) -> FlowState
async
Reanuda un flow interrumpido desde su último checkpoint.
Reconstruye el estado persistido y recomputa las ramas de router ya activadas (derivables de los routers completados + sus resultados).
Source code in src/ciel/orchestration/flows.py
¶
FlowState
dataclass
Estado mutable compartido entre pasos de un flow (estilo CrewAI state).
Source code in src/ciel/orchestration/flows.py
¶
MemoryStore
Bases: SqliteStateBackend
Alias retrocompatible a :class:SqliteStateBackend (SQLite en disco).
MemoryStore es SQLite en disco desde F5 — el nombre engaña. Para
F15 se refactorizó para heredar de StateBackend de modo que cualquier
store que hoy recibe un MemoryStore puede recibir también un
PostgresStateBackend compartido sin cambios de API.
Se sigue construyendo con la misma firma: MemoryStore(db_path).
Source code in src/ciel/runtime/memory.py
¶
Supervisor
Source code in src/ciel/orchestration/supervisor.py
¶
_build_demo_flow() -> Flow
Flow de DEMOSTRACIÓN EN MEMORIA (estilo CrewAI.Flows).
ingest -> transform -> router {a: branch_a, b: branch_b}
No usa red ni proveedor; cada paso escribe en state.data. Pensado para
smoke tests offline.
Source code in src/ciel/cli/flow.py
¶
_print_state(state: FlowState, title: str = 'Flow') -> None
Imprime con Rich el estado resultante del flow.
Source code in src/ciel/cli/flow.py
¶
resolve_db_path(db_flag: Optional[Path]) -> str
Resuelve la ruta del grafo SQLite.
Prioridad: --db > variable de entorno CIEL_FLOW_DB > archivo por
defecto en el directorio actual.
Source code in src/ciel/cli/flow.py
¶
resume(run_id: str = typer.Option(..., '--run-id', help='Run id to resume'), db: Path = typer.Option(..., '--db', help='Same SQLite db used in run'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id')) -> None
Resume an interrupted flow from its last checkpoint (requires --db).
Source code in src/ciel/cli/flow.py
¶
run(run_id: Optional[str] = typer.Option(None, '--run-id', help='Explicit run id (for later resume)'), db: Optional[Path] = typer.Option(None, '--db', help='SQLite db for the checkpointer (or CIEL_FLOW_DB)'), tenant: Optional[str] = typer.Option(None, '--tenant', help='Tenant id')) -> None
Run an in-memory demo flow offline (no network, no provider).
With --db the run is persisted via a checkpointer so it can be resumed
later with the resume command.
Source code in src/ciel/cli/flow.py
¶
DEFAULT_DB_NAME = 'ciel_board.sqlite3'
module-attribute
¶
__all__ = ['board_app', 'add', 'list_tasks', 'show', 'assign']
module-attribute
¶
board_app = typer.Typer(name='board', help='Manage the durable kanban board')
module-attribute
¶
console = Console()
module-attribute
¶
BoardTask
Source code in src/ciel/orchestration/board.py
¶
KanbanBoard
In-memory kanban board, optionally backed by a durable SQLite store.
When path is None (the default) the board keeps tasks in a process
local dict exactly as before -- this preserves the existing in-memory
API used by the gateway and the CLI. When path is provided the board
transparently reads and writes every task to a SQLite database opened in
WAL mode, so state survives process restarts.
Source code in src/ciel/orchestration/board.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | |
¶
add(title: str = typer.Argument(..., help='Task title'), task_id: Optional[str] = typer.Option(None, '--id', help='Task id'), assignee: Optional[str] = typer.Option(None, '--assignee', '-a', help='Assignee'), tenant_id: Optional[str] = typer.Option(None, '--tenant-id', help='Tenant id'), db: Optional[str] = typer.Option(None, '--db', help='Ruta del board SQLite (o CIEL_BOARD_DB)')) -> None
Source code in src/ciel/cli/board.py
¶
assign(task_id: str = typer.Argument(...), assignee: str = typer.Argument(...), db: Optional[str] = typer.Option(None, '--db', help='Ruta del board SQLite (o CIEL_BOARD_DB)')) -> None
Source code in src/ciel/cli/board.py
¶
list_tasks(status: Optional[str] = typer.Option(None, '--status', '-s'), assignee: Optional[str] = typer.Option(None, '--assignee', '-a'), tenant_id: Optional[str] = typer.Option(None, '--tenant-id'), db: Optional[str] = typer.Option(None, '--db', help='Ruta del board SQLite (o CIEL_BOARD_DB)')) -> None
Source code in src/ciel/cli/board.py
¶
resolve_db_path(db_flag: Optional[str]) -> Optional[str]
Resuelve la ruta del board SQLite.
Prioridad: --db > variable de entorno CIEL_BOARD_DB > archivo por
defecto en el directorio actual. Si nada está configurado se devuelve
None (board en memoria, comportamiento legacy).
Source code in src/ciel/cli/board.py
¶
show(task_id: str = typer.Argument(...), db: Optional[str] = typer.Option(None, '--db', help='Ruta del board SQLite (o CIEL_BOARD_DB)')) -> None
Source code in src/ciel/cli/board.py
¶
Worker = Callable[[WorkerContext], Any]
module-attribute
¶
console = Console()
module-attribute
¶
swarm_app = typer.Typer(name='swarm', help='Run agent swarms from an AgentSpec')
module-attribute
¶
AgentSpec
dataclass
Source code in src/ciel/orchestration/__init__.py
¶
Budget
dataclass
¶
RateLimiter
Source code in src/ciel/orchestration/budget.py
¶
Supervisor
Source code in src/ciel/orchestration/supervisor.py
¶
TopologyEngine
Source code in src/ciel/orchestration/topology.py
¶
_AdHocContext
dataclass
¶
_NoCounter
¶
_SupervisorStepRunner
Source code in src/ciel/cli/swarm.py
¶
swarm_run(spec: typer.FileText = typer.Option(..., '--spec', '-s', help='YAML AgentSpec file'), max_tools: int = typer.Option(8, '--max-tools', help='Max tool calls'), max_tokens: int | None = typer.Option(None, '--max-tokens', help='Max token count'), seconds: float = typer.Option(60.0, '--seconds', help='Max wall-clock seconds'), rate_limit: int = typer.Option(0, '--rate-limit', help='Per-step rate limit (0 disables)')) -> None
Source code in src/ciel/cli/swarm.py
CLI ciel cost — cost governance (offline-safe, demo en memoria).
Comandos
ciel cost record --tenant T --model gpt-4o --in 1000 --out 500 [--price-in X --price-out Y] ciel cost status --tenant T ciel cost check --tenant T --model gpt-4o --in 1000 --out 500
¶
console = Console()
module-attribute
¶
cost_app = typer.Typer(name='cost', help='Cost governance per tenant/model')
module-attribute
¶
BudgetExceededError
¶
CostGovernor
Gobernador de costos por tenant con presupuesto, alertas y corte.
Source code in src/ciel/enterprise/cost.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
¶
alerted(tenant_id: str) -> bool
True si el gasto cruzó el umbral de alerta (alert_threshold).
Source code in src/ciel/enterprise/cost.py
¶
allowed(tenant_id: str, model: str, input_tokens: int, output_tokens: int) -> bool
False si gasto actual + estimado supera el presupuesto.
Source code in src/ciel/enterprise/cost.py
¶
budget_of(tenant_id: str) -> float
Presupuesto efectivo: el del tenant o el global "*".
Source code in src/ciel/enterprise/cost.py
¶
check_budget(tenant_id: str, model: str, input_tokens: int, output_tokens: int) -> None
Lanza BudgetExceededError si la operación no está permitida.
Source code in src/ciel/enterprise/cost.py
¶
estimate(model: str, input_tokens: int, output_tokens: int) -> float
Costo estimado de una llamada (en $).
Source code in src/ciel/enterprise/cost.py
¶
record(tenant_id: str, model: str, input_tokens: int, output_tokens: int) -> float
Acumula el gasto del tenant y devuelve el gasto actual.
Source code in src/ciel/enterprise/cost.py
¶
remaining(tenant_id: str) -> float
¶
ModelCost
dataclass
¶
_governor() -> CostGovernor
¶
check(tenant: str = typer.Option(..., '--tenant', help='Tenant id'), model: str = typer.Option(..., '--model', help='Model id'), input_tokens: int = typer.Option(..., '--in', help='Input tokens'), output_tokens: int = typer.Option(..., '--out', help='Output tokens'), price_in: float = typer.Option(0.005, '--price-in', help='$/1k input'), price_out: float = typer.Option(0.015, '--price-out', help='$/1k output')) -> None
Check whether a planned usage is within budget (exit 1 if denied).
Source code in src/ciel/cli/cost.py
¶
record(tenant: str = typer.Option(..., '--tenant', help='Tenant id'), model: str = typer.Option(..., '--model', help='Model id'), input_tokens: int = typer.Option(..., '--in', help='Input tokens'), output_tokens: int = typer.Option(..., '--out', help='Output tokens'), price_in: float = typer.Option(0.005, '--price-in', help='$/1k input'), price_out: float = typer.Option(0.015, '--price-out', help='$/1k output')) -> None
Record a usage and print the running spend for the tenant.
Source code in src/ciel/cli/cost.py
¶
status(tenant: str = typer.Option(..., '--tenant', help='Tenant id')) -> None
Show current spend / budget / remaining for a tenant.
Source code in src/ciel/cli/cost.py
CLI ciel rbac — gestión de roles y permisos (offline-safe, demo en memoria).
Comandos
ciel rbac list-roles # imprime roles y permisos por defecto ciel rbac assign --subject X --role admin [--tenant T] ciel rbac check --subject X --action agent:run [--tenant T]
¶
DEFAULT_ROLES: dict[str, Role] = {'admin': Role('admin', frozenset({'agent:*', 'tools:*', 'admin:*', 'board:*', 'cost:*', 'approve:*'})), 'operator': Role('operator', frozenset({'agent:run', 'tools:exec', 'board:write'})), 'viewer': Role('viewer', frozenset({'agent:read', 'board:read'}))}
module-attribute
¶
console = Console()
module-attribute
¶
rbac_app = typer.Typer(name='rbac', help='RBAC roles and permissions')
module-attribute
¶
RBACEngine
Source code in src/ciel/enterprise/rbac.py
¶
RBACError
¶
assign(subject: str = typer.Option(..., '--subject', help='Subject (user/service)'), role: str = typer.Option(..., '--role', help='Role name'), tenant: str | None = typer.Option(None, '--tenant', help='Tenant id')) -> None
Assign a role to a subject (demo in-memory engine).
Source code in src/ciel/cli/rbac.py
¶
check(subject: str = typer.Option(..., '--subject', help='Subject (user/service)'), action: str = typer.Option(..., '--action', help='Action, e.g. agent:run'), tenant: str | None = typer.Option(None, '--tenant', help='Tenant id')) -> None
Check whether a subject may perform an action (demo in-memory engine).
Source code in src/ciel/cli/rbac.py
¶
list_roles() -> None
List built-in roles and their permissions.
Source code in src/ciel/cli/rbac.py
Project scaffolding for ciel init.
Generates a minimal, offline-runnable Ciel project: a pyproject with a
ciel.plugins entry point example, a starter agent module and a ciel.yaml.
Idempotent: refuses to overwrite existing files unless force=True.
¶
_AGENT = '"""Starter Ciel agent (offline-safe).\n\nRun with: uv run python {module}.py\n"""\nfrom __future__ import annotations\n\nfrom ciel.providers import OpenAICompatibleProvider\nfrom ciel.runtime import (\n ChatMessage,\n ChatRequest,\n DefaultAgentRuntime,\n DefaultToolDispatcher,\n ToolProvider,\n ToolRegistry,\n)\nfrom ciel.runtime.tools import ToolSpec, Tool, ToolsetSchema\n\n\ndef _echo(arguments, *, tool_call_id="", tenant_id=None):\n from ciel.runtime.tools import ToolResult\n return ToolResult(id=tool_call_id, name="echo", output={"echo": str(arguments.get("text", ""))})\n\n\ndef build_runtime():\n # Offline echo provider; swap for OpenAICompatibleProvider(base_url=..., api_key=...) with a real key.\n registry = ToolRegistry(default_toolset="default")\n registry.register_tool(\n "default",\n Tool(spec=ToolSpec(name="echo", description="Echo text", parameters={"text": {"type": "string"}}), callable_=_echo),\n )\n dispatcher = DefaultToolDispatcher(\n provider=ToolProvider(registry=registry, require_tenant_on_execution=False),\n default_toolset="default",\n )\n return DefaultAgentRuntime(provider=_EchoProvider(), dispatcher=dispatcher)\n\n\nclass _EchoProvider:\n provider_name = "echo"\n\n async def complete(self, request):\n from ciel.runtime import ChatChoice, ChatResponse\n prompt = request.messages[-1].content if request.messages else ""\n return ChatResponse(\n choice=ChatChoice(message=ChatMessage(role="assistant", content=f"echo: {prompt}"), finish_reason="stop"),\n metadata={},\n )\n\n async def stream(self, request):\n return (await self.complete(request),)\n\n async def models(self):\n from ciel.providers import ModelInfo\n return [ModelInfo(id="echo", provider="echo")]\n\n\nif __name__ == "__main__":\n import asyncio\n rt = build_runtime()\n result = asyncio.run(rt.run_agent_loop(request=ChatRequest(messages=[ChatMessage(role="user", content="hello")]), tenant_id="default"))\n print(getattr(getattr(result.response, "choice", None), "message", None).text())\n'
module-attribute
¶
_CIEL_YAML = 'project: {name}\ndefault_tenant: default\nproviders:\n - name: echo\n base_url: http://localhost:8000/v1\ntoolsets:\n - name: default\n description: default toolset\n'
module-attribute
¶
_PYPROJECT = '[project]\nname = "{name}"\nversion = "0.1.0"\ndescription = "A Ciel Agent Framework project"\nrequires-python = ">=3.11"\ndependencies = ["mana-ciel"]\n\n[project.entry-points."ciel.plugins"]\n# Register your own providers/tools/agents here, e.g.:\n# my_provider = my_project.plugins:register_provider\n\n[build-system]\nrequires = ["setuptools>=68"]\nbuild-backend = "setuptools.build_meta"\n\n[tool.setuptools]\npy-modules = ["{module}"]\n'
module-attribute
¶
scaffold_project(target: Path, *, force: bool = False) -> List[str]
Source code in src/ciel/cli/scaffold.py
CLI ciel skills — dynamic skill library management (offline-safe).
Comandos
ciel skills list ciel skills create --name N --description D --code-file F ciel skills verify --name N --test-cases FILE ciel skills remove --name N
Todo opera sobre un :class:SkillLibrary en memoria/offline (sin red ni API
keys), usando las primitivas de :mod:ciel.runtime.skills_lib
(SkillLibrary, SkillVerifier). Se exponen funciones de lógica pura
(list_registered, create_skill, verify_skill, remove_skill)
que reciben la librería explícitamente, de modo que los tests pueden inyectar
su propia instancia y compartir estado entre llamadas.
¶
console = Console()
module-attribute
¶
skills_app = typer.Typer(name='skills', help='Dynamic skill library management (offline)')
module-attribute
¶
Skill
dataclass
¶
SkillLibrary
Writable, in-memory skill store that wraps a :class:SkillRegistry.
The registry remains the source of truth for disk-loaded skills; the
library layer adds creation, registration, update (with version bump) and
removal of skills that live only in memory. Tenant isolation is supported
via the optional tenant_id key on each stored :class:Skill.
Source code in src/ciel/runtime/skills_lib.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
¶
create_from_code(*, name: str, description: str, code: str, category: Optional[str] = None, tenant_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> Skill
Compile code (syntax check) and store it as a new skill.
Raises :class:SkillError if the code does not compile. The skill is
NOT executed here — execution belongs to :class:SkillVerifier.
Source code in src/ciel/runtime/skills_lib.py
¶
history(name: str) -> List[Skill]
¶
load_from_disk() -> List[Skill]
Discover skills from the registry roots and index them in-memory.
¶
register(skill: Skill) -> Skill
Register an already-built :class:Skill (e.g. disk-loaded).
Source code in src/ciel/runtime/skills_lib.py
¶
update(*, name: str, description: Optional[str] = None, code: Optional[str] = None, category: Optional[str] = None, bump: str = 'patch') -> Skill
Create a new version of an existing skill, preserving history.
bump is one of major/minor/patch (semantic) and is
recorded in metadata.version. The previous version is preserved in
history(name).
Source code in src/ciel/runtime/skills_lib.py
¶
SkillVerificationResult
dataclass
Outcome of :meth:SkillVerifier.verify.
Source code in src/ciel/runtime/skills_lib.py
¶
SkillVerifier
Offline verifier: syntax check + executable test cases.
A test case is a dict {"call": {...}, "expect": <value>}. The verifier
executes the skill code in an isolated namespace, looks up a callable named
after the skill (or the first callable defined), invokes it with call
arguments and compares the result to expect.
Source code in src/ciel/runtime/skills_lib.py
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | |
¶
_library() -> SkillLibrary
¶
_load_test_cases(path: str) -> List[Dict[str, Any]]
Carga casos de prueba desde un archivo JSON (lista de dicts).
Source code in src/ciel/cli/skills_cli.py
¶
create_cmd(name: str = typer.Option(..., '--name', help='Skill name'), description: str = typer.Option(..., '--description', help='Skill description'), code_file: Path = typer.Option(..., '--code-file', exists=True, dir_okay=False, help='Path to a .py file with the skill code'), category: str | None = typer.Option(None, '--category', help='Optional category')) -> None
Crea un skill desde un archivo de código y lo registra en memoria.
Source code in src/ciel/cli/skills_cli.py
¶
create_skill(lib: SkillLibrary, *, name: str, description: str, code_file: str, category: str | None = None) -> Skill
Lee code_file y registra un skill vía SkillLibrary.create_from_code.
Source code in src/ciel/cli/skills_cli.py
¶
list_cmd() -> None
Lista los skills registrados en la librería en memoria.
Source code in src/ciel/cli/skills_cli.py
¶
list_registered(lib: SkillLibrary) -> List[Skill]
¶
remove_cmd(name: str = typer.Option(..., '--name', help='Skill name to remove')) -> None
Elimina un skill de la librería en memoria.
Source code in src/ciel/cli/skills_cli.py
¶
remove_skill(lib: SkillLibrary, *, name: str) -> bool
¶
verify_cmd(name: str = typer.Option(..., '--name', help='Skill name to verify'), test_cases_file: Path = typer.Option(..., '--test-cases', exists=True, dir_okay=False, help='JSON file: list of {"call": {}, "expect": value}')) -> None
Verifica un skill contra casos de prueba (offline, sin ejecución de red).
Source code in src/ciel/cli/skills_cli.py
¶
verify_skill(lib: SkillLibrary, *, name: str, test_cases: Sequence[Dict[str, Any]]) -> SkillVerificationResult
Verifica name contra test_cases usando :class:SkillVerifier.
Source code in src/ciel/cli/skills_cli.py
¶ciel skills — gestión de la Skill Library (offline)
Subcomando Typer registrado como ciel skills. Toda la operación es
offline-safe (sin red ni API keys): opera sobre una SkillLibrary en
memoria usando ciel.runtime.skills_lib.
ciel skills list
ciel skills create --name add --description "Suma dos enteros" --code-file add.py
ciel skills verify --name add --test-cases cases.json
ciel skills remove --name add
¶ciel skills list
Lista los skills registrados en la librería en memoria (última versión de cada
nombre), en una tabla Rich con columnas name / category / description. Si
no hay ninguno, imprime (no skills registered).
¶ciel skills create
Crea un skill desde un archivo de código Python y lo registra.
| Opción | Requerida | Descripción |
|---|---|---|
--name |
sí | Nombre del skill. |
--description |
sí | Descripción del skill. |
--code-file |
sí | Ruta a un .py con el código fuente (debe compilar; si no, falla con mensaje y exit 1). |
--category |
no | Categoría opcional. |
Al registrar, imprime el sha256 de los 12 primeros caracteres y la categoría.
¶ciel skills verify
Verifica un skill contra casos de prueba, offline.
| Opción | Requerida | Descripción |
|---|---|---|
--name |
sí | Nombre del skill a verificar. |
--test-cases |
sí | Archivo JSON: una lista de {"call": {...}, "expect": <valor>}. |
El verificador ejecuta el código en un namespace aislado, invoca la callable
(nombrada como el skill o la primera callable definida) con call y compara el
resultado con expect. Imprime PASS/FAIL; en fallo hace exit 1.
¶ciel skills remove
Elimina un skill de la librería en memoria. --name requerido. Imprime removed
si existía o not found + exit 1 si no.
Nota: la CLI usa una librería en memoria fresh por proceso, por lo que
create/verify/removeno persisten entre invocaciones separadas. Para flujos persistentes usa la API deciel.runtime.skills_libdirectamente (verdocs/guide/skills.md).