fix(transcend): skip repository copies in scratchpads
A scan on the real host offered 4276 candidates, 4253 of them files of two repository copies in one peer's scratchpad. scratch_candidates now walks with os.walk, skips any directory holding .git, roles, plugins, pyproject.toml, package.json or a Makefile, and descends at most one level below a scratchpad; the real scan drops to 59 candidates. test_transcend adds a nested helper that stays, a deeper one and a repository copy that drop out (negative control against the unfiltered scan fails). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
194f5e28bd
commit
3e2f7a7ddb
3 files changed
+25
-6
No files matched your search
@@ -19,7 +19,9 @@ transcend.py mark --state <same> <path> carried|dropped|later
|
||||
scratchpad, every peer session's scratchpad, and the agent homes
|
||||
(`~/.claude`, `~/.agents`, `~/.codex`) for skills, commands, agents, hooks
|
||||
and rules the skills repository does not ship. It keeps scripts, tests and
|
||||
skills, and skips files named like secrets and files over 1 MB. Carried or
|
||||
skills at most one directory below a scratchpad, and skips files named like
|
||||
secrets, files over 1 MB, and repository copies (a directory holding `.git`,
|
||||
`roles`, `plugins`, `pyproject.toml`, `package.json` or a `Makefile`). Carried or
|
||||
dropped candidates return only when their content changes; `later` returns
|
||||
every time.
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ SECRET_NAME = re.compile(
|
||||
r"(secret|token|credential|passw|auth|\.env$|id_[a-z0-9]+$|\.pem$|\.key$)", re.IGNORECASE
|
||||
)
|
||||
MAX_BYTES = 1_000_000
|
||||
MAX_DEPTH = 1
|
||||
REPO_MARKERS = {".git", "roles", "plugins", "pyproject.toml", "package.json", "Makefile"}
|
||||
HOME_SOURCES = {
|
||||
".claude/skills": "skill",
|
||||
".agents/skills": "skill",
|
||||
@@ -74,11 +76,21 @@ def session_names(home: Path) -> dict[str, str]:
|
||||
|
||||
def scratch_candidates(root: Path, source: str, owner: str) -> list[dict]:
|
||||
found = []
|
||||
for path in sorted(p for p in root.rglob("*") if p.is_file()):
|
||||
kind = file_kind(path)
|
||||
if kind:
|
||||
found.append({"source": source, "owner": owner, "kind": kind, "path": str(path)})
|
||||
return found
|
||||
for current, dirs, files in os.walk(root):
|
||||
here = Path(current)
|
||||
depth = len(here.relative_to(root).parts)
|
||||
if depth and REPO_MARKERS & {*dirs, *files}:
|
||||
dirs.clear()
|
||||
continue
|
||||
if depth >= MAX_DEPTH:
|
||||
dirs.clear()
|
||||
for name in files:
|
||||
kind = file_kind(here / name)
|
||||
if kind:
|
||||
found.append(
|
||||
{"source": source, "owner": owner, "kind": kind, "path": str(here / name)}
|
||||
)
|
||||
return sorted(found, key=lambda item: item["path"])
|
||||
|
||||
|
||||
def shipped(skills_repo: Path) -> set[str]:
|
||||
|
||||
Reference in new issue
Block a user