Skip to content

Commit 8f294b2

Browse files
committed
Update skill installation (don't edit global agent files) and bump version
1 parent d709b25 commit 8f294b2

File tree

4 files changed

+31
-135
lines changed

4 files changed

+31
-135
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,27 @@ For more advanced examples, including [Triton](/examples/triton/README.md), [CUD
126126
| `weco credits topup [amount]` | Purchase additional credits | When you need more credits (default: 10) |
127127
| `weco credits autotopup` | Configure automatic top-up | Set up automatic credit replenishment |
128128

129+
### Observe Commands
130+
131+
Track experiments from your own optimization loop (LLM agents, custom scripts, manual experiments) in the Weco dashboard:
132+
133+
| Command | Description |
134+
|---------|-------------|
135+
| `weco observe init` | Create a run and print the run ID |
136+
| `weco observe log` | Log a step with metrics and code |
137+
138+
```bash
139+
# Initialize a run
140+
WECO_RUN_ID=$(weco observe init --name "my-experiment" --metric val_bpb --goal min --source train.py)
141+
142+
# Log baseline (step 0) and experiments (step 1, 2, ...)
143+
weco observe log --run-id "$WECO_RUN_ID" --step 0 --description "baseline" --metrics '{"val_bpb": 2.36}' --source train.py
144+
weco observe log --run-id "$WECO_RUN_ID" --step 1 --description "increase batch size" --metrics '{"val_bpb": 2.26}' --source train.py
145+
weco observe log --run-id "$WECO_RUN_ID" --step 2 --status failed --description "OOM" --metrics '{"val_bpb": 0.0}'
146+
```
147+
148+
All observe commands are fire-and-forget — they always exit 0, so they never crash an agent's loop. For branching, pass `--parent-step` explicitly. See `weco observe init --help` and `weco observe log --help` for all options.
149+
129150
### Setup Commands (Experimental)
130151

131152
| Command | Description |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name = "weco"
88
authors = [{ name = "Weco AI Team", email = "contact@weco.ai" }]
99
description = "Documentation for `weco`, a CLI for using Weco AI's code optimizer."
1010
readme = "README.md"
11-
version = "0.3.19"
11+
version = "0.3.20"
1212
license = { file = "LICENSE" }
1313
requires-python = ">=3.9"
1414
dependencies = [

weco/observe/cli.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import warnings
1111

1212
from weco.auth import handle_authentication
13+
from weco.browser import open_browser
1314
from weco.observe import api
15+
from weco import __dashboard_url__
1416

1517

1618
def configure_observe_parser(observe_parser: argparse.ArgumentParser) -> None:
@@ -117,8 +119,12 @@ def _handle_init(args: argparse.Namespace, auth_headers: dict) -> None:
117119
)
118120

119121
if result and result.get("run_id"):
122+
run_id = result["run_id"]
120123
# Print only the run_id to stdout so it can be captured by $(...)
121-
print(result["run_id"])
124+
print(run_id)
125+
# Open the dashboard in the user's browser
126+
dashboard_url = f"{__dashboard_url__}/runs/{run_id}"
127+
open_browser(dashboard_url)
122128
else:
123129
print("weco observe: failed to create run", file=sys.stderr)
124130

weco/setup.py

Lines changed: 2 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from urllib.request import urlopen
1515

1616
from rich.console import Console
17-
from rich.prompt import Confirm, Prompt
17+
from rich.prompt import Prompt
1818

1919
from .events import (
2020
send_event,
@@ -23,7 +23,7 @@
2323
SkillInstallCompletedEvent,
2424
SkillInstallFailedEvent,
2525
)
26-
from .utils import copy_directory, copy_file, read_from_path, write_to_path
26+
from .utils import copy_directory, copy_file
2727

2828

2929
# =============================================================================
@@ -69,20 +69,10 @@ class SafetyError(SetupError):
6969
WECO_SKILL_DIR = CLAUDE_SKILLS_DIR / "weco"
7070
WECO_CLAUDE_SNIPPET_PATH = WECO_SKILL_DIR / "snippets" / "claude.md"
7171
WECO_CLAUDE_MD_PATH = WECO_SKILL_DIR / "CLAUDE.md"
72-
CLAUDE_MD_PATH = CLAUDE_DIR / "CLAUDE.md"
73-
WECO_CLAUDE_GLOBAL_SNIPPET_PATH = WECO_SKILL_DIR / "snippets" / "claude-global.md"
74-
75-
# Section markers for the global ~/.claude/CLAUDE.md
76-
WECO_SECTION_START = "<!-- WECO_START -->"
77-
WECO_SECTION_END = "<!-- WECO_END -->"
78-
7972
# Cursor paths
8073
CURSOR_DIR = pathlib.Path.home() / ".cursor"
81-
CURSOR_RULES_DIR = CURSOR_DIR / "rules"
82-
CURSOR_WECO_RULES_PATH = CURSOR_RULES_DIR / "weco.mdc"
8374
CURSOR_SKILLS_DIR = CURSOR_DIR / "skills"
8475
CURSOR_WECO_SKILL_DIR = CURSOR_SKILLS_DIR / "weco"
85-
CURSOR_RULES_SNIPPET_PATH = CURSOR_WECO_SKILL_DIR / "snippets" / "cursor.md"
8676

8777
# Files/directories to skip when copying local repos
8878
_COPY_IGNORE_PATTERNS = {".git", "__pycache__", ".DS_Store"}
@@ -175,53 +165,6 @@ def safe_remove_directory(path: pathlib.Path, allowed_parents: set[pathlib.Path]
175165
# =============================================================================
176166

177167

178-
def generate_cursor_mdc_content(snippet_content: str) -> str:
179-
"""Generate Cursor MDC file content with YAML frontmatter."""
180-
return f"""---
181-
description: Weco code optimization skill. Weco automates optimization by iteratively refining code against any metric you define — invoke for speed, accuracy, latency, cost, or anything else you can measure.
182-
alwaysApply: true
183-
---
184-
{snippet_content}
185-
"""
186-
187-
188-
def generate_weco_claude_section(snippet_content: str) -> str:
189-
"""Generate the Weco section for the global ~/.claude/CLAUDE.md with marker tags."""
190-
return f"""{WECO_SECTION_START}
191-
{snippet_content}
192-
{WECO_SECTION_END}"""
193-
194-
195-
def extract_weco_section(content: str) -> str | None:
196-
"""Extract the Weco section from file content, or None if not present."""
197-
if WECO_SECTION_START not in content or WECO_SECTION_END not in content:
198-
return None
199-
start = content.index(WECO_SECTION_START)
200-
end = content.index(WECO_SECTION_END) + len(WECO_SECTION_END)
201-
return content[start:end]
202-
203-
204-
def upsert_weco_section(file_path: pathlib.Path, section: str) -> None:
205-
"""
206-
Insert or replace the Weco section in a file.
207-
208-
- If the file has existing WECO_START/WECO_END markers, replace that region.
209-
- If the file exists but has no markers, append the section.
210-
- If the file doesn't exist, create it with the section.
211-
"""
212-
if file_path.exists():
213-
content = read_from_path(file_path)
214-
existing = extract_weco_section(content)
215-
if existing is not None:
216-
content = content.replace(existing, section)
217-
else:
218-
content = content.rstrip() + "\n\n" + section + "\n"
219-
else:
220-
content = section + "\n"
221-
222-
write_to_path(file_path, content, mkdir=True)
223-
224-
225168
def validate_local_skill_repo(local_path: pathlib.Path) -> None:
226169
"""
227170
Validate that a local path is a valid weco-skill repository.
@@ -413,96 +356,22 @@ def setup_claude_code(console: Console, local_path: pathlib.Path | None = None)
413356
copy_file(WECO_CLAUDE_SNIPPET_PATH, WECO_CLAUDE_MD_PATH)
414357
console.print("[green]CLAUDE.md installed to skill directory.[/]")
415358

416-
# Update global ~/.claude/CLAUDE.md with a Weco section.
417-
# The global file is user-managed and may contain other content, so we use
418-
# section markers (WECO_START/WECO_END) to insert or replace only our section.
419-
snippet_content = read_from_path(WECO_CLAUDE_GLOBAL_SNIPPET_PATH)
420-
section = generate_weco_claude_section(snippet_content.strip())
421-
422-
existing_content = None
423-
if CLAUDE_MD_PATH.exists():
424-
try:
425-
existing_content = read_from_path(CLAUDE_MD_PATH)
426-
except Exception:
427-
pass
428-
429-
existing_section = extract_weco_section(existing_content) if existing_content else None
430-
431-
if existing_section is not None and existing_section.strip() == section.strip():
432-
console.print("[dim]~/.claude/CLAUDE.md already contains the latest Weco section.[/]")
433-
else:
434-
action = "updated" if existing_section is not None else "added"
435-
console.print("\n[bold yellow]~/.claude/CLAUDE.md Update (Recommended)[/]")
436-
console.print("Adding a Weco section to ~/.claude/CLAUDE.md makes Weco invocation more reliable.")
437-
if Confirm.ask("Would you like to update ~/.claude/CLAUDE.md?", default=True):
438-
upsert_weco_section(CLAUDE_MD_PATH, section)
439-
console.print(f"[green]Weco section {action} in ~/.claude/CLAUDE.md.[/]")
440-
else:
441-
console.print("[yellow]Skipping ~/.claude/CLAUDE.md update.[/]")
442-
console.print(f"[dim]You can manually add the Weco section to {CLAUDE_MD_PATH}[/]")
443-
444359
console.print("\n[bold green]Setup complete![/]")
445360
if local_path:
446361
console.print(f"[dim]Skill copied from: {local_path}[/]")
447362
console.print(f"[dim]Skill installed at: {WECO_SKILL_DIR}[/]")
448-
console.print(f"[dim]Global CLAUDE.md at: {CLAUDE_MD_PATH}[/]")
449363

450364

451365
def setup_cursor(console: Console, local_path: pathlib.Path | None = None) -> None:
452366
"""Set up Weco rules for Cursor."""
453367
console.print("[bold blue]Setting up Weco for Cursor...[/]\n")
454368

455-
# Cursor setup is intentionally "editor-config-centric":
456-
# - Install/copy the skill into Cursor's skills directory (so we can read snippets).
457-
# - The behavior change is controlled by `~/.cursor/rules/weco.mdc`, which is *global*
458-
# editor state (not part of the installed skill folder).
459-
# - Because users may have customized that file, we:
460-
# 1) compute desired content from the snippet
461-
# 2) check if it is already up to date
462-
# 3) prompt before creating/updating it
463369
install_skill(CURSOR_WECO_SKILL_DIR, console, local_path)
464370

465-
snippet_content = read_from_path(CURSOR_RULES_SNIPPET_PATH)
466-
mdc_content = generate_cursor_mdc_content(snippet_content.strip())
467-
468-
# Check if already up to date
469-
existing_content = None
470-
if CURSOR_WECO_RULES_PATH.exists():
471-
try:
472-
existing_content = read_from_path(CURSOR_WECO_RULES_PATH)
473-
except Exception:
474-
pass
475-
476-
if existing_content is not None and existing_content.strip() == mdc_content.strip():
477-
console.print("[dim]weco.mdc already contains the latest Weco rules.[/]")
478-
console.print("\n[bold green]Setup complete![/]")
479-
console.print(f"[dim]Rules file at: {CURSOR_WECO_RULES_PATH}[/]")
480-
return
481-
482-
# Prompt user for creation/update
483-
if existing_content is not None:
484-
console.print("\n[bold yellow]weco.mdc Update[/]")
485-
console.print("The Weco rules file can be updated to the latest version.")
486-
if not Confirm.ask("Would you like to update weco.mdc?", default=True):
487-
console.print("\n[yellow]Skipping weco.mdc update.[/]")
488-
console.print(f"[dim]Skill installed but rules not configured. Create manually at {CURSOR_WECO_RULES_PATH}[/]")
489-
return
490-
else:
491-
console.print("\n[bold yellow]weco.mdc Creation[/]")
492-
console.print("To enable Weco optimization rules, we can create a weco.mdc file.")
493-
if not Confirm.ask("Would you like to create weco.mdc?", default=True):
494-
console.print("\n[yellow]Skipping weco.mdc creation.[/]")
495-
console.print(f"[dim]Skill installed but rules not configured. Create manually at {CURSOR_WECO_RULES_PATH}[/]")
496-
return
497-
498-
write_to_path(CURSOR_WECO_RULES_PATH, mdc_content, mkdir=True)
499-
console.print("[green]weco.mdc created successfully.[/]")
500-
501371
console.print("\n[bold green]Setup complete![/]")
502372
if local_path:
503373
console.print(f"[dim]Skill copied from: {local_path}[/]")
504374
console.print(f"[dim]Skill installed at: {CURSOR_WECO_SKILL_DIR}[/]")
505-
console.print(f"[dim]Rules file at: {CURSOR_WECO_RULES_PATH}[/]")
506375

507376

508377
# =============================================================================

0 commit comments

Comments
 (0)