@@ -158,10 +158,22 @@ def remove_path(path: Path) -> None:
158158 path .unlink ()
159159
160160
161+ def expected_symlink_target (path : Path , target : Path ) -> Path :
162+ return Path (os .path .relpath (target , start = path .parent ))
163+
164+
161165def is_matching_symlink (path : Path , target : Path ) -> bool :
162166 if not path .is_symlink ():
163167 return False
164- return path .resolve () == target .resolve ()
168+ return Path (os .readlink (path )) == expected_symlink_target (path , target )
169+
170+
171+ def is_within_repo (path : Path , repo_root : Path ) -> bool :
172+ try :
173+ path .resolve ().relative_to (repo_root .resolve ())
174+ return True
175+ except ValueError :
176+ return False
165177
166178
167179def find_unexpected_children (path : Path , expected_children : set [str ]) -> list [Path ]:
@@ -238,21 +250,41 @@ def sync_symlink_outputs(
238250 continue
239251
240252 remove_path (path )
241- relative_target = Path (os .path .relpath (target , start = path .parent ))
242- path .symlink_to (relative_target , target_is_directory = target .is_dir ())
253+ path .symlink_to (
254+ expected_symlink_target (path , target ), target_is_directory = target .is_dir ()
255+ )
243256 print (f"Linked { path } " )
244257
245258 return has_mismatch
246259
247260
248261def sync_managed_directories (
249- managed_directories : list [dict [str , Any ]], check_mode : bool
262+ managed_directories : list [dict [str , Any ]], repo_root : Path , check_mode : bool
250263) -> bool :
251264 has_mismatch = False
252265
253266 for directory in managed_directories :
254267 path : Path = directory ["path" ]
255268 expected_children : set [str ] = directory ["expected_children" ]
269+
270+ if path .is_symlink ():
271+ message = f"Managed generated shim directory must not be a symlink: { path } "
272+ if check_mode :
273+ has_mismatch = True
274+ print_error (message )
275+ continue
276+ raise RuntimeError (message )
277+
278+ if path .exists () and not is_within_repo (path , repo_root ):
279+ message = (
280+ f"Managed generated shim directory resolves outside the repository: { path } "
281+ )
282+ if check_mode :
283+ has_mismatch = True
284+ print_error (message )
285+ continue
286+ raise RuntimeError (message )
287+
256288 unexpected_children = find_unexpected_children (path , expected_children )
257289
258290 if not unexpected_children :
@@ -319,7 +351,7 @@ def main() -> int:
319351 skills_root = repo_root / ".agents" / "skills"
320352 shared_skill_names = sorted (
321353 entry .name
322- for entry in skills_root .iterdir ()
354+ for entry in ( skills_root .iterdir () if skills_root . exists () else [] )
323355 if entry .is_dir () and (entry / "SKILL.md" ).exists ()
324356 )
325357
@@ -354,7 +386,8 @@ def main() -> int:
354386
355387 has_mismatch = sync_symlink_outputs (symlink_outputs , check_mode ) or has_mismatch
356388 has_mismatch = (
357- sync_managed_directories (managed_directories , check_mode ) or has_mismatch
389+ sync_managed_directories (managed_directories , repo_root , check_mode )
390+ or has_mismatch
358391 )
359392
360393 return 1 if check_mode and has_mismatch else 0
0 commit comments