Skip to content

Commit b85d751

Browse files
committed
Add Lab local agent guidance support
1 parent 590bc14 commit b85d751

3 files changed

Lines changed: 219 additions & 20 deletions

File tree

packages/prime/src/prime_cli/lab_setup.py

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
)
3636

3737
VERIFIERS_REPO = "primeintellect-ai/verifiers"
38-
VERIFIERS_REF = "7d8a522df67308327cb9b8931ce6a5873a99834a"
38+
VERIFIERS_REF = "f43e42c1fabfe2604afc95b9ce62779a8f55d487"
3939
VERIFIERS_CONFIG_REF = "main"
4040
DOWNLOAD_ATTEMPTS = 3
4141
DOWNLOAD_RETRY_DELAY_SECONDS = 1.0
@@ -44,8 +44,12 @@
4444
SUPPORTED_AGENTS = known_agent_names()
4545
LAB_GITIGNORE_PATTERNS = (
4646
".env",
47+
"/AGENTS.md",
48+
"/CLAUDE.md",
49+
"/CLAUDE.local.md",
4750
"/outputs/",
4851
"/prime-rl/",
52+
"/environments/AGENTS.md",
4953
"/environments/*/outputs/",
5054
"/environments/*/dist/",
5155
"/environments/*/*.egg-info/",
@@ -55,6 +59,17 @@
5559
".pytest_cache/",
5660
".ruff_cache/",
5761
)
62+
LOCAL_CLAUDE_MD = "CLAUDE.local.md"
63+
LAB_GUIDANCE_GITIGNORE_PATHS = ("AGENTS.md", "CLAUDE.md", LOCAL_CLAUDE_MD, "environments/AGENTS.md")
64+
LOCAL_CLAUDE_GUIDANCE_TEMPLATE = "\n".join(
65+
[
66+
"# CLAUDE.local.md",
67+
"",
68+
"Add personal Claude guidance for this Lab workspace here.",
69+
"Prime Lab preserves this file during setup and sync.",
70+
"",
71+
]
72+
)
5873
PRIME_SKILLS_MANIFEST = ".prime-managed.json"
5974
WORKSPACE_SKILLS_DIR = Path(".prime") / "skills"
6075
DeprecatedConfigField = tuple[tuple[str, ...], str]
@@ -220,7 +235,7 @@ def parse_lab_setup_args(args: list[str]) -> LabSetupOptions:
220235
parser.add_argument(
221236
"--skip-agents-md",
222237
action="store_true",
223-
help="Skip AGENTS.md, CLAUDE.md, and environments/AGENTS.md.",
238+
help="Skip AGENTS.md, CLAUDE.md, CLAUDE.local.md, and environments/AGENTS.md.",
224239
)
225240
parser.add_argument(
226241
"--skip-install",
@@ -269,7 +284,7 @@ def parse_lab_sync_args(args: list[str]) -> LabSyncOptions:
269284
parser.add_argument(
270285
"--skip-docs",
271286
action="store_true",
272-
help="Skip AGENTS.md, CLAUDE.md, and environments/AGENTS.md refresh.",
287+
help="Skip AGENTS.md, CLAUDE.md, CLAUDE.local.md, and environments/AGENTS.md refresh.",
273288
)
274289
parser.add_argument(
275290
"--no-agent",
@@ -395,6 +410,7 @@ def _run_lab_sync_steps(
395410
workspace.mkdir(parents=True, exist_ok=True)
396411
(workspace / "configs").mkdir(exist_ok=True)
397412
(workspace / "environments").mkdir(exist_ok=True)
413+
_append_gitignore(workspace)
398414
emit(f"Syncing Lab assets in {workspace}\n")
399415
agents = _resolve_sync_agents(workspace, options.agents, no_agent=options.no_agent)
400416
guidance_agents = agents
@@ -432,8 +448,9 @@ def _sync_workspace_guidance(
432448
force: bool = False,
433449
) -> None:
434450
_download_file(AGENTS_MD_SRC, workspace / "AGENTS.md", emit, force=force, quiet=True)
451+
_download_file(CLAUDE_MD_SRC, workspace / "CLAUDE.md", emit, force=force, quiet=True)
435452
if "claude" in agents:
436-
_download_file(CLAUDE_MD_SRC, workspace / "CLAUDE.md", emit, force=force, quiet=True)
453+
_ensure_claude_local_guidance(workspace)
437454
_download_file(
438455
ENVS_AGENTS_MD_SRC,
439456
workspace / "environments" / "AGENTS.md",
@@ -444,6 +461,13 @@ def _sync_workspace_guidance(
444461
emit("Refreshed workspace guidance\n")
445462

446463

464+
def _ensure_claude_local_guidance(workspace: Path) -> None:
465+
path = workspace / LOCAL_CLAUDE_MD
466+
if path.exists() or path.is_symlink():
467+
return
468+
path.write_text(LOCAL_CLAUDE_GUIDANCE_TEMPLATE, encoding="utf-8")
469+
470+
447471
def _sync_prime_skills(emit: Emit) -> tuple[str, ...]:
448472
skills_dir = _global_prime_skills_dir()
449473
skills_dir.parent.mkdir(parents=True, exist_ok=True)
@@ -864,6 +888,7 @@ def _lab_doctor_checks(options: LabDoctorOptions, workspace: Path) -> list[LabDo
864888
"Run prime lab doctor --fix.",
865889
),
866890
_gitignore_check(workspace),
891+
_lab_guidance_tracking_check(workspace, fix=options.fix),
867892
_config_validity_check(workspace),
868893
_config_deprecated_fields_check(workspace),
869894
_config_environment_reference_check(workspace),
@@ -1116,15 +1141,61 @@ def _gitignore_check(workspace: Path) -> LabDoctorCheck:
11161141
missing = _missing_gitignore_patterns(existing)
11171142
if not missing:
11181143
return LabDoctorCheck(
1119-
name="Gitignore outputs",
1144+
name="Lab gitignore",
11201145
status="PASS",
1121-
message="Standard output and generated source paths are ignored.",
1146+
message="Standard Lab generated and local paths are ignored.",
11221147
)
11231148
return LabDoctorCheck(
1124-
name="Gitignore outputs",
1149+
name="Lab gitignore",
11251150
status="WARN",
11261151
message="Missing " + ", ".join(missing),
1127-
remediation="Run prime lab doctor --fix to add standard output ignores.",
1152+
remediation="Run prime lab doctor --fix to add standard Lab ignores.",
1153+
)
1154+
1155+
1156+
def _lab_guidance_tracking_check(workspace: Path, *, fix: bool) -> LabDoctorCheck:
1157+
tracked = _tracked_git_paths(workspace, LAB_GUIDANCE_GITIGNORE_PATHS)
1158+
if tracked and fix:
1159+
_untrack_git_paths(workspace, tracked)
1160+
tracked = _tracked_git_paths(workspace, LAB_GUIDANCE_GITIGNORE_PATHS)
1161+
if not tracked:
1162+
return LabDoctorCheck(
1163+
name="Lab guidance tracking",
1164+
status="PASS",
1165+
message="Lab generated and local guidance files are untracked.",
1166+
)
1167+
paths = " ".join(tracked)
1168+
return LabDoctorCheck(
1169+
name="Lab guidance tracking",
1170+
status="WARN",
1171+
message="Tracked Lab generated/local guidance files: " + ", ".join(tracked),
1172+
remediation=f"Run git rm --cached {paths} && prime lab sync.",
1173+
)
1174+
1175+
1176+
def _tracked_git_paths(workspace: Path, paths: tuple[str, ...]) -> tuple[str, ...]:
1177+
try:
1178+
result = subprocess.run(
1179+
["git", "ls-files", "--", *paths],
1180+
cwd=workspace,
1181+
check=False,
1182+
capture_output=True,
1183+
text=True,
1184+
)
1185+
except OSError:
1186+
return ()
1187+
if result.returncode != 0:
1188+
return ()
1189+
return tuple(line.strip() for line in result.stdout.splitlines() if line.strip())
1190+
1191+
1192+
def _untrack_git_paths(workspace: Path, paths: tuple[str, ...]) -> None:
1193+
subprocess.run(
1194+
["git", "rm", "--cached", "--quiet", "--", *paths],
1195+
cwd=workspace,
1196+
check=False,
1197+
capture_output=True,
1198+
text=True,
11281199
)
11291200

11301201

@@ -1422,10 +1493,11 @@ def _write_lab_docs_index(workspace: Path, agents: tuple[str, ...]) -> None:
14221493
docs_dir.mkdir(parents=True, exist_ok=True)
14231494
guidance_files = [
14241495
"- `AGENTS.md`",
1496+
"- `CLAUDE.md`",
14251497
"- `environments/AGENTS.md`",
14261498
]
14271499
if "claude" in agents:
1428-
guidance_files.insert(1, "- `CLAUDE.md`")
1500+
guidance_files.insert(2, f"- `{LOCAL_CLAUDE_MD}`")
14291501
index = docs_dir / "index.md"
14301502
index.write_text(
14311503
"\n".join(

0 commit comments

Comments
 (0)