scios-cli 0.3.2__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 (54) hide show
  1. {scios_cli-0.3.2 → scios_cli-0.4.0}/PKG-INFO +1 -1
  2. {scios_cli-0.3.2 → scios_cli-0.4.0}/pyproject.toml +1 -1
  3. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/create_flow.py +97 -46
  4. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/plugin.py +169 -81
  5. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/ag.py +9 -2
  6. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/run.py +49 -17
  7. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/setup.py +9 -6
  8. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/PKG-INFO +1 -1
  9. {scios_cli-0.3.2 → scios_cli-0.4.0}/LICENSE +0 -0
  10. {scios_cli-0.3.2 → scios_cli-0.4.0}/README.md +0 -0
  11. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/__init__.py +0 -0
  12. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/__main__.py +0 -0
  13. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/__init__.py +0 -0
  14. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/api_client.py +0 -0
  15. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/dashboards.py +0 -0
  16. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/local_runner.py +0 -0
  17. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/manifest.py +0 -0
  18. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/scaffold.py +0 -0
  19. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/ag/sync_core.py +0 -0
  20. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/app.py +0 -0
  21. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/auth_store.py +0 -0
  22. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/__init__.py +0 -0
  23. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/agent.py +0 -0
  24. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/bench.py +0 -0
  25. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/chat.py +0 -0
  26. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/db.py +0 -0
  27. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/deploy.py +0 -0
  28. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/export.py +0 -0
  29. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/houdini.py +0 -0
  30. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/langfuse.py +0 -0
  31. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/login.py +0 -0
  32. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/logs.py +0 -0
  33. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/profile.py +0 -0
  34. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/sync.py +0 -0
  35. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/train.py +0 -0
  36. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/trust.py +0 -0
  37. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/video.py +0 -0
  38. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/commands/workspace.py +0 -0
  39. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/config.py +0 -0
  40. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/houdini.py +0 -0
  41. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/houdini_recorder.py +0 -0
  42. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/install.py +0 -0
  43. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/main.py +0 -0
  44. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/mcp/__init__.py +0 -0
  45. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/mcp/server_core.py +0 -0
  46. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli/mcp/server_maxwell.py +0 -0
  47. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/SOURCES.txt +0 -0
  48. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/dependency_links.txt +0 -0
  49. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/entry_points.txt +0 -0
  50. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/requires.txt +0 -0
  51. {scios_cli-0.3.2 → scios_cli-0.4.0}/scios_cli.egg-info/top_level.txt +0 -0
  52. {scios_cli-0.3.2 → scios_cli-0.4.0}/setup.cfg +0 -0
  53. {scios_cli-0.3.2 → scios_cli-0.4.0}/setup.py +0 -0
  54. {scios_cli-0.3.2 → 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.2
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.2"
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:
@@ -1,8 +1,11 @@
1
- """Antigravity Plugin — installs Scios as a global plugin at ~/.gemini/config/plugins/scios/.
1
+ """Antigravity Plugin — installs Scios as a global plugin.
2
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).
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.
6
9
 
7
10
  Plugin layout:
8
11
  ~/.gemini/config/plugins/scios/
@@ -10,14 +13,16 @@ Plugin layout:
10
13
  ├── mcp_config.json (scios-core + scios-maxwell)
11
14
  ├── hooks.json (ruff format-on-save)
12
15
  ├── skills/
13
- │ └── scios-workflow/
14
- └── SKILL.md
16
+ │ └── scios/
17
+ ├── SKILL.md
18
+ │ ├── AGENTS.md
19
+ │ ├── docs/
20
+ │ └── resources/
15
21
  ├── rules/
16
- │ └── scios.md
22
+ │ └── scios-conventions.md
17
23
  └── sidecars/
18
24
  ├── scios-sync/
19
- ├── sidecar.json
20
- │ └── sync.sh
25
+ └── sidecar.json
21
26
  └── scios-notifier/
22
27
  ├── sidecar.json
23
28
  └── notify.sh
@@ -31,42 +36,103 @@ import shutil
31
36
  from pathlib import Path
32
37
  from typing import Callable
33
38
 
34
- PLUGIN_DIR = Path.home() / ".gemini" / "config" / "plugins" / "scios"
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]
35
46
  CONFIG_FILE = Path.home() / ".gemini" / "config" / "config.json"
36
47
 
37
48
  SCIOS_BIN = shutil.which("scios") or "scios"
38
49
 
50
+ # Items to skip when copying plugin source
51
+ _SKIP = {"node_modules", ".git", "bin", "package.json", "package-lock.json", ".npmignore", "README.md"}
52
+
39
53
 
40
54
  def plugin_installed() -> bool:
41
55
  """Check if the global Antigravity plugin is installed."""
42
56
  return (PLUGIN_DIR / "plugin.json").exists()
43
57
 
44
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
+
45
83
  def install_plugin(
46
84
  *,
47
85
  enable_notifier: bool = False,
48
86
  log: Callable[[str], None] = print,
49
87
  ) -> None:
50
- """Write all plugin files and update config.json to enable sidecars."""
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)."""
51
117
 
52
118
  # ── plugin.json ──
53
- _write_json(PLUGIN_DIR / "plugin.json", {
119
+ _write_json(plugin_dir / "plugin.json", {
120
+ "$schema": "https://antigravity.google/schemas/v1/plugin.json",
54
121
  "name": "scios",
55
- "displayName": "Scios Research Platform",
56
- "description": "Drive autonomous scientific research — stage-by-stage pipeline, MCP tools, Maxwell assistant.",
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.",
57
123
  })
58
124
 
59
125
  # ── mcp_config.json ──
60
- _write_json(PLUGIN_DIR / "mcp_config.json", {
126
+ _write_json(plugin_dir / "mcp_config.json", {
61
127
  "mcpServers": {
62
128
  "scios-core": {
63
129
  "command": SCIOS_BIN,
64
- "args": ["ag", "mcp", "core"],
130
+ "args": ["mcp", "core"],
65
131
  "env": {},
66
132
  },
67
133
  "scios-maxwell": {
68
134
  "command": SCIOS_BIN,
69
- "args": ["ag", "mcp", "maxwell"],
135
+ "args": ["mcp", "maxwell"],
70
136
  "env": {},
71
137
  },
72
138
  }
@@ -74,32 +140,44 @@ def install_plugin(
74
140
  log(" ✓ mcp_config.json (scios-core + scios-maxwell)")
75
141
 
76
142
  # ── hooks.json ──
77
- _write_json(PLUGIN_DIR / "hooks.json", {
78
- "hooks": {
79
- "afterFileEdit": [
143
+ _write_json(plugin_dir / "hooks.json", {
144
+ "scios-auto-format": {
145
+ "PostToolUse": [
80
146
  {
81
- "command": "sh -c 'command -v ruff >/dev/null 2>&1 && ruff format \"$FILE\" || true'",
82
- "glob": "agents/*.py",
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
+ ]
83
157
  }
84
158
  ]
85
159
  }
86
160
  })
87
161
  log(" ✓ hooks.json (ruff format-on-save)")
88
162
 
89
- # ── skills/scios-workflow/SKILL.md ──
90
- skill_dir = PLUGIN_DIR / "skills" / "scios-workflow"
163
+ # ── skills/scios/SKILL.md ──
164
+ skill_dir = plugin_dir / "skills" / "scios"
91
165
  skill_dir.mkdir(parents=True, exist_ok=True)
92
- (skill_dir / "SKILL.md").write_text(_WORKFLOW_SKILL)
93
- log(" ✓ skills/scios-workflow/SKILL.md")
166
+ (skill_dir / "SKILL.md").write_text(_SKILL_MD)
167
+ log(" ✓ skills/scios/SKILL.md")
94
168
 
95
- # ── rules/scios.md ──
96
- rules_dir = PLUGIN_DIR / "rules"
169
+ # ── rules/scios-conventions.md ──
170
+ rules_dir = plugin_dir / "rules"
97
171
  rules_dir.mkdir(parents=True, exist_ok=True)
98
- (rules_dir / "scios.md").write_text(_RULES)
99
- log(" ✓ rules/scios.md")
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)."""
100
178
 
101
179
  # ── sidecars/scios-sync/ ──
102
- sync_dir = PLUGIN_DIR / "sidecars" / "scios-sync"
180
+ sync_dir = plugin_dir / "sidecars" / "scios-sync"
103
181
  sync_dir.mkdir(parents=True, exist_ok=True)
104
182
  _write_json(sync_dir / "sidecar.json", {
105
183
  "description": "Auto-sync: watches agents/*.py and pushes edits to the Scios backend",
@@ -111,7 +189,7 @@ def install_plugin(
111
189
  log(" ✓ sidecars/scios-sync (auto-sync agent code)")
112
190
 
113
191
  # ── sidecars/scios-notifier/ ──
114
- notifier_dir = PLUGIN_DIR / "sidecars" / "scios-notifier"
192
+ notifier_dir = plugin_dir / "sidecars" / "scios-notifier"
115
193
  notifier_dir.mkdir(parents=True, exist_ok=True)
116
194
  (notifier_dir / "notify.sh").write_text(_NOTIFIER_SCRIPT)
117
195
  (notifier_dir / "notify.sh").chmod(0o755)
@@ -127,18 +205,13 @@ def install_plugin(
127
205
  })
128
206
  log(" ✓ sidecars/scios-notifier (status polling, opt-in)")
129
207
 
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
208
 
137
209
  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}")
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}")
142
215
  _update_config(remove=True)
143
216
  log("✓ Removed sidecar config from config.json")
144
217
 
@@ -176,48 +249,62 @@ def _write_json(path: Path, data: dict) -> None:
176
249
 
177
250
 
178
251
  # ---------------------------------------------------------------------------
179
- # Inlined content (avoids reading from skill files at install time)
252
+ # Inlined content (used when repo source is not available)
180
253
  # ---------------------------------------------------------------------------
181
254
 
182
- _WORKFLOW_SKILL = """\
255
+ _SKILL_MD = """\
183
256
  ---
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.
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.
186
264
  ---
187
265
 
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.
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.
214
305
  """
215
306
 
216
307
  _RULES = """\
217
- ---
218
- trigger: always_on
219
- ---
220
-
221
308
  # Scios Workspace Conventions
222
309
 
223
310
  This folder is a **Scios research workspace** projected from the Scios backend by
@@ -226,7 +313,7 @@ files are a synced *view*.
226
313
 
227
314
  ## Folder map
228
315
  - `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
316
+ to the backend (via `scios ag push-code` or `scios ag sync`). The first line
230
317
  `# AGENT_ID: <uuid>` is the sync key — **never delete or edit it**.
231
318
  - `workflows/definition.json` — the workflow DAG (read-only projection).
232
319
  - `execution/` — metrics.json, trace_summary.md, logs.json (read-only).
@@ -235,18 +322,19 @@ files are a synced *view*.
235
322
  - `scios.toml` — the only local-authoritative file (idea_id, endpoint, mode, budget).
236
323
 
237
324
  ## 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>`.
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.
240
327
  - To run the workflow: `scios ag run` (mode from `scios.toml`).
241
328
  - 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.
329
+ - Prefer the `scios-core` MCP tools (`scios_run`, `scios_status`, `scios_explain`)
330
+ and the `scios-maxwell` MCP assistant over shelling out manually.
244
331
 
245
332
  ## Hard rules
246
333
  - Never edit files under `execution/`, `workflows/`, `datasets/`, `dashboards/`
247
334
  by hand — they are overwritten on the next sync.
248
335
  - Never remove the `# AGENT_ID:` header from an agent file.
249
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.
250
338
  """
251
339
 
252
340
  _NOTIFIER_SCRIPT = """\
@@ -287,16 +287,22 @@ def create(
287
287
  scale: str = typer.Option("small", "--scale", help="compute_scale: small|medium|large"),
288
288
  budget_iterations: int = typer.Option(3, "--budget-iterations", help="Optimizer iterations"),
289
289
  max_wall_clock_min: int = typer.Option(60, "--max-wall-clock-min", help="Wall-clock budget (minutes)"),
290
- wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for build-execute to finish, then mount"),
290
+ wait: bool = typer.Option(False, "--wait/--no-wait", help="Wait for build-execute to finish, then mount (default: no-wait)"),
291
291
  launch: bool = typer.Option(False, "--launch/--no-launch", help="Open the folder when done"),
292
292
  ):
293
- """Create a workspace+goal+idea, kick off build-execute, then mount the result."""
293
+ """Create a workspace+goal+idea. Does NOT kick off build-execute by default.
294
+
295
+ The scientist reviews the refined goal (idea.md) before deciding to proceed.
296
+ Use `scios ag run` to kick off build-execute after review.
297
+ Use `scios ag go` for the full pipeline in one shot.
298
+ """
294
299
  from scios_cli.ag.create_flow import create_and_build
295
300
 
296
301
  create_and_build(
297
302
  goal=goal, folder=folder, endpoint=endpoint, token=_resolve_token(token),
298
303
  scale=scale, budget_iterations=budget_iterations,
299
304
  max_wall_clock_min=max_wall_clock_min, wait=wait, launch=launch,
305
+ build=False,
300
306
  log=lambda x: console.print(x),
301
307
  )
302
308
 
@@ -759,6 +765,7 @@ def go(
759
765
  token=_resolve_token(token),
760
766
  scale=scale, budget_iterations=budget_iterations,
761
767
  max_wall_clock_min=max_wall_clock_min, wait=True, launch=False,
768
+ build=True,
762
769
  log=lambda x: console.print(x),
763
770
  )
764
771
 
@@ -2,7 +2,9 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import os
5
+ from pathlib import Path
5
6
  import secrets
7
+ import shutil
6
8
  import subprocess
7
9
 
8
10
  import typer
@@ -309,36 +311,64 @@ def _get_lsb_codename() -> str:
309
311
  return r.stdout.strip() if r.returncode == 0 else "rodete"
310
312
 
311
313
 
314
+ def _is_glinux() -> bool:
315
+ """Check if running on gLinux (Rodete/Debian-derived Google desktop OS)."""
316
+ if os.path.exists("/etc/glinux") or os.path.exists("/etc/glinux_version"):
317
+ return True
318
+ if shutil.which("glinux-add-repo") or os.path.exists("/usr/sbin/glinux-add-repo") or os.path.exists("/usr/bin/glinux-add-repo"):
319
+ return True
320
+ try:
321
+ if os.path.exists("/etc/os-release"):
322
+ content = Path("/etc/os-release").read_text()
323
+ if "go/glinux" in content or "glinux" in content.lower():
324
+ return True
325
+ if os.path.exists("/etc/lsb-release"):
326
+ content = Path("/etc/lsb-release").read_text()
327
+ if "GOOGLE_ID=Goobuntu" in content or "GOOGLE_CODENAME=" in content:
328
+ return True
329
+ except Exception:
330
+ pass
331
+ return False
332
+
333
+
312
334
  def _ensure_docker_access() -> None:
313
335
  """Check that the current user can access Docker without sudo.
314
336
 
315
- If Docker is not installed on gLinux, offers to install it.
316
- If the docker socket is not accessible, guides the user to fix it.
337
+ If Docker is not installed or not working on gLinux, offers to install/configure it.
338
+ If the docker socket is not accessible, guides the user to fix permissions.
317
339
  """
318
- # Check if docker daemon is actually running
319
- r = subprocess.run(["docker", "info"], capture_output=True, text=True)
320
- if r.returncode == 0:
321
- return # Docker is accessible
340
+ is_glinux = _is_glinux()
322
341
 
323
- stderr = (r.stderr or "").lower()
324
- is_glinux = os.path.exists("/etc/glinux_version") or os.path.exists("/usr/bin/glinux-add-repo")
342
+ # Check if docker binary exists on PATH
343
+ if not shutil.which("docker"):
344
+ if is_glinux:
345
+ _install_docker_glinux()
346
+ else:
347
+ console.print("[error]Docker is not installed or not in PATH.[/error]")
348
+ console.print("[muted]Install Docker: https://docs.docker.com/engine/install/[/muted]")
349
+ raise typer.Exit(1)
325
350
 
326
- # Docker daemon not installed/running (socket missing or daemon not running)
327
- daemon_missing = ("no such file" in stderr
328
- or "is the docker daemon running" in stderr
329
- or ("connect:" in stderr and "docker.sock" in stderr))
330
- if daemon_missing:
351
+ # Check if docker daemon is reachable
352
+ try:
353
+ r = subprocess.run(["docker", "info"], capture_output=True, text=True)
354
+ except FileNotFoundError:
331
355
  if is_glinux:
332
356
  _install_docker_glinux()
333
357
  else:
334
- console.print("[error]Docker daemon is not running.[/error]")
358
+ console.print("[error]Docker is not installed or not in PATH.[/error]")
335
359
  console.print("[muted]Install Docker: https://docs.docker.com/engine/install/[/muted]")
336
360
  raise typer.Exit(1)
337
361
 
362
+ if r.returncode == 0:
363
+ return # Docker is accessible
364
+
365
+ stderr = (r.stderr or "").lower()
366
+
367
+ # Check for permission denied error
338
368
  if "permission denied" in stderr:
339
- # Check if user is configured in docker group (even if shell hasn't refreshed)
340
369
  import getpass
341
370
  import grp
371
+
342
372
  username = getpass.getuser()
343
373
  try:
344
374
  docker_group = grp.getgrnam("docker")
@@ -347,7 +377,6 @@ def _ensure_docker_access() -> None:
347
377
  in_group_configured = False
348
378
 
349
379
  if in_group_configured:
350
- # User is in docker group but current shell session hasn't picked it up
351
380
  console.print("[warning]⚠ Docker permission denied — your shell hasn't picked up the 'docker' group yet.[/warning]")
352
381
  console.print()
353
382
  console.print(" Run one of these to fix it:")
@@ -366,7 +395,10 @@ def _ensure_docker_access() -> None:
366
395
 
367
396
  raise typer.Exit(1)
368
397
 
369
- # Docker exists but some other error
398
+ # If docker info failed for any other reason on gLinux (daemon stopped, socket missing, etc.)
399
+ if is_glinux:
400
+ _install_docker_glinux()
401
+
370
402
  console.print(f"[error]Docker is not available: {r.stderr.strip()}[/error]")
371
403
  console.print("[muted]Check Docker installation — see go/installdocker[/muted]")
372
404
  raise typer.Exit(1)
@@ -101,21 +101,22 @@ _SKILL_TARGETS = None # lazy
101
101
 
102
102
 
103
103
  def _get_skill_targets():
104
- """Global agent skill directories."""
104
+ """Global agent skill directories (non-Antigravity platforms)."""
105
105
  global _SKILL_TARGETS
106
106
  if _SKILL_TARGETS is None:
107
107
  from pathlib import Path
108
108
  home = Path.home()
109
109
  _SKILL_TARGETS = [
110
- ("Antigravity / Cline / Roo", home / ".agents" / "skills" / "scios"),
111
- ("Claude Code", home / ".claude" / "skills" / "scios"),
112
- ("OpenCode", home / ".config" / "opencode" / "skills" / "scios"),
110
+ ("Cline / Roo", home / ".agents" / "skills" / "scios"),
111
+ ("Claude Code", home / ".claude" / "skills" / "scios"),
112
+ ("OpenCode", home / ".config" / "opencode" / "skills" / "scios"),
113
113
  ]
114
114
  return _SKILL_TARGETS
115
115
 
116
116
 
117
117
  def _find_skill_source():
118
118
  """Find skill source directory (repo or None)."""
119
+ # The canonical source is packages/scios-skills/skills/scios/
119
120
  for candidate in [
120
121
  REPO_ROOT / "packages" / "scios-skills" / "skills" / "scios",
121
122
  REPO_ROOT / ".agents" / "skills" / "scios",
@@ -126,10 +127,12 @@ def _find_skill_source():
126
127
 
127
128
 
128
129
  def ensure_skill_updated(*, quiet: bool = False) -> bool:
129
- """Ensure the global scios skill is installed and up-to-date.
130
+ """Ensure the global scios skill is installed to non-Antigravity agent platforms.
130
131
 
131
- Called automatically after `scios login` and available via `scios setup skill`.
132
+ For Antigravity, the plugin install (ensure_plugin_updated) handles everything.
133
+ This function handles Cline, Claude Code, OpenCode etc. which only read SKILL.md.
132
134
 
135
+ Called automatically after `scios login` and available via `scios setup skill`.
133
136
  Returns True if the skill was installed/updated, False if skipped.
134
137
  """
135
138
  targets = _get_skill_targets()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scios-cli
3
- Version: 0.3.2
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes