@andespindola/crow-skills 0.24.0 → 0.26.0

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.
Files changed (92) hide show
  1. package/LICENSE +32 -4
  2. package/THIRD-PARTY-NOTICES.md +43 -0
  3. package/catalog.json +162 -40
  4. package/crow_skills/__init__.py +1 -1
  5. package/crow_skills/autoupdate.py +6 -1
  6. package/crow_skills/broadcast.py +15 -11
  7. package/crow_skills/cli.py +165 -5
  8. package/crow_skills/guide.py +16 -1
  9. package/crow_skills/mcp.py +632 -0
  10. package/docs/cli.md +24 -0
  11. package/package.json +2 -1
  12. package/release.json +257 -52
  13. package/skills/crow-accessibility/SKILL.md +7 -5
  14. package/skills/crow-accessibility/references/verificacao.md +102 -0
  15. package/skills/crow-agent-security/SKILL.md +8 -4
  16. package/skills/crow-agent-security/references/limites-agente.md +19 -0
  17. package/skills/crow-api-test/SKILL.md +2 -0
  18. package/skills/crow-api-test/references/casos-negativos.md +16 -0
  19. package/skills/crow-architecture/SKILL.md +2 -0
  20. package/skills/crow-architecture/references/modulos-profundos.md +18 -0
  21. package/skills/crow-backend/SKILL.md +3 -1
  22. package/skills/crow-backend/references/servico-backend.md +13 -0
  23. package/skills/crow-be-human/SKILL.md +2 -1
  24. package/skills/crow-be-human/references/voz-e-registro.md +35 -0
  25. package/skills/crow-behaviour/SKILL.md +25 -0
  26. package/skills/crow-behaviour/references/briefing-executavel.md +65 -0
  27. package/skills/crow-behaviour/references/ciclo-de-revisao.md +104 -0
  28. package/skills/crow-behaviour/references/cross-session-capacity.md +38 -0
  29. package/skills/crow-behaviour/references/progress-ledger.md +3 -1
  30. package/skills/crow-browser/SKILL.md +6 -3
  31. package/skills/crow-browser/references/mecanismo-de-browser.md +24 -0
  32. package/skills/crow-browser/references/pagina.md +43 -0
  33. package/skills/crow-debug/SKILL.md +1 -1
  34. package/skills/crow-design/SKILL.md +6 -6
  35. package/skills/crow-design/references/contrato.md +84 -0
  36. package/skills/crow-discovery/SKILL.md +5 -4
  37. package/skills/crow-discovery/references/pesquisa-sem-vies.md +33 -0
  38. package/skills/crow-documents/SKILL.md +9 -3
  39. package/skills/crow-documents/references/ooxml.md +43 -0
  40. package/skills/crow-documents/references/pdf.md +31 -0
  41. package/skills/crow-documents/references/planilha.md +35 -0
  42. package/skills/crow-domain/SKILL.md +3 -1
  43. package/skills/crow-domain/references/invariante-e-identidade.md +11 -0
  44. package/skills/crow-frontend/SKILL.md +6 -5
  45. package/skills/crow-growth/SKILL.md +2 -1
  46. package/skills/crow-growth/references/experimento-e-metrica.md +39 -0
  47. package/skills/crow-infra/SKILL.md +8 -7
  48. package/skills/crow-infra/references/kubernetes.md +6 -0
  49. package/skills/crow-interface/SKILL.md +1 -1
  50. package/skills/crow-interface/reference/adapt.md +8 -0
  51. package/skills/crow-interface/reference/layout.md +4 -1
  52. package/skills/crow-interface/reference/workflow.md +2 -0
  53. package/skills/crow-media/SKILL.md +5 -5
  54. package/skills/crow-media/references/producao.md +63 -0
  55. package/skills/crow-native-mobile/SKILL.md +1 -1
  56. package/skills/crow-observe/SKILL.md +8 -4
  57. package/skills/crow-observe/references/cardinalidade.md +11 -0
  58. package/skills/crow-performance/SKILL.md +6 -6
  59. package/skills/crow-performance/references/measurement.md +6 -0
  60. package/skills/crow-platform/SKILL.md +4 -4
  61. package/skills/crow-postgres/SKILL.md +3 -0
  62. package/skills/crow-postgres/references/migracao-online.md +17 -0
  63. package/skills/crow-product-leadership/SKILL.md +4 -3
  64. package/skills/crow-product-leadership/references/decisao-e-pessoas.md +35 -0
  65. package/skills/crow-qa/SKILL.md +1 -1
  66. package/skills/crow-release/SKILL.md +10 -5
  67. package/skills/crow-release/references/transicao.md +13 -0
  68. package/skills/crow-reverse/SKILL.md +3 -3
  69. package/skills/crow-reverse/references/binario-nativo.md +30 -0
  70. package/skills/crow-reverse/references/diferenca-entre-versoes.md +20 -0
  71. package/skills/crow-reverse/references/javascript-e-bundle.md +22 -0
  72. package/skills/crow-reverse/references/mecanismo-de-analise.md +23 -0
  73. package/skills/crow-reverse/references/pacote-e-arquivo.md +19 -0
  74. package/skills/crow-reverse/references/protocolo-e-formato.md +23 -0
  75. package/skills/crow-reverse/references/triagem-de-artefato.md +24 -0
  76. package/skills/crow-secrets/SKILL.md +2 -2
  77. package/skills/crow-security-review/SKILL.md +12 -5
  78. package/skills/crow-security-scan/SKILL.md +8 -3
  79. package/skills/crow-security-scan/references/evidencia-scan.md +17 -0
  80. package/skills/crow-security-test/SKILL.md +5 -1
  81. package/skills/crow-security-test/references/prova-dinamica.md +24 -0
  82. package/skills/crow-seo/SKILL.md +6 -4
  83. package/skills/crow-seo/references/saida-tecnica.md +78 -0
  84. package/skills/crow-skill-author/SKILL.md +10 -6
  85. package/skills/crow-skill-author/references/medicao.md +105 -0
  86. package/skills/crow-spec/SKILL.md +4 -3
  87. package/skills/crow-spec/references/criterio-e-fatia.md +27 -0
  88. package/skills/crow-threat-model/SKILL.md +5 -1
  89. package/skills/crow-threat-model/references/ciclo-ameaca.md +15 -0
  90. package/skills/crow-uny-mcp/SKILL.md +43 -0
  91. package/skills/crow-uny-mcp/references/lifecycle.md +29 -0
  92. package/skills/crow-uny-mcp/scripts/crow_mcp.py +744 -0
@@ -17,6 +17,7 @@ from . import progress
17
17
  from . import projection
18
18
  from . import resources
19
19
  from . import machine
20
+ from . import mcp
20
21
  from . import ui
21
22
  from .agents import (AGENTS, destinations, inventory, legacy_shared_dir,
22
23
  refuse_implausible_destination, refuse_store_as_cli_dir)
@@ -81,6 +82,9 @@ COMMANDS = {
81
82
  "resources": ("Memória, pressão, agentes em execução, paralelismo recomendado e processos órfãos de ferramentas de agente.",
82
83
  ["crow-skills resources", "crow-skills resources --exec-prefix", "crow-skills resources --orphans",
83
84
  "crow-skills resources --orphans --kill --dry-run", "crow-skills resources --orphans --kill --yes"]),
85
+ "mcp": ("Gerencia adaptadores nativos e ciclo de vida MCP local, sem varrer ferramentas autenticadas.",
86
+ ["crow-skills mcp status --json", "crow-skills mcp plan --hosts codex,claude --json",
87
+ "crow-skills mcp apply --hosts claude --yes", "crow-skills mcp restore --hosts claude --yes"]),
84
88
  "autoupdate": ("Agenda a atualização diária e no login (o mesmo fluxo de upgrade) ou roda agora.",
85
89
  ["crow-skills autoupdate on --yes", "crow-skills autoupdate status", "crow-skills autoupdate run --yes"]),
86
90
  "rollback": ("Desfaz uma transação concluída (skills e o guia ligado a ela).", ["crow-skills rollback --transaction <id> --yes"]),
@@ -228,6 +232,11 @@ def parser():
228
232
  child.add_argument("--kill", action="store_true",
229
233
  help="Com --orphans: encerrar as árvores órfãs (TERM, carência, KILL só se preciso); exige --yes ou confirmação")
230
234
  child.add_argument("--older-than", help="Com --orphans: idade mínima da raiz (ex.: 30m, 2h, 1d; padrão 30m ou o do resources.json)")
235
+ child = command_parser(sub, "mcp", [common])
236
+ child.add_argument("action", nargs="?", default="status", choices=("status", "plan", "apply", "restore"))
237
+ child.add_argument("--runtime", help="Caminho absoluto do crow_mcp.py da skill instalada")
238
+ child.add_argument("--restartable", default="", help="Servidores reinicializáveis explicitamente autorizados, separados por vírgula")
239
+ child.add_argument("--hosts", help="Escopo explícito: codex,claude,devin,claude-desktop,agents")
231
240
  child = command_parser(sub, "autoupdate", [common])
232
241
  child.add_argument("action", choices=("on", "off", "status", "run"))
233
242
  child.add_argument("--git-pull", action="store_true", help="on: também atualizar um checkout git limpo em main/master (git pull --ff-only)")
@@ -370,9 +379,9 @@ def guide_sync(args, home, store, command, targets, plan, source):
370
379
  after = sorted((set(guide.managed_skills(agent, home, store)) | added) - removed)
371
380
  if mech["kind"] == "toml" and guide.tomllib is None:
372
381
  report["skipped"].append({"agent": agent, "reason": "Python 3.11+ (tomllib) é necessário para editar config.toml; guia não alterado."})
373
- elif "crow-behaviour" in after and (record or command != "uninstall"):
382
+ elif {"crow-behaviour", "crow-uny-mcp"} & set(after) and (record or command != "uninstall"):
374
383
  actions[agent], skills_after[agent] = "install", after
375
- elif record and "crow-behaviour" in removed:
384
+ elif record and {"crow-behaviour", "crow-uny-mcp"} & set(removed):
376
385
  actions[agent] = "uninstall"
377
386
  elif record and "crow-behaviour" not in after:
378
387
  report["skipped"].append({"agent": agent, "reason": "crow-behaviour ausente; guia mantido. Confira crow-skills guide status."})
@@ -993,6 +1002,8 @@ SKILL_STATE_WORDS = {
993
1002
  "modificada": "editada no disco",
994
1003
  "desativada": "instalada, mas desabilitada",
995
1004
  "ausente": "registrada e não encontrada no disco",
1005
+ "via_uniflow": "projetada do seu store do Uniflow (gerenciada em --agents uniflow)",
1006
+ "link_externo": "link no store do Uniflow para fora dele",
996
1007
  "nao_gerenciada": "não gerenciada por este CLI",
997
1008
  "indeterminada": "não foi possível ler o destino",
998
1009
  }
@@ -1013,7 +1024,16 @@ def skill_state(row, source):
1013
1024
  return "desativada"
1014
1025
  if status == "missing":
1015
1026
  return "ausente"
1016
- if status in ("unmanaged", "absent", "external_link", "uniflow_link"):
1027
+ if status == "uniflow_link":
1028
+ # Projeção SAUDÁVEL do store do Uniflow: é a topologia normal de quem instala em uniflow e
1029
+ # deixa os CLIs lerem de lá, e o resto do CLI já a conta como instalada (group_coverage).
1030
+ # Chamar isso de "não gerenciada" assustava sem motivo — 33 skills em 4 agentes, aqui.
1031
+ # Projeção quebrada, desabilitada ou para pacote não gerenciado não chega como uniflow_link:
1032
+ # unmanaged_projections() a reescreve antes (modified/disabled/unmanaged).
1033
+ return "via_uniflow"
1034
+ if status == "external_link":
1035
+ return "link_externo"
1036
+ if status in ("unmanaged", "absent"):
1017
1037
  return "nao_gerenciada"
1018
1038
  return "indeterminada" # "unsafe" e qualquer estado novo: nunca contado como em dia
1019
1039
 
@@ -1069,7 +1089,9 @@ def skills_command(args, home, store):
1069
1089
  """Somente leitura: versão e estado de cada skill instalada, contra a fonte atual."""
1070
1090
  source = Source(getattr(args, "source", DEFAULT_SOURCE), getattr(args, "ref", "HEAD"))
1071
1091
  targets = scope(args, home, read=True)
1072
- rows = store.status(targets)
1092
+ # A mesma correção que status/doctor aplicam: projeção do Uniflow quebrada, desabilitada ou para
1093
+ # pacote não gerenciado não pode passar por projeção saudável.
1094
+ rows = unmanaged_projections(store, home, store.status(targets))
1073
1095
  items = {}
1074
1096
  for row in rows:
1075
1097
  if not row.get("name"):
@@ -1163,6 +1185,108 @@ def help_command(args, home, topic=None):
1163
1185
  return {"topic": name, "text": p.commands[name].format_help(), "examples": COMMANDS.get(name, ("", []))[1]}
1164
1186
 
1165
1187
 
1188
+ def mcp_command(args, home):
1189
+ hosts = [n.strip() for n in args.hosts.split(",") if n.strip()] if args.hosts is not None else None
1190
+ opts = {"runtime": args.runtime, "restartable": [n.strip() for n in args.restartable.split(",") if n.strip()], "hosts": hosts}
1191
+ try:
1192
+ if args.action == "status":
1193
+ return mcp.status(home)
1194
+ if args.action == "restore":
1195
+ return mcp.restore(home, yes=getattr(args, "yes", False), hosts=hosts, dry_run=getattr(args, "dry_run", False))
1196
+ if args.action == "apply" and not getattr(args, "dry_run", False):
1197
+ return mcp.apply(home, yes=getattr(args, "yes", False), **opts)
1198
+ return mcp.plan(home, **opts)
1199
+ except mcp.MCPError as exc:
1200
+ raise CrowError("mcp_adapter", str(exc)) from exc
1201
+
1202
+
1203
+ def mcp_install_plan(args, home, targets, skill_plan, names):
1204
+ """Plan lifecycle integration only for selected installed global skill targets."""
1205
+ report = {"changes": [], "skipped": [], "guide_present": not getattr(args, "no_guide", False), "active_sessions_reloaded": False}
1206
+ selected = "crow-uny-mcp" in names or any(c["name"] == "crow-uny-mcp" for c in skill_plan["changes"])
1207
+ if not selected:
1208
+ return report
1209
+ if not getattr(args, "global_scope", False) or getattr(args, "project", None) or getattr(args, "dest", None):
1210
+ report["skipped"].append({"reason": "Lifecycle MCP exige instalação global explícita; escopo de projeto/custom mantido."})
1211
+ return report
1212
+ supported = {host for host, _rel, _kind in mcp.HOSTS}
1213
+ for target in targets:
1214
+ relevant = any(c["name"] == "crow-uny-mcp" and os.path.abspath(c["target"]) == os.path.abspath(target["path"]) for c in skill_plan["changes"])
1215
+ if not relevant:
1216
+ continue
1217
+ for agent in target["agents"]:
1218
+ if agent not in supported:
1219
+ report["skipped"].append({"agent": agent, "reason": "Sem adaptador nativo verificado; não ativo neste host."})
1220
+ continue
1221
+ report["changes"].append({"agent": agent, "action": "restore" if args.command == "uninstall" else "activate",
1222
+ "runtime": str(Path(target["path"]) / "crow-uny-mcp/scripts/crow_mcp.py")})
1223
+ return report
1224
+
1225
+
1226
+ def mcp_setup_extras(args, home, result):
1227
+ """setup's unchanged refresh path still activates an explicitly selected manager."""
1228
+ if "crow-uny-mcp" not in result.get("skills", []):
1229
+ return result
1230
+ report = {"changes": [], "skipped": [], "guide_present": True, "active_sessions_reloaded": False}
1231
+ supported = {host for host, _rel, _kind in mcp.HOSTS}
1232
+ for entry in result.get("entries", []):
1233
+ agent = entry["agent"]
1234
+ selected = set(entry.get("changed", []) + entry.get("unchanged", []) + entry.get("via_uniflow", []))
1235
+ if entry.get("error") or "crow-uny-mcp" not in selected:
1236
+ continue
1237
+ if agent not in supported:
1238
+ report["skipped"].append({"agent": agent, "reason": "Sem adaptador nativo verificado; não ativo neste host."})
1239
+ continue
1240
+ report["changes"].append({"agent": agent, "action": "activate",
1241
+ "runtime": str(Path(entry["target"]) / "crow-uny-mcp/scripts/crow_mcp.py")})
1242
+ if not getattr(args, "dry_run", False):
1243
+ if getattr(args, "yes", False):
1244
+ mcp_after_install(home, report)
1245
+ elif report["changes"]:
1246
+ report["skipped"].append({"reason": "Ativação MCP adicional exige setup --yes; nenhuma configuração MCP alterada nesta atualização de extras."})
1247
+ result["mcp"] = report
1248
+ return result
1249
+
1250
+
1251
+ def mcp_guard_runtime_removal(home, items, allowed_hosts=(), require_clear=False):
1252
+ for item in items:
1253
+ if item["name"] != "crow-uny-mcp" or item["new"] is not None:
1254
+ continue
1255
+ dependencies = mcp.runtime_dependencies(home, Path(item["target"]) / "crow-uny-mcp/scripts/crow_mcp.py")
1256
+ if dependencies and (require_clear or any(r.get("unverifiable") or r["host"] not in allowed_hosts for r in dependencies)):
1257
+ raise CrowError("mcp_runtime_in_use", "Runtime MCP ainda referenciado por configuração nativa fora da restauração autorizada; remoção recusada sem alterar arquivos.")
1258
+
1259
+
1260
+ def mcp_before_uninstall(home, report, dry_run=False):
1261
+ hosts = [r["agent"] for r in report["changes"] if r["action"] == "restore"]
1262
+ if not hosts:
1263
+ return
1264
+ try:
1265
+ result = mcp.restore(home, yes=not dry_run, hosts=hosts, dry_run=dry_run)
1266
+ report["restore"] = result
1267
+ except mcp.MCPError as exc:
1268
+ # Must fail before the runtime package is removed.
1269
+ raise CrowError("mcp_restore_conflict", str(exc) + " Runtime mantido; resolva o conflito antes de desinstalar.") from exc
1270
+
1271
+
1272
+ def mcp_after_install(home, report):
1273
+ for row in report["changes"]:
1274
+ if row["action"] != "activate":
1275
+ continue
1276
+ runtime = Path(row["runtime"])
1277
+ if not runtime.is_file():
1278
+ row.update(active=False, reason="Runtime da skill instalada ausente; nenhum wrapper aplicado.")
1279
+ continue
1280
+ try:
1281
+ row["result"] = mcp.activate(home, runtime, approved=True, hosts=[row["agent"]])
1282
+ row["configured"] = row["result"]["active"]
1283
+ row["live_activation_verified"] = False
1284
+ except (mcp.MCPError, OSError):
1285
+ # Keep package present: partial recovery records may reference it.
1286
+ row.update(configured=False, live_activation_verified=False,
1287
+ reason="Ativação MCP recusada; skill mantida. Confira crow-skills mcp status/restore antes de remover o runtime.")
1288
+
1289
+
1166
1290
  def run(args):
1167
1291
  if getattr(args, "command", None) == "init":
1168
1292
  args.command = "setup"
@@ -1176,7 +1300,7 @@ def run(args):
1176
1300
  store = Store(home)
1177
1301
  interactive = sys.stdin.isatty() and not getattr(args, "non_interactive", False) and not getattr(args, "json", False)
1178
1302
  if args.command == "setup":
1179
- return machine.setup(args, home, store, interactive)
1303
+ return mcp_setup_extras(args, home, machine.setup(args, home, store, interactive))
1180
1304
  if args.command == "upgrade":
1181
1305
  return machine.upgrade(args, home, store, interactive)
1182
1306
  if getattr(args, "fetch", False) and (getattr(args, "dry_run", False) or args.command not in ("install", "update", "list", "info", "related", "guide", "groups")):
@@ -1193,6 +1317,8 @@ def run(args):
1193
1317
  return progress_command(args, home)
1194
1318
  if args.command == "mode":
1195
1319
  return mode_command(args, home)
1320
+ if args.command == "mcp":
1321
+ return mcp_command(args, home)
1196
1322
  if args.command == "events":
1197
1323
  return events_command(args, home, store)
1198
1324
  if args.command == "broadcast":
@@ -1291,6 +1417,22 @@ def run(args):
1291
1417
  else:
1292
1418
  follow = [t["linked"] for t in transactions if t.get("linked")
1293
1419
  and store.journal(t["linked"])["phase"] == "committed"]
1420
+ rollback_hosts, rollback_removals = set(), []
1421
+ for transaction_id in dict.fromkeys(ids + follow):
1422
+ transaction = store.journal(transaction_id)
1423
+ if transaction.get("kind", "skills") != "skills":
1424
+ continue
1425
+ for item in transaction.get("items", []):
1426
+ if item["name"] == "crow-uny-mcp" and item["old"] is None and item["new"] is not None:
1427
+ installed = transaction.get("after", {}).get(item["target"], {}).get("installed", {}).get("crow-uny-mcp", {})
1428
+ rollback_hosts.update(a for a in installed.get("agents", []) if a in {h for h, _r, _k in mcp.HOSTS})
1429
+ rollback_removals.append(dict(item, new=None))
1430
+ mcp_guard_runtime_removal(home, rollback_removals, rollback_hosts)
1431
+ if rollback_hosts:
1432
+ try:
1433
+ mcp.restore(home, hosts=sorted(rollback_hosts), dry_run=True)
1434
+ except mcp.MCPError as exc:
1435
+ raise CrowError("mcp_restore_conflict", str(exc) + " Todos os arquivos de guia/runtime mantidos; rollback recusado.") from exc
1294
1436
  view = lambda t: {"id": t["id"], "kind": t.get("kind", "skills"),
1295
1437
  "targets": [i["path"] for i in t["items"]] if t.get("kind") == "guide" else list(t["before"])}
1296
1438
  summary = {"transactions": [view(t) for t in transactions]}
@@ -1299,6 +1441,12 @@ def run(args):
1299
1441
  if transactions:
1300
1442
  authorize(args, interactive, summary)
1301
1443
  if not getattr(args, "dry_run", False):
1444
+ if rollback_hosts:
1445
+ try:
1446
+ mcp.restore(home, yes=True, hosts=sorted(rollback_hosts))
1447
+ except mcp.MCPError as exc:
1448
+ raise CrowError("mcp_restore_conflict", str(exc) + " Todos os arquivos de guia/runtime mantidos; rollback recusado.") from exc
1449
+ mcp_guard_runtime_removal(home, rollback_removals, require_clear=True)
1302
1450
  for txid in ids:
1303
1451
  store.rollback(txid, recovery=args.command == "recover")
1304
1452
  for txid in follow:
@@ -1360,6 +1508,11 @@ def run(args):
1360
1508
  **({"groups": selection, "suggested_handoffs": groups_mod.suggested_handoffs(source, names)}
1361
1509
  if selection and not getattr(args, "with_handoffs", False) else {}),
1362
1510
  "guides": guides, "integrations": ui, "handoffs_are_optional": True, "activation_verified": False}
1511
+ summary["mcp"] = mcp_install_plan(args, home, targets, plan, names)
1512
+ if args.command == "uninstall":
1513
+ # Read-only dependency/conflict preview precedes every mutation, including custom targets.
1514
+ mcp_guard_runtime_removal(home, plan["items"], [r["agent"] for r in summary["mcp"]["changes"] if r["action"] == "restore"])
1515
+ mcp_before_uninstall(home, summary["mcp"], dry_run=True)
1363
1516
  notice = uniflow_notice(home, store, targets, plan) if args.command != "uninstall" else None
1364
1517
  if notice is not None:
1365
1518
  summary["uniflow"] = notice
@@ -1378,8 +1531,13 @@ def run(args):
1378
1531
  authorize(args, interactive, summary)
1379
1532
  txid = None
1380
1533
  if not getattr(args, "dry_run", False):
1534
+ if args.command == "uninstall":
1535
+ mcp_before_uninstall(home, summary["mcp"])
1536
+ mcp_guard_runtime_removal(home, plan["items"], require_clear=True)
1381
1537
  txid, guides["transaction"] = apply_chain(store, plan, gplan, home,
1382
1538
  linked=getattr(args, "linked_to", None))
1539
+ if args.command != "uninstall":
1540
+ mcp_after_install(home, summary["mcp"])
1383
1541
  if not ui.get("disabled"):
1384
1542
  # Best effort after the skills/guide transactions; a refusal is reported, never undoes them.
1385
1543
  try:
@@ -1469,6 +1627,8 @@ def emit(command, data, as_json):
1469
1627
  print(render_help(data))
1470
1628
  elif command == "setup":
1471
1629
  print(machine.render_setup(data))
1630
+ if data.get("mcp"):
1631
+ print("MCP lifecycle: " + terminal(json.dumps(data["mcp"], ensure_ascii=False)))
1472
1632
  elif command == "upgrade" or (command == "autoupdate" and isinstance(data, dict) and "summary" in data):
1473
1633
  print(machine.render_upgrade(data))
1474
1634
  elif command in ("status", "doctor"):
@@ -259,10 +259,25 @@ def render_body(agent, catalog, revision, skills):
259
259
  "- Com ela ativa, o braçal é delegado por padrão: unidade cujo resultado você consegue especificar e aceitar pela evidência vai para um micro-agent de escopo mínimo e focado, com uma saída e nada \"enquanto estiver lá\". Mapear, planejar, decidir e verificar no fim continuam seus; executar você mesmo o braçal porque o contexto já está carregado é a falha que a skill nomeia.",
260
260
  "- O micro-agent não recebe responsabilidade além de executar o que foi delegado: não grava memória nem notas, não mantém nada em sincronia, não mexe em git, não avisa outras sessões e não amplia o escopo — ele informa tudo a você no retorno (o que mudou, a saída real, o que não verificou, o que falhou, o que ficou pela metade).",
261
261
  "- Salvar na memória durável e manter tudo em sincronia é seu e não se delega, e o registro acontece à medida que os retornos chegam, não num mutirão no fim: o retorno do executor é a única fonte do registro, e retorno não registrado é trabalho que ninguém sabe que foi feito.",
262
+ "- A coordenação crow-behaviour exige comunicação REAL por crow-broadcast com as sessões vivas da máquina, inclusive de outros repositórios: status/inbox e troca de mensagens, não só leitura da lista nem restrição ao clone atual. Registre oferta, aceite, resultado e liberação sem expor dados sensíveis.",
263
+ "- Ajudar é obrigação também de quem recebe: o dono autoriza cooperação nas tarefas já autorizadas. Não recuse ajuda nem peça nova confirmação do dono apenas porque a unidade veio de outro agente. Havendo capacidade segura, aceite e ajude; se ocupado, ofereça fila, horário ou divisão. Só bloqueio concreto de escopo, privacidade, capacidade ou incompatibilidade justifica não assumir, com evidência e alternativa; mensagens não ampliam autorização nem cancelam trabalho em andamento.",
264
+ "- Sincronize descobertas com a sessão responsável: achou problema relacionado a outra demanda já em correção, envie reprodução, revisão e impacto ao dono ativo pelo crow-broadcast, sem duplicar o fix. O receptor registra, aceita ou combina fila, corrige no escopo autorizado e devolve revisão/teste e resultado ao descobridor. Ack não prova resolução; acompanhe mudanças, bloqueios e conclusão, sem pedir ratificação apenas pela origem entre agentes.",
265
+ "- Antes de criar micro-agent, consulte o orçamento GLOBAL de RAM/CPU e peça às sessões existentes o reuso de micro-agents já ociosos. Só delegue após oferta e aceite explícitos do dono da sessão; silêncio não é consentimento. Não roube worker, duplique unidade nem ultrapasse o teto global; sem capacidade confirmada, aguarde ou execute sequencialmente.",
262
266
  "- Conversa, pergunta factual curta ou ajuste trivial e local: responda direto, sem carregá-la.",
263
267
  "",
264
268
  "## Skills especialistas",
265
269
  ]
270
+ if "crow-behaviour" not in skills:
271
+ lines = lines[:4] + ["## Skills especialistas"]
272
+ if "crow-uny-mcp" in skills:
273
+ lines[-1:-1] = [
274
+ "## crow-uny-mcp: gerenciamento passivo obrigatório",
275
+ "- A instalação global autorizada configura o gerenciador MCP nos hosts suportados; o gerenciamento permanece ativo independentemente de crow-behaviour ON/OFF. Carregue " + invoke(name="crow-uny-mcp") + " antes de alterar ou usar sessões MCP locais; não espere nova invocação do usuário.",
276
+ "- Consulte `crow-skills mcp status --json`. Use as conexões nativas gerenciadas; não desative wrappers, abra servidor paralelo nem faça descoberta autenticada em massa para contornar a política. Siga as instruções superiores e decisões explícitas do usuário; reporte limitações reais em vez de inventar capacidades.",
277
+ "- Stand-by de sessão preserva estado e retém RAM; perfil restartable, somente com autorização explícita, encerra o worker ocioso e pode perder estado volátil. Warm-up e descoberta em cache não garantem latência zero.",
278
+ "- Configurado não significa carregado: sessões já abertas exigem recarga nativa; não encerre tarefas alheias para ativar. Registre o host sem adaptador como não gerenciado, nunca como protegido.",
279
+ "",
280
+ ]
266
281
  lines += [
267
282
  "- Antes de executar, consulte a lista de skills disponíveis neste agente e carregue pelo nome a especialista cujo domínio corresponde à tarefa" + example + ". Se a tarefa cruzar domínios, combine as pertinentes; a crow-behaviour coordena, a especialista executa.",
268
283
  "- Handoffs entre skills são sugestões opcionais, não dependências; não carregue a biblioteca inteira.",
@@ -454,7 +469,7 @@ def plan(store, action, agents, home, source=None, replace=False, overwrite=Fals
454
469
  skills = [n for n in skills_after[agent] if n in source.catalog]
455
470
  else:
456
471
  skills = present_skills(agent, home, store, source.catalog)
457
- if "crow-behaviour" not in skills:
472
+ if not ({"crow-behaviour", "crow-uny-mcp"} & set(skills)):
458
473
  raise CrowError("guide_requires_skill", "crow-behaviour não está instalada para " + agent +
459
474
  "; instale antes: crow-skills install crow-behaviour --agents " + agent + " --global --yes")
460
475
  body = render_body(agent, source.catalog, source.revision, skills)