dimcode-darwin-x64 0.1.1-beta.0 → 0.1.2-beta.1
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.
- package/bin/dimcode +0 -0
- package/bin/runtime/sandbox/dim-sandbox-runner +0 -0
- package/bin/runtime/sandbox/manifest.json +15 -0
- package/bin/skills-assets/deep-investigate/SKILL.md +101 -0
- package/bin/skills-assets/deep-investigate/references/prompts.md +75 -0
- package/bin/skills-assets/deep-investigate/references/templates.md +73 -0
- package/bin/skills-assets/deep-investigate/references/thinking-tools.md +36 -0
- package/bin/skills-assets/docs-sprint/SKILL.md +73 -0
- package/bin/skills-assets/docs-sprint/agents/openai.yaml +4 -0
- package/bin/skills-assets/docs-sprint/references/contract-discipline.md +30 -0
- package/bin/skills-assets/docs-sprint/references/delivery-plan.md +162 -0
- package/bin/skills-assets/docs-sprint/references/documentation-system.md +109 -0
- package/bin/skills-assets/docs-sprint/references/ui-layout.md +73 -0
- package/bin/skills-assets/docs-sprint/references/worktree-guide.md +45 -0
- package/bin/skills-assets/docx/SKILL.md +273 -0
- package/bin/skills-assets/docx/assets/styles/academic_styles.xml +250 -0
- package/bin/skills-assets/docx/assets/styles/corporate_styles.xml +284 -0
- package/bin/skills-assets/docx/assets/styles/default_styles.xml +449 -0
- package/bin/skills-assets/docx/assets/xsd/aesthetic-rules.xsd +470 -0
- package/bin/skills-assets/docx/assets/xsd/business-rules.xsd +130 -0
- package/bin/skills-assets/docx/assets/xsd/common-types.xsd +159 -0
- package/bin/skills-assets/docx/assets/xsd/wml-subset.xsd +589 -0
- package/bin/skills-assets/docx/references/cjk_typography.md +357 -0
- package/bin/skills-assets/docx/references/cjk_university_template_guide.md +184 -0
- package/bin/skills-assets/docx/references/comments_guide.md +191 -0
- package/bin/skills-assets/docx/references/design_good_bad_examples.md +829 -0
- package/bin/skills-assets/docx/references/design_principles.md +819 -0
- package/bin/skills-assets/docx/references/openxml_element_order.md +308 -0
- package/bin/skills-assets/docx/references/openxml_encyclopedia_part1.md +4061 -0
- package/bin/skills-assets/docx/references/openxml_encyclopedia_part2.md +2820 -0
- package/bin/skills-assets/docx/references/openxml_encyclopedia_part3.md +3381 -0
- package/bin/skills-assets/docx/references/openxml_namespaces.md +82 -0
- package/bin/skills-assets/docx/references/openxml_units.md +72 -0
- package/bin/skills-assets/docx/references/scenario_a_create.md +284 -0
- package/bin/skills-assets/docx/references/scenario_b_edit_content.md +295 -0
- package/bin/skills-assets/docx/references/scenario_c_apply_template.md +456 -0
- package/bin/skills-assets/docx/references/track_changes_guide.md +200 -0
- package/bin/skills-assets/docx/references/troubleshooting.md +506 -0
- package/bin/skills-assets/docx/references/typography_guide.md +294 -0
- package/bin/skills-assets/docx/references/xsd_validation_guide.md +158 -0
- package/bin/skills-assets/docx/scripts/doc_to_docx.sh +40 -0
- package/bin/skills-assets/docx/scripts/docx_preview.sh +37 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Cli/Docx.Cli.csproj +19 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Cli/Program.cs +18 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/AnalyzeCommand.cs +147 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/ApplyTemplateCommand.cs +322 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/CreateCommand.cs +324 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/DiffCommand.cs +155 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/EditContentCommand.cs +487 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/FixOrderCommand.cs +108 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/MergeRunsCommand.cs +122 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Commands/ValidateCommand.cs +107 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Docx.Core.csproj +15 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/CommentSynchronizer.cs +169 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/ElementOrder.cs +80 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/NamespaceConstants.cs +42 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/RunMerger.cs +81 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/StyleAnalyzer.cs +81 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/TrackChangesHelper.cs +99 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/OpenXml/UnitConverter.cs +23 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/DocumentCreationSamples.cs +1121 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/FieldAndTocSamples.cs +624 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/HeaderFooterSamples.cs +838 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/ImageSamples.cs +917 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/ListAndNumberingSamples.cs +826 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/StyleSystemSamples.cs +1487 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/TableSamples.cs +1163 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Samples/TrackChangesSamples.cs +595 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Typography/CjkHelper.cs +39 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Typography/FontDefaults.cs +24 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Typography/PageSizes.cs +20 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Validation/BusinessRuleValidator.cs +224 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Validation/GateCheckValidator.cs +148 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Validation/ValidationResult.cs +23 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.Core/Validation/XsdValidator.cs +69 -0
- package/bin/skills-assets/docx/scripts/dotnet/Docx.slnx +4 -0
- package/bin/skills-assets/docx/scripts/env_check.sh +196 -0
- package/bin/skills-assets/docx/scripts/setup.ps1 +274 -0
- package/bin/skills-assets/docx/scripts/setup.sh +504 -0
- package/bin/skills-assets/pdf/README.md +222 -0
- package/bin/skills-assets/pdf/SKILL.md +191 -0
- package/bin/skills-assets/pdf/design/design.md +381 -0
- package/bin/skills-assets/pdf/scripts/cover.py +1579 -0
- package/bin/skills-assets/pdf/scripts/fill_inspect.py +200 -0
- package/bin/skills-assets/pdf/scripts/fill_write.py +242 -0
- package/bin/skills-assets/pdf/scripts/make.sh +491 -0
- package/bin/skills-assets/pdf/scripts/merge.py +112 -0
- package/bin/skills-assets/pdf/scripts/palette.py +521 -0
- package/bin/skills-assets/pdf/scripts/reformat_parse.py +374 -0
- package/bin/skills-assets/pdf/scripts/render_body.py +1052 -0
- package/bin/skills-assets/pdf/scripts/render_cover.js +111 -0
- package/bin/skills-assets/pptx-generator/SKILL.md +248 -0
- package/bin/skills-assets/pptx-generator/references/design-system.md +392 -0
- package/bin/skills-assets/pptx-generator/references/editing.md +162 -0
- package/bin/skills-assets/pptx-generator/references/pitfalls.md +112 -0
- package/bin/skills-assets/pptx-generator/references/pptxgenjs.md +420 -0
- package/bin/skills-assets/pptx-generator/references/slide-types.md +413 -0
- package/bin/skills-assets/skill-creator/SKILL.md +368 -0
- package/bin/skills-assets/skill-creator/agents/openai.yaml +5 -0
- package/bin/skills-assets/skill-creator/assets/skill-creator-small.svg +3 -0
- package/bin/skills-assets/skill-creator/assets/skill-creator.png +0 -0
- package/bin/skills-assets/skill-creator/license.txt +202 -0
- package/bin/skills-assets/skill-creator/references/openai_yaml.md +49 -0
- package/bin/skills-assets/skill-creator/scripts/generate_openai_yaml.py +226 -0
- package/bin/skills-assets/skill-creator/scripts/init_skill.py +397 -0
- package/bin/skills-assets/skill-creator/scripts/quick_validate.py +101 -0
- package/bin/skills-assets/skill-installer/LICENSE.txt +202 -0
- package/bin/skills-assets/skill-installer/SKILL.md +58 -0
- package/bin/skills-assets/skill-installer/agents/openai.yaml +5 -0
- package/bin/skills-assets/skill-installer/assets/skill-installer-small.svg +3 -0
- package/bin/skills-assets/skill-installer/assets/skill-installer.png +0 -0
- package/bin/skills-assets/skill-installer/scripts/github_utils.py +21 -0
- package/bin/skills-assets/skill-installer/scripts/install-skill-from-github.py +308 -0
- package/bin/skills-assets/skill-installer/scripts/list-skills.py +107 -0
- package/bin/skills-assets/xlsx/SKILL.md +137 -0
- package/bin/skills-assets/xlsx/references/create.md +691 -0
- package/bin/skills-assets/xlsx/references/edit.md +684 -0
- package/bin/skills-assets/xlsx/references/fix.md +37 -0
- package/bin/skills-assets/xlsx/references/format.md +768 -0
- package/bin/skills-assets/xlsx/references/ooxml-cheatsheet.md +231 -0
- package/bin/skills-assets/xlsx/references/read-analyze.md +97 -0
- package/bin/skills-assets/xlsx/references/validate.md +772 -0
- package/bin/skills-assets/xlsx/scripts/formula_check.py +422 -0
- package/bin/skills-assets/xlsx/scripts/libreoffice_recalc.py +248 -0
- package/bin/skills-assets/xlsx/scripts/shared_strings_builder.py +163 -0
- package/bin/skills-assets/xlsx/scripts/style_audit.py +575 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_add_column.py +395 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_insert_row.py +274 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_pack.py +87 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_reader.py +362 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_shift_rows.py +396 -0
- package/bin/skills-assets/xlsx/scripts/xlsx_unpack.py +130 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
- package/bin/skills-assets/xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Install a skill from a GitHub repo path into $AGENTS_HOME/skills."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
import urllib.error
|
|
14
|
+
import urllib.parse
|
|
15
|
+
import zipfile
|
|
16
|
+
|
|
17
|
+
from github_utils import github_request
|
|
18
|
+
DEFAULT_REF = "main"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Args:
|
|
23
|
+
url: str | None = None
|
|
24
|
+
repo: str | None = None
|
|
25
|
+
path: list[str] | None = None
|
|
26
|
+
ref: str = DEFAULT_REF
|
|
27
|
+
dest: str | None = None
|
|
28
|
+
name: str | None = None
|
|
29
|
+
method: str = "auto"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Source:
|
|
34
|
+
owner: str
|
|
35
|
+
repo: str
|
|
36
|
+
ref: str
|
|
37
|
+
paths: list[str]
|
|
38
|
+
repo_url: str | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class InstallError(Exception):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _agents_home() -> str:
|
|
46
|
+
return os.path.expanduser("~/.agents")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _tmp_root() -> str:
|
|
50
|
+
base = os.path.join(tempfile.gettempdir(), "agents")
|
|
51
|
+
os.makedirs(base, exist_ok=True)
|
|
52
|
+
return base
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _request(url: str) -> bytes:
|
|
56
|
+
return github_request(url, "agents-skill-install")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _parse_github_url(url: str, default_ref: str) -> tuple[str, str, str, str | None]:
|
|
60
|
+
parsed = urllib.parse.urlparse(url)
|
|
61
|
+
if parsed.netloc != "github.com":
|
|
62
|
+
raise InstallError("Only GitHub URLs are supported for download mode.")
|
|
63
|
+
parts = [p for p in parsed.path.split("/") if p]
|
|
64
|
+
if len(parts) < 2:
|
|
65
|
+
raise InstallError("Invalid GitHub URL.")
|
|
66
|
+
owner, repo = parts[0], parts[1]
|
|
67
|
+
ref = default_ref
|
|
68
|
+
subpath = ""
|
|
69
|
+
if len(parts) > 2:
|
|
70
|
+
if parts[2] in ("tree", "blob"):
|
|
71
|
+
if len(parts) < 4:
|
|
72
|
+
raise InstallError("GitHub URL missing ref or path.")
|
|
73
|
+
ref = parts[3]
|
|
74
|
+
subpath = "/".join(parts[4:])
|
|
75
|
+
else:
|
|
76
|
+
subpath = "/".join(parts[2:])
|
|
77
|
+
return owner, repo, ref, subpath or None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _download_repo_zip(owner: str, repo: str, ref: str, dest_dir: str) -> str:
|
|
81
|
+
zip_url = f"https://codeload.github.com/{owner}/{repo}/zip/{ref}"
|
|
82
|
+
zip_path = os.path.join(dest_dir, "repo.zip")
|
|
83
|
+
try:
|
|
84
|
+
payload = _request(zip_url)
|
|
85
|
+
except urllib.error.HTTPError as exc:
|
|
86
|
+
raise InstallError(f"Download failed: HTTP {exc.code}") from exc
|
|
87
|
+
with open(zip_path, "wb") as file_handle:
|
|
88
|
+
file_handle.write(payload)
|
|
89
|
+
with zipfile.ZipFile(zip_path, "r") as zip_file:
|
|
90
|
+
_safe_extract_zip(zip_file, dest_dir)
|
|
91
|
+
top_levels = {name.split("/")[0] for name in zip_file.namelist() if name}
|
|
92
|
+
if not top_levels:
|
|
93
|
+
raise InstallError("Downloaded archive was empty.")
|
|
94
|
+
if len(top_levels) != 1:
|
|
95
|
+
raise InstallError("Unexpected archive layout.")
|
|
96
|
+
return os.path.join(dest_dir, next(iter(top_levels)))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _run_git(args: list[str]) -> None:
|
|
100
|
+
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
101
|
+
if result.returncode != 0:
|
|
102
|
+
raise InstallError(result.stderr.strip() or "Git command failed.")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _safe_extract_zip(zip_file: zipfile.ZipFile, dest_dir: str) -> None:
|
|
106
|
+
dest_root = os.path.realpath(dest_dir)
|
|
107
|
+
for info in zip_file.infolist():
|
|
108
|
+
extracted_path = os.path.realpath(os.path.join(dest_dir, info.filename))
|
|
109
|
+
if extracted_path == dest_root or extracted_path.startswith(dest_root + os.sep):
|
|
110
|
+
continue
|
|
111
|
+
raise InstallError("Archive contains files outside the destination.")
|
|
112
|
+
zip_file.extractall(dest_dir)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _validate_relative_path(path: str) -> None:
|
|
116
|
+
if os.path.isabs(path) or os.path.normpath(path).startswith(".."):
|
|
117
|
+
raise InstallError("Skill path must be a relative path inside the repo.")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _validate_skill_name(name: str) -> None:
|
|
121
|
+
altsep = os.path.altsep
|
|
122
|
+
if not name or os.path.sep in name or (altsep and altsep in name):
|
|
123
|
+
raise InstallError("Skill name must be a single path segment.")
|
|
124
|
+
if name in (".", ".."):
|
|
125
|
+
raise InstallError("Invalid skill name.")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _git_sparse_checkout(repo_url: str, ref: str, paths: list[str], dest_dir: str) -> str:
|
|
129
|
+
repo_dir = os.path.join(dest_dir, "repo")
|
|
130
|
+
clone_cmd = [
|
|
131
|
+
"git",
|
|
132
|
+
"clone",
|
|
133
|
+
"--filter=blob:none",
|
|
134
|
+
"--depth",
|
|
135
|
+
"1",
|
|
136
|
+
"--sparse",
|
|
137
|
+
"--single-branch",
|
|
138
|
+
"--branch",
|
|
139
|
+
ref,
|
|
140
|
+
repo_url,
|
|
141
|
+
repo_dir,
|
|
142
|
+
]
|
|
143
|
+
try:
|
|
144
|
+
_run_git(clone_cmd)
|
|
145
|
+
except InstallError:
|
|
146
|
+
_run_git(
|
|
147
|
+
[
|
|
148
|
+
"git",
|
|
149
|
+
"clone",
|
|
150
|
+
"--filter=blob:none",
|
|
151
|
+
"--depth",
|
|
152
|
+
"1",
|
|
153
|
+
"--sparse",
|
|
154
|
+
"--single-branch",
|
|
155
|
+
repo_url,
|
|
156
|
+
repo_dir,
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
_run_git(["git", "-C", repo_dir, "sparse-checkout", "set", *paths])
|
|
160
|
+
_run_git(["git", "-C", repo_dir, "checkout", ref])
|
|
161
|
+
return repo_dir
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _validate_skill(path: str) -> None:
|
|
165
|
+
if not os.path.isdir(path):
|
|
166
|
+
raise InstallError(f"Skill path not found: {path}")
|
|
167
|
+
skill_md = os.path.join(path, "SKILL.md")
|
|
168
|
+
if not os.path.isfile(skill_md):
|
|
169
|
+
raise InstallError("SKILL.md not found in selected skill directory.")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _copy_skill(src: str, dest_dir: str) -> None:
|
|
173
|
+
os.makedirs(os.path.dirname(dest_dir), exist_ok=True)
|
|
174
|
+
if os.path.exists(dest_dir):
|
|
175
|
+
raise InstallError(f"Destination already exists: {dest_dir}")
|
|
176
|
+
shutil.copytree(src, dest_dir)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _build_repo_url(owner: str, repo: str) -> str:
|
|
180
|
+
return f"https://github.com/{owner}/{repo}.git"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _build_repo_ssh(owner: str, repo: str) -> str:
|
|
184
|
+
return f"git@github.com:{owner}/{repo}.git"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _prepare_repo(source: Source, method: str, tmp_dir: str) -> str:
|
|
188
|
+
if method in ("download", "auto"):
|
|
189
|
+
try:
|
|
190
|
+
return _download_repo_zip(source.owner, source.repo, source.ref, tmp_dir)
|
|
191
|
+
except InstallError as exc:
|
|
192
|
+
if method == "download":
|
|
193
|
+
raise
|
|
194
|
+
err_msg = str(exc)
|
|
195
|
+
if "HTTP 401" in err_msg or "HTTP 403" in err_msg or "HTTP 404" in err_msg:
|
|
196
|
+
pass
|
|
197
|
+
else:
|
|
198
|
+
raise
|
|
199
|
+
if method in ("git", "auto"):
|
|
200
|
+
repo_url = source.repo_url or _build_repo_url(source.owner, source.repo)
|
|
201
|
+
try:
|
|
202
|
+
return _git_sparse_checkout(repo_url, source.ref, source.paths, tmp_dir)
|
|
203
|
+
except InstallError:
|
|
204
|
+
repo_url = _build_repo_ssh(source.owner, source.repo)
|
|
205
|
+
return _git_sparse_checkout(repo_url, source.ref, source.paths, tmp_dir)
|
|
206
|
+
raise InstallError("Unsupported method.")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _resolve_source(args: Args) -> Source:
|
|
210
|
+
if args.url:
|
|
211
|
+
owner, repo, ref, url_path = _parse_github_url(args.url, args.ref)
|
|
212
|
+
if args.path is not None:
|
|
213
|
+
paths = list(args.path)
|
|
214
|
+
elif url_path:
|
|
215
|
+
paths = [url_path]
|
|
216
|
+
else:
|
|
217
|
+
paths = []
|
|
218
|
+
if not paths:
|
|
219
|
+
raise InstallError("Missing --path for GitHub URL.")
|
|
220
|
+
return Source(owner=owner, repo=repo, ref=ref, paths=paths)
|
|
221
|
+
|
|
222
|
+
if not args.repo:
|
|
223
|
+
raise InstallError("Provide --repo or --url.")
|
|
224
|
+
if "://" in args.repo:
|
|
225
|
+
return _resolve_source(
|
|
226
|
+
Args(url=args.repo, repo=None, path=args.path, ref=args.ref)
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
repo_parts = [p for p in args.repo.split("/") if p]
|
|
230
|
+
if len(repo_parts) != 2:
|
|
231
|
+
raise InstallError("--repo must be in owner/repo format.")
|
|
232
|
+
if not args.path:
|
|
233
|
+
raise InstallError("Missing --path for --repo.")
|
|
234
|
+
paths = list(args.path)
|
|
235
|
+
return Source(
|
|
236
|
+
owner=repo_parts[0],
|
|
237
|
+
repo=repo_parts[1],
|
|
238
|
+
ref=args.ref,
|
|
239
|
+
paths=paths,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _default_dest() -> str:
|
|
244
|
+
return os.path.join(_agents_home(), "skills")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _parse_args(argv: list[str]) -> Args:
|
|
248
|
+
parser = argparse.ArgumentParser(description="Install a skill from GitHub.")
|
|
249
|
+
parser.add_argument("--repo", help="owner/repo")
|
|
250
|
+
parser.add_argument("--url", help="https://github.com/owner/repo[/tree/ref/path]")
|
|
251
|
+
parser.add_argument(
|
|
252
|
+
"--path",
|
|
253
|
+
nargs="+",
|
|
254
|
+
help="Path(s) to skill(s) inside repo",
|
|
255
|
+
)
|
|
256
|
+
parser.add_argument("--ref", default=DEFAULT_REF)
|
|
257
|
+
parser.add_argument("--dest", help="Destination skills directory")
|
|
258
|
+
parser.add_argument(
|
|
259
|
+
"--name", help="Destination skill name (defaults to basename of path)"
|
|
260
|
+
)
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
"--method",
|
|
263
|
+
choices=["auto", "download", "git"],
|
|
264
|
+
default="auto",
|
|
265
|
+
)
|
|
266
|
+
return parser.parse_args(argv, namespace=Args())
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main(argv: list[str]) -> int:
|
|
270
|
+
args = _parse_args(argv)
|
|
271
|
+
try:
|
|
272
|
+
source = _resolve_source(args)
|
|
273
|
+
source.ref = source.ref or args.ref
|
|
274
|
+
if not source.paths:
|
|
275
|
+
raise InstallError("No skill paths provided.")
|
|
276
|
+
for path in source.paths:
|
|
277
|
+
_validate_relative_path(path)
|
|
278
|
+
dest_root = args.dest or _default_dest()
|
|
279
|
+
tmp_dir = tempfile.mkdtemp(prefix="skill-install-", dir=_tmp_root())
|
|
280
|
+
try:
|
|
281
|
+
repo_root = _prepare_repo(source, args.method, tmp_dir)
|
|
282
|
+
installed = []
|
|
283
|
+
for path in source.paths:
|
|
284
|
+
skill_name = args.name if len(source.paths) == 1 else None
|
|
285
|
+
skill_name = skill_name or os.path.basename(path.rstrip("/"))
|
|
286
|
+
_validate_skill_name(skill_name)
|
|
287
|
+
if not skill_name:
|
|
288
|
+
raise InstallError("Unable to derive skill name.")
|
|
289
|
+
dest_dir = os.path.join(dest_root, skill_name)
|
|
290
|
+
if os.path.exists(dest_dir):
|
|
291
|
+
raise InstallError(f"Destination already exists: {dest_dir}")
|
|
292
|
+
skill_src = os.path.join(repo_root, path)
|
|
293
|
+
_validate_skill(skill_src)
|
|
294
|
+
_copy_skill(skill_src, dest_dir)
|
|
295
|
+
installed.append((skill_name, dest_dir))
|
|
296
|
+
finally:
|
|
297
|
+
if os.path.isdir(tmp_dir):
|
|
298
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
299
|
+
for skill_name, dest_dir in installed:
|
|
300
|
+
print(f"Installed {skill_name} to {dest_dir}")
|
|
301
|
+
return 0
|
|
302
|
+
except InstallError as exc:
|
|
303
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
304
|
+
return 1
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
if __name__ == "__main__":
|
|
308
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""List skills from a GitHub repo path."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import urllib.error
|
|
11
|
+
|
|
12
|
+
from github_utils import github_api_contents_url, github_request
|
|
13
|
+
|
|
14
|
+
DEFAULT_REPO = "openai/skills"
|
|
15
|
+
DEFAULT_PATH = "skills/.curated"
|
|
16
|
+
DEFAULT_REF = "main"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ListError(Exception):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Args(argparse.Namespace):
|
|
24
|
+
repo: str
|
|
25
|
+
path: str
|
|
26
|
+
ref: str
|
|
27
|
+
format: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _request(url: str) -> bytes:
|
|
31
|
+
return github_request(url, "agents-skill-list")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _agents_home() -> str:
|
|
35
|
+
return os.path.expanduser("~/.agents")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _installed_skills() -> set[str]:
|
|
39
|
+
root = os.path.join(_agents_home(), "skills")
|
|
40
|
+
if not os.path.isdir(root):
|
|
41
|
+
return set()
|
|
42
|
+
entries = set()
|
|
43
|
+
for name in os.listdir(root):
|
|
44
|
+
path = os.path.join(root, name)
|
|
45
|
+
if os.path.isdir(path):
|
|
46
|
+
entries.add(name)
|
|
47
|
+
return entries
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _list_skills(repo: str, path: str, ref: str) -> list[str]:
|
|
51
|
+
api_url = github_api_contents_url(repo, path, ref)
|
|
52
|
+
try:
|
|
53
|
+
payload = _request(api_url)
|
|
54
|
+
except urllib.error.HTTPError as exc:
|
|
55
|
+
if exc.code == 404:
|
|
56
|
+
raise ListError(
|
|
57
|
+
"Skills path not found: "
|
|
58
|
+
f"https://github.com/{repo}/tree/{ref}/{path}"
|
|
59
|
+
) from exc
|
|
60
|
+
raise ListError(f"Failed to fetch skills: HTTP {exc.code}") from exc
|
|
61
|
+
data = json.loads(payload.decode("utf-8"))
|
|
62
|
+
if not isinstance(data, list):
|
|
63
|
+
raise ListError("Unexpected skills listing response.")
|
|
64
|
+
skills = [item["name"] for item in data if item.get("type") == "dir"]
|
|
65
|
+
return sorted(skills)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _parse_args(argv: list[str]) -> Args:
|
|
69
|
+
parser = argparse.ArgumentParser(description="List skills.")
|
|
70
|
+
parser.add_argument("--repo", default=DEFAULT_REPO)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--path",
|
|
73
|
+
default=DEFAULT_PATH,
|
|
74
|
+
help="Repo path to list (default: skills/.curated)",
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument("--ref", default=DEFAULT_REF)
|
|
77
|
+
parser.add_argument(
|
|
78
|
+
"--format",
|
|
79
|
+
choices=["text", "json"],
|
|
80
|
+
default="text",
|
|
81
|
+
help="Output format",
|
|
82
|
+
)
|
|
83
|
+
return parser.parse_args(argv, namespace=Args())
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main(argv: list[str]) -> int:
|
|
87
|
+
args = _parse_args(argv)
|
|
88
|
+
try:
|
|
89
|
+
skills = _list_skills(args.repo, args.path, args.ref)
|
|
90
|
+
installed = _installed_skills()
|
|
91
|
+
if args.format == "json":
|
|
92
|
+
payload = [
|
|
93
|
+
{"name": name, "installed": name in installed} for name in skills
|
|
94
|
+
]
|
|
95
|
+
print(json.dumps(payload))
|
|
96
|
+
else:
|
|
97
|
+
for idx, name in enumerate(skills, start=1):
|
|
98
|
+
suffix = " (already installed)" if name in installed else ""
|
|
99
|
+
print(f"{idx}. {name}{suffix}")
|
|
100
|
+
return 0
|
|
101
|
+
except ListError as exc:
|
|
102
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: xlsx
|
|
3
|
+
description: "Open, create, read, analyze, edit, or validate Excel/spreadsheet files (.xlsx, .xlsm, .csv, .tsv). Use when the user asks to create, build, modify, analyze, read, validate, or format any Excel spreadsheet, financial model, pivot table, or tabular data file. Covers: creating new xlsx from scratch, reading and analyzing existing files, editing existing xlsx with zero format loss, formula recalculation and validation, and applying professional financial formatting standards. Triggers on 'spreadsheet', 'Excel', '.xlsx', '.csv', 'pivot table', 'financial model', 'formula', or any request to produce tabular data in Excel format."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
category: productivity
|
|
7
|
+
sources:
|
|
8
|
+
- ECMA-376 Office Open XML File Formats
|
|
9
|
+
- Microsoft Open XML SDK documentation
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# XLSX Skill
|
|
13
|
+
|
|
14
|
+
Handle the request directly. Do NOT spawn sub-agents. Always write the output file the user requests.
|
|
15
|
+
|
|
16
|
+
## Task Routing
|
|
17
|
+
|
|
18
|
+
| Task | Method | Guide |
|
|
19
|
+
|------|--------|-------|
|
|
20
|
+
| **READ** — analyze existing data | `xlsx_reader.py` + pandas | `references/read-analyze.md` |
|
|
21
|
+
| **CREATE** — new xlsx from scratch | XML template | `references/create.md` + `references/format.md` |
|
|
22
|
+
| **EDIT** — modify existing xlsx | XML unpack→edit→pack | `references/edit.md` (+ `format.md` if styling needed) |
|
|
23
|
+
| **FIX** — repair broken formulas in existing xlsx | XML unpack→fix `<f>` nodes→pack | `references/fix.md` |
|
|
24
|
+
| **VALIDATE** — check formulas | `formula_check.py` | `references/validate.md` |
|
|
25
|
+
|
|
26
|
+
## READ — Analyze data (read `references/read-analyze.md` first)
|
|
27
|
+
|
|
28
|
+
Start with `xlsx_reader.py` for structure discovery, then pandas for custom analysis. Never modify the source file.
|
|
29
|
+
|
|
30
|
+
**Formatting rule**: When the user specifies decimal places (e.g. "2 decimal places"), apply that format to ALL numeric values — use `f'{v:.2f}'` on every number. Never output `12875` when `12875.00` is required.
|
|
31
|
+
|
|
32
|
+
**Aggregation rule**: Always compute sums/means/counts directly from the DataFrame column — e.g. `df['Revenue'].sum()`. Never re-derive column values before aggregation.
|
|
33
|
+
|
|
34
|
+
## CREATE — XML template (read `references/create.md` + `references/format.md`)
|
|
35
|
+
|
|
36
|
+
Copy `templates/minimal_xlsx/` → edit XML directly → pack with `xlsx_pack.py`. Every derived value MUST be an Excel formula (`<f>SUM(B2:B9)</f>`), never a hardcoded number. Apply font colors per `format.md`.
|
|
37
|
+
|
|
38
|
+
## EDIT — XML direct-edit (read `references/edit.md` first)
|
|
39
|
+
|
|
40
|
+
**CRITICAL — EDIT INTEGRITY RULES:**
|
|
41
|
+
1. **NEVER create a new `Workbook()`** for edit tasks. Always load the original file.
|
|
42
|
+
2. The output MUST contain the **same sheets** as the input (same names, same data).
|
|
43
|
+
3. Only modify the specific cells the task asks for — everything else must be untouched.
|
|
44
|
+
4. **After saving output.xlsx, verify it**: open with `xlsx_reader.py` or `pandas` and confirm the original sheet names and a sample of original data are present. If verification fails, you wrote the wrong file — fix it before delivering.
|
|
45
|
+
|
|
46
|
+
Never use openpyxl round-trip on existing files (corrupts VBA, pivots, sparklines). Instead: unpack → use helper scripts → repack.
|
|
47
|
+
|
|
48
|
+
**"Fill cells" / "Add formulas to existing cells" = EDIT task.** If the input file already exists and you are told to fill, update, or add formulas to specific cells, you MUST use the XML edit path. Never create a new `Workbook()`. Example — fill B3 with a cross-sheet SUM formula:
|
|
49
|
+
```bash
|
|
50
|
+
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
|
|
51
|
+
# Find the target sheet's XML via xl/workbook.xml → xl/_rels/workbook.xml.rels
|
|
52
|
+
# Then use the Edit tool to add <f> inside the target <c> element:
|
|
53
|
+
# <c r="B3"><f>SUM('Sales Data'!D2:D13)</f><v></v></c>
|
|
54
|
+
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Add a column** (formulas, numfmt, styles auto-copied from adjacent column):
|
|
58
|
+
```bash
|
|
59
|
+
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
|
|
60
|
+
python3 SKILL_DIR/scripts/xlsx_add_column.py /tmp/xlsx_work/ --col G \
|
|
61
|
+
--sheet "Sheet1" --header "% of Total" \
|
|
62
|
+
--formula '=F{row}/$F$10' --formula-rows 2:9 \
|
|
63
|
+
--total-row 10 --total-formula '=SUM(G2:G9)' --numfmt '0.0%' \
|
|
64
|
+
--border-row 10 --border-style medium
|
|
65
|
+
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
|
|
66
|
+
```
|
|
67
|
+
The `--border-row` flag applies a top border to ALL cells in that row (not just the new column). Use it when the task requires accounting-style borders on total rows.
|
|
68
|
+
|
|
69
|
+
**Insert a row** (shifts existing rows, updates SUM formulas, fixes circular refs):
|
|
70
|
+
```bash
|
|
71
|
+
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
|
|
72
|
+
# IMPORTANT: Find the correct --at row by searching for the label text
|
|
73
|
+
# in the worksheet XML, NOT by using the row number from the prompt.
|
|
74
|
+
# The prompt may say "row 5 (Office Rent)" but Office Rent might actually
|
|
75
|
+
# be at row 4. Always locate the row by its text label first.
|
|
76
|
+
python3 SKILL_DIR/scripts/xlsx_insert_row.py /tmp/xlsx_work/ --at 5 \
|
|
77
|
+
--sheet "Budget FY2025" --text A=Utilities \
|
|
78
|
+
--values B=3000 C=3000 D=3500 E=3500 \
|
|
79
|
+
--formula 'F=SUM(B{row}:E{row})' --copy-style-from 4
|
|
80
|
+
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
|
|
81
|
+
```
|
|
82
|
+
**Row lookup rule**: When the task says "after row N (Label)", always find the row by searching for "Label" in the worksheet XML (`grep -n "Label" /tmp/xlsx_work/xl/worksheets/sheet*.xml` or check sharedStrings.xml). Use the actual row number + 1 for `--at`. Do NOT call `xlsx_shift_rows.py` separately — `xlsx_insert_row.py` calls it internally.
|
|
83
|
+
|
|
84
|
+
**Apply row-wide borders** (e.g. accounting line on a TOTAL row):
|
|
85
|
+
After running helper scripts, apply borders to ALL cells in the target row, not just newly added cells. In `xl/styles.xml`, append a new `<border>` with the desired style, then append a new `<xf>` in `<cellXfs>` that clones each cell's existing `<xf>` but sets the new `borderId`. Apply the new style index to every `<c>` in the row via the `s` attribute:
|
|
86
|
+
```xml
|
|
87
|
+
<!-- In xl/styles.xml, append to <borders>: -->
|
|
88
|
+
<border>
|
|
89
|
+
<left/><right/><top style="medium"/><bottom/><diagonal/>
|
|
90
|
+
</border>
|
|
91
|
+
<!-- Then append to <cellXfs> an xf clone with the new borderId for each existing style -->
|
|
92
|
+
```
|
|
93
|
+
**Key rule**: When a task says "add a border to row N", iterate over ALL cells A through the last column, not just newly added cells.
|
|
94
|
+
|
|
95
|
+
**Manual XML edit** (for anything the helper scripts don't cover):
|
|
96
|
+
```bash
|
|
97
|
+
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
|
|
98
|
+
# ... edit XML with the Edit tool ...
|
|
99
|
+
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## FIX — Repair broken formulas (read `references/fix.md` first)
|
|
103
|
+
|
|
104
|
+
This is an EDIT task. Unpack → fix broken `<f>` nodes → pack. Preserve all original sheets and data.
|
|
105
|
+
|
|
106
|
+
## VALIDATE — Check formulas (read `references/validate.md` first)
|
|
107
|
+
|
|
108
|
+
Run `formula_check.py` for static validation. Use `libreoffice_recalc.py` for dynamic recalculation when available.
|
|
109
|
+
|
|
110
|
+
## Financial Color Standard
|
|
111
|
+
|
|
112
|
+
| Cell Role | Font Color | Hex Code |
|
|
113
|
+
|-----------|-----------|----------|
|
|
114
|
+
| Hard-coded input / assumption | Blue | `0000FF` |
|
|
115
|
+
| Formula / computed result | Black | `000000` |
|
|
116
|
+
| Cross-sheet reference formula | Green | `00B050` |
|
|
117
|
+
|
|
118
|
+
## Key Rules
|
|
119
|
+
|
|
120
|
+
1. **Formula-First**: Every calculated cell MUST use an Excel formula, not a hardcoded number
|
|
121
|
+
2. **CREATE → XML template**: Copy minimal template, edit XML directly, pack with `xlsx_pack.py`
|
|
122
|
+
3. **EDIT → XML**: Never openpyxl round-trip. Use unpack/edit/pack scripts
|
|
123
|
+
4. **Always produce the output file** — this is the #1 priority
|
|
124
|
+
5. **Validate before delivery**: `formula_check.py` exit code 0 = safe
|
|
125
|
+
|
|
126
|
+
## Utility Scripts
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
python3 SKILL_DIR/scripts/xlsx_reader.py input.xlsx # structure discovery
|
|
130
|
+
python3 SKILL_DIR/scripts/formula_check.py file.xlsx --json # formula validation
|
|
131
|
+
python3 SKILL_DIR/scripts/formula_check.py file.xlsx --report # standardized report
|
|
132
|
+
python3 SKILL_DIR/scripts/xlsx_unpack.py in.xlsx /tmp/work/ # unpack for XML editing
|
|
133
|
+
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/work/ out.xlsx # repack after editing
|
|
134
|
+
python3 SKILL_DIR/scripts/xlsx_shift_rows.py /tmp/work/ insert 5 1 # shift rows for insertion
|
|
135
|
+
python3 SKILL_DIR/scripts/xlsx_add_column.py /tmp/work/ --col G ... # add column with formulas
|
|
136
|
+
python3 SKILL_DIR/scripts/xlsx_insert_row.py /tmp/work/ --at 6 ... # insert row with data
|
|
137
|
+
```
|