kosokoso 0.1.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.
kosokoso-0.1.0/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: kosokoso
3
+ Version: 0.1.0
4
+ Summary: Create local AGENTS.md and CLAUDE.md aliases in Git repositories
5
+ License-Expression: WTFPL
6
+ Project-URL: Repository, https://github.com/uonr/kosokoso
7
+ Classifier: Environment :: Console
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # Kosokoso
17
+
18
+ Kosokoso (Japanese for “stealthily”) is a small script that creates `AGENTS.md` and `CLAUDE.md` links while preventing Git from tracking them.
19
+
20
+ For example, if a repository provides `CLAUDE.md` but not `AGENTS.md`, or vice versa, this script can quickly create a link for the missing file. If neither file exists, it uses `CONTRIBUTING.md` as the source for both links.
21
+
22
+ ## Installation
23
+
24
+ Run it directly without installing anything:
25
+
26
+ ```console
27
+ $ python3 kosokoso.py
28
+ ```
29
+
30
+ Install it as an isolated command with uv:
31
+
32
+ ```console
33
+ $ uv tool install kosokoso
34
+ ```
35
+
36
+ You can also run it without installing it permanently:
37
+
38
+ ```console
39
+ $ uvx kosokoso
40
+ ```
41
+
42
+ With Nix:
43
+
44
+ ```console
45
+ $ nix run github:uonr/kosokoso
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ Run `kosokoso` from anywhere inside a Git repository:
51
+
52
+ ```console
53
+ $ kosokoso
54
+ ```
55
+
56
+ If you later want Git to track the generated links, remove their rules from the repository's local exclude file:
57
+
58
+ ```console
59
+ $ kosokoso --track
60
+ ```
61
+
62
+ This only removes the local exclude rules; it does not run `git add`.
63
+
64
+ ## License
65
+
66
+ [WTFPL](LICENSE)
@@ -0,0 +1,51 @@
1
+ # Kosokoso
2
+
3
+ Kosokoso (Japanese for “stealthily”) is a small script that creates `AGENTS.md` and `CLAUDE.md` links while preventing Git from tracking them.
4
+
5
+ For example, if a repository provides `CLAUDE.md` but not `AGENTS.md`, or vice versa, this script can quickly create a link for the missing file. If neither file exists, it uses `CONTRIBUTING.md` as the source for both links.
6
+
7
+ ## Installation
8
+
9
+ Run it directly without installing anything:
10
+
11
+ ```console
12
+ $ python3 kosokoso.py
13
+ ```
14
+
15
+ Install it as an isolated command with uv:
16
+
17
+ ```console
18
+ $ uv tool install kosokoso
19
+ ```
20
+
21
+ You can also run it without installing it permanently:
22
+
23
+ ```console
24
+ $ uvx kosokoso
25
+ ```
26
+
27
+ With Nix:
28
+
29
+ ```console
30
+ $ nix run github:uonr/kosokoso
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ Run `kosokoso` from anywhere inside a Git repository:
36
+
37
+ ```console
38
+ $ kosokoso
39
+ ```
40
+
41
+ If you later want Git to track the generated links, remove their rules from the repository's local exclude file:
42
+
43
+ ```console
44
+ $ kosokoso --track
45
+ ```
46
+
47
+ This only removes the local exclude rules; it does not run `git add`.
48
+
49
+ ## License
50
+
51
+ [WTFPL](LICENSE)
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: kosokoso
3
+ Version: 0.1.0
4
+ Summary: Create local AGENTS.md and CLAUDE.md aliases in Git repositories
5
+ License-Expression: WTFPL
6
+ Project-URL: Repository, https://github.com/uonr/kosokoso
7
+ Classifier: Environment :: Console
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # Kosokoso
17
+
18
+ Kosokoso (Japanese for “stealthily”) is a small script that creates `AGENTS.md` and `CLAUDE.md` links while preventing Git from tracking them.
19
+
20
+ For example, if a repository provides `CLAUDE.md` but not `AGENTS.md`, or vice versa, this script can quickly create a link for the missing file. If neither file exists, it uses `CONTRIBUTING.md` as the source for both links.
21
+
22
+ ## Installation
23
+
24
+ Run it directly without installing anything:
25
+
26
+ ```console
27
+ $ python3 kosokoso.py
28
+ ```
29
+
30
+ Install it as an isolated command with uv:
31
+
32
+ ```console
33
+ $ uv tool install kosokoso
34
+ ```
35
+
36
+ You can also run it without installing it permanently:
37
+
38
+ ```console
39
+ $ uvx kosokoso
40
+ ```
41
+
42
+ With Nix:
43
+
44
+ ```console
45
+ $ nix run github:uonr/kosokoso
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ Run `kosokoso` from anywhere inside a Git repository:
51
+
52
+ ```console
53
+ $ kosokoso
54
+ ```
55
+
56
+ If you later want Git to track the generated links, remove their rules from the repository's local exclude file:
57
+
58
+ ```console
59
+ $ kosokoso --track
60
+ ```
61
+
62
+ This only removes the local exclude rules; it does not run `git add`.
63
+
64
+ ## License
65
+
66
+ [WTFPL](LICENSE)
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ kosokoso.py
4
+ pyproject.toml
5
+ kosokoso.egg-info/PKG-INFO
6
+ kosokoso.egg-info/SOURCES.txt
7
+ kosokoso.egg-info/dependency_links.txt
8
+ kosokoso.egg-info/entry_points.txt
9
+ kosokoso.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kosokoso = kosokoso:main
@@ -0,0 +1 @@
1
+ kosokoso
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python3
2
+ """Create local aliases for a Git repository's agent instruction file."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import os
8
+ import subprocess
9
+ from pathlib import Path
10
+
11
+
12
+ def git_path(*args: str) -> Path:
13
+ """Return a path reported by Git, or exit with a useful error."""
14
+ try:
15
+ result = subprocess.run(
16
+ ["git", "rev-parse", *args],
17
+ check=True,
18
+ stdout=subprocess.PIPE,
19
+ stderr=subprocess.PIPE,
20
+ text=True,
21
+ )
22
+ except FileNotFoundError:
23
+ raise SystemExit("error: git is not installed or is not on PATH") from None
24
+ except subprocess.CalledProcessError:
25
+ raise SystemExit("error: the current directory is not inside a Git repository") from None
26
+
27
+ return Path(result.stdout.strip()).resolve()
28
+
29
+
30
+ def create_link(link: Path, target: Path) -> None:
31
+ """Create a relative symbolic link, falling back to a hard link."""
32
+ relative_target = os.path.relpath(target, start=link.parent)
33
+ try:
34
+ link.symlink_to(relative_target)
35
+ except (NotImplementedError, OSError) as symlink_error:
36
+ try:
37
+ os.link(target, link)
38
+ except OSError as hardlink_error:
39
+ raise SystemExit(
40
+ f"error: could not link {link.name} to {target.name}: "
41
+ f"symbolic link failed ({symlink_error}); "
42
+ f"hard link failed ({hardlink_error})"
43
+ ) from None
44
+
45
+
46
+ def tracked_paths(root: Path, paths: list[Path]) -> list[str]:
47
+ """Return the requested repository-relative paths found in Git's index."""
48
+ relative_paths = [path.relative_to(root).as_posix() for path in paths]
49
+ try:
50
+ result = subprocess.run(
51
+ ["git", "ls-files", "--cached", "-z", "--", *relative_paths],
52
+ cwd=root,
53
+ check=True,
54
+ stdout=subprocess.PIPE,
55
+ stderr=subprocess.PIPE,
56
+ text=True,
57
+ )
58
+ except subprocess.CalledProcessError as error:
59
+ message = error.stderr.strip() or "git ls-files failed"
60
+ raise SystemExit(f"error: could not inspect Git's index: {message}") from None
61
+
62
+ return sorted(path for path in result.stdout.split("\0") if path)
63
+
64
+
65
+ def add_to_git_exclude(exclude_file: Path, names: list[str]) -> None:
66
+ """Add root-anchored patterns for newly created links."""
67
+ exclude_file.parent.mkdir(parents=True, exist_ok=True)
68
+
69
+ existing_text = exclude_file.read_text() if exclude_file.exists() else ""
70
+ existing_lines = set(existing_text.splitlines())
71
+ additions = [f"/{name}" for name in names if f"/{name}" not in existing_lines]
72
+ if not additions:
73
+ return
74
+
75
+ with exclude_file.open("a") as file:
76
+ if existing_text and not existing_text.endswith("\n"):
77
+ file.write("\n")
78
+ file.write("\n".join(additions) + "\n")
79
+
80
+
81
+ def remove_from_git_exclude(exclude_file: Path, names: list[str]) -> None:
82
+ """Remove root-anchored patterns previously added by this script."""
83
+ if not exclude_file.exists():
84
+ return
85
+
86
+ patterns = {f"/{name}" for name in names}
87
+ lines = exclude_file.read_text().splitlines(keepends=True)
88
+ kept_lines = [line for line in lines if line.rstrip("\r\n") not in patterns]
89
+ if kept_lines == lines:
90
+ return
91
+
92
+ exclude_file.write_text("".join(kept_lines))
93
+ for pattern in sorted(patterns):
94
+ if any(line.rstrip("\r\n") == pattern for line in lines):
95
+ print(f"removed {pattern} from {exclude_file}")
96
+
97
+
98
+ def parse_args() -> argparse.Namespace:
99
+ parser = argparse.ArgumentParser(
100
+ description="Create local aliases for a Git repository's agent instruction file."
101
+ )
102
+ parser.add_argument(
103
+ "--track",
104
+ action="store_true",
105
+ help="remove AGENTS.md and CLAUDE.md from the repository's local exclude file",
106
+ )
107
+ return parser.parse_args()
108
+
109
+
110
+ def main() -> None:
111
+ args = parse_args()
112
+ root = git_path("--show-toplevel")
113
+ exclude_file = git_path("--git-path", "info/exclude")
114
+
115
+ if args.track:
116
+ remove_from_git_exclude(exclude_file, ["AGENTS.md", "CLAUDE.md"])
117
+ return
118
+
119
+ agents = root / "AGENTS.md"
120
+ claude = root / "CLAUDE.md"
121
+ contributing = root / "CONTRIBUTING.md"
122
+
123
+ agents_exists = os.path.lexists(agents)
124
+ claude_exists = os.path.lexists(claude)
125
+
126
+ if agents_exists and claude_exists:
127
+ return
128
+
129
+ links: list[tuple[Path, Path]]
130
+ if agents_exists:
131
+ links = [(claude, agents)]
132
+ elif claude_exists:
133
+ links = [(agents, claude)]
134
+ elif os.path.lexists(contributing):
135
+ links = [(agents, contributing), (claude, contributing)]
136
+ else:
137
+ return
138
+
139
+ tracked = tracked_paths(root, [link for link, _ in links])
140
+ if tracked:
141
+ names = ", ".join(tracked)
142
+ raise SystemExit(
143
+ f"error: refusing to create links for paths already tracked by Git: {names}\n"
144
+ " .git/info/exclude cannot ignore tracked files"
145
+ )
146
+
147
+ for link, target in links:
148
+ create_link(link, target)
149
+ print(f"created {link.name} -> {os.readlink(link) if link.is_symlink() else target.name}")
150
+ add_to_git_exclude(exclude_file, [link.name])
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "kosokoso"
7
+ version = "0.1.0"
8
+ description = "Create local AGENTS.md and CLAUDE.md aliases in Git repositories"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "WTFPL"
12
+ license-files = ["LICENSE"]
13
+ dependencies = []
14
+ classifiers = [
15
+ "Environment :: Console",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ ]
20
+
21
+ [project.scripts]
22
+ kosokoso = "kosokoso:main"
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/uonr/kosokoso"
26
+
27
+ [tool.setuptools]
28
+ py-modules = ["kosokoso"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+