scios-cli 0.3.1__tar.gz → 0.4.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.
Files changed (55) hide show
  1. {scios_cli-0.3.1 → scios_cli-0.4.0}/PKG-INFO +1 -1
  2. {scios_cli-0.3.1 → scios_cli-0.4.0}/pyproject.toml +1 -1
  3. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/create_flow.py +97 -46
  4. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/local_runner.py +1 -1
  5. scios_cli-0.4.0/scios_cli/ag/plugin.py +371 -0
  6. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/scaffold.py +18 -5
  7. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/app.py +37 -0
  8. scios_cli-0.4.0/scios_cli/commands/ag.py +1319 -0
  9. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/login.py +29 -1
  10. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/run.py +58 -45
  11. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/setup.py +164 -3
  12. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/PKG-INFO +1 -1
  13. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/SOURCES.txt +1 -0
  14. scios_cli-0.3.1/scios_cli/commands/ag.py +0 -450
  15. {scios_cli-0.3.1 → scios_cli-0.4.0}/LICENSE +0 -0
  16. {scios_cli-0.3.1 → scios_cli-0.4.0}/README.md +0 -0
  17. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/__init__.py +0 -0
  18. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/__main__.py +0 -0
  19. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/__init__.py +0 -0
  20. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/api_client.py +0 -0
  21. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/dashboards.py +0 -0
  22. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/manifest.py +0 -0
  23. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/ag/sync_core.py +0 -0
  24. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/auth_store.py +0 -0
  25. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/__init__.py +0 -0
  26. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/agent.py +0 -0
  27. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/bench.py +0 -0
  28. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/chat.py +0 -0
  29. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/db.py +0 -0
  30. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/deploy.py +0 -0
  31. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/export.py +0 -0
  32. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/houdini.py +0 -0
  33. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/langfuse.py +0 -0
  34. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/logs.py +0 -0
  35. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/profile.py +0 -0
  36. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/sync.py +0 -0
  37. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/train.py +0 -0
  38. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/trust.py +0 -0
  39. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/video.py +0 -0
  40. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/commands/workspace.py +0 -0
  41. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/config.py +0 -0
  42. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/houdini.py +0 -0
  43. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/houdini_recorder.py +0 -0
  44. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/install.py +0 -0
  45. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/main.py +0 -0
  46. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/mcp/__init__.py +0 -0
  47. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/mcp/server_core.py +0 -0
  48. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli/mcp/server_maxwell.py +0 -0
  49. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/dependency_links.txt +0 -0
  50. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/entry_points.txt +0 -0
  51. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/requires.txt +0 -0
  52. {scios_cli-0.3.1 → scios_cli-0.4.0}/scios_cli.egg-info/top_level.txt +0 -0
  53. {scios_cli-0.3.1 → scios_cli-0.4.0}/setup.cfg +0 -0
  54. {scios_cli-0.3.1 → scios_cli-0.4.0}/setup.py +0 -0
  55. {scios_cli-0.3.1 → scios_cli-0.4.0}/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.4.0
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.4.0"
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"}
@@ -86,9 +86,17 @@ def create_and_build(
86
86
  max_wall_clock_min: int,
87
87
  wait: bool,
88
88
  launch: bool,
89
+ build: bool = False,
89
90
  log: Callable[[str], None] = print,
90
91
  ) -> dict:
91
- """Full cold-start. Returns {idea_id, folder, status}."""
92
+ """Cold-start: create workspace + refine goal + idea.
93
+
94
+ When build=False (default for `scios ag create`): stops after creating the
95
+ idea. The scientist reviews the refined goal and idea before proceeding.
96
+ When build=True (used by `scios ag go`): also kicks off build-execute.
97
+
98
+ Returns {idea_id, folder, status}.
99
+ """
92
100
  from scios_cli.ag import scaffold as scaffold_mod
93
101
  from scios_cli.ag import sync_core
94
102
 
@@ -101,11 +109,8 @@ def create_and_build(
101
109
  log(f" workspace {workspace_id}")
102
110
 
103
111
  log(f"② Refining the goal (AI auto-amend, same as /dashboard/plan) …")
104
- # Use Maxwell's suggest-all-fields to turn the raw prompt into a structured
105
- # goal (clean title, measurable target metrics, methodology, constraints) —
106
- # exactly what the web /dashboard/plan page does. Fall back to the raw prompt
107
- # only if the refinement call is unavailable.
108
- title, description, target, details = goal[:120], goal, None, None
112
+ title, description, target_metrics, details = goal[:120], goal, None, None
113
+ methodology, constraints = None, None
109
114
  try:
110
115
  sess = client.maxwell_create_session(workspace_id=workspace_id)
111
116
  session_id = sess.get("id") or sess.get("sessionId") or sess.get("session_id")
@@ -113,7 +118,7 @@ def create_and_build(
113
118
  sug = client.suggest_all_fields(session_id, research_context=goal)
114
119
  title = sug.get("title") or title
115
120
  description = sug.get("description") or description
116
- target = sug.get("targetMetrics") or sug.get("target_metrics") or None
121
+ target_metrics = sug.get("targetMetrics") or sug.get("target_metrics") or None
117
122
  methodology = sug.get("methodology")
118
123
  constraints = sug.get("constraints")
119
124
  parts = [p for p in (
@@ -121,14 +126,14 @@ def create_and_build(
121
126
  (f"## Constraints\n{constraints}" if constraints else None),
122
127
  ) if p]
123
128
  details = "\n\n".join(parts) or None
124
- log(f" ✨ refined → \"{title}\"" + (f" | target: {target[:60]}" if target else ""))
129
+ log(f" ✨ refined → \"{title}\"" + (f" | target: {target_metrics[:60]}" if target_metrics else ""))
125
130
  except SciosAPIError as e:
126
131
  log(f" (auto-amend unavailable: {e}; using raw prompt)")
127
132
 
128
133
  log(f"③ Creating research goal …")
129
134
  rg = client.create_research_goal(
130
135
  workspace_id=workspace_id, title=title, description=description,
131
- target=target, details=details, original_prompt=goal,
136
+ target=target_metrics, details=details, original_prompt=goal,
132
137
  )
133
138
  goal_id = rg.get("id") or rg.get("goalId")
134
139
  log(f" goal {goal_id}")
@@ -141,31 +146,52 @@ def create_and_build(
141
146
  raise SystemExit(1)
142
147
  log(f" idea {idea_id}")
143
148
 
149
+ # Write manifest + idea.md so the scientist can review
150
+ target_folder = Path(folder).expanduser().resolve() if folder else (Path.cwd() / f"scios-{name}")
151
+ target_folder.mkdir(parents=True, exist_ok=True)
152
+
153
+ host = endpoint.rstrip("/").removesuffix("/api/v1").rstrip("/")
154
+ m = Manifest(
155
+ idea_id=idea_id, endpoint=endpoint, token="",
156
+ execution=ExecutionConfig(mode="local", host_url=host),
157
+ )
158
+ m.budget.compute_scale = scale
159
+ m.budget.optimizer_iterations = budget_iterations
160
+ m.budget.max_wall_clock_min = max_wall_clock_min
161
+ save(target_folder, m)
162
+
163
+ # Write idea.md for the scientist to review
164
+ _write_idea_md(target_folder, idea_id=idea_id, workspace_id=workspace_id,
165
+ goal_id=goal_id, title=title, description=description,
166
+ target_metrics=target_metrics, methodology=methodology,
167
+ constraints=constraints, host=host)
168
+
169
+ if not build:
170
+ # ── Stage-by-stage mode (default for `scios ag create`) ──
171
+ # Stop here. The scientist reviews idea.md, then says "run it" to
172
+ # kick off build-execute via `scios ag run`.
173
+ log(f"")
174
+ log(f"✨ Idea created: {idea_id}")
175
+ log(f" Folder: {target_folder}")
176
+ log(f" Review: {target_folder / 'idea.md'}")
177
+ log(f"")
178
+ log(f" Next steps:")
179
+ log(f" • Review idea.md — the AI-refined research goal")
180
+ log(f" • scios ag status — check build progress")
181
+ log(f" • scios ag run — kick off build-execute")
182
+ log(f" • scios ag explain — see scores after completion")
183
+ return {"idea_id": idea_id, "folder": str(target_folder), "status": "created"}
184
+
185
+ # ── Full pipeline mode (used by `scios ag go`) ──
144
186
  log(f"⑤ Kicking off build-execute (scale={scale}, iterations={budget_iterations}) …")
145
187
  stage_config = _build_stage_config(scale, budget_iterations, max_wall_clock_min)
146
188
  client.trigger_build_execute(idea_id, stage_config=stage_config)
147
189
 
148
- target = Path(folder).expanduser().resolve() if folder else (Path.cwd() / f"scios-{name}")
149
-
150
190
  if not wait:
151
- # Nothing to project yet — the workflow is built asynchronously. Write a
152
- # minimal manifest so `ag status`/`ag mount` work once it's ready, and
153
- # tell the user how to resume. (Mounting now would 404: no workflow yet.)
154
- host = endpoint.rstrip("/").removesuffix("/api/v1").rstrip("/")
155
- m = Manifest(
156
- idea_id=idea_id, endpoint=endpoint, token="",
157
- execution=ExecutionConfig(mode="local", host_url=host),
158
- )
159
- m.budget.compute_scale = scale
160
- m.budget.optimizer_iterations = budget_iterations
161
- m.budget.max_wall_clock_min = max_wall_clock_min
162
- target.mkdir(parents=True, exist_ok=True)
163
- save(target, m)
164
191
  log(f"✨ Build-execute started for idea {idea_id} (scale={scale}).")
165
- log(f" Manifest written {target / 'scios.toml'}")
166
- log(f" Track progress: scios ag status --folder {target}")
167
- log(f" When ready: scios ag mount {idea_id} --folder {target}")
168
- return {"idea_id": idea_id, "folder": str(target), "status": "building"}
192
+ log(f" Track progress: scios ag status --folder {target_folder}")
193
+ log(f" When ready: scios ag mount {idea_id} --folder {target_folder}")
194
+ return {"idea_id": idea_id, "folder": str(target_folder), "status": "building"}
169
195
 
170
196
  log("⑤ Waiting for build-execute to finish …")
171
197
 
@@ -179,26 +205,51 @@ def create_and_build(
179
205
  final = {"status": "unknown"}
180
206
  log(f" build-execute {final.get('status')} (scios_score={final.get('scios_score')})")
181
207
 
182
- log(f"⑥ Mounting → {target}")
183
- sync_core.mount(client, idea_id, target, log=log)
208
+ log(f"⑥ Mounting → {target_folder}")
209
+ sync_core.mount(client, idea_id, target_folder, log=log)
210
+ save(target_folder, m)
211
+ scaffold_mod.scaffold(target_folder, m, log=log)
184
212
 
185
- host = endpoint.rstrip("/").removesuffix("/api/v1").rstrip("/")
186
- m = Manifest(
187
- idea_id=idea_id, endpoint=endpoint, token="",
188
- execution=ExecutionConfig(mode="local", host_url=host),
189
- )
190
- m.budget.compute_scale = scale
191
- m.budget.optimizer_iterations = budget_iterations
192
- m.budget.max_wall_clock_min = max_wall_clock_min
193
- save(target, m)
194
- scaffold_mod.scaffold(target, m, log=log)
195
-
196
- log(f"✨ Ready: {target}")
197
- log(f" Open in Antigravity: antigravity {target}")
213
+ log(f"✨ Ready: {target_folder}")
214
+ log(f" Open in Antigravity: antigravity {target_folder}")
198
215
  if launch:
199
- _launch_editor(target, log)
200
-
201
- return {"idea_id": idea_id, "folder": str(target), "status": "created"}
216
+ _launch_editor(target_folder, log)
217
+
218
+ return {"idea_id": idea_id, "folder": str(target_folder), "status": "created"}
219
+
220
+
221
+ def _write_idea_md(
222
+ folder: Path, *, idea_id: str, workspace_id: str, goal_id: str,
223
+ title: str, description: str, target_metrics: str | None,
224
+ methodology: str | None, constraints: str | None, host: str,
225
+ ) -> None:
226
+ """Write idea.md for the scientist to review before proceeding."""
227
+ lines = [
228
+ f"# {title}",
229
+ "",
230
+ f"**Idea ID:** `{idea_id}`",
231
+ f"**Workspace:** `{workspace_id}`",
232
+ f"**Goal:** `{goal_id}`",
233
+ f"**View on Scios:** [{host}/ideas/{idea_id}]({host}/ideas/{idea_id})",
234
+ "",
235
+ "## Description",
236
+ description or "(no description)",
237
+ ]
238
+ if target_metrics:
239
+ lines += ["", "## Target Metrics", target_metrics]
240
+ if methodology:
241
+ lines += ["", "## Methodology", methodology]
242
+ if constraints:
243
+ lines += ["", "## Constraints", constraints]
244
+ lines += [
245
+ "",
246
+ "---",
247
+ "*Review this goal. When ready, tell the agent to proceed:*",
248
+ "- **\"run it\"** → kicks off the full build-execute pipeline",
249
+ "- **\"architect it\"** → step-by-step control (architect → convert → vibe → run)",
250
+ "- **\"status\"** → check current progress",
251
+ ]
252
+ (folder / "idea.md").write_text("\n".join(lines) + "\n")
202
253
 
203
254
 
204
255
  def _launch_editor(folder: Path, log: Callable[[str], None]) -> None:
@@ -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,371 @@
1
+ """Antigravity Plugin — installs Scios as a global plugin.
2
+
3
+ Two install modes:
4
+ 1. From repo: copies packages/scios-skills/ (the canonical plugin source)
5
+ to both ~/.gemini/config/plugins/scios/ (IDE) and
6
+ ~/.gemini/antigravity-cli/plugins/scios/ (CLI).
7
+ 2. From pip install (no repo): generates plugin files inline with the same
8
+ content.
9
+
10
+ Plugin layout:
11
+ ~/.gemini/config/plugins/scios/
12
+ ├── plugin.json
13
+ ├── mcp_config.json (scios-core + scios-maxwell)
14
+ ├── hooks.json (ruff format-on-save)
15
+ ├── skills/
16
+ │ └── scios/
17
+ │ ├── SKILL.md
18
+ │ ├── AGENTS.md
19
+ │ ├── docs/
20
+ │ └── resources/
21
+ ├── rules/
22
+ │ └── scios-conventions.md
23
+ └── sidecars/
24
+ ├── scios-sync/
25
+ │ └── sidecar.json
26
+ └── scios-notifier/
27
+ ├── sidecar.json
28
+ └── notify.sh
29
+
30
+ Sidecar config lives at ~/.gemini/config/config.json (user-controlled enable/disable).
31
+ """
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import shutil
36
+ from pathlib import Path
37
+ from typing import Callable
38
+
39
+ # Both Antigravity surfaces get the same plugin content
40
+ PLUGIN_DIRS = [
41
+ Path.home() / ".gemini" / "config" / "plugins" / "scios", # IDE
42
+ Path.home() / ".gemini" / "antigravity-cli" / "plugins" / "scios", # CLI
43
+ ]
44
+ # Primary dir for backward compat with code that reads PLUGIN_DIR
45
+ PLUGIN_DIR = PLUGIN_DIRS[0]
46
+ CONFIG_FILE = Path.home() / ".gemini" / "config" / "config.json"
47
+
48
+ SCIOS_BIN = shutil.which("scios") or "scios"
49
+
50
+ # Items to skip when copying plugin source
51
+ _SKIP = {"node_modules", ".git", "bin", "package.json", "package-lock.json", ".npmignore", "README.md"}
52
+
53
+
54
+ def plugin_installed() -> bool:
55
+ """Check if the global Antigravity plugin is installed."""
56
+ return (PLUGIN_DIR / "plugin.json").exists()
57
+
58
+
59
+ def _find_plugin_source() -> Path | None:
60
+ """Find the canonical plugin source in the repo."""
61
+ # Walk up from this file to find packages/scios-skills/
62
+ here = Path(__file__).resolve()
63
+ for parent in [here.parent.parent.parent.parent, here.parent.parent.parent.parent.parent]:
64
+ candidate = parent / "packages" / "scios-skills"
65
+ if (candidate / "plugin.json").exists():
66
+ return candidate
67
+ return None
68
+
69
+
70
+ def _copy_tree(src: Path, dest: Path) -> None:
71
+ """Recursively copy src to dest, skipping npm/git metadata."""
72
+ dest.mkdir(parents=True, exist_ok=True)
73
+ for item in src.iterdir():
74
+ if item.name in _SKIP:
75
+ continue
76
+ target = dest / item.name
77
+ if item.is_dir():
78
+ _copy_tree(item, target)
79
+ else:
80
+ target.write_bytes(item.read_bytes())
81
+
82
+
83
+ def install_plugin(
84
+ *,
85
+ enable_notifier: bool = False,
86
+ log: Callable[[str], None] = print,
87
+ ) -> None:
88
+ """Install the Scios plugin to all Antigravity plugin directories."""
89
+
90
+ plugin_src = _find_plugin_source()
91
+
92
+ for plugin_dir in PLUGIN_DIRS:
93
+ # Clean old install to remove stale files from previous versions
94
+ if plugin_dir.exists():
95
+ shutil.rmtree(plugin_dir)
96
+
97
+ if plugin_src:
98
+ # Mode 1: Copy from repo source (canonical, always current)
99
+ _copy_tree(plugin_src, plugin_dir)
100
+ log(f" ✓ Copied plugin from repo → {plugin_dir}")
101
+ else:
102
+ # Mode 2: Generate inline (pip-installed CLI without repo)
103
+ _generate_plugin_inline(plugin_dir, log=log)
104
+
105
+ # Add sidecars (not in the npm package — they need absolute paths)
106
+ _install_sidecars(plugin_dir, log=log)
107
+
108
+ # Update config.json to enable sidecars
109
+ _update_config(enable_sync=True, enable_notifier=enable_notifier)
110
+ log(f" ✓ config.json (sync: enabled, notifier: {'enabled' if enable_notifier else 'disabled — use --enable-notifier'})")
111
+
112
+ log(f"\n✓ Scios plugin installed → {len(PLUGIN_DIRS)} location(s)")
113
+
114
+
115
+ def _generate_plugin_inline(plugin_dir: Path, *, log: Callable[[str], None] = print) -> None:
116
+ """Generate plugin files from inline content (no repo available)."""
117
+
118
+ # ── plugin.json ──
119
+ _write_json(plugin_dir / "plugin.json", {
120
+ "$schema": "https://antigravity.google/schemas/v1/plugin.json",
121
+ "name": "scios",
122
+ "description": "Scios autonomous scientific research platform — create ideas, build multi-agent workflows, run experiments, optimize iteratively, and audit results. Powered by the Scios API.",
123
+ })
124
+
125
+ # ── mcp_config.json ──
126
+ _write_json(plugin_dir / "mcp_config.json", {
127
+ "mcpServers": {
128
+ "scios-core": {
129
+ "command": SCIOS_BIN,
130
+ "args": ["mcp", "core"],
131
+ "env": {},
132
+ },
133
+ "scios-maxwell": {
134
+ "command": SCIOS_BIN,
135
+ "args": ["mcp", "maxwell"],
136
+ "env": {},
137
+ },
138
+ }
139
+ })
140
+ log(" ✓ mcp_config.json (scios-core + scios-maxwell)")
141
+
142
+ # ── hooks.json ──
143
+ _write_json(plugin_dir / "hooks.json", {
144
+ "scios-auto-format": {
145
+ "PostToolUse": [
146
+ {
147
+ "matcher": "edit_file",
148
+ "hooks": [
149
+ {
150
+ "type": "command",
151
+ "command": "sh",
152
+ "args": ["-c", "command -v ruff >/dev/null 2>&1 && ruff format \"$ANTIGRAVITY_TOOL_ARG_TARGET_FILE\" 2>/dev/null || true"],
153
+ "timeout": 10,
154
+ "description": "Auto-format Python agent files with ruff after edits",
155
+ }
156
+ ]
157
+ }
158
+ ]
159
+ }
160
+ })
161
+ log(" ✓ hooks.json (ruff format-on-save)")
162
+
163
+ # ── skills/scios/SKILL.md ──
164
+ skill_dir = plugin_dir / "skills" / "scios"
165
+ skill_dir.mkdir(parents=True, exist_ok=True)
166
+ (skill_dir / "SKILL.md").write_text(_SKILL_MD)
167
+ log(" ✓ skills/scios/SKILL.md")
168
+
169
+ # ── rules/scios-conventions.md ──
170
+ rules_dir = plugin_dir / "rules"
171
+ rules_dir.mkdir(parents=True, exist_ok=True)
172
+ (rules_dir / "scios-conventions.md").write_text(_RULES)
173
+ log(" ✓ rules/scios-conventions.md")
174
+
175
+
176
+ def _install_sidecars(plugin_dir: Path, *, log: Callable[[str], None] = print) -> None:
177
+ """Install sidecar definitions (need absolute paths, can't be in npm package)."""
178
+
179
+ # ── sidecars/scios-sync/ ──
180
+ sync_dir = plugin_dir / "sidecars" / "scios-sync"
181
+ sync_dir.mkdir(parents=True, exist_ok=True)
182
+ _write_json(sync_dir / "sidecar.json", {
183
+ "description": "Auto-sync: watches agents/*.py and pushes edits to the Scios backend",
184
+ "display_name": "Scios Sync",
185
+ "command": SCIOS_BIN,
186
+ "args": ["ag", "sync"],
187
+ "restart_policy": "on-failure",
188
+ })
189
+ log(" ✓ sidecars/scios-sync (auto-sync agent code)")
190
+
191
+ # ── sidecars/scios-notifier/ ──
192
+ notifier_dir = plugin_dir / "sidecars" / "scios-notifier"
193
+ notifier_dir.mkdir(parents=True, exist_ok=True)
194
+ (notifier_dir / "notify.sh").write_text(_NOTIFIER_SCRIPT)
195
+ (notifier_dir / "notify.sh").chmod(0o755)
196
+ _write_json(notifier_dir / "sidecar.json", {
197
+ "description": "Polls build-execute status every 60s and notifies via agentapi when done",
198
+ "display_name": "Scios Notifier",
199
+ "builtin": "schedule",
200
+ "args": [
201
+ "*/1 * * * *",
202
+ "/bin/bash",
203
+ str(notifier_dir / "notify.sh"),
204
+ ],
205
+ })
206
+ log(" ✓ sidecars/scios-notifier (status polling, opt-in)")
207
+
208
+
209
+ def uninstall_plugin(*, log: Callable[[str], None] = print) -> None:
210
+ """Remove the plugin from all directories and config entries."""
211
+ for plugin_dir in PLUGIN_DIRS:
212
+ if plugin_dir.exists():
213
+ shutil.rmtree(plugin_dir)
214
+ log(f"✓ Removed {plugin_dir}")
215
+ _update_config(remove=True)
216
+ log("✓ Removed sidecar config from config.json")
217
+
218
+
219
+ def _update_config(
220
+ *,
221
+ enable_sync: bool = True,
222
+ enable_notifier: bool = False,
223
+ remove: bool = False,
224
+ ) -> None:
225
+ """Update ~/.gemini/config/config.json to enable/disable Scios sidecars."""
226
+ config: dict = {}
227
+ if CONFIG_FILE.exists():
228
+ try:
229
+ config = json.loads(CONFIG_FILE.read_text())
230
+ except (json.JSONDecodeError, OSError):
231
+ config = {}
232
+
233
+ sidecars = config.setdefault("sidecars", {})
234
+
235
+ if remove:
236
+ sidecars.pop("scios/scios-sync", None)
237
+ sidecars.pop("scios/scios-notifier", None)
238
+ else:
239
+ sidecars["scios/scios-sync"] = {"enabled": enable_sync}
240
+ sidecars["scios/scios-notifier"] = {"enabled": enable_notifier}
241
+
242
+ CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
243
+ CONFIG_FILE.write_text(json.dumps(config, indent=2) + "\n")
244
+
245
+
246
+ def _write_json(path: Path, data: dict) -> None:
247
+ path.parent.mkdir(parents=True, exist_ok=True)
248
+ path.write_text(json.dumps(data, indent=2) + "\n")
249
+
250
+
251
+ # ---------------------------------------------------------------------------
252
+ # Inlined content (used when repo source is not available)
253
+ # ---------------------------------------------------------------------------
254
+
255
+ _SKILL_MD = """\
256
+ ---
257
+ name: scios
258
+ description: >
259
+ Drive the Scios autonomous scientific research platform. Use this skill
260
+ whenever the user asks to do research, run experiments, or explore scientific
261
+ questions. The skill runs a stage-by-stage pipeline: create idea → architect
262
+ → convert to workflow → generate agent code → execute → explain scores →
263
+ optimize.
264
+ ---
265
+
266
+ # Scios — Research Pipeline Skill
267
+
268
+ ## Architecture
269
+
270
+ The skill delegates exclusively to the `scios` CLI for all backend interactions.
271
+ There are no fallback scripts — the CLI is the single source of truth.
272
+
273
+ Agents can also use the **scios-core** and **scios-maxwell** MCP tools bundled
274
+ with this plugin for structured tool-call interactions.
275
+
276
+ ## Quick Start
277
+
278
+ ```bash
279
+ scios ag go "Your research question here"
280
+ ```
281
+
282
+ ## Stage-by-Stage
283
+
284
+ | # | Command | Purpose |
285
+ |---|---------|---------|
286
+ | 1 | `scios ag create "<goal>"` | Create workspace + idea |
287
+ | 2 | `scios ag architect` | Refine hypothesis & methodology |
288
+ | 3 | `scios ag convert` | Build workflow DAG |
289
+ | 4 | `scios ag vibe` | Generate agent code |
290
+ | 5 | `scios ag run` | Execute workflow |
291
+ | 6 | `scios ag explain` | Scores, traces, failures |
292
+ | 7 | `scios ag optimize [-n N]` | Improve iteratively |
293
+ | 8 | `scios ag diff` | See what optimizer changed |
294
+
295
+ ## Auth
296
+
297
+ 1. `SCIOS_API_TOKEN` env var (first priority)
298
+ 2. `~/.scios/credentials.json` (written by `scios login`)
299
+
300
+ ## Hard Rules
301
+
302
+ - Run one stage, report, STOP. Never chain without user asking.
303
+ - Never present unaudited results — always show verified scores.
304
+ - If something fails, report root cause. No fallbacks, no retries.
305
+ """
306
+
307
+ _RULES = """\
308
+ # Scios Workspace Conventions
309
+
310
+ This folder is a **Scios research workspace** projected from the Scios backend by
311
+ the `scios ag` CLI. Treat the backend DB as the single source of truth — these
312
+ files are a synced *view*.
313
+
314
+ ## Folder map
315
+ - `agents/*.py` — vibed agent code. **Two-way synced**: editing a file pushes it
316
+ to the backend (via `scios ag push-code` or `scios ag sync`). The first line
317
+ `# AGENT_ID: <uuid>` is the sync key — **never delete or edit it**.
318
+ - `workflows/definition.json` — the workflow DAG (read-only projection).
319
+ - `execution/` — metrics.json, trace_summary.md, logs.json (read-only).
320
+ - `datasets/` — inline data, `.url` shortcuts (gs://), or `.metadata.json`.
321
+ - `dashboards/` — generated result dashboards (read-only).
322
+ - `scios.toml` — the only local-authoritative file (idea_id, endpoint, mode, budget).
323
+
324
+ ## How to work here
325
+ - To change an agent's behaviour, edit its file in `agents/` and save.
326
+ Push with `scios ag push-code <file>` or use `scios ag sync` for auto-push.
327
+ - To run the workflow: `scios ag run` (mode from `scios.toml`).
328
+ - To check progress/scores: `scios ag status` or `scios ag explain`.
329
+ - Prefer the `scios-core` MCP tools (`scios_run`, `scios_status`, `scios_explain`)
330
+ and the `scios-maxwell` MCP assistant over shelling out manually.
331
+
332
+ ## Hard rules
333
+ - Never edit files under `execution/`, `workflows/`, `datasets/`, `dashboards/`
334
+ by hand — they are overwritten on the next sync.
335
+ - Never remove the `# AGENT_ID:` header from an agent file.
336
+ - Do not invent new API endpoints; the CLI already wraps the backend.
337
+ - After editing agents, push code before running. After optimize, pull code before editing.
338
+ """
339
+
340
+ _NOTIFIER_SCRIPT = """\
341
+ #!/usr/bin/env bash
342
+ # Scios status notifier — polls build-execute status, notifies via agentapi when done.
343
+ # Runs as a scheduled sidecar (every 60s). Writes state to $ANTIGRAVITY_EXECUTABLE_DATA_DIR.
344
+
345
+ set -euo pipefail
346
+
347
+ STATE_DIR="${ANTIGRAVITY_EXECUTABLE_DATA_DIR:-/tmp/scios-notifier}"
348
+ mkdir -p "$STATE_DIR"
349
+ LAST_STATUS_FILE="$STATE_DIR/last_status"
350
+
351
+ # Get current status (requires scios.toml in CWD or nearest parent)
352
+ STATUS_JSON=$(scios ag status 2>/dev/null | head -5 || true)
353
+ if [[ -z "$STATUS_JSON" ]]; then
354
+ exit 0 # No workspace mounted, nothing to do
355
+ fi
356
+
357
+ # Extract status and score
358
+ CURRENT_STATUS=$(echo "$STATUS_JSON" | grep -oP 'status:\\s*\\K\\S+' || echo "unknown")
359
+ SCORE=$(echo "$STATUS_JSON" | grep -oP 'scios_score:\\s*\\K\\S+' || echo "—")
360
+ LAST_STATUS=$(cat "$LAST_STATUS_FILE" 2>/dev/null || echo "unknown")
361
+
362
+ # Notify only on state transitions to terminal states
363
+ if [[ "$CURRENT_STATUS" != "$LAST_STATUS" ]]; then
364
+ echo "$CURRENT_STATUS" > "$LAST_STATUS_FILE"
365
+ if [[ "$CURRENT_STATUS" == "completed" ]]; then
366
+ agentapi new-conversation "✅ Scios execution completed — scios_score: $SCORE. Run \\`scios ag explain\\` for details." 2>/dev/null || true
367
+ elif [[ "$CURRENT_STATUS" == "failed" ]]; then
368
+ agentapi new-conversation "❌ Scios execution failed. Run \\`scios ag explain\\` to see what went wrong." 2>/dev/null || true
369
+ fi
370
+ fi
371
+ """
@@ -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