@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 +7 -3
- package/crow_skills/__init__.py +1 -1
- package/crow_skills/cli.py +112 -4
- package/crow_skills/integrations.py +520 -0
- package/crow_skills/progress.py +667 -0
- package/docs/cli.md +61 -3
- package/package.json +1 -1
- package/release.json +31 -11
- package/skills/crow-behaviour/SKILL.md +23 -18
- package/skills/crow-behaviour/agents/openai.yaml +1 -1
- package/skills/crow-behaviour/references/orchestration.md +19 -9
- package/skills/crow-behaviour/references/progress-ledger.md +83 -0
- package/skills/crow-behaviour/references/quality-patterns.md +22 -2
- package/skills/crow-behaviour/scripts/crow_ledger.py +279 -0
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
|
|
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
|
|
package/crow_skills/__init__.py
CHANGED
package/crow_skills/cli.py
CHANGED
|
@@ -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
|
-
|
|
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"]:
|