@andespindola/crow-skills 0.7.0 → 0.8.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 (50) hide show
  1. package/README.md +6 -5
  2. package/crow_skills/__init__.py +1 -1
  3. package/crow_skills/autoupdate.py +398 -0
  4. package/crow_skills/cli.py +89 -3
  5. package/crow_skills/events.py +111 -0
  6. package/crow_skills/guide.py +7 -1
  7. package/crow_skills/hook.py +560 -0
  8. package/crow_skills/integrations.py +427 -63
  9. package/crow_skills/progress.py +106 -14
  10. package/crow_skills/source.py +40 -0
  11. package/crow_skills/storage.py +5 -4
  12. package/docs/cli.md +96 -17
  13. package/package.json +1 -1
  14. package/release.json +63 -48
  15. package/skills/crow-accessibility/SKILL.md +5 -5
  16. package/skills/crow-agent-security/SKILL.md +5 -5
  17. package/skills/crow-api-test/SKILL.md +5 -5
  18. package/skills/crow-architecture/SKILL.md +5 -5
  19. package/skills/crow-backend/SKILL.md +5 -5
  20. package/skills/crow-behaviour/SKILL.md +3 -3
  21. package/skills/crow-behaviour/references/progress-ledger.md +17 -3
  22. package/skills/crow-behaviour/scripts/crow_ledger.py +45 -2
  23. package/skills/crow-browser/SKILL.md +5 -5
  24. package/skills/crow-debug/SKILL.md +5 -5
  25. package/skills/crow-design/SKILL.md +5 -5
  26. package/skills/crow-discovery/SKILL.md +5 -5
  27. package/skills/crow-documents/SKILL.md +5 -5
  28. package/skills/crow-domain/SKILL.md +5 -5
  29. package/skills/crow-frontend/SKILL.md +5 -5
  30. package/skills/crow-growth/SKILL.md +5 -5
  31. package/skills/crow-infra/SKILL.md +5 -5
  32. package/skills/crow-interface/SKILL.md +2 -2
  33. package/skills/crow-media/SKILL.md +5 -5
  34. package/skills/crow-native-mobile/SKILL.md +5 -5
  35. package/skills/crow-observe/SKILL.md +6 -6
  36. package/skills/crow-performance/SKILL.md +5 -5
  37. package/skills/crow-platform/SKILL.md +5 -5
  38. package/skills/crow-postgres/SKILL.md +5 -5
  39. package/skills/crow-product-leadership/SKILL.md +5 -5
  40. package/skills/crow-qa/SKILL.md +5 -5
  41. package/skills/crow-release/SKILL.md +5 -5
  42. package/skills/crow-reverse/SKILL.md +5 -5
  43. package/skills/crow-secrets/SKILL.md +5 -5
  44. package/skills/crow-security-review/SKILL.md +5 -5
  45. package/skills/crow-security-scan/SKILL.md +5 -5
  46. package/skills/crow-security-test/SKILL.md +5 -5
  47. package/skills/crow-seo/SKILL.md +5 -5
  48. package/skills/crow-skill-author/SKILL.md +5 -5
  49. package/skills/crow-spec/SKILL.md +5 -5
  50. package/skills/crow-threat-model/SKILL.md +5 -5
@@ -0,0 +1,111 @@
1
+ """Read side of the event bus: ``crow-skills events snapshot|tail``.
2
+
3
+ The hooks (crow_hook.py, see hook.py) append ``crow-event/1`` lines and keep
4
+ one ``crow-session/1`` snapshot per session. This module aggregates them with
5
+ the mode, the live crow-behaviour ledger runs and the autoupdate status into
6
+ ``crow-snapshot/1``, the document terminals such as crow-term poll. The
7
+ contract is in docs/events.md.
8
+ """
9
+ from datetime import datetime, timezone
10
+ import json
11
+ import sys
12
+ import time
13
+
14
+ from . import autoupdate, hook, progress
15
+ from .source import CrowError
16
+
17
+ SNAPSHOT_SCHEMA = "crow-snapshot/1"
18
+
19
+
20
+ def ledger_runs(home, cwds):
21
+ """Live crow-behaviour runs for the sessions' directories and ~/.crow/runs."""
22
+ seen, runs = set(), []
23
+ for cwd in list(dict.fromkeys(cwds)) or [str(home)]:
24
+ for run in progress.discover(cwd, home, env={}):
25
+ if run["path"] in seen or not progress.active(run):
26
+ continue
27
+ seen.add(run["path"])
28
+ info = progress.summary(run)
29
+ runs.append({"id": run["id"], "title": run["title"], "host": run["host"], "cwd": run["cwd"], "status": run["status"],
30
+ "session": run["session"].get("id"), "path": run["path"], "updatedAt": run.get("updatedAt"),
31
+ "done": info["done"], "failed": info["failed"], "total": info["total"], "running": info["running"],
32
+ "progress": info["progress"],
33
+ "agents": [{"owner": progress.owner_label(u["owner"]), "unit": u["id"], "activity": u["title"]}
34
+ for u in run["units"] if u["status"] == "running"]})
35
+ return runs
36
+
37
+
38
+ def snapshot(home, store):
39
+ live = hook.sessions(home)
40
+ runs = ledger_runs(home, [s.get("cwd") for s in live if s.get("cwd")])
41
+ for session in live:
42
+ # Link a session to its ledger run: same session id, else the same directory.
43
+ match = [r for r in runs if r["session"] and r["session"] == session.get("session")] or \
44
+ [r for r in runs if not r["session"] and r["cwd"] and session.get("cwd") and progress.related({"cwd": r["cwd"]}, session["cwd"])]
45
+ session["ledgerRun"] = match[0]["id"] if match else None
46
+ try:
47
+ update = autoupdate.status(store, home)
48
+ update = {k: update[k] for k in ("enabled", "lastRun", "healthy")} | {"scheduler": (update.get("scheduler") or {}).get("kind")}
49
+ if update["lastRun"]:
50
+ last = update["lastRun"]
51
+ update["lastRun"] = {"startedAt": last.get("startedAt"), "endedAt": last.get("endedAt"), "ok": last.get("ok"),
52
+ "self": (last.get("self") or {}).get("result"),
53
+ "updated": sum(len(s.get("updated") or []) for s in last.get("skills", []))}
54
+ except CrowError as exc:
55
+ update = {"enabled": None, "error": exc.code}
56
+ mode = hook.read_mode(home)
57
+ return {"schema": SNAPSHOT_SCHEMA, "generatedAt": datetime.now(timezone.utc).isoformat(timespec="seconds"),
58
+ "mode": {k: mode[k] for k in ("mode", "updatedAt", "updatedBy", "source")}, "indicator": hook.indicator(mode["mode"]),
59
+ "sessions": live, "runs": runs, "autoupdate": update}
60
+
61
+
62
+ def render(data, unicode=True):
63
+ lines = [data["indicator"] + " · " + str(len(data["sessions"])) + " sessão(ões) ativa(s) · " +
64
+ str(len(data["runs"])) + " execução(ões) crow"]
65
+ for s in data["sessions"]:
66
+ flag = " [bloqueada]" if s.get("blocked") else ""
67
+ lines.append("- " + " ".join(str(x) for x in (s.get("host"), s.get("state"), s.get("cwd") or "", s.get("branch") or "") if x) +
68
+ flag + (" tool=" + s["currentTool"] if s.get("currentTool") else "") +
69
+ (" subagentes=" + str(s["subagents"]) if s.get("subagents") else ""))
70
+ for run in data["runs"]:
71
+ lines.append("")
72
+ lines += progress.agent_rows(progress.validate({"id": run["id"], "title": run["title"], "status": run["status"],
73
+ "units": [{"id": a["unit"], "title": a["activity"], "owner": a["owner"],
74
+ "status": "running"} for a in run["agents"]]}),
75
+ unicode, counts=False)
76
+ lines.append(("✓ " if unicode else "ok ") + str(run["done"]) + "/" + str(run["total"]) + " " + run["title"])
77
+ update = data["autoupdate"]
78
+ lines.append("")
79
+ lines.append("autoupdate: " + ("ligado" if update.get("enabled") else "desligado") +
80
+ ((" · última execução " + str(update["lastRun"]["startedAt"]) + (" ok" if update["lastRun"]["ok"] else " com falha"))
81
+ if update.get("lastRun") else ""))
82
+ return "\n".join(lines)
83
+
84
+
85
+ def follow(home, interval=0.5, stream=None, max_seconds=None):
86
+ """Stream new crow-event/1 lines (JSONL) until interrupted."""
87
+ stream = stream or sys.stdout
88
+ path = hook.events_dir(home) / "events.jsonl"
89
+ position = path.stat().st_size if path.exists() else 0
90
+ started = time.time()
91
+ try:
92
+ while max_seconds is None or time.time() - started < max_seconds:
93
+ if path.exists():
94
+ size = path.stat().st_size
95
+ if size < position: # rotated
96
+ position = 0
97
+ if size > position:
98
+ with open(path, "rb") as handle:
99
+ handle.seek(position)
100
+ chunk = handle.read(size - position)
101
+ position = size
102
+ for raw in chunk.splitlines():
103
+ try:
104
+ stream.write(json.dumps(json.loads(raw), ensure_ascii=False, separators=(",", ":")) + "\n")
105
+ except ValueError:
106
+ continue
107
+ stream.flush()
108
+ time.sleep(interval)
109
+ except KeyboardInterrupt:
110
+ return 0
111
+ return 0
@@ -201,7 +201,13 @@ def render_body(agent, catalog, revision, skills):
201
201
  "",
202
202
  "Gerado por crow-skills guide a partir do catálogo " + revision[:12] + ". Complementa as instruções do usuário e do projeto; havendo conflito, elas prevalecem.",
203
203
  "",
204
- "## crow-behaviour: quando aplicar",
204
+ "## Modo crow (liga/desliga)",
205
+ "- O usuário liga ou desliga a crow-behaviour para todas as sessões e CLIs com `crow-skills mode on|off` (ou `/crow on|off`). O estado fica em `~/.config/crow-skills/mode.json`; sem esse arquivo, o modo é ON.",
206
+ "- Obedeça ao modo vigente mais recente: o hook do crow-skills o informa a cada prompt numa linha `[crow-skills] modo crow: ON|OFF`. Sem essa linha (hook ausente ou não aprovado), leia o arquivo uma vez no início da sessão.",
207
+ "- ON é o modo de orquestração permanente: as regras abaixo valem para toda tarefa substantiva, até o usuário desligar.",
208
+ "- OFF: não carregue nem aplique a crow-behaviour, salvo se o usuário a invocar explicitamente no pedido; as skills especialistas continuam disponíveis.",
209
+ "",
210
+ "## crow-behaviour: quando aplicar (modo ON)",
205
211
  "- Tarefa substantiva (várias etapas ou partes, exploração e decisões, mudanças em vários arquivos ou componentes, ou entrega que exige verificação): carregue a skill crow-behaviour ANTES de começar, por iniciativa própria, sem esperar pedido. Para carregar, " + mech["load"] + "; por exemplo, " + invoke(name="crow-behaviour") + ". Siga-a até a entrega.",
206
212
  "- Vale também quando a entrega é só um plano, um diagnóstico ou uma revisão, ou quando não há código no diretório: depurar, auditar, investigar, migrar ou planejar uma mudança com várias etapas continua sendo tarefa substantiva.",
207
213
  "- Conversa, pergunta factual curta ou ajuste trivial e local: responda direto, sem carregá-la.",