guild-cli 0.1.0__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.
guild/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """guild — an agent and CLI that manages skills for the AgentCulture mesh."""
2
+
3
+ from importlib.metadata import PackageNotFoundError
4
+ from importlib.metadata import version as _v
5
+
6
+ try:
7
+ __version__ = _v("guild-cli")
8
+ except PackageNotFoundError:
9
+ __version__ = "0.0.0+local"
10
+
11
+ __all__ = ["__version__"]
guild/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Allow running guild as ``python -m guild``."""
2
+
3
+ import sys
4
+
5
+ from guild.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())
guild/cli/__init__.py ADDED
@@ -0,0 +1,86 @@
1
+ """Unified CLI entry point for guild.
2
+
3
+ Every handler raises :class:`guild.cli._errors.GuildError` on failure;
4
+ ``main()`` catches it via :func:`_dispatch` and routes through
5
+ :mod:`guild.cli._output`. Argparse errors route through
6
+ ``_GuildArgumentParser`` so they share the same structured output.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import sys
13
+
14
+ from guild import __version__
15
+ from guild.cli._errors import EXIT_USER_ERROR, GuildError
16
+ from guild.cli._output import emit_error
17
+
18
+
19
+ class _GuildArgumentParser(argparse.ArgumentParser):
20
+ """ArgumentParser that routes errors through :func:`emit_error`."""
21
+
22
+ def error(self, message: str) -> None: # type: ignore[override]
23
+ err = GuildError(
24
+ code=EXIT_USER_ERROR,
25
+ message=message,
26
+ remediation=f"run '{self.prog} --help' to see valid arguments",
27
+ )
28
+ emit_error(err)
29
+ raise SystemExit(err.code)
30
+
31
+
32
+ def _build_parser() -> argparse.ArgumentParser:
33
+ # Deferred import to avoid coupling the parser module to the command modules
34
+ # at import time (matches the afi-cli pattern; cheap insurance).
35
+ from guild.cli._commands import explain as _explain_cmd
36
+ from guild.cli._commands import learn as _learn_cmd
37
+ from guild.cli._commands import whoami as _whoami_cmd
38
+
39
+ parser = _GuildArgumentParser(
40
+ prog="guild",
41
+ description="guild — an agent and CLI that manages skills for the AgentCulture mesh",
42
+ )
43
+ parser.add_argument(
44
+ "--version",
45
+ action="version",
46
+ version=f"%(prog)s {__version__}",
47
+ )
48
+ sub = parser.add_subparsers(dest="command", parser_class=_GuildArgumentParser)
49
+
50
+ _whoami_cmd.register(sub)
51
+ _learn_cmd.register(sub)
52
+ _explain_cmd.register(sub)
53
+
54
+ return parser
55
+
56
+
57
+ def _dispatch(args: argparse.Namespace) -> int:
58
+ try:
59
+ rc = args.func(args)
60
+ except GuildError as err:
61
+ emit_error(err)
62
+ return err.code
63
+ except Exception as err: # noqa: BLE001 - last-resort: wrap so no traceback leaks
64
+ wrapped = GuildError(
65
+ code=EXIT_USER_ERROR,
66
+ message=f"unexpected: {err.__class__.__name__}: {err}",
67
+ remediation="file a bug at https://github.com/agentculture/guildmaster/issues",
68
+ )
69
+ emit_error(wrapped)
70
+ return wrapped.code
71
+ return rc if rc is not None else 0
72
+
73
+
74
+ def main(argv: list[str] | None = None) -> int:
75
+ parser = _build_parser()
76
+ args = parser.parse_args(argv)
77
+
78
+ if args.command is None:
79
+ parser.print_help()
80
+ return 0
81
+
82
+ return _dispatch(args)
83
+
84
+
85
+ if __name__ == "__main__":
86
+ sys.exit(main())
@@ -0,0 +1,22 @@
1
+ """guild subcommands.
2
+
3
+ ``VERBS`` is the canonical one-line summary of each agent-affordance verb,
4
+ shared by ``learn`` (which lists them) and ``explain`` (which can detail one).
5
+ Keep it in sync with the registered subcommands.
6
+ """
7
+
8
+ VERBS: dict[str, str] = {
9
+ "whoami": (
10
+ "Report this agent's identity — suffix, backend, and version. "
11
+ "The smallest offline auth/identity probe."
12
+ ),
13
+ "learn": (
14
+ "Onboard here: list the CLI verbs and vendored skills, and point "
15
+ "at the runtime prompt (CLAUDE.md)."
16
+ ),
17
+ "explain": (
18
+ "Explain one topic in depth — a vendored skill (prints its SKILL.md) " "or a CLI verb."
19
+ ),
20
+ }
21
+
22
+ __all__ = ["VERBS"]
@@ -0,0 +1,85 @@
1
+ """``guild explain <topic>`` — explain one skill or verb in depth.
2
+
3
+ The narrow agent-affordance verb (``learn`` is the broad survey). If the topic
4
+ names a vendored skill, its full ``SKILL.md`` is printed; if it names a CLI
5
+ verb, the verb's one-line summary is printed; otherwise the command fails with
6
+ the list of valid topics. Offline and deterministic.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+
14
+ from guild.cli._commands import VERBS
15
+ from guild.cli._errors import EXIT_USER_ERROR, GuildError
16
+ from guild.cli._output import emit_result
17
+ from guild.cli._repo import iter_skills, repo_root
18
+
19
+
20
+ def register(sub: argparse._SubParsersAction) -> None:
21
+ parser = sub.add_parser(
22
+ "explain",
23
+ help="Explain one topic in depth — a vendored skill or a CLI verb.",
24
+ description=(
25
+ "Print the full SKILL.md for a vendored skill, or the summary for "
26
+ "a CLI verb. Run `guild learn` to see the available topics."
27
+ ),
28
+ )
29
+ parser.add_argument(
30
+ "topic",
31
+ help="A vendored skill name (under .claude/skills/) or a CLI verb.",
32
+ )
33
+ parser.add_argument(
34
+ "--json",
35
+ action="store_true",
36
+ help="Wrap the explanation in a JSON object on stdout.",
37
+ )
38
+ parser.set_defaults(func=_handle)
39
+
40
+
41
+ def _handle(args: argparse.Namespace) -> int:
42
+ topic = args.topic
43
+ root = repo_root()
44
+
45
+ # Index skills by both frontmatter name and directory name so `explain`
46
+ # works even if a skill's frontmatter name drifts from its directory.
47
+ skills = {}
48
+ for sk in iter_skills(root):
49
+ skills.setdefault(sk.name, sk)
50
+ skills.setdefault(sk.path.name, sk)
51
+
52
+ if topic in skills:
53
+ sk = skills[topic]
54
+ content = sk.skill_md.read_text(encoding="utf-8")
55
+ if args.json:
56
+ emit_result(
57
+ json.dumps(
58
+ {
59
+ "kind": "skill",
60
+ "name": sk.name,
61
+ "path": str(sk.skill_md),
62
+ "content": content,
63
+ },
64
+ indent=2,
65
+ )
66
+ )
67
+ else:
68
+ emit_result(content)
69
+ return 0
70
+
71
+ if topic in VERBS:
72
+ if args.json:
73
+ emit_result(
74
+ json.dumps({"kind": "verb", "name": topic, "summary": VERBS[topic]}, indent=2)
75
+ )
76
+ else:
77
+ emit_result(f"guild {topic} — {VERBS[topic]}")
78
+ return 0
79
+
80
+ valid = sorted(set(VERBS) | set(skills))
81
+ raise GuildError(
82
+ code=EXIT_USER_ERROR,
83
+ message=f"unknown topic '{topic}'",
84
+ remediation="valid topics: " + ", ".join(valid),
85
+ )
@@ -0,0 +1,75 @@
1
+ """``guild learn`` — onboard an agent (or human) to this repo.
2
+
3
+ The broad agent-affordance verb: with no argument it surveys what you can do
4
+ here — the CLI verbs and the vendored skills — and points at the runtime
5
+ prompt. For depth on a single skill or verb, use ``guild explain <topic>``.
6
+ Offline and deterministic: everything is read from the filesystem.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+
14
+ from guild.cli._commands import VERBS
15
+ from guild.cli._output import emit_result
16
+ from guild.cli._repo import iter_skills, repo_root
17
+
18
+ _DESC_TRUNCATE = 100
19
+
20
+
21
+ def register(sub: argparse._SubParsersAction) -> None:
22
+ parser = sub.add_parser(
23
+ "learn",
24
+ help="Survey this repo: CLI verbs + vendored skills + the runtime prompt.",
25
+ description=(
26
+ "List the CLI verbs and the skills vendored under .claude/skills/, "
27
+ "and point at the runtime prompt (CLAUDE.md). The onboarding "
28
+ "affordance — run `guild explain <topic>` for depth."
29
+ ),
30
+ )
31
+ parser.add_argument(
32
+ "--json",
33
+ action="store_true",
34
+ help="Emit the inventory as a JSON object to stdout instead of prose.",
35
+ )
36
+ parser.set_defaults(func=_handle)
37
+
38
+
39
+ def _truncate(text: str) -> str:
40
+ if len(text) <= _DESC_TRUNCATE:
41
+ return text
42
+ return text[: _DESC_TRUNCATE - 1].rstrip() + "…"
43
+
44
+
45
+ def _handle(args: argparse.Namespace) -> int:
46
+ root = repo_root()
47
+ skills = iter_skills(root)
48
+ has_prompt = (root / "CLAUDE.md").is_file()
49
+
50
+ if args.json:
51
+ payload = {
52
+ "version_prompt": "CLAUDE.md" if has_prompt else None,
53
+ "verbs": [{"name": name, "summary": summary} for name, summary in VERBS.items()],
54
+ "skills": [{"name": sk.name, "description": sk.description} for sk in skills],
55
+ }
56
+ emit_result(json.dumps(payload, indent=2))
57
+ return 0
58
+
59
+ lines = ["guild — what you can do here", "", "Verbs:"]
60
+ for name, summary in VERBS.items():
61
+ lines.append(f" guild {name} — {_truncate(summary)}")
62
+ lines.append("")
63
+ if skills:
64
+ lines.append(f"Skills ({len(skills)}):")
65
+ for sk in skills:
66
+ desc = _truncate(sk.description) if sk.description else "(no description)"
67
+ lines.append(f" {sk.name} — {desc}")
68
+ else:
69
+ lines.append("Skills: (none vendored under .claude/skills/)")
70
+ lines.append("")
71
+ if has_prompt:
72
+ lines.append("Project shape & conventions: see CLAUDE.md")
73
+ lines.append("Run `guild explain <topic>` for a skill's SKILL.md or a verb's detail.")
74
+ emit_result("\n".join(lines))
75
+ return 0
@@ -0,0 +1,57 @@
1
+ """``guild whoami`` — report this agent's identity.
2
+
3
+ The smallest offline probe: it reads ``culture.yaml`` from the enclosing repo
4
+ and reports the declared agent suffix(es) + backend alongside the installed
5
+ ``guild`` version. No network, no shelling out — so it is safe to call in a
6
+ loop and deterministic under test.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+
14
+ from guild import __version__
15
+ from guild.cli._output import emit_result
16
+ from guild.cli._repo import declared_agents, repo_root
17
+
18
+
19
+ def register(sub: argparse._SubParsersAction) -> None:
20
+ parser = sub.add_parser(
21
+ "whoami",
22
+ help="Report this agent's identity (suffix, backend, version).",
23
+ description=(
24
+ "Read the enclosing repo's culture.yaml and report the declared "
25
+ "agent(s) plus the installed guild version. Offline and "
26
+ "side-effect-free — the smallest identity probe."
27
+ ),
28
+ )
29
+ parser.add_argument(
30
+ "--json",
31
+ action="store_true",
32
+ help="Emit identity as a JSON object to stdout instead of human-readable lines.",
33
+ )
34
+ parser.set_defaults(func=_handle)
35
+
36
+
37
+ def _handle(args: argparse.Namespace) -> int:
38
+ root = repo_root()
39
+ agents = declared_agents(root)
40
+
41
+ if args.json:
42
+ payload = {
43
+ "version": __version__,
44
+ "repo_root": str(root),
45
+ "agents": [{"suffix": a.get("suffix"), "backend": a.get("backend")} for a in agents],
46
+ }
47
+ emit_result(json.dumps(payload, indent=2))
48
+ return 0
49
+
50
+ lines = [f"guild {__version__}", f"repo: {root}"]
51
+ if agents:
52
+ for a in agents:
53
+ lines.append(f"agent: {a.get('suffix', '?')} (backend: {a.get('backend', '?')})")
54
+ else:
55
+ lines.append("agent: (none declared — no culture.yaml)")
56
+ emit_result("\n".join(lines))
57
+ return 0
guild/cli/_errors.py ADDED
@@ -0,0 +1,37 @@
1
+ """GuildError and exit-code policy.
2
+
3
+ Every failure inside guild raises :class:`GuildError`. The top-level
4
+ ``main()`` catches it, formats via :mod:`guild.cli._output`, and exits with
5
+ :attr:`GuildError.code`. This guarantees:
6
+
7
+ * no Python traceback leaks to stderr;
8
+ * every error has a structured shape ``{code, message, remediation}``;
9
+ * the exit-code policy is centralised in one place.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+
16
+ EXIT_SUCCESS = 0
17
+ EXIT_USER_ERROR = 1
18
+ EXIT_ENV_ERROR = 2
19
+
20
+
21
+ @dataclass
22
+ class GuildError(Exception):
23
+ """Structured error raised within guild; carries a remediation hint."""
24
+
25
+ code: int
26
+ message: str
27
+ remediation: str = ""
28
+
29
+ def __post_init__(self) -> None:
30
+ super().__init__(self.message)
31
+
32
+ def to_dict(self) -> dict[str, object]:
33
+ return {
34
+ "code": self.code,
35
+ "message": self.message,
36
+ "remediation": self.remediation,
37
+ }
guild/cli/_output.py ADDED
@@ -0,0 +1,41 @@
1
+ """stdout / stderr helpers with a strict split.
2
+
3
+ Rule: results go to stdout, diagnostics and errors go to stderr. Agents
4
+ parsing guild output can rely on this invariant.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ from typing import Any, TextIO
11
+
12
+ from guild.cli._errors import GuildError
13
+
14
+
15
+ def emit_result(data: Any, *, stream: TextIO | None = None) -> None:
16
+ """Write a command result to stdout (default)."""
17
+ s = stream if stream is not None else sys.stdout
18
+ text = data if isinstance(data, str) else str(data)
19
+ s.write(text)
20
+ if not text.endswith("\n"):
21
+ s.write("\n")
22
+
23
+
24
+ def emit_error(err: GuildError, *, stream: TextIO | None = None) -> None:
25
+ """Write a :class:`GuildError` to stderr.
26
+
27
+ Renders as one or two lines::
28
+
29
+ error: <message>
30
+ hint: <remediation>
31
+ """
32
+ s = stream if stream is not None else sys.stderr
33
+ s.write(f"error: {err.message}\n")
34
+ if err.remediation:
35
+ s.write(f"hint: {err.remediation}\n")
36
+
37
+
38
+ def emit_diagnostic(message: str, *, stream: TextIO | None = None) -> None:
39
+ """Write a human diagnostic to stderr."""
40
+ s = stream if stream is not None else sys.stderr
41
+ s.write(message if message.endswith("\n") else message + "\n")
guild/cli/_repo.py ADDED
@@ -0,0 +1,116 @@
1
+ """Filesystem helpers shared by guild's read-only commands.
2
+
3
+ These resolve the enclosing repo, its ``culture.yaml`` agent declaration, and
4
+ the vendored skills under ``.claude/skills/``. Everything here is pure and
5
+ offline — no network, no shelling out — so the agent-affordance verbs
6
+ (``whoami`` / ``learn`` / ``explain``) stay deterministic and testable.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ import yaml
16
+
17
+
18
+ def find_git_root(start: Path | None = None) -> Path | None:
19
+ """Return the nearest enclosing directory containing ``.git`` (or None).
20
+
21
+ Walks up from ``start`` (default: cwd) but never past the filesystem root.
22
+ Bounding lookups to the git checkout keeps a command from picking up an
23
+ ancestor directory outside the user's current repo.
24
+ """
25
+ base = (start or Path.cwd()).resolve()
26
+ for directory in (base, *base.parents):
27
+ if (directory / ".git").exists():
28
+ return directory
29
+ return None
30
+
31
+
32
+ def repo_root(start: Path | None = None) -> Path:
33
+ """Best-effort repo root: the git root if there is one, else cwd."""
34
+ base = (start or Path.cwd()).resolve()
35
+ return find_git_root(base) or base
36
+
37
+
38
+ def load_culture_yaml(root: Path) -> dict[str, Any] | None:
39
+ """Parse ``<root>/culture.yaml`` into a dict, or None if absent/empty.
40
+
41
+ Raises nothing on malformed YAML beyond what ``yaml.safe_load`` raises;
42
+ callers that want graceful degradation should guard the call.
43
+ """
44
+ path = root / "culture.yaml"
45
+ if not path.is_file():
46
+ return None
47
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
48
+ return data if isinstance(data, dict) else None
49
+
50
+
51
+ def declared_agents(root: Path) -> list[dict[str, Any]]:
52
+ """Return the ``agents:`` list from ``culture.yaml`` (empty if none)."""
53
+ data = load_culture_yaml(root)
54
+ if not data:
55
+ return []
56
+ agents = data.get("agents")
57
+ if not isinstance(agents, list):
58
+ return []
59
+ return [a for a in agents if isinstance(a, dict)]
60
+
61
+
62
+ @dataclass
63
+ class Skill:
64
+ """A vendored skill: its directory name plus SKILL.md frontmatter."""
65
+
66
+ name: str
67
+ path: Path
68
+ description: str
69
+
70
+ @property
71
+ def skill_md(self) -> Path:
72
+ return self.path / "SKILL.md"
73
+
74
+
75
+ def _parse_frontmatter(text: str) -> dict[str, Any]:
76
+ """Return the YAML frontmatter block of a markdown file as a dict.
77
+
78
+ Frontmatter is the content between a leading ``---`` line and the next
79
+ ``---`` line. Returns ``{}`` when there is no frontmatter or it doesn't
80
+ parse to a mapping.
81
+ """
82
+ lines = text.splitlines()
83
+ if not lines or lines[0].strip() != "---":
84
+ return {}
85
+ block: list[str] = []
86
+ for line in lines[1:]:
87
+ if line.strip() == "---":
88
+ break
89
+ block.append(line)
90
+ try:
91
+ data = yaml.safe_load("\n".join(block))
92
+ except yaml.YAMLError:
93
+ return {}
94
+ return data if isinstance(data, dict) else {}
95
+
96
+
97
+ def iter_skills(root: Path) -> list[Skill]:
98
+ """Discover skills under ``<root>/.claude/skills/*/SKILL.md``.
99
+
100
+ Sorted by directory name. A skill directory without a ``SKILL.md`` is
101
+ skipped (it isn't a usable skill); frontmatter ``name``/``description``
102
+ fall back to the directory name / empty string when absent.
103
+ """
104
+ skills_dir = root / ".claude" / "skills"
105
+ if not skills_dir.is_dir():
106
+ return []
107
+ found: list[Skill] = []
108
+ for child in sorted(skills_dir.iterdir()):
109
+ skill_md = child / "SKILL.md"
110
+ if not (child.is_dir() and skill_md.is_file()):
111
+ continue
112
+ fm = _parse_frontmatter(skill_md.read_text(encoding="utf-8"))
113
+ name = str(fm.get("name") or child.name)
114
+ description = " ".join(str(fm.get("description", "")).split())
115
+ found.append(Skill(name=name, path=child, description=description))
116
+ return found
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: guild-cli
3
+ Version: 0.1.0
4
+ Summary: guildmaster — an agent and CLI that manages skills for the AgentCulture mesh.
5
+ Project-URL: Homepage, https://github.com/agentculture/guildmaster
6
+ Project-URL: Issues, https://github.com/agentculture/guildmaster/issues
7
+ Author: AgentCulture
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: pyyaml>=6.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # guildmaster
20
+
21
+ **An agent and CLI that manages skills** for the
22
+ [AgentCulture](https://github.com/agentculture) mesh.
23
+
24
+ guildmaster is a sibling to [`steward`](https://github.com/agentculture/steward)
25
+ (resident-agent alignment), [`culture`](https://github.com/agentculture/culture)
26
+ (the IRC-based agent mesh), and [`daria`](https://github.com/agentculture/daria)
27
+ (the awareness agent) in the Organic Development framework. Its mission is skill
28
+ and skillversion management plus an overview surface for the mesh: per the
29
+ settled division of labor ([issue #1](https://github.com/agentculture/guildmaster/issues/1)),
30
+ guildmaster becomes the skills **supplier/manager** while steward retreats to
31
+ agent-alignment. It onboards as a **consumer** first — vendoring the canonical
32
+ skills like every other sibling — before taking over the upstream ledger,
33
+ broadcast, and version tracking.
34
+
35
+ The repo and the Culture agent are named `guildmaster`; the CLI ships as
36
+ `guild-cli` on PyPI and installs the `guild` binary.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ # From PyPI (Trusted Publishing):
42
+ uv tool install guild-cli
43
+
44
+ # From source (dev):
45
+ uv sync
46
+ uv run guild --version
47
+ ```
48
+
49
+ ## Commands
50
+
51
+ All verbs are read-only, offline, and deterministic — safe to call in agent
52
+ loops. Add `--json` to any of them for structured output.
53
+
54
+ | Verb | What it does |
55
+ |------|--------------|
56
+ | `guild whoami` | Report this agent's identity — suffix + backend (from `culture.yaml`) + version. The smallest identity probe. |
57
+ | `guild learn` | Survey the repo: the CLI verbs, the vendored skills under `.claude/skills/`, and a pointer to `CLAUDE.md`. |
58
+ | `guild explain <topic>` | Explain one topic in depth — print a vendored skill's `SKILL.md`, or a verb's summary. |
59
+
60
+ ```bash
61
+ uv run guild whoami
62
+ uv run guild learn
63
+ uv run guild explain cicd
64
+ ```
65
+
66
+ ## Develop
67
+
68
+ ```bash
69
+ uv sync
70
+ uv run pytest -n auto -v
71
+ uv run black --check guild tests && uv run isort --check-only guild tests
72
+ uv run flake8 guild tests && uv run bandit -c pyproject.toml -r guild
73
+ ```
74
+
75
+ Every PR bumps the version (CI's `version-check` enforces it):
76
+
77
+ ```bash
78
+ python3 .claude/skills/version-bump/scripts/bump.py patch
79
+ ```
80
+
81
+ See [`CLAUDE.md`](CLAUDE.md) for the full project shape, conventions, and the
82
+ build/test/publish toolchain.
@@ -0,0 +1,15 @@
1
+ guild/__init__.py,sha256=ONFAFjYjdPPKthDFPl6tDXPKgImAsuC2dJJYID0GfKo,306
2
+ guild/__main__.py,sha256=sEQZF5lcGvj7AM-8WYses3beS16xjXHU7T8BvxUrevM,139
3
+ guild/cli/__init__.py,sha256=k0VbojF1LMpS5eABioRtFNMgP3JiU9NY2jmHNHPp2Hg,2622
4
+ guild/cli/_errors.py,sha256=Dx1cj8ebXeKqwSWrO0xIhsFWKBHgDEf5Gssc0l8NZkM,957
5
+ guild/cli/_output.py,sha256=fYcfca1XjTi4QshAv9-lCa-KfbfJPYz6uJ87yoxN15k,1226
6
+ guild/cli/_repo.py,sha256=kRpYBW1L6mo_cUHmN2T-yyJk0GsqLW0x25-P1oYnj_8,3893
7
+ guild/cli/_commands/__init__.py,sha256=K7hksxWmOYQJh3jpnfq5nMloxY9QlapD_8xYJom5BQE,693
8
+ guild/cli/_commands/explain.py,sha256=-1JsRX8ZBnSHxg78JGCZLzC86Ou-MYSFg0NiUs4SisI,2684
9
+ guild/cli/_commands/learn.py,sha256=6RwnQLTgpwEa6mAXRKEOiooGbR8sSrq1P4WZmDwldIQ,2639
10
+ guild/cli/_commands/whoami.py,sha256=JxPyvdTA1rqzHoQs6HlHoAnGDHot5btanTfxl0rszXM,1859
11
+ guild_cli-0.1.0.dist-info/METADATA,sha256=svPNEXVLFK2XIqvjFXgMkR4ssEnqgFrYQiBGC5lviTo,2919
12
+ guild_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
13
+ guild_cli-0.1.0.dist-info/entry_points.txt,sha256=YjelaFL3IWmYDp7Xymp0JU1YRHa585RjRZ9gq6fNPd0,41
14
+ guild_cli-0.1.0.dist-info/licenses/LICENSE,sha256=wCcdPywGtFXx1P8N0j0eEDINSWfSjrIsU7ds1YZl-MA,1069
15
+ guild_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ guild = guild.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentCulture
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.