logion-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.
- cli/__init__.py +2 -0
- cli/_config.py +51 -0
- cli/_confirm.py +16 -0
- cli/_context.py +17 -0
- cli/_course_bundle.py +46 -0
- cli/_course_capabilities.py +580 -0
- cli/_credentials.py +104 -0
- cli/_errors.py +82 -0
- cli/_first_run.py +90 -0
- cli/_harness/__init__.py +68 -0
- cli/_harness/base.py +106 -0
- cli/_harness/claude_code.py +168 -0
- cli/_harness/codex.py +79 -0
- cli/_harness/custom.py +55 -0
- cli/_harness/hermes.py +93 -0
- cli/_harness/opencode.py +255 -0
- cli/_local_state.py +1053 -0
- cli/_options.py +36 -0
- cli/_output.py +47 -0
- cli/_parser.py +73 -0
- cli/_recall_calibration.py +90 -0
- cli/_recall_ranker.py +74 -0
- cli/_taxonomy.py +120 -0
- cli/_update_policy.py +152 -0
- cli/_utils.py +16 -0
- cli/_version.py +26 -0
- cli/commands/__init__.py +2 -0
- cli/commands/admin.py +535 -0
- cli/commands/bounties.py +490 -0
- cli/commands/course_reviews/__init__.py +6 -0
- cli/commands/course_reviews/_download_handler.py +104 -0
- cli/commands/course_reviews/_render.py +129 -0
- cli/commands/course_reviews/handlers.py +197 -0
- cli/commands/course_reviews/parser.py +93 -0
- cli/commands/courses/__init__.py +6 -0
- cli/commands/courses/_capability_render.py +183 -0
- cli/commands/courses/_cmd_help.py +18 -0
- cli/commands/courses/_purchase.py +76 -0
- cli/commands/courses/_review_helpers.py +93 -0
- cli/commands/courses/_taxonomy_data.py +173 -0
- cli/commands/courses/_upload_bundle_validation.py +28 -0
- cli/commands/courses/_uploads_push.py +243 -0
- cli/commands/courses/capabilities.py +250 -0
- cli/commands/courses/capability_frontmatter.py +150 -0
- cli/commands/courses/handlers.py +50 -0
- cli/commands/courses/mutations.py +217 -0
- cli/commands/courses/parser.py +66 -0
- cli/commands/courses/parser_capabilities.py +95 -0
- cli/commands/courses/parser_sections.py +239 -0
- cli/commands/courses/parser_uploads.py +84 -0
- cli/commands/courses/parser_utils.py +65 -0
- cli/commands/courses/publication.py +60 -0
- cli/commands/courses/report_usage.py +131 -0
- cli/commands/courses/reviews.py +237 -0
- cli/commands/courses/taxonomy_handler.py +61 -0
- cli/commands/courses/taxonomy_suggest.py +197 -0
- cli/commands/courses/uploads.py +142 -0
- cli/commands/courses/versions.py +65 -0
- cli/commands/credits/__init__.py +6 -0
- cli/commands/credits/_helpers.py +153 -0
- cli/commands/credits/handlers.py +218 -0
- cli/commands/credits/parser.py +115 -0
- cli/commands/docs/__init__.py +6 -0
- cli/commands/docs/handlers.py +137 -0
- cli/commands/docs/parser.py +27 -0
- cli/commands/health/__init__.py +6 -0
- cli/commands/health/handlers.py +26 -0
- cli/commands/health/parser.py +20 -0
- cli/commands/identity/__init__.py +6 -0
- cli/commands/identity/_autopost.py +97 -0
- cli/commands/identity/_closing_copy.py +89 -0
- cli/commands/identity/_companion.py +232 -0
- cli/commands/identity/_companion_source.py +135 -0
- cli/commands/identity/_harness_select.py +85 -0
- cli/commands/identity/_onboarding_helpers.py +168 -0
- cli/commands/identity/handlers.py +173 -0
- cli/commands/identity/onboarding.py +246 -0
- cli/commands/identity/parser.py +72 -0
- cli/commands/listings/__init__.py +6 -0
- cli/commands/listings/handlers.py +135 -0
- cli/commands/listings/parser.py +57 -0
- cli/commands/notifications/__init__.py +6 -0
- cli/commands/notifications/handlers.py +120 -0
- cli/commands/notifications/parser.py +49 -0
- cli/commands/payments/__init__.py +6 -0
- cli/commands/payments/_orders_helpers.py +114 -0
- cli/commands/payments/handlers.py +138 -0
- cli/commands/payments/parser.py +97 -0
- cli/commands/recall/__init__.py +7 -0
- cli/commands/recall/handlers.py +87 -0
- cli/commands/recall/parser.py +70 -0
- cli/commands/referrals/__init__.py +6 -0
- cli/commands/referrals/_helpers.py +63 -0
- cli/commands/referrals/handlers.py +100 -0
- cli/commands/referrals/parser.py +65 -0
- cli/commands/reports/__init__.py +6 -0
- cli/commands/reports/handlers.py +57 -0
- cli/commands/reports/parser.py +52 -0
- cli/commands/skills/__init__.py +7 -0
- cli/commands/skills/_agent_symlink.py +161 -0
- cli/commands/skills/_finalize.py +112 -0
- cli/commands/skills/_inspect_handler.py +218 -0
- cli/commands/skills/_install_helpers.py +186 -0
- cli/commands/skills/_query_handlers.py +83 -0
- cli/commands/skills/_search_handler.py +136 -0
- cli/commands/skills/_update_handler.py +110 -0
- cli/commands/skills/_verify_handler.py +109 -0
- cli/commands/skills/handlers.py +202 -0
- cli/commands/skills/parser.py +154 -0
- cli/commands/workspace.py +406 -0
- cli/docs/README.md +5 -0
- cli/docs/__init__.py +1 -0
- cli/docs/bounties-and-referrals.md +18 -0
- cli/docs/concepts.md +47 -0
- cli/docs/creating-courses.md +25 -0
- cli/docs/credits-and-purchases.md +30 -0
- cli/docs/credits-terms.md +23 -0
- cli/docs/getting-started.md +95 -0
- cli/docs/marketplace-loop.md +108 -0
- cli/docs/privacy.md +30 -0
- cli/docs/referral-terms.md +24 -0
- cli/docs/reviews.md +47 -0
- cli/docs/safety.md +28 -0
- cli/docs/terms.md +54 -0
- cli/main.py +84 -0
- cli/templates/__init__.py +2 -0
- cli/templates/course_capabilities.template.yaml +189 -0
- cli/templates/course_license_apache-2.0.template.txt +30 -0
- cli/templates/course_license_logion-standard-course-v1.template.txt +49 -0
- cli/templates/course_license_mit.template.txt +21 -0
- logion_cli-0.1.0.dist-info/METADATA +49 -0
- logion_cli-0.1.0.dist-info/RECORD +135 -0
- logion_cli-0.1.0.dist-info/WHEEL +4 -0
- logion_cli-0.1.0.dist-info/entry_points.txt +4 -0
- logion_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Handlers for the ``skills`` command group.
|
|
3
|
+
|
|
4
|
+
These commands operate entirely on the local ``~/.logion/`` cache; they
|
|
5
|
+
do not call the marketplace API. The companion package (and any
|
|
6
|
+
agent) talks to the local cache through this CLI surface so install,
|
|
7
|
+
update, and inspection paths run through the same validators as the
|
|
8
|
+
rest of the CLI.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from cli._course_bundle import CourseBundleError, validate_course_bundle
|
|
19
|
+
from cli._local_state import (
|
|
20
|
+
LockHeldError,
|
|
21
|
+
UnsafeIdentifierError,
|
|
22
|
+
_utc_iso_now,
|
|
23
|
+
acquire_lock,
|
|
24
|
+
enrich_manifest,
|
|
25
|
+
installed_dir,
|
|
26
|
+
validate_manifest,
|
|
27
|
+
)
|
|
28
|
+
from cli._output import emit_json
|
|
29
|
+
|
|
30
|
+
from ._agent_symlink import (
|
|
31
|
+
apply_post_install_symlink,
|
|
32
|
+
resolve_symlink_intent,
|
|
33
|
+
)
|
|
34
|
+
from ._finalize import copy_and_finalize
|
|
35
|
+
from ._inspect_handler import handle_skills_inspect # noqa: F401 (re-export)
|
|
36
|
+
from ._install_helpers import (
|
|
37
|
+
check_existing_install,
|
|
38
|
+
copy_skill_files,
|
|
39
|
+
read_capabilities,
|
|
40
|
+
resolve_target,
|
|
41
|
+
)
|
|
42
|
+
from ._query_handlers import ( # noqa: F401 (re-export)
|
|
43
|
+
handle_skills_installed,
|
|
44
|
+
handle_skills_updates,
|
|
45
|
+
)
|
|
46
|
+
from ._update_handler import handle_skills_update # noqa: F401 (re-export)
|
|
47
|
+
from ._verify_handler import handle_skills_verify # noqa: F401 (re-export)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _build_manifest_data(
|
|
51
|
+
args: argparse.Namespace,
|
|
52
|
+
course_id: str,
|
|
53
|
+
version_id: str,
|
|
54
|
+
source_dir: Path,
|
|
55
|
+
) -> dict[str, Any]:
|
|
56
|
+
"""Build the manifest dict for an install."""
|
|
57
|
+
install_source = getattr(args, "install_source", "manual")
|
|
58
|
+
is_marketplace = install_source == "logion-marketplace"
|
|
59
|
+
data: dict[str, Any] = {
|
|
60
|
+
"course_id": course_id,
|
|
61
|
+
"version_id": version_id,
|
|
62
|
+
"title": args.title or "",
|
|
63
|
+
"source": install_source,
|
|
64
|
+
"installed_at": "",
|
|
65
|
+
"entrypoint": "SKILL.md",
|
|
66
|
+
"capabilities": [],
|
|
67
|
+
"required_tools": ["terminal", "file"],
|
|
68
|
+
"permissions": [],
|
|
69
|
+
"env_vars": [],
|
|
70
|
+
"execution_policy": "approval-required",
|
|
71
|
+
"content_sha256": "",
|
|
72
|
+
"review_status": "approved",
|
|
73
|
+
"entitlement_status": "active" if is_marketplace else "unknown",
|
|
74
|
+
"license_scope": "unknown",
|
|
75
|
+
"official_update_channel": is_marketplace,
|
|
76
|
+
"last_verified_at": _utc_iso_now() if is_marketplace else None,
|
|
77
|
+
}
|
|
78
|
+
return read_capabilities(source_dir / "course" / "capabilities.yaml", data)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _validate_pre_install(
|
|
82
|
+
manifest_data: dict[str, Any],
|
|
83
|
+
course_id: str,
|
|
84
|
+
version_id: str,
|
|
85
|
+
home: Path,
|
|
86
|
+
) -> int:
|
|
87
|
+
"""Validate manifest before filesystem writes. Returns 0 on success."""
|
|
88
|
+
pre_manifest = enrich_manifest(
|
|
89
|
+
{
|
|
90
|
+
**manifest_data,
|
|
91
|
+
"installed_at": (
|
|
92
|
+
manifest_data.get("installed_at") or _utc_iso_now()
|
|
93
|
+
),
|
|
94
|
+
"content_sha256": "_",
|
|
95
|
+
},
|
|
96
|
+
course_id,
|
|
97
|
+
version_id,
|
|
98
|
+
home,
|
|
99
|
+
)
|
|
100
|
+
pre_errors = validate_manifest(pre_manifest)
|
|
101
|
+
if pre_errors:
|
|
102
|
+
for e in pre_errors:
|
|
103
|
+
print(f"MANIFEST ERROR: {e}", file=sys.stderr)
|
|
104
|
+
return 3
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def handle_skills_install(args: argparse.Namespace) -> int: # noqa: C901
|
|
109
|
+
"""Install a skill bundle from a local source directory."""
|
|
110
|
+
home = resolve_target(args)
|
|
111
|
+
source_dir = args.source.resolve()
|
|
112
|
+
course_id: str = args.course_id
|
|
113
|
+
version_id: str = args.version_id
|
|
114
|
+
|
|
115
|
+
if not (source_dir / "SKILL.md").is_file():
|
|
116
|
+
print(
|
|
117
|
+
f"ERROR: source directory must contain SKILL.md: {source_dir}",
|
|
118
|
+
file=sys.stderr,
|
|
119
|
+
)
|
|
120
|
+
return 1
|
|
121
|
+
try:
|
|
122
|
+
validate_course_bundle(source_dir)
|
|
123
|
+
except CourseBundleError as exc:
|
|
124
|
+
print(f"ERROR: invalid course bundle: {exc}", file=sys.stderr)
|
|
125
|
+
return 1
|
|
126
|
+
|
|
127
|
+
# Ask about the symlink up-front, before any filesystem writes.
|
|
128
|
+
# The user's choice is captured here and applied after install.
|
|
129
|
+
skill_name, symlink_parent = resolve_symlink_intent(source_dir, args)
|
|
130
|
+
|
|
131
|
+
if not args.force:
|
|
132
|
+
try:
|
|
133
|
+
rc = check_existing_install(
|
|
134
|
+
course_id, version_id, source_dir, home
|
|
135
|
+
)
|
|
136
|
+
except UnsafeIdentifierError as exc:
|
|
137
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
138
|
+
return 1
|
|
139
|
+
if rc != 0:
|
|
140
|
+
return rc
|
|
141
|
+
|
|
142
|
+
manifest_data = _build_manifest_data(
|
|
143
|
+
args, course_id, version_id, source_dir
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Validate the manifest *before* touching the filesystem so an
|
|
147
|
+
# invalid bundle (including dry-run) cannot leave a partial copy
|
|
148
|
+
# behind or report success without a real install.
|
|
149
|
+
rc = _validate_pre_install(manifest_data, course_id, version_id, home)
|
|
150
|
+
if rc != 0:
|
|
151
|
+
return rc
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
dest = installed_dir(course_id, version_id, home)
|
|
155
|
+
except UnsafeIdentifierError as exc:
|
|
156
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
157
|
+
return 1
|
|
158
|
+
|
|
159
|
+
if args.dry_run:
|
|
160
|
+
copied = copy_skill_files(source_dir, dest, dry_run=True)
|
|
161
|
+
print(
|
|
162
|
+
f"Would install: {course_id}/{version_id} "
|
|
163
|
+
f"({len(copied)} files) -> {dest}"
|
|
164
|
+
)
|
|
165
|
+
return 0
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
acquire_lock(course_id, version_id, home)
|
|
169
|
+
except LockHeldError as exc:
|
|
170
|
+
print(
|
|
171
|
+
f"ERROR: another install/update holds the lock for "
|
|
172
|
+
f"{course_id}/{version_id} at {exc.path}. Wait for it to "
|
|
173
|
+
"finish or remove the stale lock file.",
|
|
174
|
+
file=sys.stderr,
|
|
175
|
+
)
|
|
176
|
+
return 4
|
|
177
|
+
|
|
178
|
+
rc, copied = copy_and_finalize(
|
|
179
|
+
source_dir, dest, course_id, version_id, manifest_data, home
|
|
180
|
+
)
|
|
181
|
+
if rc != 0:
|
|
182
|
+
return rc
|
|
183
|
+
|
|
184
|
+
print(
|
|
185
|
+
f"Installed: {course_id}/{version_id} ({len(copied)} files) -> {dest}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if symlink_parent and skill_name:
|
|
189
|
+
apply_post_install_symlink(symlink_parent, skill_name, dest)
|
|
190
|
+
|
|
191
|
+
if getattr(args, "json_output", False):
|
|
192
|
+
emit_json(
|
|
193
|
+
"logion.skills.install",
|
|
194
|
+
{
|
|
195
|
+
"course_id": course_id,
|
|
196
|
+
"version_id": version_id,
|
|
197
|
+
"destination": str(dest),
|
|
198
|
+
"files_installed": len(copied),
|
|
199
|
+
"symlinked": bool(symlink_parent and skill_name),
|
|
200
|
+
},
|
|
201
|
+
)
|
|
202
|
+
return 0
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Parser registration for skills commands."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from cli._options import COMMON_PARSER
|
|
10
|
+
|
|
11
|
+
from ._search_handler import handle_skills_search
|
|
12
|
+
from .handlers import (
|
|
13
|
+
handle_skills_inspect,
|
|
14
|
+
handle_skills_install,
|
|
15
|
+
handle_skills_installed,
|
|
16
|
+
handle_skills_update,
|
|
17
|
+
handle_skills_updates,
|
|
18
|
+
handle_skills_verify,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
23
|
+
"""Register the ``skills`` subcommand group."""
|
|
24
|
+
parser = subparsers.add_parser(
|
|
25
|
+
"skills",
|
|
26
|
+
help="Manage locally installed marketplace skills",
|
|
27
|
+
)
|
|
28
|
+
sub = parser.add_subparsers(
|
|
29
|
+
dest="skills_command",
|
|
30
|
+
required=True,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
install = sub.add_parser(
|
|
34
|
+
"install",
|
|
35
|
+
help="Install a skill bundle from a local source directory",
|
|
36
|
+
parents=[COMMON_PARSER],
|
|
37
|
+
)
|
|
38
|
+
install.add_argument(
|
|
39
|
+
"--source",
|
|
40
|
+
type=Path,
|
|
41
|
+
required=True,
|
|
42
|
+
help="Path to the skill bundle source directory",
|
|
43
|
+
)
|
|
44
|
+
install.add_argument("--course-id", required=True)
|
|
45
|
+
install.add_argument("--version-id", required=True)
|
|
46
|
+
install.add_argument("--title", default=None)
|
|
47
|
+
install.add_argument(
|
|
48
|
+
"--target",
|
|
49
|
+
type=Path,
|
|
50
|
+
default=None,
|
|
51
|
+
help="Override LOGION_HOME for the install target",
|
|
52
|
+
)
|
|
53
|
+
install.add_argument(
|
|
54
|
+
"--dry-run",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help="Show what would be installed without writing files",
|
|
57
|
+
)
|
|
58
|
+
install.add_argument(
|
|
59
|
+
"--force",
|
|
60
|
+
action="store_true",
|
|
61
|
+
help="Overwrite an existing install with different content",
|
|
62
|
+
)
|
|
63
|
+
install.add_argument(
|
|
64
|
+
"--install-source",
|
|
65
|
+
default="manual",
|
|
66
|
+
choices=["manual", "logion-marketplace"],
|
|
67
|
+
help="Provenance of the install (default: manual)",
|
|
68
|
+
)
|
|
69
|
+
install.add_argument(
|
|
70
|
+
"--symlink-dir",
|
|
71
|
+
default=None,
|
|
72
|
+
help=(
|
|
73
|
+
"Symlink the installed skill into this directory "
|
|
74
|
+
"(e.g. ~/.claude/skills). Skip the interactive prompt."
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
install.add_argument(
|
|
78
|
+
"--no-symlink",
|
|
79
|
+
action="store_true",
|
|
80
|
+
help="Skip the agent-symlink prompt entirely (canonical install only)",
|
|
81
|
+
)
|
|
82
|
+
install.set_defaults(handler=handle_skills_install)
|
|
83
|
+
|
|
84
|
+
installed = sub.add_parser(
|
|
85
|
+
"installed",
|
|
86
|
+
help="List installed skills",
|
|
87
|
+
parents=[COMMON_PARSER],
|
|
88
|
+
)
|
|
89
|
+
installed.add_argument("--target", type=Path, default=None)
|
|
90
|
+
installed.set_defaults(handler=handle_skills_installed)
|
|
91
|
+
|
|
92
|
+
inspect = sub.add_parser(
|
|
93
|
+
"inspect",
|
|
94
|
+
help="Show the manifest for an installed skill",
|
|
95
|
+
parents=[COMMON_PARSER],
|
|
96
|
+
)
|
|
97
|
+
inspect.add_argument("course_id", metavar="COURSE_ID")
|
|
98
|
+
inspect.add_argument("--version-id", default=None)
|
|
99
|
+
inspect.add_argument("--verbose", action="store_true", default=False)
|
|
100
|
+
inspect.add_argument("--target", type=Path, default=None)
|
|
101
|
+
inspect.set_defaults(handler=handle_skills_inspect)
|
|
102
|
+
|
|
103
|
+
updates = sub.add_parser(
|
|
104
|
+
"updates",
|
|
105
|
+
help="Report integrity and update status for installed skills",
|
|
106
|
+
parents=[COMMON_PARSER],
|
|
107
|
+
)
|
|
108
|
+
updates.add_argument("--target", type=Path, default=None)
|
|
109
|
+
updates.set_defaults(handler=handle_skills_updates)
|
|
110
|
+
|
|
111
|
+
update = sub.add_parser(
|
|
112
|
+
"update",
|
|
113
|
+
help="Apply an update to an installed skill",
|
|
114
|
+
parents=[COMMON_PARSER],
|
|
115
|
+
)
|
|
116
|
+
update.add_argument("course_id", metavar="COURSE_ID")
|
|
117
|
+
update.add_argument("--version-id", required=True)
|
|
118
|
+
update.add_argument(
|
|
119
|
+
"--source",
|
|
120
|
+
type=Path,
|
|
121
|
+
required=True,
|
|
122
|
+
help="Path to the new skill bundle source directory",
|
|
123
|
+
)
|
|
124
|
+
update.add_argument("--title", default=None)
|
|
125
|
+
update.add_argument("--target", type=Path, default=None)
|
|
126
|
+
update.add_argument(
|
|
127
|
+
"--force",
|
|
128
|
+
action="store_true",
|
|
129
|
+
help="Overwrite a locally modified installation",
|
|
130
|
+
)
|
|
131
|
+
update.set_defaults(handler=handle_skills_update)
|
|
132
|
+
|
|
133
|
+
verify = sub.add_parser(
|
|
134
|
+
"verify",
|
|
135
|
+
help="Re-state locally stored entitlement status for installed skills",
|
|
136
|
+
parents=[COMMON_PARSER],
|
|
137
|
+
)
|
|
138
|
+
verify.add_argument("course_id", nargs="?", default=None)
|
|
139
|
+
verify.add_argument("--target", type=Path, default=None)
|
|
140
|
+
verify.set_defaults(handler=handle_skills_verify)
|
|
141
|
+
|
|
142
|
+
search = sub.add_parser(
|
|
143
|
+
"search",
|
|
144
|
+
help=(
|
|
145
|
+
"Search marketplace listings"
|
|
146
|
+
" with installed/entitlement annotations"
|
|
147
|
+
),
|
|
148
|
+
parents=[COMMON_PARSER],
|
|
149
|
+
)
|
|
150
|
+
search.add_argument("query", metavar="QUERY")
|
|
151
|
+
search.add_argument("--limit", type=int, default=5)
|
|
152
|
+
search.add_argument("--verbose", action="store_true", default=False)
|
|
153
|
+
search.add_argument("--target", type=Path, default=None)
|
|
154
|
+
search.set_defaults(handler=handle_skills_search)
|