gaia-cli 4.3.6__tar.gz → 4.3.8__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.
- {gaia_cli-4.3.6/src/gaia_cli.egg-info → gaia_cli-4.3.8}/PKG-INFO +6 -2
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/README.md +5 -1
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/pyproject.toml +1 -1
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/graph.py +10 -1
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/main.py +73 -4
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/push.py +10 -3
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/registry.py +1 -1
- {gaia_cli-4.3.6 → gaia_cli-4.3.8/src/gaia_cli.egg-info}/PKG-INFO +6 -2
- gaia_cli-4.3.8/tests/test_evidence_inheritance.py +97 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_workflows.py +1 -12
- gaia_cli-4.3.6/tests/test_evidence_inheritance.py +0 -62
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/LICENSE +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/setup.cfg +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/__init__.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/__main__.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/authz.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/cardRenderer.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/combinator.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/commands/__init__.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/commands/dev.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/commands/stats.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/gaia.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/named/devin-ai/autonomous-swe.md +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/named/karpathy/autoresearch.md +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/named-skills.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/combination.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/meta.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/namedSkill.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/pluginConfig.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/realSkillCatalog.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/skill.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/skillBatch.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/skillSuite.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/skillTree.schema.json +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/embeddings.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/evidence.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/formatting.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/hook.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/install.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/interactive.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/leveling.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/localContext.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/name.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/pathEngine.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/prWriter.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/promotion.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/redaction.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/resolver.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/scanner.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/semantic_search.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/timeline.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/treeManager.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/__init__.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/app.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/__init__.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/agent.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/hero.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/levelup.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/scan.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/screens/tree.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/theme.tcss +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/tui/tokens.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/versioning.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli.egg-info/SOURCES.txt +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli.egg-info/dependency_links.txt +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli.egg-info/entry_points.txt +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli.egg-info/requires.txt +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli.egg-info/top_level.txt +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_authz.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_card_renderer.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_crawlers.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_docs_site.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_docs_skill_explorer.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_dx.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_embeddings.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_formatting.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_graph.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_install.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_intake.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_interactive.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_leveling.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_lifecycle.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_local_context.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_meta_ops.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_named_skills.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_packaging.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_path_command.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_path_engine.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_pr540_review.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_pr_writer.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_promotion.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_push.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_real_skill_catalog.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_redaction.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_registry_layout.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_scanner.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_stats.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_suite_install.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_timelines.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_treeManager.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_tui_tokens.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_unlocked_at_datetime.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_validate.py +0 -0
- {gaia_cli-4.3.6 → gaia_cli-4.3.8}/tests/test_verify_evidence.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaia-cli
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.8
|
|
4
4
|
Summary: Gaia AI Agent Skill Registry CLI
|
|
5
5
|
Author: Gaia contributors
|
|
6
6
|
License: MIT
|
|
@@ -167,7 +167,7 @@ Skills rank up through **verifiable evidence** (Class A/B/C) and can be demoted
|
|
|
167
167
|
**1. Install the CLI**
|
|
168
168
|
|
|
169
169
|
<!-- gaia:version-start -->
|
|
170
|
-
Current Gaia CLI version: `4.3.
|
|
170
|
+
Current Gaia CLI version: `4.3.8`.
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
173
|
curl -fsSL https://gaia.tiongson.co/install.sh | sh
|
|
@@ -212,6 +212,8 @@ packaging-specific tests are skipped locally with guidance to install developer
|
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
gaia init --user your-username
|
|
215
|
+
# Detected repo: your-username/your-repo
|
|
216
|
+
# Initialize Gaia on this repository? [Y/n]: Y
|
|
215
217
|
gaia scan
|
|
216
218
|
```
|
|
217
219
|
|
|
@@ -221,9 +223,11 @@ Detects skills your agent demonstrates.
|
|
|
221
223
|
|
|
222
224
|
```bash
|
|
223
225
|
gaia push
|
|
226
|
+
# Push skills to gaia registry from your-username/your-repo? [Y/n]: Y
|
|
224
227
|
```
|
|
225
228
|
|
|
226
229
|
A GitHub issue opens automatically. Maintainers review and promote; your name attaches at 2★.
|
|
230
|
+
If no remote is detected, Gaia will guide you to add one.
|
|
227
231
|
|
|
228
232
|
**4. Bond your agent (optional)**
|
|
229
233
|
|
|
@@ -118,7 +118,7 @@ Skills rank up through **verifiable evidence** (Class A/B/C) and can be demoted
|
|
|
118
118
|
**1. Install the CLI**
|
|
119
119
|
|
|
120
120
|
<!-- gaia:version-start -->
|
|
121
|
-
Current Gaia CLI version: `4.3.
|
|
121
|
+
Current Gaia CLI version: `4.3.8`.
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
124
|
curl -fsSL https://gaia.tiongson.co/install.sh | sh
|
|
@@ -163,6 +163,8 @@ packaging-specific tests are skipped locally with guidance to install developer
|
|
|
163
163
|
|
|
164
164
|
```bash
|
|
165
165
|
gaia init --user your-username
|
|
166
|
+
# Detected repo: your-username/your-repo
|
|
167
|
+
# Initialize Gaia on this repository? [Y/n]: Y
|
|
166
168
|
gaia scan
|
|
167
169
|
```
|
|
168
170
|
|
|
@@ -172,9 +174,11 @@ Detects skills your agent demonstrates.
|
|
|
172
174
|
|
|
173
175
|
```bash
|
|
174
176
|
gaia push
|
|
177
|
+
# Push skills to gaia registry from your-username/your-repo? [Y/n]: Y
|
|
175
178
|
```
|
|
176
179
|
|
|
177
180
|
A GitHub issue opens automatically. Maintainers review and promote; your name attaches at 2★.
|
|
181
|
+
If no remote is detected, Gaia will guide you to add one.
|
|
178
182
|
|
|
179
183
|
**4. Bond your agent (optional)**
|
|
180
184
|
|
|
@@ -39,6 +39,11 @@ def _registry_root(registry_path: str | os.PathLike[str]) -> Path:
|
|
|
39
39
|
|
|
40
40
|
def load_graph(registry_path: str | os.PathLike[str] = ".") -> dict[str, Any]:
|
|
41
41
|
graph_path = Path(registry_graph_path(_registry_root(registry_path)))
|
|
42
|
+
if not graph_path.exists():
|
|
43
|
+
raise FileNotFoundError(
|
|
44
|
+
f"Registry graph not found at {graph_path}. "
|
|
45
|
+
"Run gaia init from a gaia-skill-tree clone."
|
|
46
|
+
)
|
|
42
47
|
with graph_path.open("r", encoding="utf-8") as f:
|
|
43
48
|
return json.load(f)
|
|
44
49
|
|
|
@@ -1204,7 +1209,11 @@ def graph_command(args: Any) -> None:
|
|
|
1204
1209
|
except Exception:
|
|
1205
1210
|
pass # Degrade gracefully to canon mode
|
|
1206
1211
|
|
|
1207
|
-
|
|
1212
|
+
try:
|
|
1213
|
+
out_path = write_graph_artifact(registry_path, output=output, fmt=fmt, user_ctx=user_ctx)
|
|
1214
|
+
except FileNotFoundError as exc:
|
|
1215
|
+
print(str(exc), file=sys.stderr)
|
|
1216
|
+
return
|
|
1208
1217
|
print(f" saved {out_path}")
|
|
1209
1218
|
|
|
1210
1219
|
# Regenerate the GEXF from current node data
|
|
@@ -14,7 +14,7 @@ from gaia_cli.resolver import resolve_skills
|
|
|
14
14
|
from gaia_cli.combinator import get_combinations
|
|
15
15
|
from gaia_cli.treeManager import load_tree, save_tree, show_status, show_tree
|
|
16
16
|
from gaia_cli.prWriter import open_pr, open_intake_issue
|
|
17
|
-
from gaia_cli.push import build_skill_batch, write_skill_batch, build_proposed_skill, detect_source_repo
|
|
17
|
+
from gaia_cli.push import build_skill_batch, write_skill_batch, build_proposed_skill, detect_source_repo, NonPublicRepoError
|
|
18
18
|
from gaia_cli.embeddings import generate_embeddings
|
|
19
19
|
from gaia_cli.semantic_search import search as semantic_search, load_embeddings
|
|
20
20
|
from gaia_cli.name import find_awakened_skill, promote_to_named, update_batch_lifecycle
|
|
@@ -321,6 +321,29 @@ def init_command(args):
|
|
|
321
321
|
print(f" user: {username}")
|
|
322
322
|
print(f" scanPaths: {scan_paths}")
|
|
323
323
|
|
|
324
|
+
try:
|
|
325
|
+
source = detect_source_repo({"gaiaUser": username})
|
|
326
|
+
if sys.stdin.isatty() and not getattr(args, 'yes', False):
|
|
327
|
+
try:
|
|
328
|
+
ans = input(f"Detected repo: {source}\nInitialize Gaia on this repository? [Y/n]: ").strip().lower()
|
|
329
|
+
except (KeyboardInterrupt, EOFError):
|
|
330
|
+
print()
|
|
331
|
+
import shutil; shutil.rmtree(config_dir, ignore_errors=True)
|
|
332
|
+
sys.exit(1)
|
|
333
|
+
if ans == 'n':
|
|
334
|
+
import shutil; shutil.rmtree(config_dir, ignore_errors=True)
|
|
335
|
+
print("Aborted.")
|
|
336
|
+
return
|
|
337
|
+
except NonPublicRepoError:
|
|
338
|
+
print(
|
|
339
|
+
"\nNo GitHub remote detected in this directory.\n"
|
|
340
|
+
" → To unlock the full workflow:\n"
|
|
341
|
+
" • Add a remote: git remote add origin https://github.com/<you>/<repo>\n"
|
|
342
|
+
" • Or clone the gaia-skill-tree registry and run gaia init there\n"
|
|
343
|
+
"Your skills are still scannable and pushable — once linked to a public repo,\n"
|
|
344
|
+
"approved skills will start at 2★ instead of 1★.\n"
|
|
345
|
+
)
|
|
346
|
+
|
|
324
347
|
# If we're inside a registry clone, register its path globally so that
|
|
325
348
|
# commands like `gaia push` work from any project without --registry.
|
|
326
349
|
tree_path = user_tree_path(".", username)
|
|
@@ -358,6 +381,11 @@ def scan_command(args):
|
|
|
358
381
|
scan_result = scan_repo_detailed()
|
|
359
382
|
raw_tokens = {t.lstrip('/') for t in scan_result["tokens"]}
|
|
360
383
|
graph_path = registry_graph_path(args.registry)
|
|
384
|
+
|
|
385
|
+
from gaia_cli.registry import bundled_registry_path
|
|
386
|
+
if not quiet and not use_json and str(args.registry) == str(bundled_registry_path()):
|
|
387
|
+
print("Note: using bundled registry (no local registry clone found).")
|
|
388
|
+
|
|
361
389
|
resolved = resolve_skills(raw_tokens, registry_path=graph_path)
|
|
362
390
|
|
|
363
391
|
username = config.get('gaiaUser')
|
|
@@ -387,6 +415,9 @@ def scan_command(args):
|
|
|
387
415
|
if resolved:
|
|
388
416
|
# Colored skill list with type glyphs
|
|
389
417
|
graph_path_file = registry_graph_path(args.registry)
|
|
418
|
+
if not os.path.exists(graph_path_file):
|
|
419
|
+
print("Registry graph not found. Run `gaia init` from a gaia-skill-tree clone.")
|
|
420
|
+
return
|
|
390
421
|
with open(graph_path_file, 'r', encoding='utf-8') as gf:
|
|
391
422
|
gdata = json.load(gf)
|
|
392
423
|
smap = {s['id']: s for s in gdata.get('skills', [])}
|
|
@@ -769,7 +800,11 @@ def promote_command(args):
|
|
|
769
800
|
|
|
770
801
|
tree = load_tree(username, registry_path=args.registry)
|
|
771
802
|
if not tree:
|
|
772
|
-
|
|
803
|
+
if not os.path.exists(promotion_candidates_path(args.registry)):
|
|
804
|
+
print("No promotion candidates found. Run `gaia scan` first to detect skills.",
|
|
805
|
+
file=sys.stderr)
|
|
806
|
+
else:
|
|
807
|
+
print(f"No skill tree found for user '{username}'.", file=sys.stderr)
|
|
773
808
|
return
|
|
774
809
|
|
|
775
810
|
skill_id = getattr(args, 'skillId', None)
|
|
@@ -803,7 +838,12 @@ def promote_command(args):
|
|
|
803
838
|
if picked:
|
|
804
839
|
skill_id = picked
|
|
805
840
|
if not skill_id:
|
|
806
|
-
|
|
841
|
+
from gaia_cli.registry import promotion_candidates_path
|
|
842
|
+
if not os.path.exists(promotion_candidates_path(args.registry)):
|
|
843
|
+
print("No promotion candidates found. Run `gaia scan` first to detect skills.",
|
|
844
|
+
file=sys.stderr)
|
|
845
|
+
else:
|
|
846
|
+
print("Usage: gaia promote <skill> or gaia promote --all", file=sys.stderr)
|
|
807
847
|
sys.exit(2)
|
|
808
848
|
result = promote_from_candidates(username, skill_id, args.registry, new_display_name=display_name)
|
|
809
849
|
except ValueError as exc:
|
|
@@ -1020,6 +1060,12 @@ def tree_command(args):
|
|
|
1020
1060
|
show_tree(tree, graph_data=graph_data, registry_path=args.registry, mode=mode, canon=canon)
|
|
1021
1061
|
if tree:
|
|
1022
1062
|
render_user_tree_outputs(config.get('gaiaUser'), tree, graph_data, args.registry, quiet=False)
|
|
1063
|
+
try:
|
|
1064
|
+
detect_source_repo(config)
|
|
1065
|
+
except NonPublicRepoError:
|
|
1066
|
+
print("\nTip: link a public GitHub repo and approved skills will start at 2★ once named.")
|
|
1067
|
+
except Exception:
|
|
1068
|
+
pass
|
|
1023
1069
|
|
|
1024
1070
|
def fuse_command(args):
|
|
1025
1071
|
config = load_config()
|
|
@@ -1160,7 +1206,29 @@ def push_command(args):
|
|
|
1160
1206
|
sys.exit(1)
|
|
1161
1207
|
|
|
1162
1208
|
raw_tokens = scan_repo()
|
|
1163
|
-
|
|
1209
|
+
try:
|
|
1210
|
+
batch = build_skill_batch(raw_tokens, config, args.registry)
|
|
1211
|
+
source_repo = batch["sourceRepo"]
|
|
1212
|
+
if sys.stdin.isatty() and not getattr(args, 'yes', False):
|
|
1213
|
+
try:
|
|
1214
|
+
ans = input(f"Push skills to gaia registry from {source_repo}? [Y/n]: ").strip().lower()
|
|
1215
|
+
except (KeyboardInterrupt, EOFError):
|
|
1216
|
+
print()
|
|
1217
|
+
sys.exit(1)
|
|
1218
|
+
if ans == 'n':
|
|
1219
|
+
print("Aborted.")
|
|
1220
|
+
return
|
|
1221
|
+
except NonPublicRepoError as exc:
|
|
1222
|
+
print(
|
|
1223
|
+
"\nYour skills are ready for review!\n"
|
|
1224
|
+
"Skills pushed from outside a public GitHub repo start at 1★ in the registry.\n"
|
|
1225
|
+
"Once you link a public repo, approved skills will start at 2★ instead.\n"
|
|
1226
|
+
" → Add a remote: git remote add origin https://github.com/<you>/<repo>\n",
|
|
1227
|
+
file=sys.stderr,
|
|
1228
|
+
)
|
|
1229
|
+
username_fallback = str(exc)
|
|
1230
|
+
batch = build_skill_batch(raw_tokens, config, args.registry,
|
|
1231
|
+
source_repo=f"{username_fallback}/local-repo")
|
|
1164
1232
|
|
|
1165
1233
|
# Guard 1: check if empty initially
|
|
1166
1234
|
if not batch.get("proposedSkills") and not batch.get("knownSkills"):
|
|
@@ -1689,6 +1757,7 @@ def get_parser():
|
|
|
1689
1757
|
push_parser.add_argument('--dry-run', action='store_true', help="Print the skill batch without writing it")
|
|
1690
1758
|
push_parser.add_argument('--no-issue', action='store_true', dest='no_issue', help="Write intake record without creating a GitHub issue")
|
|
1691
1759
|
push_parser.add_argument('--no-pr', action='store_true', dest='no_issue', help=argparse.SUPPRESS) # backward compat alias
|
|
1760
|
+
push_parser.add_argument('--yes', '-y', action='store_true', dest='yes', help="Skip confirmation prompts")
|
|
1692
1761
|
propose_parser = subparsers.add_parser('propose', help="Propose a single canonical skill as a named PR")
|
|
1693
1762
|
propose_parser.add_argument('skillId', help="Canonical skill ID (accepts /skill-id form)")
|
|
1694
1763
|
propose_parser.add_argument('--target', help="Named skill target in contributor/skill-name format")
|
|
@@ -15,6 +15,12 @@ from gaia_cli.registry import registry_graph_path, skill_batches_dir
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
SKILL_ID_RE = re.compile(r"^[a-z][a-z0-9]*(-[a-z0-9]+)*$")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NonPublicRepoError(RuntimeError):
|
|
21
|
+
"""Raised when no public GitHub remote is detected for the working directory."""
|
|
22
|
+
|
|
23
|
+
|
|
18
24
|
MIN_PROPOSED_TOKEN_LENGTH = 3
|
|
19
25
|
PROPOSED_STOPWORDS = {
|
|
20
26
|
"a", "an", "and", "are", "as", "at", "be", "by", "for", "from", "in", "is", "it", "of", "on", "or", "that", "the", "to", "was", "were", "with", "already",
|
|
@@ -68,7 +74,7 @@ def detect_source_repo(config):
|
|
|
68
74
|
if host == "github.com" or host.endswith(".github.com"):
|
|
69
75
|
return parsed.path.lstrip("/")
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
raise NonPublicRepoError(config.get('gaiaUser', 'unknown'))
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
def build_proposed_skill(skill_id, source_repo):
|
|
@@ -126,11 +132,12 @@ def filter_proposed_ids(valid_tokens, canonical_ids):
|
|
|
126
132
|
return filtered
|
|
127
133
|
|
|
128
134
|
|
|
129
|
-
def build_skill_batch(raw_tokens, config, registry_root, now=None):
|
|
135
|
+
def build_skill_batch(raw_tokens, config, registry_root, now=None, source_repo=None):
|
|
130
136
|
graph_path = registry_graph_path(registry_root)
|
|
131
137
|
canonical_ids = load_canonical_skills(graph_path)
|
|
132
138
|
canonical_map = load_canonical_skill_map(graph_path)
|
|
133
|
-
source_repo
|
|
139
|
+
if source_repo is None:
|
|
140
|
+
source_repo = detect_source_repo(config)
|
|
134
141
|
timestamp = now or datetime.now(timezone.utc)
|
|
135
142
|
generated_at = timestamp.replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
136
143
|
|
|
@@ -6,7 +6,7 @@ from importlib import resources
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
WRITE_COMMANDS = {"push", "propose", "name", "fuse", "embed", "sync", "promote", "
|
|
9
|
+
WRITE_COMMANDS = {"push", "propose", "name", "fuse", "embed", "sync", "promote", "release", "docs", "merge", "split", "add", "evidence"}
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def _gaia_home_dir() -> Path:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaia-cli
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.8
|
|
4
4
|
Summary: Gaia AI Agent Skill Registry CLI
|
|
5
5
|
Author: Gaia contributors
|
|
6
6
|
License: MIT
|
|
@@ -167,7 +167,7 @@ Skills rank up through **verifiable evidence** (Class A/B/C) and can be demoted
|
|
|
167
167
|
**1. Install the CLI**
|
|
168
168
|
|
|
169
169
|
<!-- gaia:version-start -->
|
|
170
|
-
Current Gaia CLI version: `4.3.
|
|
170
|
+
Current Gaia CLI version: `4.3.8`.
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
173
|
curl -fsSL https://gaia.tiongson.co/install.sh | sh
|
|
@@ -212,6 +212,8 @@ packaging-specific tests are skipped locally with guidance to install developer
|
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
gaia init --user your-username
|
|
215
|
+
# Detected repo: your-username/your-repo
|
|
216
|
+
# Initialize Gaia on this repository? [Y/n]: Y
|
|
215
217
|
gaia scan
|
|
216
218
|
```
|
|
217
219
|
|
|
@@ -221,9 +223,11 @@ Detects skills your agent demonstrates.
|
|
|
221
223
|
|
|
222
224
|
```bash
|
|
223
225
|
gaia push
|
|
226
|
+
# Push skills to gaia registry from your-username/your-repo? [Y/n]: Y
|
|
224
227
|
```
|
|
225
228
|
|
|
226
229
|
A GitHub issue opens automatically. Maintainers review and promote; your name attaches at 2★.
|
|
230
|
+
If no remote is detected, Gaia will guide you to add one.
|
|
227
231
|
|
|
228
232
|
**4. Bond your agent (optional)**
|
|
229
233
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Tests for the rank-less generic / inherited-evidence model."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
sys.path.insert(
|
|
9
|
+
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from gaia_cli.evidence import (
|
|
13
|
+
inherited_evidence,
|
|
14
|
+
merge_evidence,
|
|
15
|
+
build_generic_evidence_map,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestInheritedEvidence(unittest.TestCase):
|
|
22
|
+
def test_named_inherits_generic_capability_evidence(self):
|
|
23
|
+
generic = {
|
|
24
|
+
"id": "automated-testing",
|
|
25
|
+
"evidence": [
|
|
26
|
+
{"class": "A", "source": "https://arxiv.org/abs/0001"},
|
|
27
|
+
],
|
|
28
|
+
}
|
|
29
|
+
named = {
|
|
30
|
+
"id": "x/pytest",
|
|
31
|
+
"evidence": [
|
|
32
|
+
{"class": "C", "source": "https://github.com/x/pytest"},
|
|
33
|
+
],
|
|
34
|
+
}
|
|
35
|
+
pool = inherited_evidence(named, generic)
|
|
36
|
+
sources = {e["source"] for e in pool}
|
|
37
|
+
self.assertEqual(
|
|
38
|
+
sources, {"https://arxiv.org/abs/0001", "https://github.com/x/pytest"}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def test_merge_evidence_edge_cases(self):
|
|
42
|
+
self.assertEqual(merge_evidence(), [])
|
|
43
|
+
self.assertEqual(merge_evidence(None, None), [])
|
|
44
|
+
self.assertEqual(merge_evidence([], []), [])
|
|
45
|
+
self.assertEqual(merge_evidence([{}], [{}]), [{}])
|
|
46
|
+
self.assertEqual(
|
|
47
|
+
merge_evidence(
|
|
48
|
+
None, [{"source": "s1"}], [], [{"source": "s1"}, {"source": "s2"}]
|
|
49
|
+
),
|
|
50
|
+
[{"source": "s1"}, {"source": "s2"}],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def test_dedup_by_source(self):
|
|
54
|
+
shared = {"class": "A", "source": "https://arxiv.org/abs/0001"}
|
|
55
|
+
pool = merge_evidence([shared], [dict(shared)])
|
|
56
|
+
self.assertEqual(len(pool), 1)
|
|
57
|
+
|
|
58
|
+
def test_no_generic_evidence(self):
|
|
59
|
+
named = {"id": "x/y", "evidence": [{"class": "C", "source": "s"}]}
|
|
60
|
+
self.assertEqual(len(inherited_evidence(named, None)), 1)
|
|
61
|
+
|
|
62
|
+
def test_build_generic_evidence_map(self):
|
|
63
|
+
skills = [{"id": "a", "evidence": [{"source": "s"}]}, {"id": "b"}]
|
|
64
|
+
m = build_generic_evidence_map(skills)
|
|
65
|
+
self.assertEqual(m["a"], [{"source": "s"}])
|
|
66
|
+
self.assertEqual(m["b"], [])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestRanklessGenerics(unittest.TestCase):
|
|
70
|
+
def test_no_generic_node_has_level_or_demerits(self):
|
|
71
|
+
graph = json.load(
|
|
72
|
+
open(os.path.join(REPO_ROOT, "registry", "gaia.json"), encoding="utf-8")
|
|
73
|
+
)
|
|
74
|
+
bad = [
|
|
75
|
+
s["id"]
|
|
76
|
+
for s in graph["skills"]
|
|
77
|
+
if "level" in s or "demerits" in s or "realVariants" in s
|
|
78
|
+
]
|
|
79
|
+
self.assertEqual(bad, [], f"Generic refs must be rank-less; offenders: {bad}")
|
|
80
|
+
|
|
81
|
+
def test_named_skills_retain_levels(self):
|
|
82
|
+
idx = json.load(
|
|
83
|
+
open(
|
|
84
|
+
os.path.join(REPO_ROOT, "registry", "named-skills.json"),
|
|
85
|
+
encoding="utf-8",
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
valid = {"1★", "2★", "3★", "4★", "5★", "6★"}
|
|
89
|
+
for entries in idx["buckets"].values():
|
|
90
|
+
for e in entries:
|
|
91
|
+
self.assertIn(
|
|
92
|
+
e["level"], valid, f"{e['id']} has invalid level {e['level']}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
unittest.main()
|
|
@@ -4,7 +4,6 @@ import unittest
|
|
|
4
4
|
|
|
5
5
|
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
6
6
|
AUTO_TRIAGE_PATH = os.path.join(REPO_ROOT, ".github", "workflows", "auto-triage.yml")
|
|
7
|
-
DOCS_REMINDER_PATH = os.path.join(REPO_ROOT, ".github", "workflows", "pr-docs-reminder.yml")
|
|
8
7
|
BRANCH_SCOPE_PATH = os.path.join(REPO_ROOT, ".github", "workflows", "branch-scope.yml")
|
|
9
8
|
|
|
10
9
|
|
|
@@ -25,21 +24,11 @@ class TestWorkflowConfig(unittest.TestCase):
|
|
|
25
24
|
with open(BRANCH_SCOPE_PATH, "r", encoding="utf-8") as f:
|
|
26
25
|
content = f.read()
|
|
27
26
|
|
|
28
|
-
self.assertIn("unrestricted branches (dev/*, claude/*, codex/*, chore/*) have no forward restriction", content)
|
|
27
|
+
self.assertIn("unrestricted branches (dev/*, claude/*, codex/*, gemini/*, chore/*) have no forward restriction", content)
|
|
29
28
|
self.assertIn('[ "$PREFIX" != "unrestricted" ]', content)
|
|
30
29
|
self.assertIn("skip-scope-check", content)
|
|
31
30
|
self.assertNotIn("!startsWith(github.head_ref || '', 'dev/')", content)
|
|
32
31
|
|
|
33
|
-
def test_pr_docs_reminder_shows_copyable_docs_check(self):
|
|
34
|
-
with open(DOCS_REMINDER_PATH, "r", encoding="utf-8") as f:
|
|
35
|
-
content = f.read()
|
|
36
|
-
|
|
37
|
-
self.assertIn("pull_request:", content)
|
|
38
|
-
self.assertIn("Repo Docs Before PR reminder", content)
|
|
39
|
-
self.assertIn("python scripts/build_docs.py --check", content)
|
|
40
|
-
self.assertIn("GITHUB_STEP_SUMMARY", content)
|
|
41
|
-
self.assertNotIn("actions/checkout", content)
|
|
42
|
-
|
|
43
32
|
|
|
44
33
|
if __name__ == "__main__":
|
|
45
34
|
unittest.main()
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"""Tests for the rank-less generic / inherited-evidence model."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
import unittest
|
|
7
|
-
|
|
8
|
-
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
|
9
|
-
|
|
10
|
-
from gaia_cli.evidence import (
|
|
11
|
-
inherited_evidence,
|
|
12
|
-
merge_evidence,
|
|
13
|
-
build_generic_evidence_map,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class TestInheritedEvidence(unittest.TestCase):
|
|
20
|
-
def test_named_inherits_generic_capability_evidence(self):
|
|
21
|
-
generic = {"id": "automated-testing", "evidence": [
|
|
22
|
-
{"class": "A", "source": "https://arxiv.org/abs/0001"},
|
|
23
|
-
]}
|
|
24
|
-
named = {"id": "x/pytest", "evidence": [
|
|
25
|
-
{"class": "C", "source": "https://github.com/x/pytest"},
|
|
26
|
-
]}
|
|
27
|
-
pool = inherited_evidence(named, generic)
|
|
28
|
-
sources = {e["source"] for e in pool}
|
|
29
|
-
self.assertEqual(sources, {"https://arxiv.org/abs/0001", "https://github.com/x/pytest"})
|
|
30
|
-
|
|
31
|
-
def test_dedup_by_source(self):
|
|
32
|
-
shared = {"class": "A", "source": "https://arxiv.org/abs/0001"}
|
|
33
|
-
pool = merge_evidence([shared], [dict(shared)])
|
|
34
|
-
self.assertEqual(len(pool), 1)
|
|
35
|
-
|
|
36
|
-
def test_no_generic_evidence(self):
|
|
37
|
-
named = {"id": "x/y", "evidence": [{"class": "C", "source": "s"}]}
|
|
38
|
-
self.assertEqual(len(inherited_evidence(named, None)), 1)
|
|
39
|
-
|
|
40
|
-
def test_build_generic_evidence_map(self):
|
|
41
|
-
skills = [{"id": "a", "evidence": [{"source": "s"}]}, {"id": "b"}]
|
|
42
|
-
m = build_generic_evidence_map(skills)
|
|
43
|
-
self.assertEqual(m["a"], [{"source": "s"}])
|
|
44
|
-
self.assertEqual(m["b"], [])
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class TestRanklessGenerics(unittest.TestCase):
|
|
48
|
-
def test_no_generic_node_has_level_or_demerits(self):
|
|
49
|
-
graph = json.load(open(os.path.join(REPO_ROOT, "registry", "gaia.json"), encoding="utf-8"))
|
|
50
|
-
bad = [s["id"] for s in graph["skills"] if "level" in s or "demerits" in s or "realVariants" in s]
|
|
51
|
-
self.assertEqual(bad, [], f"Generic refs must be rank-less; offenders: {bad}")
|
|
52
|
-
|
|
53
|
-
def test_named_skills_retain_levels(self):
|
|
54
|
-
idx = json.load(open(os.path.join(REPO_ROOT, "registry", "named-skills.json"), encoding="utf-8"))
|
|
55
|
-
valid = {"1★", "2★", "3★", "4★", "5★", "6★"}
|
|
56
|
-
for entries in idx["buckets"].values():
|
|
57
|
-
for e in entries:
|
|
58
|
-
self.assertIn(e["level"], valid, f"{e['id']} has invalid level {e['level']}")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if __name__ == "__main__":
|
|
62
|
-
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/named/devin-ai/autonomous-swe.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/pluginConfig.schema.json
RENAMED
|
File without changes
|
{gaia_cli-4.3.6 → gaia_cli-4.3.8}/src/gaia_cli/data/registry/schema/realSkillCatalog.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|