@andespindola/crow-skills 0.19.1 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/crow_skills/__init__.py +1 -1
- package/crow_skills/autoupdate.py +75 -0
- package/crow_skills/machine.py +18 -2
- package/package.json +1 -1
- package/release.json +6 -6
package/crow_skills/__init__.py
CHANGED
|
@@ -319,6 +319,71 @@ def broken_projections(agent, home, store, rows):
|
|
|
319
319
|
return [{"name": n, "status": "uniflow_link_modified", "fix": broken_fix(n)} for n in sorted(names)]
|
|
320
320
|
|
|
321
321
|
|
|
322
|
+
|
|
323
|
+
def catalog_gap(agent, home, source, rows, projected, broken):
|
|
324
|
+
"""What this agent is missing from the catalog, and whether upgrade may install it.
|
|
325
|
+
|
|
326
|
+
WHY THIS EXISTS. `upgrade` only ever touched skills already RECORDED for the agent, so a skill
|
|
327
|
+
that is new in the catalog was published and never arrived: 0.17.0 shipped crow-be-human, every
|
|
328
|
+
CLI reported "atualizadas" and `doctor` stayed healthy while the skill existed nowhere on disk.
|
|
329
|
+
A release that the command the user runs cannot deliver is not delivered.
|
|
330
|
+
|
|
331
|
+
Returns {"missing": [names], "install": [names]}. The decision is per skill and read from the
|
|
332
|
+
GROUPS, because "has everything else" is vacuous — the set of missing names is by definition
|
|
333
|
+
everything it does not have. A missing skill is installed when some group of it is otherwise
|
|
334
|
+
COMPLETE here: the user took that group wholesale, and a new member belongs to it. A skill whose
|
|
335
|
+
every group is partial was curated by hand, so it is only reported, with the command.
|
|
336
|
+
"""
|
|
337
|
+
if source is None:
|
|
338
|
+
return {"missing": [], "install": []}
|
|
339
|
+
present = {r.get("name") for r in rows if r.get("name")}
|
|
340
|
+
present |= set(projected or [])
|
|
341
|
+
present |= {k.get("name") for k in (broken or []) if isinstance(k, dict) and k.get("name")}
|
|
342
|
+
missing = sorted(set(source.catalog) - present)
|
|
343
|
+
if not missing:
|
|
344
|
+
return {"missing": [], "install": []}
|
|
345
|
+
try:
|
|
346
|
+
from . import groups as groups_mod
|
|
347
|
+
available = [g for g in groups_mod.load(source) if g["name"] != groups_mod.ALL]
|
|
348
|
+
except (CrowError, OSError, ValueError, KeyError):
|
|
349
|
+
available = []
|
|
350
|
+
install = []
|
|
351
|
+
for name in missing:
|
|
352
|
+
for group in available:
|
|
353
|
+
members = [s for s in group["skills"] if s != name]
|
|
354
|
+
if name in group["skills"] and members and all(m in present for m in members):
|
|
355
|
+
install.append(name)
|
|
356
|
+
break
|
|
357
|
+
return {"missing": missing, "install": install}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def install_new(home, entry, names, agent, dry_run, trust):
|
|
361
|
+
"""Install catalog skills the agent is missing — only when it already has every other one.
|
|
362
|
+
|
|
363
|
+
The decision is read from the machine, not assumed: an agent holding the whole catalog minus the
|
|
364
|
+
new names has said "all of them", so a new skill belongs there. An agent with a SUBSET chose that
|
|
365
|
+
subset, and adding to it would be deciding for the user, so those names are only reported.
|
|
366
|
+
"""
|
|
367
|
+
if not names:
|
|
368
|
+
return
|
|
369
|
+
argv = ["install", *names, "--agents", agent, "--global", "--json", "--home", str(home),
|
|
370
|
+
"--dry-run" if dry_run else "--yes"]
|
|
371
|
+
if not trust:
|
|
372
|
+
argv.append("--no-trust")
|
|
373
|
+
from .cli import parser, run as cli_run
|
|
374
|
+
try:
|
|
375
|
+
data = cli_run(parser().parse_args(argv))
|
|
376
|
+
except CrowError as exc:
|
|
377
|
+
entry["newInCatalogError"] = {"code": exc.code, "message": str(exc)}
|
|
378
|
+
return
|
|
379
|
+
done = [c["name"] for c in data.get("changes") or [] if c.get("action") != "unchanged"]
|
|
380
|
+
# NÃO entra em "updated": verificado com o binário publicado, a linha do CLI dizia as duas coisas
|
|
381
|
+
# da mesma skill — "a atualizar: crow-be-human · instalaria novas: crow-be-human". Instalar agora
|
|
382
|
+
# e atualizar o que já existia são fatos diferentes e cada um tem a sua coluna.
|
|
383
|
+
entry["installedNew"] = sorted(done)
|
|
384
|
+
if data.get("transaction"):
|
|
385
|
+
entry["newTransaction"] = data["transaction"]
|
|
386
|
+
|
|
322
387
|
def reinstall_behaviour(home, entry, dry_run, trust):
|
|
323
388
|
"""Codex without a working crow-behaviour (its projection is broken, no native copy): install it natively,
|
|
324
389
|
as setup does, so the guide and hooks keep a coordinator to point to."""
|
|
@@ -402,6 +467,9 @@ def skills_update(home, trust=True, dry_run=False):
|
|
|
402
467
|
from . import machine
|
|
403
468
|
entry = {"agent": agent, "target": target["path"], "updated": [], "unchanged": 0, "kept": list(broken), "error": None,
|
|
404
469
|
"via_uniflow": projected}
|
|
470
|
+
gap = catalog_gap(agent, home, current, rows, projected, broken)
|
|
471
|
+
if gap["missing"]:
|
|
472
|
+
entry["newInCatalog"] = gap["missing"]
|
|
405
473
|
if dry_run:
|
|
406
474
|
entry["dry_run"] = True
|
|
407
475
|
try:
|
|
@@ -413,6 +481,8 @@ def skills_update(home, trust=True, dry_run=False):
|
|
|
413
481
|
entry["error"] = {"code": exc.code, "message": str(exc)}
|
|
414
482
|
except (OSError, ValueError, KeyError, TypeError):
|
|
415
483
|
entry["error"] = {"code": "io_error", "message": "Falha de leitura/gravação; nada concluído para este agente."}
|
|
484
|
+
if gap["install"] and not entry.get("error"):
|
|
485
|
+
install_new(home, entry, gap["install"], agent, dry_run, trust)
|
|
416
486
|
if agent == "codex" and not entry.get("error"):
|
|
417
487
|
# Ramo mais provável de quem vinha da 0.14.0: no diretório do Codex só há links do
|
|
418
488
|
# Uniflow, e TODAS as cópias do codex estão na pasta compartilhada. Sem migrar aqui,
|
|
@@ -431,6 +501,9 @@ def skills_update(home, trust=True, dry_run=False):
|
|
|
431
501
|
names.append(r["name"])
|
|
432
502
|
entry = {"agent": agent, "target": target["path"], "updated": [], "unchanged": 0, "kept": kept + list(broken),
|
|
433
503
|
"error": None, "via_uniflow": projected}
|
|
504
|
+
gap = catalog_gap(agent, home, current, rows, projected, broken)
|
|
505
|
+
if gap["missing"]:
|
|
506
|
+
entry["newInCatalog"] = gap["missing"]
|
|
434
507
|
if dry_run:
|
|
435
508
|
entry["dry_run"] = True
|
|
436
509
|
if agent == "codex":
|
|
@@ -462,6 +535,8 @@ def skills_update(home, trust=True, dry_run=False):
|
|
|
462
535
|
entry["error"] = {"code": exc.code, "message": str(exc)}
|
|
463
536
|
except (OSError, ValueError, KeyError, TypeError):
|
|
464
537
|
entry["error"] = {"code": "io_error", "message": "Falha de leitura/gravação; nada concluído para este agente."}
|
|
538
|
+
if gap["install"] and not entry.get("error"):
|
|
539
|
+
install_new(home, entry, gap["install"], agent, dry_run, trust)
|
|
465
540
|
if agent == "codex" and not entry.get("error"):
|
|
466
541
|
# Mesma migração do setup, e pelo mesmo motivo: sem isto, uma máquina que só roda
|
|
467
542
|
# autoupdate/upgrade mantém a pasta compartilhada antiga para sempre. Depois da
|
package/crow_skills/machine.py
CHANGED
|
@@ -778,6 +778,17 @@ def upgrade_summary(skills, dry_run):
|
|
|
778
778
|
k["name"] + " --agents " + agent + " --global --yes")
|
|
779
779
|
else:
|
|
780
780
|
item["needs_user"].append(k["name"] + ": " + k["status"])
|
|
781
|
+
# Skill nova no catálogo: instalada quando o agente já tinha todas as outras, e NOMEADA com o
|
|
782
|
+
# comando quando ele tem um subconjunto escolhido. Antes disto o upgrade dizia "atualizadas" e
|
|
783
|
+
# a skill não existia em disco em lugar nenhum.
|
|
784
|
+
item["installed_new"] = s.get("installedNew") or []
|
|
785
|
+
pending = [n for n in (s.get("newInCatalog") or []) if n not in item["installed_new"]]
|
|
786
|
+
if pending:
|
|
787
|
+
item["needs_user"].append(str(len(pending)) + " skill(s) nova(s) no catálogo não instalada(s) aqui (" +
|
|
788
|
+
", ".join(pending[:3]) + "); este agente tem um subconjunto escolhido. Para instalar: "
|
|
789
|
+
"crow-skills install " + " ".join(pending[:3]) + " --agents " + agent + " --global --yes")
|
|
790
|
+
if s.get("newInCatalogError"):
|
|
791
|
+
item["needs_user"].insert(0, "skill nova do catálogo não instalada: " + s["newInCatalogError"]["message"])
|
|
781
792
|
if s.get("error"):
|
|
782
793
|
item["needs_user"].insert(0, s["error"]["message"])
|
|
783
794
|
item["state"] = "fail" if s.get("error") else ("warn" if item["needs_user"] else ("plan" if dry_run and (item["changed"] or item["removed"]) else "ok"))
|
|
@@ -904,9 +915,14 @@ def render_upgrade(data, stream=None):
|
|
|
904
915
|
changed = ", ".join(item["changed"]) if len(item["changed"]) <= 3 else str(len(item["changed"])) + " skills"
|
|
905
916
|
removed = ((" · " if changed else "") + str(len(item["removed"])) + (" a remover" if dry else " removidas") +
|
|
906
917
|
" (duplicavam o Uniflow)") if item.get("removed") else ""
|
|
918
|
+
fresh = item.get("installed_new") or []
|
|
919
|
+
# Dito na própria linha do CLI: uma skill nova do catálogo não é "atualizada", é instalada
|
|
920
|
+
# agora — e foi exatamente isso que faltou quando a 0.17.0 publicou a crow-be-human.
|
|
921
|
+
new_text = ((" · " if changed or removed else "") + (("instalaria " if dry else "instaladas ") +
|
|
922
|
+
("novas: " + ", ".join(fresh[:3]) if len(fresh) <= 3 else str(len(fresh)) + " novas"))) if fresh else ""
|
|
907
923
|
rows.append([ui.mark(STATE_MARK[item["state"]], stream), item["label"],
|
|
908
|
-
((("a atualizar: " if dry else "atualizadas: ") + changed) if changed else ("" if removed else "sem mudanças"))
|
|
909
|
-
+ removed,
|
|
924
|
+
((("a atualizar: " if dry else "atualizadas: ") + changed) if changed else ("" if removed or new_text else "sem mudanças"))
|
|
925
|
+
+ removed + new_text,
|
|
910
926
|
str(item["unchanged"]), str(item.get("via_uniflow", 0)), str(len(item["kept"]))])
|
|
911
927
|
if rows:
|
|
912
928
|
lines.append("")
|
package/package.json
CHANGED
package/release.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"crow_skills/__init__.py": {
|
|
19
19
|
"executable": false,
|
|
20
20
|
"kind": "file",
|
|
21
|
-
"sha256": "
|
|
21
|
+
"sha256": "fe7f42f4b197f8dbf5e96979a3d2d2ef6ff79c7d2b099d45415d8b2a9e96364c"
|
|
22
22
|
},
|
|
23
23
|
"crow_skills/__main__.py": {
|
|
24
24
|
"executable": false,
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"crow_skills/autoupdate.py": {
|
|
34
34
|
"executable": false,
|
|
35
35
|
"kind": "file",
|
|
36
|
-
"sha256": "
|
|
36
|
+
"sha256": "c192495c895a95b957cad04c2d7fc058aa86cd67e76077200fa202d7c96fb039"
|
|
37
37
|
},
|
|
38
38
|
"crow_skills/broadcast.py": {
|
|
39
39
|
"executable": false,
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"crow_skills/machine.py": {
|
|
74
74
|
"executable": false,
|
|
75
75
|
"kind": "file",
|
|
76
|
-
"sha256": "
|
|
76
|
+
"sha256": "83d3f6fa0e296b4de87d90ff90ff50784241cf346bc1947ab78dd415409ebc0d"
|
|
77
77
|
},
|
|
78
78
|
"crow_skills/npm_entry.py": {
|
|
79
79
|
"executable": false,
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"package.json": {
|
|
129
129
|
"executable": false,
|
|
130
130
|
"kind": "file",
|
|
131
|
-
"sha256": "
|
|
131
|
+
"sha256": "1d95441781de5db5184c2962d48feaecb9a8c31b15f1b9afab21c7bbb08e34ca"
|
|
132
132
|
},
|
|
133
133
|
"skills/crow-accessibility/SKILL.md": {
|
|
134
134
|
"executable": false,
|
|
@@ -721,7 +721,7 @@
|
|
|
721
721
|
"sha256": "e37132ef8b060548191fbf14a5a18a9dd3acd84ea9a35e802174455b9e374d3d"
|
|
722
722
|
}
|
|
723
723
|
},
|
|
724
|
-
"revision": "
|
|
724
|
+
"revision": "d8b071036d6d9bda4f894d0764dfca45556ce9ff",
|
|
725
725
|
"schema_version": 1,
|
|
726
|
-
"version": "0.
|
|
726
|
+
"version": "0.20.1"
|
|
727
727
|
}
|