@andespindola/crow-skills 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Skills com escopo explícito, conhecimento especializado preservado e fluxos comuns reutilizáveis.
4
4
 
5
+ **Versão 0.4.0** ([mudanças](CHANGELOG.md)): crow-behaviour com paridade de orquestração, ledger de progresso, `crow-skills progress` e integrações de interface por agente.
6
+
5
7
  **Reconstrução em andamento.** A primeira versão com 32 contratos genéricos não atende à preservação de capacidades solicitada. Não use esse número nem o mapeamento por categoria como prova de substituição das originais. A revisão passa a abranger cada definição e seus recursos, modos e dependências, sem número fixo de skills ou limite artificial de linhas. Veja [critérios e progresso](docs/reconstruction.md).
6
8
 
7
9
  ## Uso
@@ -12,7 +14,7 @@ Skills com escopo explícito, conhecimento especializado preservado e fluxos com
12
14
  4. Entregue o artefato com alvo/revisão, evidência e pendências. Desconhecido não significa aprovado.
13
15
  5. Carregue outra skill só quando houver decisão distinta a tomar. Handoffs são opcionais, não dependências ocultas.
14
16
 
15
- Para adotar uma atuação mais coordenada em qualquer domínio, use `crow-behaviour`: tarefas substantivas recebem plano, delegação suportada, verificação e síntese; conversa e ajustes triviais continuam diretos. A skill adapta o comportamento às ferramentas reais, inclusive execução sequencial, sem depender do Workflow do Claude. Ela não muda o modelo/effort nem força carregamento automático pelo host. [Escopo e evidência](analysis/crow-behaviour.md).
17
+ Para adotar uma atuação mais coordenada em qualquer domínio, use `crow-behaviour`: tarefas substantivas recebem plano, delegação suportada, verificação e síntese; conversa e ajustes triviais continuam diretos. A skill adapta o comportamento às ferramentas reais, inclusive execução sequencial, sem depender do Workflow do Claude. Em tarefa substantiva ela mantém um ledger de progresso (`.crow/runs/`), mostra um resumo por fase no chat e, dentro do tmux, um painel ao vivo com `crow-skills progress --watch`. Ela não muda o modelo/effort nem força carregamento automático pelo host. [Escopo e evidência](analysis/crow-behaviour.md).
16
18
 
17
19
  Exemplos:
18
20
  - Plataforma nova: `crow-platform` coordena; `crow-discovery` e `crow-spec` definem o que falta; as especialistas implementam/verificam por fatia.
@@ -25,9 +27,11 @@ Exemplos:
25
27
 
26
28
  ## Instalação e portabilidade
27
29
 
28
- O pacote de cada skill é somente sua pasta em `skills/`. O [CLI crow-skills](docs/cli.md) instala cópias completas e versionadas em agentes/escopos explícitos, com preview, saída JSON, detecção de conflitos e recuperação. Requer Python 3.10+; Git para fontes em clone, Node 18+ para o launcher npm; mutações em macOS/Linux. A distribuição npm privada `@andespindola/crow-skills` inclui um snapshot completo das skills, sem clone ou hooks que as instalem automaticamente. Em `--global`, instalar a crow-behaviour também grava o guia aditivo do agente (claude, codex, gemini, devin) para usar as skills; `--no-guide` desliga.
30
+ O pacote de cada skill é somente sua pasta em `skills/`. O [CLI crow-skills](docs/cli.md) instala cópias completas e versionadas em agentes/escopos explícitos, com preview, saída JSON, detecção de conflitos e recuperação. Requer Python 3.10+; Git para fontes em clone, Node 18+ para o launcher npm; mutações em macOS/Linux. A distribuição npm privada `@andespindola/crow-skills` inclui um snapshot completo das skills, sem clone ou hooks que as instalem automaticamente. Em `--global`, instalar a crow-behaviour também grava o guia aditivo do agente (claude, codex, gemini, devin) para usar as skills; `--no-guide` desliga. As integrações de interface (status line do Claude Code e do Codex, segmento tmux) seguem o mesmo fluxo, preservando a configuração existente; `--no-integrations` desliga.
29
31
 
30
32
  ```sh
33
+ npm install -g @andespindola/crow-skills@0.4.0
34
+ crow-skills progress
31
35
  ./bin/crow-skills list
32
36
  ./bin/crow-skills install crow-behaviour --agents claude,codex --global --dry-run
33
37
  ./bin/crow-skills install crow-behaviour --agents claude,codex --global --yes --json
@@ -36,7 +40,7 @@ O pacote de cada skill é somente sua pasta em `skills/`. O [CLI crow-skills](do
36
40
 
37
41
  Também é possível copiar/referenciar manualmente as pastas escolhidas no diretório suportado pelo host. Para symlink manual, mantenha o checkout no mesmo caminho. O CLI v0.1 só instala cópias; não sobrescreve conflitos ou edições sem opção explícita e backup.
38
42
 
39
- Os nomes têm prefixo `crow-` para coexistir com o catálogo anterior. Não há instalação global automática, auto-update ou reconfiguração de agentes. O CLI mostra complementaridade por role/owner/handoffs e overlays, mas só instala as selecionadas; `--with-handoffs` é opt-in para handoffs diretos. Operações especializadas podem depender de ferramentas explicitamente documentadas: `crow-interface`, por exemplo, inclui adaptador de motor versionado e instalação explícita dessa dependência. A descoberta depende do runtime; presença do arquivo não prova carregamento.
43
+ Os nomes têm prefixo `crow-` para coexistir com o catálogo anterior. Não há instalação global automática nem auto-update; em `--global`, a única reconfiguração de agentes é o guia aditivo e as integrações de interface, ambos desligáveis (`--no-guide`, `--no-integrations`). O CLI mostra complementaridade por role/owner/handoffs e overlays, mas só instala as selecionadas; `--with-handoffs` é opt-in para handoffs diretos. Operações especializadas podem depender de ferramentas explicitamente documentadas: `crow-interface`, por exemplo, inclui adaptador de motor versionado e instalação explícita dessa dependência. A descoberta depende do runtime; presença do arquivo não prova carregamento.
40
44
 
41
45
  Cada pacote mantém suas referências locais e declara dependências operacionais. Os nomes de próximos responsáveis são sugestões, não downloads implícitos. Sem ferramenta necessária, entregue somente o que puder comprovar e descreva a capacidade não executada, em vez de alegar equivalência.
42
46
 
@@ -1,3 +1,3 @@
1
1
  """Portable, local-first installer for the crow-skills catalogue."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -7,12 +7,14 @@ import sys
7
7
 
8
8
  from . import __version__
9
9
  from . import guide
10
+ from . import integrations
11
+ from . import progress
10
12
  from .agents import AGENTS, destinations, inventory
11
13
  from .source import CrowError, Source, valid_name
12
14
  from .storage import Store, checked
13
15
 
14
16
  DEFAULT_SOURCE = Path(__file__).resolve().parents[1]
15
- MUTATIONS = {"install", "update", "uninstall", "rollback", "recover", "guide"}
17
+ MUTATIONS = {"install", "update", "uninstall", "rollback", "recover", "guide", "integrations"}
16
18
 
17
19
 
18
20
  class Parser(argparse.ArgumentParser):
@@ -54,10 +56,18 @@ def parser():
54
56
  child.add_argument("--with-handoffs", action="store_true", help="Incluir apenas handoffs diretos opcionais do catálogo")
55
57
  child.add_argument("--overwrite-modified", action="store_true", help="Autorizar backup e substituição/remoção de edições locais")
56
58
  child.add_argument("--no-guide", action="store_true", help="Não aplicar/atualizar/remover o guia do agente (--global)")
59
+ child.add_argument("--no-integrations", action="store_true",
60
+ help="Não aplicar/remover as integrações de interface (status line, tmux) em --global")
57
61
  child = sub.add_parser("guide", parents=[common], help="Instruções gerenciadas para o agente usar as skills instaladas")
58
62
  child.add_argument("action", choices=("install", "status", "uninstall"))
59
63
  child.add_argument("--replace", action="store_true", help="Autorizar backup e substituição de conteúdo não gerenciado no caminho do guia")
60
64
  child.add_argument("--overwrite-modified", action="store_true", help="Autorizar backup e substituição/remoção de guia editado")
65
+ child = sub.add_parser("integrations", parents=[common], help="Status line/tmux que mostram a execução crow-behaviour ativa")
66
+ child.add_argument("action", choices=("install", "status", "uninstall"))
67
+ child = sub.add_parser("progress", parents=[common], help="Painel de progresso da execução crow-behaviour (ledger, barras, tokens)")
68
+ child.add_argument("--watch", action="store_true", help="Redesenhar até a execução terminar ou Ctrl-C")
69
+ child.add_argument("--run", help="ID da execução; padrão: a ativa mais recente do diretório")
70
+ child.add_argument("--cwd", help="Diretório da execução; padrão: o atual")
61
71
  for name in ("rollback", "recover"):
62
72
  child = sub.add_parser(name, parents=[common])
63
73
  child.add_argument("--transaction", required=name == "rollback")
@@ -228,6 +238,84 @@ def apply_chain(store, plan, gplan, home):
228
238
  raise
229
239
 
230
240
 
241
+ def behaviour_agents(home, store, plan=None):
242
+ """Agents with a verified guide mechanism that have crow-behaviour after the plan applies."""
243
+ touched = {}
244
+ for item in (plan or {}).get("items", []):
245
+ added, removed = touched.setdefault(os.path.abspath(item["target"]), (set(), set()))
246
+ (removed if item["new"] is None else added).add(item["name"])
247
+ result = []
248
+ for agent in guide.MECHANISMS:
249
+ path = os.path.abspath(destinations(agent, home)[0]["path"])
250
+ added, removed = touched.get(path, (set(), set()))
251
+ if "crow-behaviour" in (set(guide.managed_skills(agent, home, store)) | added) - removed:
252
+ result.append(agent)
253
+ return result
254
+
255
+
256
+ def integrations_preview(args, home, store, plan):
257
+ """Integrations follow the skills in --global unless --no-integrations ("disabled" otherwise)."""
258
+ if getattr(args, "no_integrations", False):
259
+ return {"changes": [], "skipped": [{"integration": "*", "reason": "--no-integrations: integrações não alteradas."}], "disabled": True}
260
+ if getattr(args, "project", None) or getattr(args, "dest", None):
261
+ return {"changes": [], "skipped": [{"integration": "*", "reason": "Integrações de interface só existem no escopo do usuário (--global)."}],
262
+ "disabled": True}
263
+ return integrations.sync(store, home, integration_agents(args, home, store, plan), dry_run=True)
264
+
265
+
266
+ def integration_agents(args, home, store, plan=None, selected=None):
267
+ """Agents whose integrations should exist: crow-behaviour present and either selected now
268
+ or already integrated (like the guide, a shared-root install does not opt codex in)."""
269
+ if selected is None:
270
+ selected = [a for t in destinations(getattr(args, "agents", None), home) for a in t["agents"]] if getattr(args, "agents", None) else []
271
+ try:
272
+ owners = {a for name in integrations.load_state(store)["integrations"] for a in integrations.OWNERS.get(name, ())}
273
+ except CrowError:
274
+ owners = set()
275
+ return [a for a in behaviour_agents(home, store, plan) if a in selected or a in owners]
276
+
277
+
278
+ def integrations_command(args, home, store, interactive):
279
+ if args.action == "status":
280
+ return {"integrations": integrations.status(store, home)}
281
+ if not getattr(args, "global_scope", False):
282
+ raise CrowError("usage", "integrations " + args.action + " exige --global explícito (único escopo suportado).")
283
+ wanted = []
284
+ if args.action == "install":
285
+ wanted = integration_agents(args, home, store) if getattr(args, "agents", None) else behaviour_agents(home, store)
286
+ if args.action == "install" and not wanted:
287
+ raise CrowError("guide_requires_skill", "crow-behaviour não está instalada para nenhum agente; instale antes: "
288
+ "crow-skills install crow-behaviour --agents claude --global --yes")
289
+ preview = integrations.sync(store, home, wanted, dry_run=True)
290
+ if preview["changes"]:
291
+ authorize(args, interactive, preview)
292
+ if getattr(args, "dry_run", False):
293
+ return {**preview, "applied": False}
294
+ return {**integrations.sync(store, home, wanted), "applied": True}
295
+
296
+
297
+ def progress_command(args, home):
298
+ cwd = getattr(args, "cwd", None) or os.getcwd()
299
+ if args.watch:
300
+ if getattr(args, "json", False):
301
+ raise CrowError("usage", "--watch não combina com --json.")
302
+ progress.watch(cwd, home, args.run)
303
+ return None
304
+ run, usage = progress.snapshot(cwd, home, args.run)
305
+ data = progress.as_json(run, usage)
306
+ data["text"] = progress.render(run, usage, color=False, unicode=progress.unicode_ok()) if run else None
307
+ return data
308
+
309
+
310
+ def resync_integrations(home, store):
311
+ """After rollback/recover, drop integrations whose agents no longer have crow-behaviour."""
312
+ try:
313
+ if integrations.load_state(store)["integrations"]:
314
+ integrations.sync(store, home, integration_agents(None, home, store, selected=[]))
315
+ except CrowError:
316
+ pass # Reported by status/doctor; never turns a completed rollback into a failure.
317
+
318
+
231
319
  def linked_guides(store, txid):
232
320
  """Committed guide transactions that completed the given skills transaction."""
233
321
  return [t["id"] for t in store.transactions() if t.get("kind") == "guide" and t.get("linked") == txid and t["phase"] == "committed"]
@@ -247,6 +335,10 @@ def run(args):
247
335
  return {"agents": inventory(home), "note": "Detecção por PATH/diretórios não comprova carregamento da skill."}
248
336
  if args.command == "guide":
249
337
  return guide_command(args, home, store, interactive)
338
+ if args.command == "integrations":
339
+ return integrations_command(args, home, store, interactive)
340
+ if args.command == "progress":
341
+ return progress_command(args, home)
250
342
  if args.command in ("status", "doctor"):
251
343
  targets = scope(args, home, read=True)
252
344
  rows = store.status(targets)
@@ -254,7 +346,11 @@ def run(args):
254
346
  global_view = not (getattr(args, "project", None) or getattr(args, "dest", None))
255
347
  names = [n.strip() for n in getattr(args, "agents", "").split(",")] if getattr(args, "agents", None) else list(guide.MECHANISMS)
256
348
  guides = guide.status(store, home, names) if global_view else []
257
- result = {"installations": rows, "guides": guides, "pending_transactions": pending,
349
+ try:
350
+ ui = integrations.status(store, home) if global_view else []
351
+ except CrowError as exc:
352
+ ui = [{"integration": "*", "status": "unsafe", "note": str(exc)}]
353
+ result = {"installations": rows, "guides": guides, "integrations": ui, "pending_transactions": pending,
258
354
  "activation_verified": False, "reload_hint": "Peça ao host para recarregar/listar skills; reinicie a sessão se necessário."}
259
355
  if args.command == "doctor":
260
356
  result.update(healthy=not pending and all(r["status"] not in ("modified", "missing", "unsafe") for r in rows + guides),
@@ -290,6 +386,7 @@ def run(args):
290
386
  store.rollback(txid, recovery=args.command == "recover")
291
387
  for txid in follow:
292
388
  store.rollback(txid)
389
+ resync_integrations(home, store)
293
390
  return {**summary, "dry_run": getattr(args, "dry_run", False), "applied": not getattr(args, "dry_run", False)}
294
391
  source = None
295
392
  if args.command != "uninstall":
@@ -328,14 +425,22 @@ def run(args):
328
425
  names.extend(n for n in source.catalog[name]["next"] if n not in names)
329
426
  plan = store.plan(args.command, names, targets, source, getattr(args, "replace", False), args.overwrite_modified)
330
427
  guides, gplan = guide_sync(args, home, store, args.command, targets, plan, source)
428
+ ui = integrations_preview(args, home, store, plan)
331
429
  summary = {"changes": plan["changes"], "revision": source.revision if source else None,
332
430
  "relationships": {n: source.related(n) for n in names} if source else {},
333
- "guides": guides, "handoffs_are_optional": True, "activation_verified": False}
334
- if plan["items"] or (gplan and gplan["items"]):
431
+ "guides": guides, "integrations": ui, "handoffs_are_optional": True, "activation_verified": False}
432
+ if plan["items"] or (gplan and gplan["items"]) or ui.get("changes"):
335
433
  authorize(args, interactive, summary)
336
434
  txid = None
337
435
  if not getattr(args, "dry_run", False):
338
436
  txid, guides["transaction"] = apply_chain(store, plan, gplan, home)
437
+ if not ui.get("disabled"):
438
+ # Best effort after the skills/guide transactions; a refusal is reported, never undoes them.
439
+ try:
440
+ summary["integrations"] = integrations.sync(store, home, integration_agents(args, home, store))
441
+ except (CrowError, OSError) as exc:
442
+ summary["integrations"] = {"changes": [], "skipped": [{"integration": "*", "reason": "Integrações não aplicadas (" +
443
+ (str(exc) or "falha de gravação") + "); skills e guia mantidos."}]}
339
444
  return {**summary, "transaction": txid, "dry_run": getattr(args, "dry_run", False),
340
445
  "applied": not getattr(args, "dry_run", False),
341
446
  "reload_hint": ("Somente preview; nada instalado." if getattr(args, "dry_run", False) else
@@ -346,6 +451,9 @@ def emit(command, data, machine):
346
451
  envelope = {"schema_version": 1, "ok": True, "command": command, "data": data}
347
452
  if machine:
348
453
  print(json.dumps(envelope, ensure_ascii=False, separators=(",", ":")))
454
+ elif command == "progress":
455
+ if data is not None:
456
+ print(data["text"] or "crow: nenhuma execução registrada neste diretório.")
349
457
  elif command == "list":
350
458
  print("Fonte: " + data["revision"])
351
459
  for item in data["skills"]: