degardis 1.0.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.
- degardis-1.0.0/LICENSE +21 -0
- degardis-1.0.0/MANIFEST.in +4 -0
- degardis-1.0.0/PKG-INFO +134 -0
- degardis-1.0.0/README.md +106 -0
- degardis-1.0.0/degardis/__init__.py +1 -0
- degardis-1.0.0/degardis/__main__.py +4 -0
- degardis-1.0.0/degardis/build.py +90 -0
- degardis-1.0.0/degardis/cli.py +197 -0
- degardis-1.0.0/degardis/icons.py +166 -0
- degardis-1.0.0/degardis/markdown.py +223 -0
- degardis-1.0.0/degardis/model.py +198 -0
- degardis-1.0.0/degardis/output.py +129 -0
- degardis-1.0.0/degardis/package.py +197 -0
- degardis-1.0.0/degardis/registry.py +263 -0
- degardis-1.0.0/degardis/resolver.py +374 -0
- degardis-1.0.0/degardis/validate.py +244 -0
- degardis-1.0.0/degardis.egg-info/PKG-INFO +134 -0
- degardis-1.0.0/degardis.egg-info/SOURCES.txt +53 -0
- degardis-1.0.0/degardis.egg-info/dependency_links.txt +1 -0
- degardis-1.0.0/degardis.egg-info/entry_points.txt +2 -0
- degardis-1.0.0/degardis.egg-info/requires.txt +7 -0
- degardis-1.0.0/degardis.egg-info/top_level.txt +1 -0
- degardis-1.0.0/docs/artifact-format.md +123 -0
- degardis-1.0.0/docs/authoring-guide.md +333 -0
- degardis-1.0.0/docs/concepts.md +99 -0
- degardis-1.0.0/docs/getting-started.md +165 -0
- degardis-1.0.0/docs/reference.md +297 -0
- degardis-1.0.0/examples/structured-summary/assets/icon.svg +4 -0
- degardis-1.0.0/examples/structured-summary/assets/template.md +10 -0
- degardis-1.0.0/examples/structured-summary/entries/audience.yaml +10 -0
- degardis-1.0.0/examples/structured-summary/entries/fidelity.yaml +10 -0
- degardis-1.0.0/examples/structured-summary/profiles/detailed.yaml +8 -0
- degardis-1.0.0/examples/structured-summary/profiles/details/detailed.md +4 -0
- degardis-1.0.0/examples/structured-summary/scripts/list_headings.py +26 -0
- degardis-1.0.0/examples/structured-summary/skill.yaml +29 -0
- degardis-1.0.0/examples/structured-summary/workflows/compose.yaml +15 -0
- degardis-1.0.0/examples/structured-summary/workflows/inspect.yaml +10 -0
- degardis-1.0.0/pyproject.toml +55 -0
- degardis-1.0.0/setup.cfg +4 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/assets/template.md +3 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/entries/rule-one.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/profiles/alpha-only.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/profiles/shared.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/scripts/greet.py +4 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/skill.yaml +19 -0
- degardis-1.0.0/tests/fixtures/skills/demo/alpha/workflows/run.yaml +6 -0
- degardis-1.0.0/tests/fixtures/skills/demo/beta/entries/rule-two.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/beta/profiles/beta-only.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/beta/profiles/shared.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/beta/skill.yaml +19 -0
- degardis-1.0.0/tests/fixtures/skills/demo/beta/workflows/run.yaml +6 -0
- degardis-1.0.0/tests/fixtures/skills/demo/gamma/entries/rule-three.yaml +5 -0
- degardis-1.0.0/tests/fixtures/skills/demo/gamma/skill.yaml +19 -0
- degardis-1.0.0/tests/fixtures/skills/demo/gamma/workflows/run.yaml +6 -0
- degardis-1.0.0/tests/test_compiler.py +1508 -0
degardis-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kambiz Khojasteh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
degardis-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: degardis
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Compile validated Agent Skill sources into portable, installable bundles.
|
|
5
|
+
Author: Kambiz Khojasteh
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/Khojasteh/degardis/tree/main/docs
|
|
8
|
+
Project-URL: Issues, https://github.com/Khojasteh/degardis/issues
|
|
9
|
+
Project-URL: Repository, https://github.com/Khojasteh/degardis
|
|
10
|
+
Keywords: agent-skills,ai-agents,cli,compiler
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: Pillow<13,>=10
|
|
22
|
+
Requires-Dist: PyYAML>=6.0
|
|
23
|
+
Requires-Dist: resvg_py<1.0,>=0.3
|
|
24
|
+
Provides-Extra: release
|
|
25
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
26
|
+
Requires-Dist: twine>=6; extra == "release"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# Degardis
|
|
30
|
+
|
|
31
|
+
Degardis is a command-line compiler for authors of portable Agent Skills. It
|
|
32
|
+
turns structured YAML sources into self-contained bundles for Claude, Codex,
|
|
33
|
+
Copilot, Cursor, Roo, and ChatGPT. Before writing an artifact, it validates the
|
|
34
|
+
source, selected profiles, generated paths, and generated links.
|
|
35
|
+
|
|
36
|
+
Authoring stays separate from generated output: edit the source files, validate
|
|
37
|
+
them, and rebuild either an installable folder or a distributable ZIP.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
Degardis requires Python 3.10 or later. Install it from PyPI:
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
python -m pip install degardis
|
|
45
|
+
degardis --version
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For an isolated command-line installation, use `pipx install degardis`.
|
|
49
|
+
|
|
50
|
+
To work on Degardis from this repository instead:
|
|
51
|
+
|
|
52
|
+
```console
|
|
53
|
+
python -m pip install -e .
|
|
54
|
+
degardis validate examples/structured-summary
|
|
55
|
+
degardis list examples/structured-summary
|
|
56
|
+
degardis build examples/structured-summary --output .artifacts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The successful build ends with `Summary: 1 skill built as folder.` and reports
|
|
60
|
+
the artifact path. The generated folder is ready to inspect or install:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
.artifacts/structured-summary/
|
|
64
|
+
SKILL.md
|
|
65
|
+
agents/openai.yaml
|
|
66
|
+
references/
|
|
67
|
+
scripts/
|
|
68
|
+
assets/
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Degardis writes only beneath the requested output root. Rebuilding a skill
|
|
72
|
+
replaces that skill's existing folder and ZIP there, while preserving unrelated
|
|
73
|
+
entries. See [Artifact format](https://github.com/Khojasteh/degardis/blob/main/docs/artifact-format.md#replace-an-artifact)
|
|
74
|
+
before building directly into an agent's skill directory.
|
|
75
|
+
|
|
76
|
+
Add the example's optional profile or produce a ZIP:
|
|
77
|
+
|
|
78
|
+
```console
|
|
79
|
+
degardis build examples/structured-summary --profile detailed --output .artifacts
|
|
80
|
+
degardis build examples/structured-summary --zip --output .artifacts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`examples/structured-summary` is the canonical public example used throughout
|
|
84
|
+
the guides and CLI help. It summarizes supplied material from any subject;
|
|
85
|
+
synthetic skills under `tests/fixtures` are compiler test data.
|
|
86
|
+
|
|
87
|
+
## Companion skills
|
|
88
|
+
|
|
89
|
+
The [Degardis skills catalog](https://github.com/Khojasteh/degardis-skills)
|
|
90
|
+
contains reusable skills authored with Degardis.
|
|
91
|
+
|
|
92
|
+
For the smoothest authoring experience, use
|
|
93
|
+
[Degardis Authoring](https://github.com/Khojasteh/degardis-skills/tree/main/skills/degardis-authoring).
|
|
94
|
+
It guides a skill from initial design through review, validation, packaging,
|
|
95
|
+
and installation while applying Degardis conventions across manifests,
|
|
96
|
+
workflows, entries, profiles, scripts, and assets.
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
```console
|
|
101
|
+
degardis list PATH [PATH ...]
|
|
102
|
+
degardis validate PATH [PATH ...]
|
|
103
|
+
degardis build PATH [PATH ...] --output PATH [--profile [SKILL:]PROFILE] [--zip]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Run `degardis COMMAND --help` for exact options and examples. All commands
|
|
107
|
+
accept individual skill directories, directories containing skills at any
|
|
108
|
+
depth, or a mixture of both. This lets a source tree group skills into
|
|
109
|
+
subdirectories while building all of them with one command.
|
|
110
|
+
|
|
111
|
+
An unqualified profile name applies to every selected skill that defines it.
|
|
112
|
+
`SKILL:PROFILE` selects one owner, and `all` selects all available profiles.
|
|
113
|
+
Supplying explicit selectors replaces manifest defaults for that build.
|
|
114
|
+
|
|
115
|
+
For exact command behavior, profile selectors, source schemas, and the Python
|
|
116
|
+
API, see the [reference](https://github.com/Khojasteh/degardis/blob/main/docs/reference.md).
|
|
117
|
+
|
|
118
|
+
## Documentation
|
|
119
|
+
|
|
120
|
+
| Reader goal | Document |
|
|
121
|
+
| -------------------------------------------- | ------------------------------------------ |
|
|
122
|
+
| Build and install the example | [Getting started](https://github.com/Khojasteh/degardis/blob/main/docs/getting-started.md) |
|
|
123
|
+
| Understand the source and artifact models | [Concepts](https://github.com/Khojasteh/degardis/blob/main/docs/concepts.md) |
|
|
124
|
+
| Create or modify a skill | [Authoring guide](https://github.com/Khojasteh/degardis/blob/main/docs/authoring-guide.md) |
|
|
125
|
+
| Look up commands, schemas, or the Python API | [Reference](https://github.com/Khojasteh/degardis/blob/main/docs/reference.md) |
|
|
126
|
+
| Inspect and install generated output | [Artifact format](https://github.com/Khojasteh/degardis/blob/main/docs/artifact-format.md) |
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```console
|
|
131
|
+
python -m unittest discover -s tests -v
|
|
132
|
+
python -m degardis validate examples/structured-summary
|
|
133
|
+
python -m degardis build examples/structured-summary --profile all --output .artifacts
|
|
134
|
+
```
|
degardis-1.0.0/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Degardis
|
|
2
|
+
|
|
3
|
+
Degardis is a command-line compiler for authors of portable Agent Skills. It
|
|
4
|
+
turns structured YAML sources into self-contained bundles for Claude, Codex,
|
|
5
|
+
Copilot, Cursor, Roo, and ChatGPT. Before writing an artifact, it validates the
|
|
6
|
+
source, selected profiles, generated paths, and generated links.
|
|
7
|
+
|
|
8
|
+
Authoring stays separate from generated output: edit the source files, validate
|
|
9
|
+
them, and rebuild either an installable folder or a distributable ZIP.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
Degardis requires Python 3.10 or later. Install it from PyPI:
|
|
14
|
+
|
|
15
|
+
```console
|
|
16
|
+
python -m pip install degardis
|
|
17
|
+
degardis --version
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For an isolated command-line installation, use `pipx install degardis`.
|
|
21
|
+
|
|
22
|
+
To work on Degardis from this repository instead:
|
|
23
|
+
|
|
24
|
+
```console
|
|
25
|
+
python -m pip install -e .
|
|
26
|
+
degardis validate examples/structured-summary
|
|
27
|
+
degardis list examples/structured-summary
|
|
28
|
+
degardis build examples/structured-summary --output .artifacts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The successful build ends with `Summary: 1 skill built as folder.` and reports
|
|
32
|
+
the artifact path. The generated folder is ready to inspect or install:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
.artifacts/structured-summary/
|
|
36
|
+
SKILL.md
|
|
37
|
+
agents/openai.yaml
|
|
38
|
+
references/
|
|
39
|
+
scripts/
|
|
40
|
+
assets/
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Degardis writes only beneath the requested output root. Rebuilding a skill
|
|
44
|
+
replaces that skill's existing folder and ZIP there, while preserving unrelated
|
|
45
|
+
entries. See [Artifact format](https://github.com/Khojasteh/degardis/blob/main/docs/artifact-format.md#replace-an-artifact)
|
|
46
|
+
before building directly into an agent's skill directory.
|
|
47
|
+
|
|
48
|
+
Add the example's optional profile or produce a ZIP:
|
|
49
|
+
|
|
50
|
+
```console
|
|
51
|
+
degardis build examples/structured-summary --profile detailed --output .artifacts
|
|
52
|
+
degardis build examples/structured-summary --zip --output .artifacts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`examples/structured-summary` is the canonical public example used throughout
|
|
56
|
+
the guides and CLI help. It summarizes supplied material from any subject;
|
|
57
|
+
synthetic skills under `tests/fixtures` are compiler test data.
|
|
58
|
+
|
|
59
|
+
## Companion skills
|
|
60
|
+
|
|
61
|
+
The [Degardis skills catalog](https://github.com/Khojasteh/degardis-skills)
|
|
62
|
+
contains reusable skills authored with Degardis.
|
|
63
|
+
|
|
64
|
+
For the smoothest authoring experience, use
|
|
65
|
+
[Degardis Authoring](https://github.com/Khojasteh/degardis-skills/tree/main/skills/degardis-authoring).
|
|
66
|
+
It guides a skill from initial design through review, validation, packaging,
|
|
67
|
+
and installation while applying Degardis conventions across manifests,
|
|
68
|
+
workflows, entries, profiles, scripts, and assets.
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
degardis list PATH [PATH ...]
|
|
74
|
+
degardis validate PATH [PATH ...]
|
|
75
|
+
degardis build PATH [PATH ...] --output PATH [--profile [SKILL:]PROFILE] [--zip]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Run `degardis COMMAND --help` for exact options and examples. All commands
|
|
79
|
+
accept individual skill directories, directories containing skills at any
|
|
80
|
+
depth, or a mixture of both. This lets a source tree group skills into
|
|
81
|
+
subdirectories while building all of them with one command.
|
|
82
|
+
|
|
83
|
+
An unqualified profile name applies to every selected skill that defines it.
|
|
84
|
+
`SKILL:PROFILE` selects one owner, and `all` selects all available profiles.
|
|
85
|
+
Supplying explicit selectors replaces manifest defaults for that build.
|
|
86
|
+
|
|
87
|
+
For exact command behavior, profile selectors, source schemas, and the Python
|
|
88
|
+
API, see the [reference](https://github.com/Khojasteh/degardis/blob/main/docs/reference.md).
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
| Reader goal | Document |
|
|
93
|
+
| -------------------------------------------- | ------------------------------------------ |
|
|
94
|
+
| Build and install the example | [Getting started](https://github.com/Khojasteh/degardis/blob/main/docs/getting-started.md) |
|
|
95
|
+
| Understand the source and artifact models | [Concepts](https://github.com/Khojasteh/degardis/blob/main/docs/concepts.md) |
|
|
96
|
+
| Create or modify a skill | [Authoring guide](https://github.com/Khojasteh/degardis/blob/main/docs/authoring-guide.md) |
|
|
97
|
+
| Look up commands, schemas, or the Python API | [Reference](https://github.com/Khojasteh/degardis/blob/main/docs/reference.md) |
|
|
98
|
+
| Inspect and install generated output | [Artifact format](https://github.com/Khojasteh/degardis/blob/main/docs/artifact-format.md) |
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```console
|
|
103
|
+
python -m unittest discover -s tests -v
|
|
104
|
+
python -m degardis validate examples/structured-summary
|
|
105
|
+
python -m degardis build examples/structured-summary --profile all --output .artifacts
|
|
106
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from tempfile import TemporaryDirectory
|
|
6
|
+
|
|
7
|
+
from .model import DegardisError, DegardisWarning
|
|
8
|
+
from .package import (
|
|
9
|
+
ArchivePackager,
|
|
10
|
+
ArtifactWriter,
|
|
11
|
+
replace_skill_artifacts,
|
|
12
|
+
)
|
|
13
|
+
from .resolver import SkillResolver
|
|
14
|
+
from .validate import bundle_warnings, validate_skill
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkillCompiler:
|
|
18
|
+
def __init__(self, sources: Path | list[Path]) -> None:
|
|
19
|
+
self.resolver = SkillResolver(sources)
|
|
20
|
+
self.writer = ArtifactWriter()
|
|
21
|
+
self.packager = ArchivePackager()
|
|
22
|
+
|
|
23
|
+
def _check_output_path(self, output: Path) -> None:
|
|
24
|
+
resolved_output = output.resolve()
|
|
25
|
+
for source in self.resolver.skill_paths:
|
|
26
|
+
resolved_source = source.resolve()
|
|
27
|
+
if (
|
|
28
|
+
resolved_output == resolved_source
|
|
29
|
+
or resolved_output in resolved_source.parents
|
|
30
|
+
or resolved_source in resolved_output.parents
|
|
31
|
+
):
|
|
32
|
+
raise DegardisError(
|
|
33
|
+
f"Output directory {resolved_output} must not overlap "
|
|
34
|
+
f"skill source {resolved_source}"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def build(
|
|
38
|
+
self,
|
|
39
|
+
output: Path,
|
|
40
|
+
profiles: list[str] | None = None,
|
|
41
|
+
as_zip: bool = False,
|
|
42
|
+
) -> list[Path]:
|
|
43
|
+
self._check_output_path(output)
|
|
44
|
+
validation_errors = [
|
|
45
|
+
error
|
|
46
|
+
for skill_path in self.resolver.skill_paths
|
|
47
|
+
for error in validate_skill(skill_path)
|
|
48
|
+
]
|
|
49
|
+
if validation_errors:
|
|
50
|
+
raise DegardisError("; ".join(validation_errors))
|
|
51
|
+
bundles = self.resolver.collect(profiles)
|
|
52
|
+
if not bundles:
|
|
53
|
+
raise DegardisError("at least one skill is required")
|
|
54
|
+
for bundle in bundles:
|
|
55
|
+
for message in bundle_warnings(bundle):
|
|
56
|
+
warnings.warn(message, DegardisWarning, stacklevel=2)
|
|
57
|
+
output.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
paths: list[Path] = []
|
|
59
|
+
for bundle in bundles:
|
|
60
|
+
name = bundle.primary.name
|
|
61
|
+
with TemporaryDirectory(
|
|
62
|
+
prefix="degardis-build-",
|
|
63
|
+
) as directory:
|
|
64
|
+
staging_root = Path(directory)
|
|
65
|
+
staged_folder = staging_root / name
|
|
66
|
+
self.writer.write_skill(bundle, staged_folder)
|
|
67
|
+
if as_zip:
|
|
68
|
+
destination = output / f"{name}.zip"
|
|
69
|
+
staged = staging_root / f"{name}.zip"
|
|
70
|
+
self.packager.create(staged_folder, staged)
|
|
71
|
+
else:
|
|
72
|
+
destination = output / name
|
|
73
|
+
staged = staged_folder
|
|
74
|
+
replace_skill_artifacts(
|
|
75
|
+
output,
|
|
76
|
+
name,
|
|
77
|
+
staged,
|
|
78
|
+
destination,
|
|
79
|
+
)
|
|
80
|
+
paths.append(destination)
|
|
81
|
+
return paths
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def build_skills(
|
|
85
|
+
sources: Path | list[Path],
|
|
86
|
+
output_dir: Path,
|
|
87
|
+
profiles: list[str] | None = None,
|
|
88
|
+
as_zip: bool = False,
|
|
89
|
+
) -> list[Path]:
|
|
90
|
+
return SkillCompiler(sources).build(output_dir, profiles, as_zip)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import warnings
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .build import SkillCompiler
|
|
11
|
+
from .model import DegardisError, DegardisWarning
|
|
12
|
+
from .output import (
|
|
13
|
+
write_build_report,
|
|
14
|
+
write_profile_matches,
|
|
15
|
+
write_skill_list,
|
|
16
|
+
write_validation_report,
|
|
17
|
+
)
|
|
18
|
+
from .registry import discover_skill_paths, load_skill_path, load_skill_profiles
|
|
19
|
+
from .resolver import collect_skills, profile_matches
|
|
20
|
+
from .validate import bundle_warnings, validate_skill
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
HELP_FORMATTER = argparse.RawDescriptionHelpFormatter
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _expand_path(value: str) -> Path:
|
|
27
|
+
"""Expand and resolve a CLI path using one uniform policy."""
|
|
28
|
+
return Path(os.path.expandvars(os.path.expanduser(value))).resolve()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _add_skill_paths(command: argparse.ArgumentParser) -> None:
|
|
32
|
+
command.add_argument(
|
|
33
|
+
"paths",
|
|
34
|
+
nargs="+",
|
|
35
|
+
type=_expand_path,
|
|
36
|
+
metavar="PATH",
|
|
37
|
+
help=(
|
|
38
|
+
"skill directory, or a directory recursively containing skill "
|
|
39
|
+
"directories"
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def parser() -> argparse.ArgumentParser:
|
|
45
|
+
command = argparse.ArgumentParser(
|
|
46
|
+
prog="degardis",
|
|
47
|
+
description="Compile self-contained agent skills into installable bundles.",
|
|
48
|
+
epilog="""\
|
|
49
|
+
Run `degardis COMMAND -h` for that command's options and examples.
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
degardis list examples/structured-summary
|
|
53
|
+
degardis validate examples/structured-summary
|
|
54
|
+
degardis build examples/structured-summary --profile detailed --output .artifacts
|
|
55
|
+
""",
|
|
56
|
+
formatter_class=HELP_FORMATTER,
|
|
57
|
+
)
|
|
58
|
+
command.add_argument(
|
|
59
|
+
"-v",
|
|
60
|
+
"--version",
|
|
61
|
+
action="version",
|
|
62
|
+
version=f"%(prog)s {__version__}",
|
|
63
|
+
)
|
|
64
|
+
subcommands = command.add_subparsers(
|
|
65
|
+
dest="command", required=True, title="commands", metavar="COMMAND"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
build = subcommands.add_parser(
|
|
69
|
+
"build",
|
|
70
|
+
help="build one or more self-contained skills",
|
|
71
|
+
description="Build selected skills into installable skill bundles.",
|
|
72
|
+
epilog="""\
|
|
73
|
+
Examples:
|
|
74
|
+
degardis build examples/structured-summary --output .artifacts
|
|
75
|
+
degardis build examples/structured-summary --profile detailed --output .artifacts
|
|
76
|
+
degardis build examples/structured-summary --zip --output dist
|
|
77
|
+
""",
|
|
78
|
+
formatter_class=HELP_FORMATTER,
|
|
79
|
+
)
|
|
80
|
+
_add_skill_paths(build)
|
|
81
|
+
build.add_argument(
|
|
82
|
+
"--profile",
|
|
83
|
+
action="append",
|
|
84
|
+
dest="profiles",
|
|
85
|
+
metavar="[SKILL:]PROFILE",
|
|
86
|
+
help=(
|
|
87
|
+
"profile to include; an unqualified name matches every selected "
|
|
88
|
+
"skill, and all includes every selected skill's profiles"
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
build.add_argument(
|
|
92
|
+
"--output",
|
|
93
|
+
type=_expand_path,
|
|
94
|
+
required=True,
|
|
95
|
+
metavar="PATH",
|
|
96
|
+
help="output directory",
|
|
97
|
+
)
|
|
98
|
+
build.add_argument(
|
|
99
|
+
"--zip",
|
|
100
|
+
action="store_true",
|
|
101
|
+
help="output one zip archive per skill instead of an uncompressed folder",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
validate_command = subcommands.add_parser(
|
|
105
|
+
"validate",
|
|
106
|
+
help="validate one or more skill sources",
|
|
107
|
+
description="Validate explicit skills or recursively discovered skills.",
|
|
108
|
+
epilog="""\
|
|
109
|
+
Examples:
|
|
110
|
+
degardis validate examples/structured-summary
|
|
111
|
+
""",
|
|
112
|
+
formatter_class=HELP_FORMATTER,
|
|
113
|
+
)
|
|
114
|
+
_add_skill_paths(validate_command)
|
|
115
|
+
|
|
116
|
+
list_command = subcommands.add_parser(
|
|
117
|
+
"list",
|
|
118
|
+
help="inspect selected skills and profiles",
|
|
119
|
+
description="Show metadata and profiles for selected skills.",
|
|
120
|
+
epilog="""\
|
|
121
|
+
Examples:
|
|
122
|
+
degardis list examples/structured-summary
|
|
123
|
+
""",
|
|
124
|
+
formatter_class=HELP_FORMATTER,
|
|
125
|
+
)
|
|
126
|
+
_add_skill_paths(list_command)
|
|
127
|
+
return command
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _normalize_help_position(argv: list[str]) -> list[str]:
|
|
131
|
+
"""Let ``-h``/``--help`` work before or after the command name.
|
|
132
|
+
|
|
133
|
+
argparse only recognizes ``-h`` on the parser that reaches it, so
|
|
134
|
+
``degardis -h build`` shows the top-level help instead of the ``build``
|
|
135
|
+
command's help that ``degardis build -h`` shows. Moving a leading
|
|
136
|
+
``-h``/``--help`` after the command name makes both spellings equivalent.
|
|
137
|
+
"""
|
|
138
|
+
if (
|
|
139
|
+
len(argv) >= 2
|
|
140
|
+
and argv[0] in ("-h", "--help")
|
|
141
|
+
and not argv[1].startswith("-")
|
|
142
|
+
):
|
|
143
|
+
return [argv[1], argv[0], *argv[2:]]
|
|
144
|
+
return argv
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _run(argv: list[str] | None = None) -> int:
|
|
148
|
+
tokens = list(sys.argv[1:] if argv is None else argv)
|
|
149
|
+
args = parser().parse_args(_normalize_help_position(tokens))
|
|
150
|
+
skill_paths = discover_skill_paths(args.paths)
|
|
151
|
+
|
|
152
|
+
if args.command == "list":
|
|
153
|
+
skills = [load_skill_path(path) for path in skill_paths]
|
|
154
|
+
details = [(skill, load_skill_profiles(skill)) for skill in skills]
|
|
155
|
+
write_skill_list(sys.stdout, details)
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
if args.command == "validate":
|
|
159
|
+
results = []
|
|
160
|
+
for path in skill_paths:
|
|
161
|
+
skill = load_skill_path(path)
|
|
162
|
+
errors = validate_skill(path)
|
|
163
|
+
skill_warnings = (
|
|
164
|
+
[]
|
|
165
|
+
if errors
|
|
166
|
+
else bundle_warnings(collect_skills([path])[0])
|
|
167
|
+
)
|
|
168
|
+
results.append((skill, errors, skill_warnings))
|
|
169
|
+
write_validation_report(sys.stdout, results)
|
|
170
|
+
return int(any(errors for _, errors, _ in results))
|
|
171
|
+
|
|
172
|
+
compiler = SkillCompiler(skill_paths)
|
|
173
|
+
if args.profiles:
|
|
174
|
+
write_profile_matches(
|
|
175
|
+
sys.stdout,
|
|
176
|
+
profile_matches(skill_paths, args.profiles),
|
|
177
|
+
)
|
|
178
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
179
|
+
warnings.simplefilter("always", DegardisWarning)
|
|
180
|
+
paths = compiler.build(args.output, args.profiles, as_zip=args.zip)
|
|
181
|
+
for warning in caught:
|
|
182
|
+
print(f"[WARNING] {warning.message}", file=sys.stderr)
|
|
183
|
+
skills = [load_skill_path(path) for path in skill_paths]
|
|
184
|
+
write_build_report(sys.stdout, skills, paths, as_zip=args.zip)
|
|
185
|
+
return 0
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def main(argv: list[str] | None = None) -> int:
|
|
189
|
+
try:
|
|
190
|
+
return _run(argv)
|
|
191
|
+
except (DegardisError, OSError) as error:
|
|
192
|
+
print(f"[ERROR] {error}", file=sys.stderr)
|
|
193
|
+
return 1
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == "__main__":
|
|
197
|
+
raise SystemExit(main())
|