SkillLink 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

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.
SkillLink/SkillLink.py ADDED
@@ -0,0 +1,103 @@
1
+ import logging
2
+ import os
3
+ import requests
4
+ import zipfile
5
+ import shutil
6
+ from io import BytesIO
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class SkillLink:
11
+ def __init__(self, githubRepo: str = None, repoFolder: str = None, localDir: str = None):
12
+ self.githubRepo = githubRepo
13
+ self.repoFolder = repoFolder
14
+ self.localDir = localDir
15
+ if not self.githubRepo:
16
+ raise ValueError("GitHub repo must be specified e.g., 'TristanMcBrideSr/SkillForge'")
17
+ if not self.repoFolder:
18
+ raise ValueError("Repo folder must be specified e.g., 'SkillForge'")
19
+ if not self.localDir:
20
+ raise ValueError("Local dir must be specified e.g., '/your/path/skills'")
21
+
22
+ def syncNewFiles(self, srcDir, dstDir, skillList=None, override=False):
23
+ onlyFiles = None
24
+ if skillList:
25
+ normalized = set()
26
+ for name in skillList:
27
+ name = name.lower()
28
+ normalized.add(name)
29
+ if not name.endswith(".py"):
30
+ normalized.add(f"{name}.py")
31
+ onlyFiles = normalized
32
+
33
+ for root, dirs, files in os.walk(srcDir):
34
+ relRoot = os.path.relpath(root, srcDir)
35
+ targetRoot = os.path.join(dstDir, relRoot) if relRoot != '.' else dstDir
36
+ os.makedirs(targetRoot, exist_ok=True)
37
+ for file in files:
38
+ if onlyFiles and file.lower() not in onlyFiles:
39
+ continue
40
+ srcFile = os.path.join(root, file)
41
+ dstFile = os.path.join(targetRoot, file)
42
+ if os.path.exists(dstFile):
43
+ if override:
44
+ shutil.copy2(srcFile, dstFile)
45
+ print(f"Overridden {dstFile} with {srcFile}")
46
+ logger.info(f"Overridden {dstFile} with {srcFile}")
47
+ else:
48
+ print(f"File {dstFile} already exists locally. Skipping (preserving local).")
49
+ logger.info(f"File {dstFile} already exists locally. Skipping (preserving local).")
50
+ continue
51
+ shutil.copy2(srcFile, dstFile)
52
+ print(f"Copied {srcFile} to {dstFile}")
53
+ logger.info(f"Copied {srcFile} to {dstFile}")
54
+
55
+ def startSync(self, **kwargs):
56
+ """
57
+ Syncs skills from a GitHub repo zip to local directory.
58
+
59
+ Kwargs:
60
+ skillList: list of skills to sync (default: all)
61
+ override: bool, if True always overwrite local (default: False)
62
+ githubToken: str, GitHub token for private repos (default: None)
63
+ branch: str, Git branch (default: 'master')
64
+ """
65
+ skillList = kwargs.get('skillList')
66
+ override = kwargs.get('override', False)
67
+ githubToken = kwargs.get('githubToken')
68
+ branch = kwargs.get('branch', 'master')
69
+
70
+ logger.info(f"Starting {self.repoFolder} connection...")
71
+ zipUrl = f"https://github.com/{self.githubRepo}/archive/refs/heads/{branch}.zip"
72
+ logger.info("Starting sync...")
73
+ logger.info(f"Downloading {zipUrl} ...")
74
+ headers = {"User-Agent": "Mozilla/5.0"}
75
+ if githubToken:
76
+ headers["Authorization"] = f"Bearer {githubToken}"
77
+
78
+ try:
79
+ r = requests.get(zipUrl, headers=headers)
80
+ r.raise_for_status()
81
+ tempDir = "tmpDownload"
82
+ z = zipfile.ZipFile(BytesIO(r.content))
83
+ z.extractall(tempDir)
84
+
85
+ extractedRoot = os.path.join(tempDir, os.listdir(tempDir)[0])
86
+ skillsSrc = os.path.join(extractedRoot, self.repoFolder)
87
+
88
+ if not os.path.exists(skillsSrc):
89
+ logger.error(f"Can't find {self.repoFolder} in the repo!")
90
+ shutil.rmtree(tempDir)
91
+ raise FileNotFoundError(f"Can't find {self.repoFolder} in the repo!")
92
+
93
+ os.makedirs(self.localDir, exist_ok=True)
94
+
95
+ self.syncNewFiles(skillsSrc, self.localDir, skillList, override)
96
+
97
+ shutil.rmtree(tempDir)
98
+ logger.info("Sync complete.")
99
+ return True
100
+
101
+ except Exception as e:
102
+ logger.error(f"Sync failed: {e}", exc_info=True)
103
+ return False
SkillLink/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .SkillLink import SkillLink
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SkillLink
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A Modern way to sync skills
5
5
  Author: Tristan McBride Sr.
6
6
  Author-email: "Tristan McBride Sr." <142635792+TristanMcBrideSr@users.noreply.github.com>
@@ -167,6 +167,12 @@ syncer.startSync(
167
167
 
168
168
  ---
169
169
 
170
+ ## Code Examples
171
+
172
+ You can find code examples on my [GitHub repository](https://github.com/TristanMcBrideSr/TechBook).
173
+
174
+ ---
175
+
170
176
  ## License
171
177
 
172
178
  This project is licensed under the [Apache License, Version 2.0](LICENSE).
@@ -174,7 +180,9 @@ Copyright 2025 Tristan McBride Sr.
174
180
 
175
181
  ---
176
182
 
177
- **Authors:**
183
+ ## Acknowledgements
184
+
185
+ Project by:
178
186
  - Tristan McBride Sr.
179
187
  - Sybil
180
188
 
@@ -0,0 +1,6 @@
1
+ SkillLink/SkillLink.py,sha256=boenTeVIr4sAfeHcLiNcZCAw1FHEw7Orp1-UN-esAaU,4341
2
+ SkillLink/__init__.py,sha256=b6TVK2Ve_Cct2wu3FLn_2X7cXzCqhI6kTIM81OorPMM,32
3
+ skilllink-0.1.1.dist-info/METADATA,sha256=CrnAat_Xw7T-rplELWnBZO6E-QWz8wJfuUuqURMW7N4,4710
4
+ skilllink-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ skilllink-0.1.1.dist-info/top_level.txt,sha256=8Nq2y-9CjjC46x0nlmsBxabHDFhJcw5FnprPDQz3RKY,10
6
+ skilllink-0.1.1.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ SkillLink
@@ -1,4 +0,0 @@
1
- skilllink-0.1.0.dist-info/METADATA,sha256=kfBFsjjH2DhlPLSRmN0FjRKGWTrpymX_tmiNR2NJ-EY,4558
2
- skilllink-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3
- skilllink-0.1.0.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
- skilllink-0.1.0.dist-info/RECORD,,
@@ -1 +0,0 @@
1
-