scios-cli 0.3.1__tar.gz → 0.3.2__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.
Files changed (55) hide show
  1. {scios_cli-0.3.1 → scios_cli-0.3.2}/PKG-INFO +1 -1
  2. {scios_cli-0.3.1 → scios_cli-0.3.2}/pyproject.toml +1 -1
  3. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/local_runner.py +1 -1
  4. scios_cli-0.3.2/scios_cli/ag/plugin.py +283 -0
  5. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/scaffold.py +18 -5
  6. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/app.py +37 -0
  7. scios_cli-0.3.2/scios_cli/commands/ag.py +1312 -0
  8. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/login.py +29 -1
  9. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/run.py +24 -43
  10. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/setup.py +161 -3
  11. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/PKG-INFO +1 -1
  12. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/SOURCES.txt +1 -0
  13. scios_cli-0.3.1/scios_cli/commands/ag.py +0 -450
  14. {scios_cli-0.3.1 → scios_cli-0.3.2}/LICENSE +0 -0
  15. {scios_cli-0.3.1 → scios_cli-0.3.2}/README.md +0 -0
  16. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/__init__.py +0 -0
  17. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/__main__.py +0 -0
  18. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/__init__.py +0 -0
  19. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/api_client.py +0 -0
  20. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/create_flow.py +0 -0
  21. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/dashboards.py +0 -0
  22. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/manifest.py +0 -0
  23. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/ag/sync_core.py +0 -0
  24. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/auth_store.py +0 -0
  25. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/__init__.py +0 -0
  26. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/agent.py +0 -0
  27. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/bench.py +0 -0
  28. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/chat.py +0 -0
  29. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/db.py +0 -0
  30. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/deploy.py +0 -0
  31. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/export.py +0 -0
  32. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/houdini.py +0 -0
  33. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/langfuse.py +0 -0
  34. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/logs.py +0 -0
  35. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/profile.py +0 -0
  36. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/sync.py +0 -0
  37. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/train.py +0 -0
  38. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/trust.py +0 -0
  39. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/video.py +0 -0
  40. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/commands/workspace.py +0 -0
  41. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/config.py +0 -0
  42. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/houdini.py +0 -0
  43. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/houdini_recorder.py +0 -0
  44. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/install.py +0 -0
  45. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/main.py +0 -0
  46. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/mcp/__init__.py +0 -0
  47. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/mcp/server_core.py +0 -0
  48. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli/mcp/server_maxwell.py +0 -0
  49. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/dependency_links.txt +0 -0
  50. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/entry_points.txt +0 -0
  51. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/requires.txt +0 -0
  52. {scios_cli-0.3.1 → scios_cli-0.3.2}/scios_cli.egg-info/top_level.txt +0 -0
  53. {scios_cli-0.3.1 → scios_cli-0.3.2}/setup.cfg +0 -0
  54. {scios_cli-0.3.1 → scios_cli-0.3.2}/setup.py +0 -0
  55. {scios_cli-0.3.1 → scios_cli-0.3.2}/tests/test_ag.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scios-cli
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Scios unified CLI — dev environment, deployment, database, logs, and more
5
5
  Author: Scios Team
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scios-cli"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Scios unified CLI — dev environment, deployment, database, logs, and more"
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
@@ -159,7 +159,7 @@ def _run_node(
159
159
  # Match the backend orchestrator's init_args EXACTLY (agent_orchestrator.py):
160
160
  # only name + model. Passing the raw node `configuration` collides with fields
161
161
  # the primitive __init__ already sets (e.g. `description`) → TypeError.
162
- init_args = {"name": agent_name, "model": "gemini-3.5-flash"}
162
+ init_args = {"name": agent_name, "model": "gemini-3.6-flash"}
163
163
 
164
164
  node_dir = work_dir / node_id
165
165
  node_dir.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,283 @@
1
+ """Antigravity Plugin — installs Scios as a global plugin at ~/.gemini/config/plugins/scios/.
2
+
3
+ Provides MCP servers, skills, rules, and sidecars that work across all workspaces
4
+ without per-workspace `.agents/` duplication. Per the Antigravity plugin spec
5
+ (docs/integrations/antigravity/antigravity-docs-cache/markdown/plugins.md).
6
+
7
+ Plugin layout:
8
+ ~/.gemini/config/plugins/scios/
9
+ ├── plugin.json
10
+ ├── mcp_config.json (scios-core + scios-maxwell)
11
+ ├── hooks.json (ruff format-on-save)
12
+ ├── skills/
13
+ │ └── scios-workflow/
14
+ │ └── SKILL.md
15
+ ├── rules/
16
+ │ └── scios.md
17
+ └── sidecars/
18
+ ├── scios-sync/
19
+ │ ├── sidecar.json
20
+ │ └── sync.sh
21
+ └── scios-notifier/
22
+ ├── sidecar.json
23
+ └── notify.sh
24
+
25
+ Sidecar config lives at ~/.gemini/config/config.json (user-controlled enable/disable).
26
+ """
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import shutil
31
+ from pathlib import Path
32
+ from typing import Callable
33
+
34
+ PLUGIN_DIR = Path.home() / ".gemini" / "config" / "plugins" / "scios"
35
+ CONFIG_FILE = Path.home() / ".gemini" / "config" / "config.json"
36
+
37
+ SCIOS_BIN = shutil.which("scios") or "scios"
38
+
39
+
40
+ def plugin_installed() -> bool:
41
+ """Check if the global Antigravity plugin is installed."""
42
+ return (PLUGIN_DIR / "plugin.json").exists()
43
+
44
+
45
+ def install_plugin(
46
+ *,
47
+ enable_notifier: bool = False,
48
+ log: Callable[[str], None] = print,
49
+ ) -> None:
50
+ """Write all plugin files and update config.json to enable sidecars."""
51
+
52
+ # ── plugin.json ──
53
+ _write_json(PLUGIN_DIR / "plugin.json", {
54
+ "name": "scios",
55
+ "displayName": "Scios Research Platform",
56
+ "description": "Drive autonomous scientific research — stage-by-stage pipeline, MCP tools, Maxwell assistant.",
57
+ })
58
+
59
+ # ── mcp_config.json ──
60
+ _write_json(PLUGIN_DIR / "mcp_config.json", {
61
+ "mcpServers": {
62
+ "scios-core": {
63
+ "command": SCIOS_BIN,
64
+ "args": ["ag", "mcp", "core"],
65
+ "env": {},
66
+ },
67
+ "scios-maxwell": {
68
+ "command": SCIOS_BIN,
69
+ "args": ["ag", "mcp", "maxwell"],
70
+ "env": {},
71
+ },
72
+ }
73
+ })
74
+ log(" ✓ mcp_config.json (scios-core + scios-maxwell)")
75
+
76
+ # ── hooks.json ──
77
+ _write_json(PLUGIN_DIR / "hooks.json", {
78
+ "hooks": {
79
+ "afterFileEdit": [
80
+ {
81
+ "command": "sh -c 'command -v ruff >/dev/null 2>&1 && ruff format \"$FILE\" || true'",
82
+ "glob": "agents/*.py",
83
+ }
84
+ ]
85
+ }
86
+ })
87
+ log(" ✓ hooks.json (ruff format-on-save)")
88
+
89
+ # ── skills/scios-workflow/SKILL.md ──
90
+ skill_dir = PLUGIN_DIR / "skills" / "scios-workflow"
91
+ skill_dir.mkdir(parents=True, exist_ok=True)
92
+ (skill_dir / "SKILL.md").write_text(_WORKFLOW_SKILL)
93
+ log(" ✓ skills/scios-workflow/SKILL.md")
94
+
95
+ # ── rules/scios.md ──
96
+ rules_dir = PLUGIN_DIR / "rules"
97
+ rules_dir.mkdir(parents=True, exist_ok=True)
98
+ (rules_dir / "scios.md").write_text(_RULES)
99
+ log(" ✓ rules/scios.md")
100
+
101
+ # ── sidecars/scios-sync/ ──
102
+ sync_dir = PLUGIN_DIR / "sidecars" / "scios-sync"
103
+ sync_dir.mkdir(parents=True, exist_ok=True)
104
+ _write_json(sync_dir / "sidecar.json", {
105
+ "description": "Auto-sync: watches agents/*.py and pushes edits to the Scios backend",
106
+ "display_name": "Scios Sync",
107
+ "command": SCIOS_BIN,
108
+ "args": ["ag", "sync"],
109
+ "restart_policy": "on-failure",
110
+ })
111
+ log(" ✓ sidecars/scios-sync (auto-sync agent code)")
112
+
113
+ # ── sidecars/scios-notifier/ ──
114
+ notifier_dir = PLUGIN_DIR / "sidecars" / "scios-notifier"
115
+ notifier_dir.mkdir(parents=True, exist_ok=True)
116
+ (notifier_dir / "notify.sh").write_text(_NOTIFIER_SCRIPT)
117
+ (notifier_dir / "notify.sh").chmod(0o755)
118
+ _write_json(notifier_dir / "sidecar.json", {
119
+ "description": "Polls build-execute status every 60s and notifies via agentapi when done",
120
+ "display_name": "Scios Notifier",
121
+ "builtin": "schedule",
122
+ "args": [
123
+ "*/1 * * * *",
124
+ "/bin/bash",
125
+ str(notifier_dir / "notify.sh"),
126
+ ],
127
+ })
128
+ log(" ✓ sidecars/scios-notifier (status polling, opt-in)")
129
+
130
+ # ── config.json (enable sidecars) ──
131
+ _update_config(enable_sync=True, enable_notifier=enable_notifier)
132
+ log(f" ✓ config.json (sync: enabled, notifier: {'enabled' if enable_notifier else 'disabled — use --enable-notifier'})")
133
+
134
+ log(f"\n✓ Scios plugin installed → {PLUGIN_DIR}")
135
+
136
+
137
+ def uninstall_plugin(*, log: Callable[[str], None] = print) -> None:
138
+ """Remove the plugin directory and config entries."""
139
+ if PLUGIN_DIR.exists():
140
+ shutil.rmtree(PLUGIN_DIR)
141
+ log(f"✓ Removed {PLUGIN_DIR}")
142
+ _update_config(remove=True)
143
+ log("✓ Removed sidecar config from config.json")
144
+
145
+
146
+ def _update_config(
147
+ *,
148
+ enable_sync: bool = True,
149
+ enable_notifier: bool = False,
150
+ remove: bool = False,
151
+ ) -> None:
152
+ """Update ~/.gemini/config/config.json to enable/disable Scios sidecars."""
153
+ config: dict = {}
154
+ if CONFIG_FILE.exists():
155
+ try:
156
+ config = json.loads(CONFIG_FILE.read_text())
157
+ except (json.JSONDecodeError, OSError):
158
+ config = {}
159
+
160
+ sidecars = config.setdefault("sidecars", {})
161
+
162
+ if remove:
163
+ sidecars.pop("scios/scios-sync", None)
164
+ sidecars.pop("scios/scios-notifier", None)
165
+ else:
166
+ sidecars["scios/scios-sync"] = {"enabled": enable_sync}
167
+ sidecars["scios/scios-notifier"] = {"enabled": enable_notifier}
168
+
169
+ CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
170
+ CONFIG_FILE.write_text(json.dumps(config, indent=2) + "\n")
171
+
172
+
173
+ def _write_json(path: Path, data: dict) -> None:
174
+ path.parent.mkdir(parents=True, exist_ok=True)
175
+ path.write_text(json.dumps(data, indent=2) + "\n")
176
+
177
+
178
+ # ---------------------------------------------------------------------------
179
+ # Inlined content (avoids reading from skill files at install time)
180
+ # ---------------------------------------------------------------------------
181
+
182
+ _WORKFLOW_SKILL = """\
183
+ ---
184
+ name: scios-workflow
185
+ description: Runs, optimizes, and explains Scios research workflows in this workspace. Use when the user asks to run the workflow, iterate on an agent, check scores or build-execute progress, optimize, or understand why an execution failed. Applies to any Scios workspace containing scios.toml and an agents/ folder.
186
+ ---
187
+
188
+ # Scios Workflow Skill
189
+
190
+ This workspace is a Scios research project. Use the `scios-core` MCP tools and the
191
+ `scios` CLI to drive it.
192
+
193
+ ## Common tasks
194
+ - **Run the workflow**: call the `scios_run` tool, or run `scios ag run`. Respect
195
+ the `mode` in `scios.toml` (local vs cloud). For a one-off cloud run use
196
+ `scios ag run --mode cloud`.
197
+ - **Check status / scores**: call `scios_status` or run `scios ag status`. Report
198
+ the `scios_score` and the current build-execute stage.
199
+ - **Explain results**: run `scios ag explain` for scores, per-node trace, and
200
+ failure diagnostics. This is the most important command after a run.
201
+ - **Edit an agent**: open the relevant file in `agents/`, make the change, save.
202
+ The sync sidecar pushes it to the backend automatically.
203
+ - **Optimize**: run `scios ag optimize --iterations N` (rides the existing
204
+ optimizer settings). Larger `compute_scale` in `scios.toml` sizes runs bigger.
205
+ - **Explain a failure**: call `scios_explain` or read `execution/trace_summary.md`
206
+ and `execution/logs.json`.
207
+ - **Diff after optimize**: run `scios ag diff` to see what the optimizer changed.
208
+
209
+ ## Guidance
210
+ - The backend is the source of truth; files are a view. Don't hand-edit read-only
211
+ folders (`execution/`, `workflows/`, `datasets/`, `dashboards/`).
212
+ - Prefer the MCP tools for structured results; fall back to the CLI for anything
213
+ not exposed as a tool.
214
+ """
215
+
216
+ _RULES = """\
217
+ ---
218
+ trigger: always_on
219
+ ---
220
+
221
+ # Scios Workspace Conventions
222
+
223
+ This folder is a **Scios research workspace** projected from the Scios backend by
224
+ the `scios ag` CLI. Treat the backend DB as the single source of truth — these
225
+ files are a synced *view*.
226
+
227
+ ## Folder map
228
+ - `agents/*.py` — vibed agent code. **Two-way synced**: editing a file pushes it
229
+ to the backend (via the sync sidecar or `scios ag push-code`). The first line
230
+ `# AGENT_ID: <uuid>` is the sync key — **never delete or edit it**.
231
+ - `workflows/definition.json` — the workflow DAG (read-only projection).
232
+ - `execution/` — metrics.json, trace_summary.md, logs.json (read-only).
233
+ - `datasets/` — inline data, `.url` shortcuts (gs://), or `.metadata.json`.
234
+ - `dashboards/` — generated result dashboards (read-only).
235
+ - `scios.toml` — the only local-authoritative file (idea_id, endpoint, mode, budget).
236
+
237
+ ## How to work here
238
+ - To change an agent's behaviour, edit its file in `agents/` and save. The
239
+ sync sidecar pushes it automatically, or use `scios ag push-code <file>`.
240
+ - To run the workflow: `scios ag run` (mode from `scios.toml`).
241
+ - To check progress/scores: `scios ag status` or `scios ag explain`.
242
+ - Prefer the `scios-core` MCP tools (`scios_run`, `scios_status`, `scios_logs`,
243
+ `scios_explain`) and the `scios-maxwell` assistant over shelling out manually.
244
+
245
+ ## Hard rules
246
+ - Never edit files under `execution/`, `workflows/`, `datasets/`, `dashboards/`
247
+ by hand — they are overwritten on the next sync.
248
+ - Never remove the `# AGENT_ID:` header from an agent file.
249
+ - Do not invent new API endpoints; the CLI already wraps the backend.
250
+ """
251
+
252
+ _NOTIFIER_SCRIPT = """\
253
+ #!/usr/bin/env bash
254
+ # Scios status notifier — polls build-execute status, notifies via agentapi when done.
255
+ # Runs as a scheduled sidecar (every 60s). Writes state to $ANTIGRAVITY_EXECUTABLE_DATA_DIR.
256
+
257
+ set -euo pipefail
258
+
259
+ STATE_DIR="${ANTIGRAVITY_EXECUTABLE_DATA_DIR:-/tmp/scios-notifier}"
260
+ mkdir -p "$STATE_DIR"
261
+ LAST_STATUS_FILE="$STATE_DIR/last_status"
262
+
263
+ # Get current status (requires scios.toml in CWD or nearest parent)
264
+ STATUS_JSON=$(scios ag status 2>/dev/null | head -5 || true)
265
+ if [[ -z "$STATUS_JSON" ]]; then
266
+ exit 0 # No workspace mounted, nothing to do
267
+ fi
268
+
269
+ # Extract status and score
270
+ CURRENT_STATUS=$(echo "$STATUS_JSON" | grep -oP 'status:\\s*\\K\\S+' || echo "unknown")
271
+ SCORE=$(echo "$STATUS_JSON" | grep -oP 'scios_score:\\s*\\K\\S+' || echo "—")
272
+ LAST_STATUS=$(cat "$LAST_STATUS_FILE" 2>/dev/null || echo "unknown")
273
+
274
+ # Notify only on state transitions to terminal states
275
+ if [[ "$CURRENT_STATUS" != "$LAST_STATUS" ]]; then
276
+ echo "$CURRENT_STATUS" > "$LAST_STATUS_FILE"
277
+ if [[ "$CURRENT_STATUS" == "completed" ]]; then
278
+ agentapi new-conversation "✅ Scios execution completed — scios_score: $SCORE. Run \\`scios ag explain\\` for details." 2>/dev/null || true
279
+ elif [[ "$CURRENT_STATUS" == "failed" ]]; then
280
+ agentapi new-conversation "❌ Scios execution failed. Run \\`scios ag explain\\` to see what went wrong." 2>/dev/null || true
281
+ fi
282
+ fi
283
+ """
@@ -301,16 +301,29 @@ def _write(path: Path, content: str, *, limit: int | None = None) -> None:
301
301
 
302
302
 
303
303
  def scaffold(folder: Path, manifest: Manifest, *, log=print) -> None:
304
- """(Re)write all Antigravity + VS Code integration files into `folder`."""
304
+ """(Re)write all Antigravity + VS Code integration files into `folder`.
305
+
306
+ If the global Antigravity plugin is installed (~/.gemini/config/plugins/scios/),
307
+ skip the .agents/ MCP config, rules, skills, and hooks — the plugin provides
308
+ those globally. Only write workspace-specific files (workflows + .vscode).
309
+ """
310
+ from scios_cli.ag.plugin import plugin_installed
311
+
305
312
  agents_dir = folder / ".agents"
313
+ has_global_plugin = plugin_installed()
314
+
315
+ if has_global_plugin:
316
+ log(" ℹ Global Antigravity plugin detected — skipping .agents/ MCP/rules/skills (provided by plugin)")
317
+ else:
318
+ _write(agents_dir / "mcp_config.json", json.dumps(_mcp_config(manifest), indent=2))
319
+ _write(agents_dir / "rules" / "scios.md", _RULES, limit=CHAR_LIMIT)
320
+ _write(agents_dir / "skills" / "scios-workflow" / "SKILL.md", _SKILL)
321
+ _write(agents_dir / "hooks.json", json.dumps(_hooks(), indent=2))
306
322
 
307
- _write(agents_dir / "mcp_config.json", json.dumps(_mcp_config(manifest), indent=2))
308
- _write(agents_dir / "rules" / "scios.md", _RULES, limit=CHAR_LIMIT)
309
- _write(agents_dir / "skills" / "scios-workflow" / "SKILL.md", _SKILL)
323
+ # Workflows are always workspace-specific (they reference workspace commands)
310
324
  _write(agents_dir / "workflows" / "scios-run.md", _WF_RUN, limit=CHAR_LIMIT)
311
325
  _write(agents_dir / "workflows" / "scios-optimize.md", _WF_OPTIMIZE, limit=CHAR_LIMIT)
312
326
  _write(agents_dir / "workflows" / "scios-explain.md", _WF_EXPLAIN, limit=CHAR_LIMIT)
313
- _write(agents_dir / "hooks.json", json.dumps(_hooks(), indent=2))
314
327
 
315
328
  import shutil
316
329
 
@@ -94,6 +94,40 @@ COMMAND_GROUPS = [
94
94
 
95
95
 
96
96
 
97
+ # ── Throttled skill sync (at most once per day) ──────────────────────────
98
+ _SKILL_SYNC_INTERVAL = 86400 # 24 hours in seconds
99
+
100
+
101
+ def _maybe_sync_skill() -> None:
102
+ """Silently sync the agent skill if it hasn't been updated today.
103
+
104
+ Writes a timestamp to ~/.scios/.skill_sync_ts. If the file is missing or
105
+ older than _SKILL_SYNC_INTERVAL, runs ensure_skill_updated(quiet=True).
106
+ Never blocks, never errors — worst case it's a no-op.
107
+ """
108
+ import time
109
+ from pathlib import Path
110
+
111
+ ts_file = Path.home() / ".scios" / ".skill_sync_ts"
112
+ now = time.time()
113
+
114
+ try:
115
+ if ts_file.exists():
116
+ last = float(ts_file.read_text().strip())
117
+ if now - last < _SKILL_SYNC_INTERVAL:
118
+ return # Already synced recently
119
+ except Exception:
120
+ pass
121
+
122
+ try:
123
+ from scios_cli.commands.setup import ensure_skill_updated
124
+ ensure_skill_updated(quiet=True)
125
+ ts_file.parent.mkdir(parents=True, exist_ok=True)
126
+ ts_file.write_text(str(now))
127
+ except Exception:
128
+ pass # Non-critical — never block CLI on skill sync failure
129
+
130
+
97
131
  @app.callback()
98
132
  def _root_callback(
99
133
  ctx: typer.Context,
@@ -107,6 +141,9 @@ def _root_callback(
107
141
  # Check Google auth access before any command runs
108
142
  check_cli_access()
109
143
 
144
+ # Auto-sync agent skill (at most once per day, silent)
145
+ _maybe_sync_skill()
146
+
110
147
  if ctx.invoked_subcommand is not None:
111
148
  return # A subcommand was given — let it run
112
149