@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
package/crow_skills/progress.py
CHANGED
|
@@ -31,6 +31,20 @@ MAX_LEDGER = 2 * 1024 * 1024
|
|
|
31
31
|
MAX_LOG_LINE = 8 * 1024 * 1024
|
|
32
32
|
RECENT_DONE = 300 # seconds a finished run stays visible in compact views
|
|
33
33
|
STALE = 2 * 3600 # a "running" ledger untouched for this long is treated as abandoned
|
|
34
|
+
MAX_AGENT_ROWS = 6 # process rows shown in a status line before "+N"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def hook_module():
|
|
38
|
+
"""The mode/event helpers: the package module, or crow_hook.py copied beside this script."""
|
|
39
|
+
try:
|
|
40
|
+
from . import hook # type: ignore[attr-defined]
|
|
41
|
+
return hook
|
|
42
|
+
except ImportError:
|
|
43
|
+
try:
|
|
44
|
+
import crow_hook as hook # sibling copy in the integrations directory
|
|
45
|
+
return hook
|
|
46
|
+
except ImportError:
|
|
47
|
+
return None
|
|
34
48
|
|
|
35
49
|
|
|
36
50
|
# --- ledger -------------------------------------------------------------------------
|
|
@@ -501,6 +515,7 @@ def render(run, usage=None, now=None, color=False, unicode=True, width=20):
|
|
|
501
515
|
bar(info["progress"], width, unicode) + " " + str(int(info["progress"] * 100)).rjust(3) + "% " +
|
|
502
516
|
str(info["done"]) + "/" + str(info["total"]) + " unidades" +
|
|
503
517
|
(" - " + str(info["failed"]) + " falha(s)" if info["failed"] else "") + " " + token_text(usage, unicode)]
|
|
518
|
+
lines += ["", *agent_rows(run, unicode, counts=False), ""]
|
|
504
519
|
phase_ids = [p["id"] for p in run["phases"]]
|
|
505
520
|
groups = [(p, [u for u in run["units"] if u["phase"] == p["id"]]) for p in run["phases"]]
|
|
506
521
|
loose = [u for u in run["units"] if u["phase"] not in phase_ids]
|
|
@@ -527,6 +542,50 @@ def render(run, usage=None, now=None, color=False, unicode=True, width=20):
|
|
|
527
542
|
return "\n".join(lines)
|
|
528
543
|
|
|
529
544
|
|
|
545
|
+
def owner_label(owner):
|
|
546
|
+
"""subagent:leitor -> leitor; workflow:x and sequential stay as written."""
|
|
547
|
+
owner = owner or "main"
|
|
548
|
+
return owner.split(":", 1)[1] if owner.startswith("subagent:") and owner[9:] else owner
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def agent_rows(run, unicode=True, limit=None, counts=True):
|
|
552
|
+
"""Live process list, like the host's own agent list:
|
|
553
|
+
|
|
554
|
+
● main <what the coordinator is doing>
|
|
555
|
+
○ <owner> <running unit>
|
|
556
|
+
✓ 3 concluídas · ✗ 1 falhou · 2 pendentes
|
|
557
|
+
|
|
558
|
+
Rows come only from the ledger (units the coordinator recorded); nothing is invented.
|
|
559
|
+
"""
|
|
560
|
+
dot, ring = ("●", "○") if unicode else ("*", "o")
|
|
561
|
+
sep = " · " if unicode else " - "
|
|
562
|
+
running = [u for u in run["units"] if u["status"] == "running"]
|
|
563
|
+
mine = [u for u in running if owner_label(u["owner"]) in ("main", "sequential")]
|
|
564
|
+
others = [u for u in running if u not in mine]
|
|
565
|
+
head = dot + " main"
|
|
566
|
+
if mine:
|
|
567
|
+
head += " " + safe(mine[0]["title"], 60) + (" (+" + str(len(mine) - 1) + ")" if len(mine) > 1 else "")
|
|
568
|
+
elif run["status"] == "running":
|
|
569
|
+
head += " " + safe(run["title"], 60)
|
|
570
|
+
lines = [head]
|
|
571
|
+
shown = others if limit is None else others[:limit]
|
|
572
|
+
for unit in shown:
|
|
573
|
+
lines.append(ring + " " + safe(owner_label(unit["owner"]), 32) + " " + safe(unit["title"], 60))
|
|
574
|
+
if limit is not None and len(others) > limit:
|
|
575
|
+
lines.append(ring + " +" + str(len(others) - limit))
|
|
576
|
+
if counts:
|
|
577
|
+
done = sum(u["status"] == "done" for u in run["units"])
|
|
578
|
+
failed = sum(u["status"] == "failed" for u in run["units"])
|
|
579
|
+
pending = sum(u["status"] == "pending" for u in run["units"])
|
|
580
|
+
parts = [("✓ " if unicode else "ok ") + str(done) + " concluída(s)"]
|
|
581
|
+
if failed:
|
|
582
|
+
parts.append(("✗ " if unicode else "x ") + str(failed) + " falha(s)")
|
|
583
|
+
if pending:
|
|
584
|
+
parts.append(str(pending) + " pendente(s)")
|
|
585
|
+
lines.append(sep.join(parts))
|
|
586
|
+
return lines
|
|
587
|
+
|
|
588
|
+
|
|
530
589
|
def compact(run, usage=None, unicode=True, width=10):
|
|
531
590
|
info = summary(run)
|
|
532
591
|
mark = "✓" if run["status"] == "done" else ("✗" if run["status"] in ("failed", "cancelled") else "▸")
|
|
@@ -580,19 +639,41 @@ def watch(cwd, home, run_id=None, interval=1.0, stream=None, linger=5.0, max_fra
|
|
|
580
639
|
return 0
|
|
581
640
|
|
|
582
641
|
|
|
583
|
-
def
|
|
584
|
-
|
|
642
|
+
def mode_text(home, unicode=True):
|
|
643
|
+
hook = hook_module()
|
|
644
|
+
if hook is None:
|
|
645
|
+
return ""
|
|
646
|
+
try:
|
|
647
|
+
return hook.indicator(hook.read_mode(home)["mode"], unicode)
|
|
648
|
+
except (OSError, ValueError, KeyError):
|
|
649
|
+
return ""
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def statusline(home, stdin_text, chain=None, host="claude"):
|
|
653
|
+
"""Status line for Claude Code and Antigravity (agy).
|
|
654
|
+
|
|
655
|
+
First line: the previous (chained) status line, the mode indicator and the compact
|
|
656
|
+
run; then one row per running unit of the ledger, like the host's agent list. agy
|
|
657
|
+
stacks it under its default line (stack_with_default), so no chain is needed there.
|
|
658
|
+
"""
|
|
585
659
|
try:
|
|
586
660
|
payload = json.loads(stdin_text) if stdin_text.strip() else {}
|
|
587
661
|
except ValueError:
|
|
588
662
|
payload = {}
|
|
589
663
|
if not isinstance(payload, dict):
|
|
590
664
|
payload = {}
|
|
591
|
-
|
|
665
|
+
workspace = payload.get("workspace") if isinstance(payload.get("workspace"), dict) else {}
|
|
666
|
+
cwd = workspace.get("current_dir") or payload.get("cwd") or os.getcwd()
|
|
592
667
|
transcript = payload.get("transcript_path")
|
|
593
|
-
session = payload.get("session_id")
|
|
668
|
+
session = payload.get("session_id") or payload.get("conversation_id")
|
|
594
669
|
if not (isinstance(session, str) and session):
|
|
595
670
|
session = Path(transcript).stem if isinstance(transcript, str) and transcript.endswith(".jsonl") else None
|
|
671
|
+
hook = hook_module()
|
|
672
|
+
if hook is not None:
|
|
673
|
+
try:
|
|
674
|
+
hook.update_metrics(home, host, payload)
|
|
675
|
+
except Exception: # noqa: BLE001 - metrics never break the status line
|
|
676
|
+
pass
|
|
596
677
|
previous = ""
|
|
597
678
|
if chain:
|
|
598
679
|
try:
|
|
@@ -603,9 +684,10 @@ def statusline(home, stdin_text, chain=None):
|
|
|
603
684
|
except (OSError, subprocess.SubprocessError):
|
|
604
685
|
previous = ""
|
|
605
686
|
run = select(discover(cwd, home), cwd, strict=True, session=session)
|
|
606
|
-
|
|
687
|
+
parts = [p for p in (mode_text(home),) if p]
|
|
688
|
+
rows = []
|
|
607
689
|
if active(run):
|
|
608
|
-
if run["host"] == "claude" and not run["session"].get("id") and session:
|
|
690
|
+
if run["host"] == "claude" and host == "claude" and not run["session"].get("id") and session:
|
|
609
691
|
# The status line knows its own session: exact usage instead of every session in the directory.
|
|
610
692
|
try:
|
|
611
693
|
usage = claude_tokens(dict(run, session={"id": session}), Path(home), *window(run))
|
|
@@ -613,19 +695,28 @@ def statusline(home, stdin_text, chain=None):
|
|
|
613
695
|
usage = None
|
|
614
696
|
else:
|
|
615
697
|
usage = tokens(run, home)
|
|
616
|
-
|
|
698
|
+
parts.append(compact(run, usage))
|
|
699
|
+
if run["status"] == "running":
|
|
700
|
+
rows = agent_rows(run, limit=MAX_AGENT_ROWS, counts=False)
|
|
701
|
+
if len(rows) == 1:
|
|
702
|
+
rows = [] # only "● main": the compact segment already says it
|
|
703
|
+
ours = " │ ".join(parts)
|
|
617
704
|
if not previous.strip():
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
705
|
+
first_line = ours
|
|
706
|
+
elif not ours:
|
|
707
|
+
first_line = previous
|
|
708
|
+
else:
|
|
709
|
+
first_line = previous + " │ " + ours
|
|
710
|
+
return "\n".join([first_line, *rows]) if rows else first_line
|
|
622
711
|
|
|
623
712
|
|
|
624
713
|
def tmux_segment(home, cwd):
|
|
714
|
+
mode = mode_text(home)
|
|
625
715
|
run = select(discover(cwd, home), cwd, strict=True)
|
|
626
716
|
if not active(run):
|
|
627
|
-
return
|
|
628
|
-
|
|
717
|
+
return mode
|
|
718
|
+
text = compact(run, tokens(run, home), unicode=True, width=8)
|
|
719
|
+
return ((mode + " · " if mode else "") + text).replace("#", "##")
|
|
629
720
|
|
|
630
721
|
|
|
631
722
|
def main(argv=None):
|
|
@@ -636,6 +727,7 @@ def main(argv=None):
|
|
|
636
727
|
p.add_argument("--cwd", default=None)
|
|
637
728
|
p.add_argument("--home", default=None)
|
|
638
729
|
p.add_argument("--chain-file", default=None, help="JSON com o comando de status line anterior, encadeado antes do crow")
|
|
730
|
+
p.add_argument("--host", default="claude", choices=("claude", "agy"), help="CLI que chama a status line")
|
|
639
731
|
args = p.parse_args(argv)
|
|
640
732
|
home = Path(args.home).expanduser() if args.home else Path.home()
|
|
641
733
|
cwd = args.cwd or os.getcwd()
|
|
@@ -646,7 +738,7 @@ def main(argv=None):
|
|
|
646
738
|
chain = json.loads(Path(args.chain_file).read_text(encoding="utf-8")).get("command")
|
|
647
739
|
except (OSError, ValueError, AttributeError):
|
|
648
740
|
chain = None
|
|
649
|
-
print(statusline(home, sys.stdin.read() if not sys.stdin.isatty() else "", chain))
|
|
741
|
+
print(statusline(home, sys.stdin.read() if not sys.stdin.isatty() else "", chain, args.host))
|
|
650
742
|
return 0
|
|
651
743
|
if args.mode == "tmux":
|
|
652
744
|
print(tmux_segment(home, cwd))
|
package/crow_skills/source.py
CHANGED
|
@@ -8,6 +8,11 @@ import stat
|
|
|
8
8
|
import subprocess
|
|
9
9
|
|
|
10
10
|
NAME = re.compile(r"crow-[a-z0-9]+(?:-[a-z0-9]+)*\Z")
|
|
11
|
+
PACKAGE = "@andespindola/crow-skills"
|
|
12
|
+
# Where a packaged release (npm tarball, also used by the Homebrew formula) lives. The
|
|
13
|
+
# version is part of the Homebrew path and npx caches, so it changes on every upgrade.
|
|
14
|
+
PACKAGED = (re.compile(r"/Cellar/crow-skills/[^/]+/libexec\Z"), re.compile(r"/node_modules/@andespindola/crow-skills\Z"),
|
|
15
|
+
re.compile(r"/_npx/[^/]+/node_modules/@andespindola/crow-skills\Z"))
|
|
11
16
|
SHA = re.compile(r"[a-f0-9]{40,64}\Z")
|
|
12
17
|
MAX_FILE = 8 * 1024 * 1024
|
|
13
18
|
MAX_PACKAGE = 32 * 1024 * 1024
|
|
@@ -80,7 +85,9 @@ class Source:
|
|
|
80
85
|
if ref not in ("HEAD", self.revision):
|
|
81
86
|
raise CrowError("immutable_release", "A referência não está neste snapshot npm; selecione um clone Git para outras refs.")
|
|
82
87
|
raw = self.bundle_file("catalog.json")
|
|
88
|
+
self.identity = self._bundle_identity()
|
|
83
89
|
else:
|
|
90
|
+
self.identity = "git:" + str(self.path)
|
|
84
91
|
if fetch:
|
|
85
92
|
self.git("fetch", "--no-recurse-submodules", "origin", timeout=120)
|
|
86
93
|
self.revision = self.git("rev-parse", "--verify", "--end-of-options", ref + "^{commit}").decode().strip()
|
|
@@ -138,6 +145,18 @@ class Source:
|
|
|
138
145
|
except (OSError, ValueError, TypeError, KeyError) as exc:
|
|
139
146
|
raise CrowError("invalid_release", "Manifesto do snapshot npm inválido.") from exc
|
|
140
147
|
|
|
148
|
+
def _bundle_identity(self):
|
|
149
|
+
"""A verified snapshot of the published package: same identity on every version/path."""
|
|
150
|
+
try:
|
|
151
|
+
name = json.loads(self.bundle_file("package.json")).get("name")
|
|
152
|
+
except (CrowError, ValueError, AttributeError):
|
|
153
|
+
name = None
|
|
154
|
+
# Only where a release really lives (Homebrew Cellar, npm global, npx cache): a folder
|
|
155
|
+
# elsewhere that merely claims the name is another origin.
|
|
156
|
+
if name == PACKAGE and any(p.search(str(self.path)) for p in PACKAGED):
|
|
157
|
+
return "package:" + name
|
|
158
|
+
return "bundle:" + str(self.path)
|
|
159
|
+
|
|
141
160
|
def bundle_file(self, name):
|
|
142
161
|
safe_relative(name)
|
|
143
162
|
try:
|
|
@@ -254,3 +273,24 @@ class Source:
|
|
|
254
273
|
except (ValueError, KeyError, UnicodeError) as exc:
|
|
255
274
|
raise CrowError("invalid_package", "Frontmatter/referências do pacote não correspondem ao catálogo.") from exc
|
|
256
275
|
return Package(name, files, entries_for(files))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def identity_of(record_source, record_identity=None):
|
|
279
|
+
"""Identity recorded for an installation. Records made before 0.8.0 only kept the path:
|
|
280
|
+
a packaged-release path (Homebrew Cellar, npm global or npx cache) means the package."""
|
|
281
|
+
if record_identity:
|
|
282
|
+
return record_identity
|
|
283
|
+
if isinstance(record_source, str) and any(p.search(record_source) for p in PACKAGED):
|
|
284
|
+
return "package:" + PACKAGE
|
|
285
|
+
return "git:" + str(record_source)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def source_identity(source):
|
|
289
|
+
return getattr(source, "identity", None) or "git:" + str(source.path)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def same_source(record, source):
|
|
293
|
+
"""An update keeps the recorded origin: the same path, or the same package at another version."""
|
|
294
|
+
if record.get("source") == str(source.path):
|
|
295
|
+
return True
|
|
296
|
+
return identity_of(record.get("source"), record.get("source_id")) == source_identity(source)
|
package/crow_skills/storage.py
CHANGED
|
@@ -16,7 +16,7 @@ import stat
|
|
|
16
16
|
import uuid
|
|
17
17
|
|
|
18
18
|
from .agents import uniflow_store
|
|
19
|
-
from .source import CrowError, MAX_FILE, MAX_PACKAGE, MAX_FILES, SHA, digest, file_entry, safe_relative, valid_name
|
|
19
|
+
from .source import CrowError, MAX_FILE, MAX_PACKAGE, MAX_FILES, SHA, digest, file_entry, safe_relative, same_source, source_identity, valid_name
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def checked(path):
|
|
@@ -314,8 +314,9 @@ class Store:
|
|
|
314
314
|
raise CrowError("unmanaged_conflict", "Destino já existe e não é gerenciado: " + name + ". Use --replace após revisar.")
|
|
315
315
|
new = None
|
|
316
316
|
if command != "uninstall":
|
|
317
|
-
if record and command == "update" and record
|
|
318
|
-
raise CrowError("source_mismatch", "Update requer a mesma origem
|
|
317
|
+
if record and command == "update" and not same_source(record, source):
|
|
318
|
+
raise CrowError("source_mismatch", "Update requer a mesma origem (o mesmo clone, ou o mesmo pacote "
|
|
319
|
+
"publicado em outra versão); para trocar de origem, use install com --source.")
|
|
319
320
|
if name not in packages:
|
|
320
321
|
packages[name] = source.package(name)
|
|
321
322
|
new = packages[name].entries
|
|
@@ -337,7 +338,7 @@ class Store:
|
|
|
337
338
|
after[key]["installed"].pop(name)
|
|
338
339
|
else:
|
|
339
340
|
after[key]["installed"][name] = {
|
|
340
|
-
"revision": source.revision, "source": str(source.path), "entries": new,
|
|
341
|
+
"revision": source.revision, "source": str(source.path), "source_id": source_identity(source), "entries": new,
|
|
341
342
|
"package_sha256": digest(new), "agents": target_info["agents"],
|
|
342
343
|
"installed_at": datetime.now(timezone.utc).isoformat(),
|
|
343
344
|
}
|
package/docs/cli.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# crow-skills CLI 0.
|
|
1
|
+
# crow-skills CLI 0.8.0
|
|
2
2
|
|
|
3
|
-
CLI local-first para humanos e agentes. Python 3.10+, sem bibliotecas Python externas; Git é necessário para fontes em clone, não para o snapshot npm. O launcher npm requer Node 18+. Mutações suportadas em macOS/Linux (locks POSIX). Não instala o agente, não executa scripts das skills e não muda modelos, prompts base ou permissões. Instruções de uso das skills (o guia do agente, abaixo) são gravadas pelo comando `guide` e, por padrão, por `install`/`update`/`uninstall` em `--global` quando a crow-behaviour fica instalada para um agente com mecanismo verificado; `--no-guide` desliga isso. Os mesmos comandos também aplicam as [integrações de interface](#integrações-de-interface-integrations) (status
|
|
3
|
+
CLI local-first para humanos e agentes. Python 3.10+, sem bibliotecas Python externas; Git é necessário para fontes em clone, não para o snapshot npm. O launcher npm requer Node 18+. Mutações suportadas em macOS/Linux (locks POSIX). Não instala o agente, não executa scripts das skills e não muda modelos, prompts base ou permissões. Instruções de uso das skills (o guia do agente, abaixo) são gravadas pelo comando `guide` e, por padrão, por `install`/`update`/`uninstall` em `--global` quando a crow-behaviour fica instalada para um agente com mecanismo verificado; `--no-guide` desliga isso. Os mesmos comandos também aplicam as [integrações de interface](#integrações-de-interface-integrations) (status lines do Claude Code, do Codex e do Antigravity, hooks do modo crow em cada CLI, comando `/crow`, segmento tmux); `--no-integrations` desliga. `crow-skills mode on|off` liga e desliga a crow-behaviour em [todas as sessões e CLIs](#modo-crow-mode); `crow-skills progress` mostra o [painel de progresso](#progresso-progress) da execução ativa; `crow-skills events` expõe o [barramento de eventos](#eventos-events) dos hooks; `crow-skills autoupdate on` liga a [atualização automática](#atualização-automática-autoupdate).
|
|
4
4
|
|
|
5
5
|
## Executar
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ Para testar/distribuir o arquivo local sem registry:
|
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
33
|
npm pack
|
|
34
|
-
npm install -g ./andespindola-crow-skills-0.
|
|
34
|
+
npm install -g ./andespindola-crow-skills-0.8.0.tgz --ignore-scripts
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
O pacote contém o CLI Python e todas as skills do commit, com `release.json` de hashes. Não requer acesso ao GitHub ou clone para list/install. O launcher Node encaminha argumentos sem shell para Python em modo isolado, evitando imports pelo diretório do chamador/PYTHONPATH. `CROW_SKILLS_PYTHON` pode apontar para outro executável Python 3.10+. Não há dependências npm nem hooks de instalação que instalem skills automaticamente.
|
|
@@ -80,9 +80,9 @@ A fonte em checkout deve ser um clone Git já existente. O padrão é o checkout
|
|
|
80
80
|
|
|
81
81
|
`--fetch` busca `origin` somente quando solicitado, usando credenciais Git externas; não atualiza o checkout. É incompatível com `--dry-run`. Sem fetch, uma ref remota pode estar desatualizada. Git replace-objects e hooks não são usados para interpretar a fonte; erros de Git não imprimem stderr que possa conter credenciais. URL arbitrária/credencial não é argumento suportado de `--source`.
|
|
82
82
|
|
|
83
|
-
Atualização é explícita. `update` sem nomes atualiza apenas instalações gerenciadas nos destinos selecionados; não instala o catálogo inteiro. A origem deve corresponder à registrada. Mudança intencional de origem requer install/revisão explícita, não troca silenciosa durante update.
|
|
83
|
+
Atualização é explícita (ou agendada com [autoupdate](#atualização-automática-autoupdate)). `update` sem nomes atualiza apenas instalações gerenciadas nos destinos selecionados; não instala o catálogo inteiro. A origem deve corresponder à registrada. Mudança intencional de origem requer install/revisão explícita, não troca silenciosa durante update.
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Desde a 0.8.0, a origem registrada é também uma identidade (`source_id`): `git:<caminho do clone>` para um clone, ou `package:@andespindola/crow-skills` para o snapshot publicado, só quando ele está onde um release é instalado (Cellar do Homebrew, `lib/node_modules` do npm global, cache do npx); uma pasta em outro lugar que só declare esse nome é `bundle:<caminho>`, outra origem. O caminho do pacote muda a cada versão (`.../Cellar/crow-skills/<versão>/libexec`, cache do npx), mas a identidade não: `update` aceita o mesmo pacote em outro caminho e registra o novo. Registros anteriores à 0.8.0, que só guardavam o caminho, são reconhecidos pelo formato do caminho de um pacote publicado (Cellar do Homebrew, `node_modules/@andespindola/crow-skills`). Um snapshot com outro nome de pacote, ou um clone Git, continua sendo outra origem: `source_mismatch`, e a troca exige install. No npm, primeiro atualize o pacote e então execute o update das skills (o autoupdate faz os dois).
|
|
86
86
|
|
|
87
87
|
## Estado, conflitos e recuperação
|
|
88
88
|
|
|
@@ -139,7 +139,7 @@ Erros retornam `ok:false` e `error.code/message`. Saídas reais incluem alteraç
|
|
|
139
139
|
|
|
140
140
|
Codex/shared são deduplicados quando apontam para o mesmo diretório. O diretório compartilhado também pode ser lido por outros hosts; não é isolamento entre agentes. `.codex/skills` é uma localização legada encontrada em ambientes existentes; o adaptador usa a localização moderna documentada. Para um destino legado específico, use `--dest` explicitamente.
|
|
141
141
|
|
|
142
|
-
`agents` só verifica PATH e caminhos, sem iniciar agente/LLM. `status`/`doctor` conferem arquivos e estado de instalação, não provam carregamento, ativação, independência dos workers ou execução correta da skill. Gemini oferece `/skills reload`; para outros hosts, confira sua lista/reload ou reinicie a sessão conforme a versão. Nenhum
|
|
142
|
+
`agents` só verifica PATH e caminhos, sem iniciar agente/LLM. `status`/`doctor` conferem arquivos e estado de instalação, não provam carregamento, ativação, independência dos workers ou execução correta da skill. Gemini oferece `/skills reload`; para outros hosts, confira sua lista/reload ou reinicie a sessão conforme a versão. Nenhum system prompt base é alterado; o guia abaixo e os hooks do modo crow, aplicados por padrão em `--global` e desligáveis com `--no-guide` e `--no-integrations`, são aditivos: só informam ao agente o modo escolhido pelo usuário.
|
|
143
143
|
|
|
144
144
|
### Uniflow
|
|
145
145
|
|
|
@@ -177,7 +177,7 @@ crow-skills guide status
|
|
|
177
177
|
crow-skills guide uninstall --agents codex --global --yes
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
`guide` grava, no mecanismo nativo e sempre carregado de cada CLI, instruções curtas em pt-BR para o agente usar as skills instaladas: carregar a crow-behaviour por iniciativa própria antes de tarefas substantivas (várias etapas ou partes, decisões, vários arquivos, verificação), responder direto em conversa ou ajuste trivial, consultar a lista de skills disponíveis no agente (crow ou não) e carregar a especialista pertinente pelo nome. O conteúdo é gerado do catálogo e das skills gerenciadas pelo CLI naquele agente, com a forma de invocação dele. Exige a crow-behaviour instalada para o agente.
|
|
180
|
+
`guide` grava, no mecanismo nativo e sempre carregado de cada CLI, instruções curtas em pt-BR para o agente usar as skills instaladas: obedecer ao [modo crow](#modo-crow-mode) vigente (anunciado pelo hook a cada prompt ou, sem hook, lido de `~/.config/crow-skills/mode.json`), carregar a crow-behaviour por iniciativa própria antes de tarefas substantivas (várias etapas ou partes, decisões, vários arquivos, verificação), responder direto em conversa ou ajuste trivial, consultar a lista de skills disponíveis no agente (crow ou não) e carregar a especialista pertinente pelo nome. O conteúdo é gerado do catálogo e das skills gerenciadas pelo CLI naquele agente, com a forma de invocação dele. Exige a crow-behaviour instalada para o agente.
|
|
181
181
|
|
|
182
182
|
Aplicação automática (padrão em `install`, `update` e `uninstall` com `--global`, desligada por `--no-guide`):
|
|
183
183
|
|
|
@@ -211,6 +211,8 @@ crow-skills progress --watch # redesenha a cada segundo; sai sozinho ap
|
|
|
211
211
|
crow-skills progress --run ID --json # leitura por agente/automação
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
O painel começa pela lista de processos no formato da lista de agentes do host: `● main <atividade do coordenador>` e uma linha `○ <dono> <atividade>` por unidade em andamento de outro dono (o prefixo `subagent:` sai do nome). É o mesmo bloco que a crow-behaviour mostra na conversa (`crow_ledger.py show`) e que a status line do Claude Code e do Antigravity exibem abaixo da linha principal.
|
|
215
|
+
|
|
214
216
|
Lê o ledger `crow-run/1` que a crow-behaviour mantém em tarefas substantivas (formato em `skills/crow-behaviour/references/progress-ledger.md`): `$CROW_LEDGER`, `.crow/runs/*.json` no diretório atual e nos pais, e `~/.crow/runs/`. Sem `--run`, prefere execuções do diretório (ou de pais e filhos dele), depois as em andamento, depois a mais recente; um ledger `running` sem atualização há mais de 2 h conta como abandonado. Status line e tmux só mostram execuções do próprio diretório. Somente leitura; não inicia agentes.
|
|
215
217
|
|
|
216
218
|
Tokens vêm dos logs locais do próprio host, filtrados pela janela da execução (`startedAt` até `endedAt` ou agora) e pela sessão (`session.id` no ledger) ou, sem ela, pelo diretório de trabalho (marcado como aproximado, pois outras sessões no mesmo diretório e janela entram na soma). Só campos numéricos de uso são lidos e impressos; conteúdo de mensagens, prompts e credenciais nunca é exibido.
|
|
@@ -237,13 +239,21 @@ crow-skills integrations uninstall --global --yes
|
|
|
237
239
|
crow-skills install crow-behaviour --agents claude --global --no-integrations --yes # sem integrações
|
|
238
240
|
```
|
|
239
241
|
|
|
240
|
-
Aplicadas por padrão junto com `install`/`update` em `--global` para os agentes selecionados que ficam com a crow-behaviour, e removidas quando ela sai (`uninstall`, `rollback`, `recover`). `--no-integrations` desliga o passo; `--project`/`--dest` não têm integração. São um passo posterior às transações de skills e guia: uma recusa aparece em `integrations.skipped` e não desfaz as skills.
|
|
242
|
+
Aplicadas por padrão junto com `install`/`update` em `--global` para os agentes selecionados que ficam com a crow-behaviour, e removidas quando ela sai (`uninstall`, `rollback`, `recover`). `--no-integrations` desliga o passo; `--project`/`--dest` não têm integração. São um passo posterior às transações de skills e guia: uma recusa aparece em `integrations.skipped` e não desfaz as skills. Integrações de um CLI que não está configurado no perfil (sem `~/.codex`, `~/.gemini/settings.json`, `~/.gemini/antigravity-cli` ou `~/.config/devin/config.json`) são puladas com o motivo, sem criar arquivos.
|
|
241
243
|
|
|
242
|
-
| Integração | Agentes | Mecanismo | O que
|
|
244
|
+
| Integração | Agentes | Mecanismo | O que faz |
|
|
243
245
|
|---|---|---|---|
|
|
244
|
-
| claude-statusline | claude | chave `statusLine` (`type: command`) em `~/.claude/settings.json` |
|
|
246
|
+
| claude-statusline | claude | chave `statusLine` (`type: command`) em `~/.claude/settings.json` | 1ª linha: `▸▸ crow on │ crow ▸ título x/y ████░░ 46% · 1.2M tok` (o indicador sempre; o resto enquanto houver execução ativa no diretório da sessão, nunca de outro projeto, de outra sessão ou de um ledger parado há mais de 2 h); linhas seguintes: a lista de processos (`● main …`, `○ dono atividade`, até 6) |
|
|
247
|
+
| claude-hooks | claude | entradas em `hooks` de `~/.claude/settings.json` | modo por prompt (`UserPromptSubmit`, síncrono) e eventos (demais, `async: true`) |
|
|
248
|
+
| claude-command | claude | `~/.claude/commands/crow.md` (arquivo próprio) | `/crow on\|off\|status`: roda o toggle por `!` com `allowed-tools` restrito ao script; `disable-model-invocation` impede o modelo de chamá-lo |
|
|
245
249
|
| codex-statusline | codex | `[tui].status_line` em `~/.codex/config.toml`, em bloco marcado | itens nativos `model-with-reasoning`, `context-remaining`, `current-dir`, `task-progress`, `used-tokens`; `task-progress` reflete o plano nativo que a skill espelha |
|
|
246
|
-
|
|
|
250
|
+
| codex-hooks | codex | entradas em `~/.codex/hooks.json` | modo por prompt, `/crow on\|off\|status` interceptado e bloqueado com o resultado, `systemMessage` com o modo no início da sessão e quando muda, eventos (`async: true`) |
|
|
251
|
+
| gemini-hooks | gemini (Gemini CLI) | `hooks` em `~/.gemini/settings.json` | modo por prompt (`BeforeAgent`), `systemMessage` quando muda, eventos |
|
|
252
|
+
| gemini-command | gemini (Gemini CLI) | `~/.gemini/commands/crow.toml` (arquivo próprio) | `/crow on\|off\|status` por `!{…}` (o Gemini pede confirmação do shell, salvo política que o permita) |
|
|
253
|
+
| agy-hooks | gemini (Antigravity) | chave `crow-skills` em `~/.gemini/config/hooks.json` | modo por prompt (`PreInvocation` → `injectSteps`), `/crow …` interceptado quando o agy envia o texto (`lastUserInput`, 1.4.7+), eventos |
|
|
254
|
+
| agy-statusline | gemini (Antigravity) | `statusLine` em `~/.gemini/antigravity-cli/settings.json` com `stack_with_default: true` | indicador e lista de processos empilhados sob a linha padrão do agy |
|
|
255
|
+
| devin-hooks | devin | `hooks` em `~/.config/devin/config.json` | modo por prompt, `/crow …` interceptado, `systemMessage` quando muda, eventos |
|
|
256
|
+
| tmux-status | claude, codex, gemini (agy), devin | `~/.config/crow-skills/tmux.conf` (arquivo próprio) e um bloco `source-file -q` no fim do seu `tmux.conf` | `▸▸ crow on · crow ▸ …` acrescentado ao `status-right` (`set -ag`, sem substituir o seu), com o diretório do painel ativo |
|
|
247
257
|
|
|
248
258
|
Regras de preservação:
|
|
249
259
|
|
|
@@ -251,12 +261,81 @@ Regras de preservação:
|
|
|
251
261
|
- Claude: um `statusLine` existente do tipo `command` é encadeado: o comando anterior roda primeiro, com o mesmo stdin, sua saída é mantida inteira (todas as linhas, espaços internos) e o segmento crow entra no fim da última linha; as demais chaves dele (`padding` etc.) são preservadas. Outro tipo é mantido e a integração é pulada. Se você mudar o `statusLine` depois, ele não é sobrescrito nem removido. No uninstall, se `settings.json` ainda tiver exatamente o que o crow-skills gravou, os bytes originais do backup voltam; se você editou outra parte do arquivo, a restauração é só da chave `statusLine` (JSON reescrito com indentação 2).
|
|
252
262
|
- Codex: só aplicado se você ainda não definiu `tui.status_line`; um `[tui]` existente recebe o bloco logo após o cabeçalho, senão o bloco cria a tabela no fim do arquivo. A edição é verificada por parse TOML (Python 3.11+). O uninstall só remove o bloco se ele estiver como foi gravado e se o resultado tiver as mesmas chaves que o arquivo atual menos `tui.status_line`; sem outras edições, o arquivo volta byte a byte. Chaves acrescentadas a um `[tui]` criado pelo crow-skills mantêm o cabeçalho `[tui]`; bloco editado ou remoção que mudaria outra chave é recusado e reportado em `skipped`. O Codex não aceita comando externo na status line, então ela não lê o ledger.
|
|
253
263
|
- tmux: só quando o `tmux` está no PATH e já existe `~/.tmux.conf` ou `~/.config/tmux/tmux.conf`. Se o `tmux.conf` for symlink, o snippet é instalado mas o arquivo não é editado: o resultado traz a linha `source-file -q ...` para você adicionar. O snippet acrescenta o segmento só quando `status-right` ainda não o contém, então ele volta depois de cada `tmux source-file` do seu `tmux.conf` sem duplicar. O diretório do painel chega ao script escapado por `#{q:pane_current_path}` (tmux 3.0 ou mais novo): nome de diretório com `$(...)` ou crase não é executado. Depois de remover, o segmento sai da sessão atual quando o `status-right` é redefinido (recarregar um `tmux.conf` que o define) ou quando o servidor tmux reinicia.
|
|
254
|
-
-
|
|
264
|
+
- Hooks: as entradas do crow-skills são acrescentadas depois das suas, em todos os eventos (no Codex, a aprovação de `/hooks` é por índice, então o hook que você já aprovou continua aprovado), e reconhecidas pelo caminho do script. No Antigravity, ficam numa chave própria (`crow-skills`) de `hooks.json`, ao lado das suas. Uninstall tira só essas entradas; se ninguém mais editou o arquivo, os bytes originais voltam.
|
|
265
|
+
- Arquivos JSON compartilhados por duas integrações (o `settings.json` do Claude Code recebe a status line e os hooks) têm um único ponto de restauração por arquivo: o conteúdo antes da primeira gravação do crow-skills. Removidas as duas, ele volta byte a byte; se o arquivo foi editado por você no meio do caminho, só as chaves/entradas do crow-skills saem.
|
|
266
|
+
- O Devin também lê os hooks do `~/.claude/settings.json`. Para não duplicar nem rotular errado, o hook instalado para o Claude Code só age quando quem o executa é o Claude Code: a transcrição informada fica em `~/.claude/projects` (ou em `$CLAUDE_CONFIG_DIR/projects`), ou, sem transcrição, o ambiente traz `CLAUDECODE=1` (exportado pelo Claude Code para os processos filhos); um campo exclusivo do Devin (`tool_provenance`) também o silencia. O Devin usa a entrada própria, rotulada `devin`. Do mesmo modo, a entrada do Gemini CLI fica em silêncio quando o Antigravity a executa (`ANTIGRAVITY_AGENT=1`).
|
|
267
|
+
- Codex: hooks novos ou alterados só rodam depois de aprovados em `/hooks` dentro do Codex, e só com `[features] hooks = true` no `config.toml`; o crow-skills não altera esse flag e avisa na nota da instalação quando ele falta.
|
|
268
|
+
- Os scripts usados pela status line, pelo tmux, pelos hooks e pelos comandos `/crow` são cópias de `crow_skills/progress.py` (`crow_progress.py`) e `crow_skills/hook.py` (`crow_hook.py`) em `~/.local/state/crow-skills/integrations/`, executadas com o `python3` do PATH no momento da instalação (os hooks com `-I -S`); não dependem do caminho do cache npm ou do Homebrew.
|
|
269
|
+
|
|
270
|
+
Sem superfície que mostre um valor externo persistente (conferido em 2026-10-28, Codex CLI 0.165.0, Gemini CLI 0.66.0, Devin CLI 3000.11.228):
|
|
271
|
+
|
|
272
|
+
- Codex: `[tui].status_line` e `terminal_title` só aceitam itens nativos; o rodapé não mostra o modo crow. O hook mostra `▸▸ crow on|off` como `systemMessage` no início da sessão e quando o modo muda.
|
|
273
|
+
- Gemini CLI: `ui.footer.items` aceita só itens nativos; idem, `systemMessage` quando muda.
|
|
274
|
+
- Devin CLI: sem status line ou rodapé configurável; idem, `systemMessage` quando muda.
|
|
275
|
+
- Nenhum CLI aceita alimentar a lista nativa de subagentes de fora; a lista de processos vem do ledger (status line do Claude Code e do agy, `crow-skills progress`, bloco no chat).
|
|
276
|
+
|
|
277
|
+
O Antigravity (`agy`, agente `gemini` do instalador) ganhou `statusLine` por comando: a integração agy-statusline usa `stack_with_default` (o agy desliga a status line após falhas seguidas; o script sempre sai com código 0). O formato da chave foi lido do binário 1.4.4 e das notas de versão, sem sessão de verificação: se a linha não aparecer, rode `/statusline` no agy. No ledger, o host dele é `agy`; `gemini` no ledger significa Gemini CLI. Para os demais, use o segmento tmux ou `crow-skills progress --watch` num painel ao lado.
|
|
278
|
+
|
|
279
|
+
## Modo crow (mode)
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
crow-skills mode # status: ▸▸ crow on
|
|
283
|
+
crow-skills mode off # desliga em todas as sessões e CLIs, a partir do próximo prompt
|
|
284
|
+
crow-skills mode on
|
|
285
|
+
crow-skills mode toggle --json
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
O modo é um interruptor único para a crow-behaviour em todos os CLIs. `on` (padrão, também quando o arquivo não existe) é o modo de orquestração permanente: o agente carrega e segue a crow-behaviour em toda tarefa substantiva, sem o usuário pedir. `off`: o agente não a carrega nem aplica, salvo se o usuário a invocar no pedido; as especialistas continuam disponíveis. O estado fica em `~/.config/crow-skills/mode.json` (`crow-mode/1`, gravação atômica com `updatedAt` e quem mudou).
|
|
289
|
+
|
|
290
|
+
Como cada CLI fica sabendo, sem reiniciar a sessão:
|
|
291
|
+
|
|
292
|
+
| CLI | A cada prompt | Alternar dentro da sessão | Indicador visível |
|
|
293
|
+
|---|---|---|---|
|
|
294
|
+
| Claude Code | hook `UserPromptSubmit` injeta `[crow-skills] modo crow: ON\|OFF` | `/crow on\|off\|status` (`~/.claude/commands/crow.md`) | status line `▸▸ crow on` |
|
|
295
|
+
| Codex | hook `UserPromptSubmit` (após aprovar em `/hooks`) | prompt exatamente `/crow on\|off\|status`: o hook alterna e bloqueia o prompt com o resultado | `systemMessage` no início e quando muda; o rodapé não aceita item externo |
|
|
296
|
+
| Gemini CLI | hook `BeforeAgent` | `/crow on\|off\|status` (`~/.gemini/commands/crow.toml`) | `systemMessage` quando muda |
|
|
297
|
+
| Antigravity (agy) | hook `PreInvocation` (`injectSteps`) | `/crow …` interceptado só quando o agy envia o texto do prompt (1.4.7+); senão peça ao agente para rodar `crow-skills mode on\|off` | status line `▸▸ crow on` (empilhada) |
|
|
298
|
+
| Devin | hook `UserPromptSubmit` | prompt `/crow on\|off\|status`, como no Codex | `systemMessage` quando muda |
|
|
299
|
+
| qualquer um | — | `crow-skills mode on\|off` num terminal | segmento tmux `▸▸ crow on` |
|
|
300
|
+
|
|
301
|
+
O guia de cada agente diz para obedecer ao anúncio mais recente e, sem hook (não instalado ou não aprovado), ler o arquivo de modo no início da sessão. O modelo nunca muda o modo por conta própria (no Claude Code, o comando tem `disable-model-invocation`). `mode` sem `--yes` é intencional: alternar é o pedido explícito do usuário e não altera arquivos de configuração dos CLIs.
|
|
302
|
+
|
|
303
|
+
## Eventos (events)
|
|
304
|
+
|
|
305
|
+
```sh
|
|
306
|
+
crow-skills events # resumo: sessões ativas, execuções crow, autoupdate
|
|
307
|
+
crow-skills events snapshot --json # crow-snapshot/1 para terminais (crow-term)
|
|
308
|
+
crow-skills events tail --json --limit 100
|
|
309
|
+
crow-skills events tail --follow # JSONL puro até Ctrl-C
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Os hooks gravam eventos compactos (`crow-event/1`) num JSONL rotacionado e um snapshot por sessão (host, sessão, diretório, branch, modelo, modo de permissão, modo crow, estado, ferramenta atual, última ação, subagentes em andamento, shells iniciados, bloqueio em aprovação, custo e uso quando a status line os envia). Nunca entram texto de prompt, mensagem, entrada/saída de ferramenta ou segredos. Contrato completo, campos e limites de disco em [docs/events.md](events.md).
|
|
313
|
+
|
|
314
|
+
## Atualização automática (autoupdate)
|
|
315
|
+
|
|
316
|
+
```sh
|
|
317
|
+
crow-skills autoupdate on --dry-run
|
|
318
|
+
crow-skills autoupdate on --yes # agenda diário e no login
|
|
319
|
+
crow-skills autoupdate status
|
|
320
|
+
crow-skills autoupdate run --yes # agora, manualmente
|
|
321
|
+
crow-skills autoupdate run --skills-only --yes
|
|
322
|
+
crow-skills autoupdate off --yes
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
`on` instala um agendador do usuário que roda `crow-skills autoupdate run --yes --json` todo dia e no login:
|
|
326
|
+
|
|
327
|
+
| Sistema | Arquivos | Quando |
|
|
328
|
+
|---|---|---|
|
|
329
|
+
| macOS | `~/Library/LaunchAgents/com.andersonflima.crow-skills.autoupdate.plist`, carregado com `launchctl bootstrap gui/<uid>` | `RunAtLoad` (login) e `StartCalendarInterval` diário às 10:17 (roda ao acordar se o Mac dormia) |
|
|
330
|
+
| Linux | `~/.config/systemd/user/crow-skills-autoupdate.{service,timer}`, habilitado com `systemctl --user enable --now` | `OnStartupSec=5min` e `OnCalendar=daily` com `Persistent=true` |
|
|
331
|
+
| outros | — | `unsupported_platform`; agende `crow-skills autoupdate run --yes --json` por conta própria (por exemplo, cron) |
|
|
332
|
+
|
|
333
|
+
O agendador chama o launcher estável (`<prefixo do Homebrew>/bin/crow-skills`, `<prefixo do npm>/bin/crow-skills` ou `bin/crow-skills` do checkout), não o caminho versionado. Um arquivo de agendador que já existe e não foi gravado pelo crow-skills é recusado (`unmanaged_conflict`); `off` não remove um arquivo editado depois da instalação (informa em `kept`). Com `--home` isolado, os arquivos são gravados mas nada é carregado.
|
|
255
334
|
|
|
256
|
-
|
|
335
|
+
Cada execução:
|
|
257
336
|
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
|
|
337
|
+
1. Atualiza o próprio crow-skills conforme a instalação: Homebrew (`brew upgrade andersonflima/tap/crow-skills`), npm global (`npm install -g @andespindola/crow-skills@latest`). Checkout Git só com `autoupdate on --git-pull`, e só se estiver limpo em `main`/`master` (`git pull --ff-only`); caso contrário, é pulado com o motivo. Saída de brew/npm/git não é guardada nem impressa, só código de saída e versões.
|
|
338
|
+
2. Se a versão mudou, a versão nova (o launcher) faz o passo seguinte, para usar o snapshot novo como fonte.
|
|
339
|
+
3. Para cada agente com skills gerenciadas (claude, codex/`~/.agents/skills`, gemini, devin, uniflow), roda o equivalente a `update <skills> --agents <agente> --global --yes`: regenera guia, integrações e hooks; a [identidade do pacote](#origem-e-versões) evita `source_mismatch` depois do upgrade. Skills editadas localmente (`modified`), ausentes ou instaladas de outra origem (`other_origin`, por exemplo de um clone Git) ficam como estão e aparecem em `kept`; nada é sobrescrito. Uma reaplicação dos hooks troca as entradas do crow-skills no lugar, sem mudar a posição das suas. Uma recusa num agente (por exemplo, guia editado) aparece em `error` e não interrompe os outros.
|
|
261
340
|
|
|
262
|
-
|
|
341
|
+
O resultado fica em `~/.local/state/crow-skills/autoupdate/state.json` (`lastRun` e as últimas 20 execuções), aparece em `autoupdate status`, em `status`/`doctor` (doctor `healthy:false` se a última execução falhou ou o agendador foi alterado/removido) e em `events snapshot`. Duas execuções simultâneas são recusadas (`busy`).
|