@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.
- package/README.md +6 -5
- package/crow_skills/__init__.py +1 -1
- package/crow_skills/autoupdate.py +398 -0
- package/crow_skills/cli.py +89 -3
- package/crow_skills/events.py +111 -0
- package/crow_skills/guide.py +7 -1
- package/crow_skills/hook.py +560 -0
- package/crow_skills/integrations.py +427 -63
- package/crow_skills/progress.py +106 -14
- package/crow_skills/source.py +40 -0
- package/crow_skills/storage.py +5 -4
- package/docs/cli.md +96 -17
- package/package.json +1 -1
- package/release.json +63 -48
- package/skills/crow-accessibility/SKILL.md +5 -5
- package/skills/crow-agent-security/SKILL.md +5 -5
- package/skills/crow-api-test/SKILL.md +5 -5
- package/skills/crow-architecture/SKILL.md +5 -5
- package/skills/crow-backend/SKILL.md +5 -5
- package/skills/crow-behaviour/SKILL.md +3 -3
- package/skills/crow-behaviour/references/progress-ledger.md +17 -3
- package/skills/crow-behaviour/scripts/crow_ledger.py +45 -2
- package/skills/crow-browser/SKILL.md +5 -5
- package/skills/crow-debug/SKILL.md +5 -5
- package/skills/crow-design/SKILL.md +5 -5
- package/skills/crow-discovery/SKILL.md +5 -5
- package/skills/crow-documents/SKILL.md +5 -5
- package/skills/crow-domain/SKILL.md +5 -5
- package/skills/crow-frontend/SKILL.md +5 -5
- package/skills/crow-growth/SKILL.md +5 -5
- package/skills/crow-infra/SKILL.md +5 -5
- package/skills/crow-interface/SKILL.md +2 -2
- package/skills/crow-media/SKILL.md +5 -5
- package/skills/crow-native-mobile/SKILL.md +5 -5
- package/skills/crow-observe/SKILL.md +6 -6
- package/skills/crow-performance/SKILL.md +5 -5
- package/skills/crow-platform/SKILL.md +5 -5
- package/skills/crow-postgres/SKILL.md +5 -5
- package/skills/crow-product-leadership/SKILL.md +5 -5
- package/skills/crow-qa/SKILL.md +5 -5
- package/skills/crow-release/SKILL.md +5 -5
- package/skills/crow-reverse/SKILL.md +5 -5
- package/skills/crow-secrets/SKILL.md +5 -5
- package/skills/crow-security-review/SKILL.md +5 -5
- package/skills/crow-security-scan/SKILL.md +5 -5
- package/skills/crow-security-test/SKILL.md +5 -5
- package/skills/crow-seo/SKILL.md +5 -5
- package/skills/crow-skill-author/SKILL.md +5 -5
- package/skills/crow-spec/SKILL.md +5 -5
- 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
|
package/crow_skills/guide.py
CHANGED
|
@@ -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
|
|
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.",
|