@aksp/opencrew 1.10.0 → 1.12.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/CHANGELOG.md +100 -0
- package/README.md +15 -4
- package/package.json +1 -1
- package/src/commands/init.js +4 -5
- package/src/commands/update.js +8 -0
- package/src/lib/resumo.js +5 -1
- package/templates/AGENTS.md +16 -9
- package/templates/_opencrew/.opencrew-version +1 -1
- package/templates/_opencrew/core/architect.agent.yaml +25 -16
- package/templates/_opencrew/core/best-practices/_catalog.yaml +5 -0
- package/templates/_opencrew/core/best-practices/texto-livre.md +23 -0
- package/templates/_opencrew/core/formato-da-crew.md +162 -0
- package/templates/_opencrew/core/prompts/build.prompt.md +35 -59
- package/templates/_opencrew/core/prompts/design.prompt.md +16 -16
- package/templates/_opencrew/core/prompts/discovery.prompt.md +28 -8
- package/templates/_opencrew/core/prompts/documento.prompt.md +9 -5
- package/templates/_opencrew/core/prompts/entrega.prompt.md +7 -2
- package/templates/_opencrew/core/prompts/repair.prompt.md +75 -84
- package/templates/_opencrew/core/runner.pipeline.md +25 -28
- package/templates/_opencrew/core/scripts/caminho/argumentos.mjs +3 -1
- package/templates/_opencrew/core/scripts/caminho/nucleo.mjs +16 -0
- package/templates/_opencrew/core/scripts/caminho.mjs +11 -8
- package/templates/_opencrew/core/scripts/conferir-fontes/relatorio.mjs +4 -2
- package/templates/_opencrew/core/scripts/conferir-fontes.mjs +22 -16
- package/templates/_opencrew/core/scripts/conserto/achados.mjs +158 -0
- package/templates/_opencrew/core/scripts/conserto/aplicar.mjs +156 -0
- package/templates/_opencrew/core/scripts/conserto/argumentos.mjs +51 -0
- package/templates/_opencrew/core/scripts/conserto/crew.mjs +126 -0
- package/templates/_opencrew/core/scripts/conserto/edicoes.mjs +133 -0
- package/templates/_opencrew/core/scripts/conserto/gravar.mjs +55 -0
- package/templates/_opencrew/core/scripts/conserto.mjs +82 -0
- package/templates/_opencrew/core/scripts/documento/markdown.mjs +2 -1
- package/templates/_opencrew/core/scripts/documento/pacote.mjs +1 -0
- package/templates/_opencrew/core/scripts/documento/perfil.mjs +14 -0
- package/templates/_opencrew/core/scripts/entrega/pendencias.mjs +4 -1
- package/templates/_opencrew/core/scripts/entrega/separar.mjs +2 -1
- package/templates/_opencrew/core/scripts/verificar/documento.mjs +20 -0
- package/templates/_opencrew/core/scripts/verificar/medicao.mjs +2 -1
- package/templates/_opencrew/core/scripts/verificar/proibicoes.mjs +30 -5
- package/templates/_opencrew/core/scripts/verificar.mjs +3 -1
|
@@ -1,114 +1,105 @@
|
|
|
1
|
-
# Repair —
|
|
1
|
+
# Repair — bring an existing crew up to date (conserto)
|
|
2
2
|
|
|
3
|
-
You are the opencrew Repair agent.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
You are the opencrew Repair agent. A crew built by an older version misses what later versions
|
|
4
|
+
added: the format of each text, the project sources, bans the checker can enforce, the persona
|
|
5
|
+
names. Your job is to show the user what is missing in **one crew that already exists** and fix
|
|
6
|
+
one point at a time, each with the user's yes.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
`
|
|
11
|
-
files and rewrite the manifest. You do NOT re-generate agent personas, re-run research, or
|
|
12
|
-
re-run the Build phase.
|
|
8
|
+
**You never write inside `crews/` yourself.** Every change is one command of the script below,
|
|
9
|
+
which keeps a `.bak` copy of the file before changing it. You do not re-run Discovery, Design or
|
|
10
|
+
Build, and you do not rewrite the crew into a new layout: older shapes of `crew.yaml` and
|
|
11
|
+
`pipeline.yaml` still work (see `_opencrew/core/formato-da-crew.md`).
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
You may ONLY touch these files under `crews/{code}/`:
|
|
17
|
-
- `crews/{code}/crew-party.csv`
|
|
18
|
-
- `crews/{code}/agents/*.agent.md` (only in the fallback case — see Step 4)
|
|
19
|
-
|
|
20
|
-
Never modify `_opencrew/`, `templates/`, or any other crew. Use the Write tool for all file
|
|
21
|
-
writes (never Bash `mkdir`).
|
|
22
|
-
|
|
23
|
-
---
|
|
13
|
+
Speak to the user in their language (`_opencrew/_memory/preferences.md`). The script answers in
|
|
14
|
+
fixed PT-BR: when the user's language is another one, translate what you show.
|
|
24
15
|
|
|
25
16
|
## Step 1: Identify the crew
|
|
26
17
|
|
|
27
|
-
- If the user passed a crew
|
|
28
|
-
- Otherwise
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
Verify `crews/{code}/crew.yaml` and `crews/{code}/agents/` exist. If not, report and stop.
|
|
18
|
+
- If the user passed a crew (`/opencrew repair <name>`), use it.
|
|
19
|
+
- Otherwise list the crews and ask which one. **A folder under `crews/` without a `crew.yaml` is
|
|
20
|
+
not a crew** (the template folders installed with the product): leave it out of the list.
|
|
21
|
+
- Exactly 1 crew: offer it plus a "Cancelar" option.
|
|
22
|
+
- 0 crews: say there is nothing to repair and stop.
|
|
33
23
|
|
|
34
|
-
## Step 2:
|
|
24
|
+
## Step 2: Diagnose
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
- `id` (or derive it from the filename: `researcher.agent.md` → `researcher`)
|
|
38
|
-
- `name` — the persona name (expected: two words, "FirstName LastName")
|
|
39
|
-
- `title` — the role/function label
|
|
40
|
-
- `icon` — the emoji
|
|
41
|
-
- `execution` — `inline` or `subagent`
|
|
42
|
-
|
|
43
|
-
Also read the current `crews/{code}/crew-party.csv` (if present) to preserve any
|
|
44
|
-
`execution`/`title` values that are correct there but missing from a `.agent.md`.
|
|
45
|
-
|
|
46
|
-
## Step 3: Rebuild `crew-party.csv`
|
|
47
|
-
|
|
48
|
-
Write `crews/{code}/crew-party.csv` with the canonical header and one row per agent:
|
|
26
|
+
From the project root, with the crew folder between double quotes:
|
|
49
27
|
|
|
50
28
|
```
|
|
51
|
-
|
|
29
|
+
node _opencrew/core/scripts/conserto.mjs --crew "crews/{code}"
|
|
52
30
|
```
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
- `title` = the agent's `title:`.
|
|
56
|
-
- `icon` = the agent's `icon:`.
|
|
57
|
-
- `path` = `./agents/{id}.agent.md`.
|
|
58
|
-
- `execution` = the agent's `execution:` (default `inline` if absent).
|
|
59
|
-
- Quote any field containing a space or comma with double quotes.
|
|
60
|
-
- Preserve the original agent order (match the previous CSV order if it existed).
|
|
32
|
+
It only reads. Its last line is the status:
|
|
61
33
|
|
|
62
|
-
|
|
34
|
+
- `CONSERTO:OK` — say "A crew {nome} está em dia: não há o que consertar." and stop.
|
|
35
|
+
- `CONSERTO:PENDENTE` — one block per finding, each starting with `[código]`. Go to Step 3.
|
|
36
|
+
- `CONSERTO:ERRO`, or the script did not run (no Node, an error) — show the user the message as
|
|
37
|
+
it came and stop. Do not repair by hand.
|
|
63
38
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
`_opencrew/core/prompts/design.prompt.md`:
|
|
39
|
+
Open with: "Olhei a crew {nome}. Encontrei {n} ponto(s) para consertar. Vou mostrar um por vez;
|
|
40
|
+
nada é gravado sem o seu sim, e cada arquivo alterado ganha uma cópia `.bak`."
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
2. Generate a two-word name: "FirstName LastName" — both words start with the SAME letter
|
|
70
|
-
(alliteration); the first name is common in the user's language; the last name is a
|
|
71
|
-
playful reference to the agent's function (from its `title:`). Each agent in the crew
|
|
72
|
-
must use a DIFFERENT initial letter.
|
|
73
|
-
3. Update BOTH the `.agent.md` `name:` frontmatter AND the `# {Name}` heading in that file.
|
|
74
|
-
4. Use the new name as the `displayName` in the rebuilt CSV.
|
|
42
|
+
## Step 3: One finding at a time
|
|
75
43
|
|
|
76
|
-
|
|
77
|
-
|
|
44
|
+
Take the findings in the order the script printed them. For each: say what it is, ask the
|
|
45
|
+
question, wait for the answer, and only then run the command. Never group two findings in one
|
|
46
|
+
question. A "não" leaves the point as it is: go on to the next one.
|
|
78
47
|
|
|
79
|
-
|
|
48
|
+
The script's output is for you. To the user, say each point in plain words: do not show the
|
|
49
|
+
codes between brackets, the `--aplicar` lines or the `CONSERTO:` status line.
|
|
80
50
|
|
|
81
|
-
|
|
51
|
+
| Finding | What you say and ask | Command after the yes |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `nome-de-agente` | The agent has no two-word persona name. Propose one by the Agent Naming Convention of `_opencrew/core/prompts/design.prompt.md` (two words with the same initial, a different initial for each agent of the crew), keeping the first name the agent already has, and ask: "O agente {id} está sem nome de pessoa. Proponho {Nome Sobrenome}. Posso gravar?" | `--aplicar "nome:{id}={Nome Sobrenome}"`; the names reach the list of the crew with `manifesto`, below — when `manifesto` is not among the findings, run it right after this one |
|
|
54
|
+
| `manifesto` | "O arquivo de nomes da crew está incompleto; por isso aparece a função no lugar do nome. Posso refazer a partir dos arquivos dos agentes?" | `--aplicar "manifesto"` |
|
|
55
|
+
| `formato` | Read each listed step and propose one format per step, among the files of `_opencrew/core/best-practices/` (and `_opencrew/best-practices.local/`). A text that must become a Word file to print, sign or file — ata, ofício, contrato, parecer — gets `documento-oficial`; a text with no channel that does not become a Word file (a plan, an internal report, a proposal or a draft that becomes HTML or PDF) gets `texto-livre`. When you cannot tell which of the two it is (a proposta, a minuta), ask first: "O texto do passo {n} precisa virar um arquivo Word para imprimir ou assinar?" — sim: `documento-oficial`; não: `texto-livre`. When the user refuses your proposal and says what the text is, propose once more with the right format. Then: "Estes passos não dizem que tipo de texto produzem; sem isso, o redator não recebe o guia do tipo de texto e o verificador procura no arquivo peças de rede. Minha proposta: {passo → formato}. Posso gravar assim?" | one `--aplicar "formato:{passo}={formato}"` per step |
|
|
56
|
+
| `fontes` | "Esta crew não registra os arquivos do projeto que ela deve ler antes de escrever. Quais arquivos ou pastas ela precisa conhecer? (Pode responder 'nenhum'.)" Confirm that each one exists (search the project when only a name was given) and ask what the crew uses it for | one `--aplicar "fonte:{caminho}={para que}"` per file or folder, the path relative to the project root |
|
|
57
|
+
| `proibicao` | For each listed item: "Esta proibição não tem um trecho entre aspas, então o verificador não consegue barrar: «{item}». Qual trecho exato devo barrar? Se for uma regra de conteúdo, e não uma palavra ou expressão, responda 'revisão humana': ela fica para o revisor." The excerpt must be words of the item itself | `--aplicar "proibicao:{n}={trecho}"` or `--aplicar "proibicao:{n}=revisao-humana"` |
|
|
58
|
+
| `irreversivel` | For each listed step: "O passo {n} é feito por um agente que tem uma ferramenta de publicar ou enviar ({skill}). Este passo publica ou envia alguma coisa para fora do projeto? Se sim, marco o passo para que ele nunca seja repetido sozinho." | `--aplicar "irreversivel:{n}"` only for a yes |
|
|
59
|
+
| `sem-revisao` | "Esta crew não tem passo de revisão: nada é conferido antes de chegar a você. Isso se resolve editando a crew: /opencrew edit {nome}." | none |
|
|
60
|
+
| `sem-aprovacao-final` | "Depois da revisão não há um ponto de aprovação seu. Isso se resolve editando a crew: /opencrew edit {nome}." | none |
|
|
61
|
+
| `publica-antes` | "O passo {n} publica ou envia antes da revisão e da sua aprovação final. Enquanto estiver assim, o que sai não passou pela revisão. Isso se resolve editando a crew: /opencrew edit {nome}." | none |
|
|
62
|
+
| `passo-faltando` | Show the lines the script printed and say that it is solved by editing the crew: `/opencrew edit {nome}` | none |
|
|
82
63
|
|
|
64
|
+
The command is always the same line, with the item between double quotes:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
node _opencrew/core/scripts/conserto.mjs --crew "crews/{code}" --aplicar "{item}"
|
|
83
68
|
```
|
|
84
|
-
Crew "{name}" repaired.
|
|
85
69
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| copywriter | Guilherme | ✍️ Guilherme Gancho | generated |
|
|
70
|
+
Several items of the same finding may go in one call (`--aplicar "…" --aplicar "…"`). The last
|
|
71
|
+
line must be `CONSERTO:APLICADO`. With `CONSERTO:ERRO` nothing was written: read the reason to the
|
|
72
|
+
user, correct the item and ask again — do not write the file yourself.
|
|
90
73
|
|
|
91
|
-
|
|
74
|
+
## Step 4: Project paths
|
|
92
75
|
|
|
93
|
-
|
|
76
|
+
After the findings, run the sources check:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
node _opencrew/core/scripts/conferir-fontes.mjs --crew "crews/{code}"
|
|
94
80
|
```
|
|
95
81
|
|
|
96
|
-
|
|
97
|
-
|
|
82
|
+
When it lists a path with a suggestion (`Sugestão: …` — an absolute path left in a step, or a
|
|
83
|
+
file that moved), show the user each path and its suggestion and ask: "Posso corrigir estes
|
|
84
|
+
caminhos nos arquivos da crew?" After a yes, run the same command ending with `--corrigir`: the
|
|
85
|
+
script rewrites only those paths and keeps a copy of each file it changes. A missing file with
|
|
86
|
+
no suggestion is the user's to solve: say which one.
|
|
87
|
+
|
|
88
|
+
## Step 5: Report
|
|
89
|
+
|
|
90
|
+
Run the diagnosis of Step 2 once more and close with what really happened:
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
needed."
|
|
92
|
+
"Pronto: {k} conserto(s) gravado(s). Cópias do que mudou: {lista de .bak}. Ficou pendente:
|
|
93
|
+
{lista ou 'nada'}." — `{k}` is the number of points the user said yes to and the script wrote.
|
|
102
94
|
|
|
103
|
-
|
|
95
|
+
Then: `Run it: /opencrew run {code}`.
|
|
104
96
|
|
|
105
97
|
## Rules
|
|
106
98
|
|
|
107
|
-
- **DO**
|
|
108
|
-
- **DO**
|
|
109
|
-
- **DO**
|
|
110
|
-
|
|
111
|
-
- **DO NOT**
|
|
112
|
-
|
|
113
|
-
- **DO NOT**
|
|
114
|
-
- **DO NOT** fabricate a summary — report only what you actually changed.
|
|
99
|
+
- **DO** run the diagnosis before and after; report only what the script printed.
|
|
100
|
+
- **DO** ask before every `--aplicar`, one finding at a time.
|
|
101
|
+
- **DO NOT** create, edit or delete any file under `crews/` with your own tools — not even to
|
|
102
|
+
"finish" a repair the script refused.
|
|
103
|
+
- **DO NOT** reorder, renumber, add or remove steps here: that is `/opencrew edit`.
|
|
104
|
+
- **DO NOT** touch `_opencrew/` or any other crew.
|
|
105
|
+
- **DO NOT** delete the `.bak` copies: they belong to the user.
|
|
@@ -114,23 +114,19 @@ Before starting execution:
|
|
|
114
114
|
briefing, the research or your own assumptions, the sources take precedence over them
|
|
115
115
|
(as fontes valem sobre o briefing e a pesquisa) — and say so when it matters.
|
|
116
116
|
|
|
117
|
-
2. Read `crews/{name}/pipeline/pipeline.yaml` for the pipeline definition
|
|
117
|
+
2. Read `crews/{name}/pipeline/pipeline.yaml` for the pipeline definition. The format of the crew files — where each field lives and the older shapes that still count — is in `_opencrew/core/formato-da-crew.md`; read it when a field is not where you expect.
|
|
118
118
|
3. **Resolve skills**: Read `crew.yaml` → `skills` section. For each non-native skill (anything other than web_search, web_fetch):
|
|
119
119
|
a. Verify `skills/{skill}/SKILL.md` exists
|
|
120
120
|
- If missing → ask user: "Skill '{skill}' is not installed. Install now? (y/n)"
|
|
121
121
|
- If yes → read `_opencrew/core/skills.engine.md`, follow Operation 2 (Install)
|
|
122
122
|
- If no → **ERROR**: stop pipeline
|
|
123
123
|
b. Read SKILL.md, parse frontmatter for type
|
|
124
|
-
c. If type: mcp, verify MCP is configured in `.
|
|
124
|
+
c. If type: mcp, verify the MCP server is configured in your IDE's MCP configuration (the project's `.mcp.json`, or the file your IDE uses)
|
|
125
125
|
- If missing → **ERROR**: "Skill '{skill}' MCP not configured. Reinstall the skill."
|
|
126
126
|
All skills must resolve successfully before the pipeline starts (fail fast).
|
|
127
|
-
4. **Model tiers**:
|
|
128
|
-
- Read `crew.
|
|
129
|
-
|
|
130
|
-
- `standard`: mixed — research/data steps use `fast`, creative/review steps use `powerful`
|
|
131
|
-
- `full`: all steps use `model_tier: powerful` by default
|
|
132
|
-
- If a step has its own `model_tier` in frontmatter → step-level override takes priority over crew-level default.
|
|
133
|
-
- If neither crew tier nor step model_tier is set → default to `powerful` at dispatch.
|
|
127
|
+
4. **Model tiers**: a `subagent` step declares its own `model_tier` (`fast` or `powerful`), set at crew creation by the crew's tier; inline steps carry none.
|
|
128
|
+
- Read the crew's tier for the run header: `crew.tier` in `crew.yaml` (older crews: `tier` loose at the top level).
|
|
129
|
+
- A subagent step with no `model_tier` → `powerful` at dispatch.
|
|
134
130
|
|
|
135
131
|
4b. **Pre-Execution Agent Selection** — Decide which agents actually run for this task.
|
|
136
132
|
Run this step ONLY if `crew.yaml` declares an `agent_dependencies:` field (even an
|
|
@@ -221,11 +217,11 @@ Before starting execution:
|
|
|
221
217
|
```
|
|
222
218
|
When the selection step was skipped (no `agent_dependencies:` in crew.yaml), this is
|
|
223
219
|
identical to today: all agents listed, no Skipped line.
|
|
224
|
-
5b. **Initialize run folder**:
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
-
|
|
220
|
+
5b. **Initialize run folder**: the script names the run — never build the date or the time yourself:
|
|
221
|
+
- Run the `pasta` command (see "Output Path Transformation" below). It creates the folder of this run and answers `CAMINHO:OK crews/{name}/output/{run_id}`
|
|
222
|
+
- The `run_id` is the last segment of that path: `YYYY-MM-DD-HHmmss` from the computer's clock (e.g. `2026-03-03-143022`; `-2`, `-3` when that folder already exists)
|
|
223
|
+
- The date of this run, wherever one is asked below, is the first 10 characters of the `run_id`
|
|
224
|
+
- Never create a folder by command yourself
|
|
229
225
|
- Store `run_id` in working memory for this run — it will be used for ALL output paths
|
|
230
226
|
6. **Escritório** — if it is on, run `iniciar`, then one `pular` per deselected agent, one after the other (see "Escritório" below).
|
|
231
227
|
|
|
@@ -234,7 +230,7 @@ Before starting execution:
|
|
|
234
230
|
A local page that shows the crew at work, off by default. Follow this section only when the
|
|
235
231
|
already-loaded `preferences.md` has `Dashboard: enabled` (written `- **Dashboard:** enabled` or
|
|
236
232
|
plain `Dashboard: enabled`, any letter case); otherwise run none of these commands. When it is on,
|
|
237
|
-
run
|
|
233
|
+
run, from the project root, the one-line command of each moment:
|
|
238
234
|
|
|
239
235
|
| Moment | Command |
|
|
240
236
|
|---|---|
|
|
@@ -452,7 +448,7 @@ root the one-line command of each moment and read the last line (`CAMINHO:OK {pa
|
|
|
452
448
|
|
|
453
449
|
| Moment | Command |
|
|
454
450
|
|---|---|
|
|
455
|
-
| Start of the run (Initialization, step 5b) | `node _opencrew/core/scripts/caminho.mjs "{name}" pasta
|
|
451
|
+
| Start of the run (Initialization, step 5b) | `node _opencrew/core/scripts/caminho.mjs "{name}" pasta` (no `--run`: the script creates the `run_id`) |
|
|
456
452
|
| Before a step, for its `inputFile` | `node _opencrew/core/scripts/caminho.mjs "{name}" entrada --run "{run_id}" --arquivo "{inputFile}"` |
|
|
457
453
|
| Before a step writes, for the first `outputFile` of each group | `node _opencrew/core/scripts/caminho.mjs "{name}" saida --run "{run_id}" --arquivo "{outputFile}"` |
|
|
458
454
|
| After a step wrote, for each output file | `node _opencrew/core/scripts/caminho.mjs "{name}" conferir --arquivo "{path}"` |
|
|
@@ -480,7 +476,7 @@ root the one-line command of each moment and read the last line (`CAMINHO:OK {pa
|
|
|
480
476
|
and reads `…/v1/pesquisa.md`. Never assume `v1`.
|
|
481
477
|
- **Script that does not run** (no Node, an error, or no `CAMINHO:` line): tell the user once per
|
|
482
478
|
run `Não consegui rodar a conferência de caminhos; sigo pela regra escrita e marco os arquivos como não verificados.`,
|
|
483
|
-
build the path by the rule above (
|
|
479
|
+
build the path by the rule above (your file-writing tool creates the folder) and continue. A file handled
|
|
484
480
|
this way skips its gate and is listed at the final approval:
|
|
485
481
|
`{arquivo} — não verificado: a conferência de caminhos não rodou`.
|
|
486
482
|
|
|
@@ -522,10 +518,10 @@ root the one-line command of each moment and read the last line (`CAMINHO:OK {pa
|
|
|
522
518
|
- Read the step's `model_tier` frontmatter field (if present).
|
|
523
519
|
Valid values: `fast` or `powerful`. If absent or any other value: default to `powerful`.
|
|
524
520
|
- **Before building the subagent prompt**: Resolve all output paths referenced in the step file with the `saida` command (Output Path Transformation, once per group). Store the transformed path(s) in working memory — they will be used both in the prompt and in post-completion verification. Never pass raw paths from the step file to the subagent.
|
|
525
|
-
-
|
|
521
|
+
- Dispatch the step with your IDE's subagent mechanism (an IDE without one: run the step inline, in this conversation):
|
|
526
522
|
- If `model_tier: fast`: use the fastest/lightest model available in your current IDE.
|
|
527
523
|
- If `model_tier: powerful` or absent/invalid: use the default model (no model override needed)
|
|
528
|
-
- In the
|
|
524
|
+
- In the subagent's prompt, include:
|
|
529
525
|
- The full agent persona from the party CSV
|
|
530
526
|
- The full agent `.agent.md` content (persona, principles, voice guidance, anti-patterns)
|
|
531
527
|
- If the agent has tasks: include ALL task files in order with instructions to execute sequentially, piping output from each task to the next
|
|
@@ -562,16 +558,17 @@ root the one-line command of each moment and read the last line (`CAMINHO:OK {pa
|
|
|
562
558
|
(e.g. the organization's name, the main audience), ask: "Isso vale para todas as crews?
|
|
563
559
|
Atualizo o perfil da empresa?" — change `company.md` only after a yes.
|
|
564
560
|
- **If the step frontmatter contains `outputFile`**: after collecting the user's full response,
|
|
565
|
-
insert only the run_id in the `outputFile` path (item 1 of the rule in Output Path Transformation — no version folder, no `saida` command), then write the response to that path
|
|
566
|
-
|
|
561
|
+
insert only the run_id in the `outputFile` path (item 1 of the rule in Output Path Transformation — no version folder, no `saida` command), then write the response to that path with your file-writing tool (it creates the folder) before moving to the next step. Checkpoint files are user input captures, not versioned output: they live in the group itself, where `entrada` finds them.
|
|
562
|
+
For the checkpoint that precedes the researcher, use this format:
|
|
567
563
|
```
|
|
568
564
|
# Research Focus
|
|
569
565
|
|
|
570
566
|
**Topic:** {user's typed topic}
|
|
571
567
|
**Time Range:** {selected time range label, e.g., "Últimos 7 dias"}
|
|
572
|
-
**Date:** {
|
|
568
|
+
**Date:** {the date of this run, YYYY-MM-DD}
|
|
573
569
|
```
|
|
574
|
-
|
|
570
|
+
For any other checkpoint: `# {the checkpoint's title}`, the user's answer as given (the option chosen and every comment), and `**Date:** {the date of this run, YYYY-MM-DD}`.
|
|
571
|
+
This file is the `inputFile` of the step that follows.
|
|
575
572
|
|
|
576
573
|
### Post-Step Output Validation
|
|
577
574
|
|
|
@@ -602,7 +599,7 @@ After a step produces output (subagent or inline) and BEFORE Veto Condition Enfo
|
|
|
602
599
|
- If the step does not declare an `outputFile` (e.g., steps that only produce inline console output) → skip output validation.
|
|
603
600
|
- Checkpoint steps (`type: checkpoint`) are exempt — their output is the user's response, not a file.
|
|
604
601
|
|
|
605
|
-
**IMPORTANT**: Do NOT rely on reading the file
|
|
602
|
+
**IMPORTANT**: Do NOT rely on reading the file yourself to "verify" output: what you read can be misinterpreted. Use ONLY the `conferir` command — its last line is binary and cannot be hallucinated.
|
|
606
603
|
|
|
607
604
|
### Output Contract Validation
|
|
608
605
|
|
|
@@ -646,7 +643,7 @@ After an agent completes a step (before moving to the next step):
|
|
|
646
643
|
- Check each condition (e.g., "slides exceed 30 words", "no CTA", "missing sources")
|
|
647
644
|
3. If ANY veto condition is triggered:
|
|
648
645
|
- Inform user: "⚠️ {Agent Name}'s output triggered a veto: {condition}"
|
|
649
|
-
- Ask the agent to fix the specific issue (re-execute with targeted correction)
|
|
646
|
+
- Ask the agent to fix the specific issue (re-execute with targeted correction), then run the `conferir` command on the rewritten file again (Post-Step Output Validation) before judging the veto once more
|
|
650
647
|
- Maximum 2 veto fix attempts per step
|
|
651
648
|
- After 2 failed attempts, present to user for manual decision
|
|
652
649
|
- **Never auto-fix an irreversible step** (`side_effects: irreversible`): re-executing it
|
|
@@ -681,8 +678,8 @@ When a step has `on_reject: {step-id}` (a review step):
|
|
|
681
678
|
(real data only the user has): do NOT reject for them — the reviewer judges the rest, and the
|
|
682
679
|
final approval below collects the missing data from the user.
|
|
683
680
|
3. Track the review cycle count: a **cycle** is one pass of the reviewer. The maximum is
|
|
684
|
-
`max_review_cycles`, an integer from 1 declared where the step declares `on_reject` (the
|
|
685
|
-
frontmatter or its `pipeline.yaml` entry); absent or invalid: 3. On every rejection, with or
|
|
681
|
+
`max_review_cycles`, an integer from 1: the one declared where the step declares `on_reject` (the
|
|
682
|
+
step frontmatter or its `pipeline.yaml` entry); without it, the one in `crew.yaml`; absent or invalid in both: 3. On every rejection, with or
|
|
686
683
|
without a block, send the reviewer's feedback to the writer and go back to the referenced step.
|
|
687
684
|
4. If the last allowed pass also rejects, stop; the status of the last report picks the message, as
|
|
688
685
|
in item 2 — `VERIFICACAO:BLOQUEADA`: the blocks; any other status: the reviewer's feedback, also
|
|
@@ -781,7 +778,7 @@ One script turns the approved files into `crews/{name}/output/{run_id}/entrega/`
|
|
|
781
778
|
Then proceed to prepend the new row.
|
|
782
779
|
|
|
783
780
|
Read `crews/{name}/_memory/runs.md`. Prepend one new row to the table (immediately after the header row), with:
|
|
784
|
-
- `Data`:
|
|
781
|
+
- `Data`: the date of this run (the first 10 characters of the `run_id`)
|
|
785
782
|
- `Run ID`: the `run_id` for this execution
|
|
786
783
|
- `Tema`: the topic or user request from this run (1 sentence max)
|
|
787
784
|
- `Output`: brief description of what was generated (e.g., "Carrossel 9 slides", "Thread 7 posts")
|
|
@@ -41,7 +41,9 @@ export function erroDeArgumentos({ crew, acao, run, arquivo, secoes }) {
|
|
|
41
41
|
if (!crew) return 'Falta o nome da crew.';
|
|
42
42
|
if (!acao) return `Falta a ação. Ações: ${LISTA}.`;
|
|
43
43
|
if (!ACOES.includes(acao)) return `Ação desconhecida: ${limpar(acao)}. Ações: ${LISTA}.`;
|
|
44
|
-
|
|
44
|
+
// `pasta` sem --run: o script dá o nome à execução (data e hora do computador).
|
|
45
|
+
if (acao !== 'conferir' && acao !== 'pasta' && !run) return MSG.faltaOpcao('--run');
|
|
46
|
+
if (run === '') return MSG.faltaOpcao('--run'); // `--run` sem valor não vira "sem --run"
|
|
45
47
|
if (run !== undefined && !RUN.test(run)) return 'O --run só aceita letras, dígitos, ponto, sublinhado e hífen.';
|
|
46
48
|
if (acao !== 'pasta' && !arquivo) return MSG.faltaOpcao('--arquivo');
|
|
47
49
|
if (secoes !== undefined && !INTEIRO.test(secoes)) return 'O --secoes é um número inteiro a partir de 1.';
|
|
@@ -29,6 +29,22 @@ export function naExecucao(declarado, crew, run) {
|
|
|
29
29
|
return { grupo: [`${saida}${run}`, ...partes].join('/'), nome };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const dois = (n) => String(n).padStart(2, '0');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* O nome de uma execução nova: `AAAA-MM-DD-HHmmss` na hora local; se já existe pasta com esse
|
|
36
|
+
* nome, `-2`, `-3`… (spec fase-u5a-polimento-do-uso.md, decisão 2).
|
|
37
|
+
* @param {Date} agora · @param {string[]} existentes as pastas que já estão em `output/`
|
|
38
|
+
*/
|
|
39
|
+
export function novoRun(agora, existentes) {
|
|
40
|
+
const base = `${agora.getFullYear()}-${dois(agora.getMonth() + 1)}-${dois(agora.getDate())}-${dois(agora.getHours())}${dois(agora.getMinutes())}${dois(agora.getSeconds())}`;
|
|
41
|
+
const usados = new Set(existentes);
|
|
42
|
+
for (let n = 1; ; n++) {
|
|
43
|
+
const nome = n === 1 ? base : `${base}-${n}`;
|
|
44
|
+
if (!usados.has(nome)) return nome;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
/** O número de uma pasta de versão (`v` + número), ou `null` para qualquer outro nome. */
|
|
33
49
|
function numero(nome) {
|
|
34
50
|
const [, digitos] = /^v(\d{1,9})$/.exec(nome) ?? [];
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Caminho da execução de uma crew: diz onde cada passo grava, de onde lê e se o arquivo gravado
|
|
3
3
|
// está lá. Quem calcula é este script, igual em qualquer sistema — a IA não monta o caminho.
|
|
4
4
|
// Uso (na pasta do projeto): node _opencrew/core/scripts/caminho.mjs <crew> <ação> --run <id> [opções]
|
|
5
|
-
// pasta --run <id>
|
|
5
|
+
// pasta [--run <id>] cria crews/<crew>/output/<id>/; sem --run, o id é a
|
|
6
|
+
// data e a hora do computador (AAAA-MM-DD-HHmmss)
|
|
6
7
|
// saida --run <id> --arquivo <declarado> onde o passo grava (abre a pasta de versão seguinte)
|
|
7
8
|
// entrada --run <id> --arquivo <declarado> a saída mais nova desse arquivo
|
|
8
9
|
// conferir --arquivo <caminho já resolvido> [--secoes N] [--tldr]
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
import path from 'node:path';
|
|
18
19
|
import { MSG, dentroDoProjeto, ehPrincipal, realDentroDe } from './comum.mjs';
|
|
19
20
|
import { USO, erroDeArgumentos, lerArgs, limpar } from './caminho/argumentos.mjs';
|
|
20
|
-
import { MOTIVO, daMaisNova, motivoDeReprovacao, naExecucao, normalizar, proximaVersao } from './caminho/nucleo.mjs';
|
|
21
|
+
import { MOTIVO, daMaisNova, motivoDeReprovacao, naExecucao, normalizar, novoRun, proximaVersao } from './caminho/nucleo.mjs';
|
|
21
22
|
import { criarPasta, ehPasta, lerTexto, pastasDe, temConteudo } from './caminho/disco.mjs';
|
|
22
23
|
|
|
23
24
|
const ok = (caminho) => `CAMINHO:OK ${caminho}`;
|
|
@@ -31,7 +32,8 @@ const reprovado = (motivo) => `CAMINHO:REPROVADO ${motivo}`;
|
|
|
31
32
|
*/
|
|
32
33
|
function erroDoArquivo(raiz, crew, { acao, run, arquivo }) {
|
|
33
34
|
if (!dentroDoProjeto(raiz, arquivo)) return MSG.foraDoProjeto(limpar(arquivo));
|
|
34
|
-
|
|
35
|
+
// `pasta` não usa arquivo, e `conferir` recebe o caminho já resolvido: nenhum dos dois o põe na execução.
|
|
36
|
+
const local = acao === 'conferir' || acao === 'pasta' ? null : naExecucao(arquivo, crew, run);
|
|
35
37
|
if (!local) return null;
|
|
36
38
|
if (!local.nome) return `Falta o nome do arquivo em --arquivo: ${limpar(arquivo)}`;
|
|
37
39
|
const execucao = path.resolve(raiz, 'crews', crew, 'output', run);
|
|
@@ -88,23 +90,24 @@ function conferir(raiz, { arquivo, secoes, tldr }) {
|
|
|
88
90
|
return motivo ? reprovado(motivo) : ok(caminho);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
function responder(raiz, crew, args) {
|
|
93
|
+
function responder(raiz, crew, args, agora) {
|
|
92
94
|
if (args.acao === 'saida') return saida(raiz, crew, args);
|
|
93
95
|
if (args.acao === 'entrada') return entrada(raiz, crew, args);
|
|
94
96
|
if (args.acao === 'conferir') return conferir(raiz, args);
|
|
95
|
-
const
|
|
97
|
+
const run = args.run || novoRun(agora(), pastasDe(path.resolve(raiz, 'crews', crew, 'output')));
|
|
98
|
+
const pasta = `crews/${crew}/output/${run}`;
|
|
96
99
|
criarPasta(path.resolve(raiz, pasta));
|
|
97
100
|
return ok(pasta);
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
/**
|
|
101
104
|
* @param {string[]} argv
|
|
102
|
-
* @param {object} [deps] `cwd` (a pasta do projeto) e `
|
|
105
|
+
* @param {object} [deps] `cwd` (a pasta do projeto), `escrever` e `agora` (o relógio, para o id da execução)
|
|
103
106
|
* @returns {number} 0 = a linha `CAMINHO:` saiu · 1 = erro de uso, ou falha ao ler ou criar pasta
|
|
104
107
|
* (a linha de uso e o motivo, ou só o erro; sem linha `CAMINHO:`)
|
|
105
108
|
*/
|
|
106
109
|
export function main(argv, deps = {}) {
|
|
107
|
-
const { cwd = process.cwd(), escrever = (s) => process.stdout.write(`${s}\n`) } = deps;
|
|
110
|
+
const { cwd = process.cwd(), escrever = (s) => process.stdout.write(`${s}\n`), agora = () => new Date() } = deps;
|
|
108
111
|
const args = lerArgs(argv);
|
|
109
112
|
const local = localizar(cwd, args);
|
|
110
113
|
if (local.erro) {
|
|
@@ -113,7 +116,7 @@ export function main(argv, deps = {}) {
|
|
|
113
116
|
return 1;
|
|
114
117
|
}
|
|
115
118
|
try {
|
|
116
|
-
escrever(responder(cwd, local.crew, args));
|
|
119
|
+
escrever(responder(cwd, local.crew, args, agora));
|
|
117
120
|
return 0;
|
|
118
121
|
} catch (erro) {
|
|
119
122
|
escrever(`Não consegui resolver o caminho: ${limpar(erro?.message ?? erro)}`);
|
|
@@ -7,7 +7,8 @@ const plural = (n, um, varios) => `${n} ${n === 1 ? um : varios}`;
|
|
|
7
7
|
|
|
8
8
|
export const MSG = {
|
|
9
9
|
nadaACorrigir: 'Nada a corrigir.',
|
|
10
|
-
|
|
10
|
+
corrigido: (arquivo, copia) => `Corrigi: ${arquivo} (cópia: ${copia})`,
|
|
11
|
+
comoCorrigir: 'Para trocar os caminhos que têm sugestão, rode de novo com --corrigir (cada arquivo alterado ganha uma cópia).',
|
|
11
12
|
semCorrecaoAutomatica: (n) => `Não há correção automática para ${n} pendência(s): escolha um candidato ou corrija o caminho na crew.`,
|
|
12
13
|
buscaParcial: (limite) => `Procurei só nos primeiros ${limite} itens do projeto; pode existir um arquivo com esse nome que eu não vi.`,
|
|
13
14
|
linkParaFora: (arquivo) => `Não corrigi \`${arquivo}\`: é um link que aponta para fora da crew. O caminho citado nele continua como estava.`,
|
|
@@ -59,6 +60,7 @@ export function formatar(r) {
|
|
|
59
60
|
}
|
|
60
61
|
if (apontados.length) linhas.push(''); // sem pendência nem alerta, uma linha em branco só
|
|
61
62
|
const alertas = contar('nao-portatil') + contar('nao-conferido');
|
|
62
|
-
|
|
63
|
+
const pendentes = contar('faltando');
|
|
64
|
+
linhas.push(`**Resumo: ${plural(r.refs.length, 'fonte', 'fontes')} — ${contar('ok')} ok, ${plural(pendentes, 'pendente', 'pendentes')}, ${plural(alertas, 'alerta', 'alertas')}**`, '');
|
|
63
65
|
return linhas.join('\n');
|
|
64
66
|
}
|
|
@@ -87,18 +87,17 @@ export async function conferir({ raiz, crew, limite = LIMITE_DA_BUSCA }) {
|
|
|
87
87
|
return { crew, raiz, refs, status, buscaParcial: Boolean(ctx.indice?.parcial), limite };
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** @returns {Promise<string>} o caminho da cópia que ficou */
|
|
90
91
|
async function copiaDeSeguranca(arquivo) {
|
|
91
92
|
const bak = existsSync(`${arquivo}.bak`) ? `${arquivo}.bak-${new Date().toISOString().replace(/[:.]/g, '-')}` : `${arquivo}.bak`;
|
|
92
93
|
await copyFile(arquivo, bak);
|
|
94
|
+
return bak;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
/** Regrava a citação num arquivo; a cópia .bak é feita uma vez por arquivo. */
|
|
97
|
+
/** Regrava a citação num arquivo; a cópia .bak é feita uma vez por arquivo (`tocados`: arquivo → cópia). */
|
|
96
98
|
async function regravar(arquivo, item, tocados) {
|
|
97
99
|
const texto = await readFile(arquivo, 'utf8');
|
|
98
|
-
if (!tocados.has(arquivo))
|
|
99
|
-
await copiaDeSeguranca(arquivo);
|
|
100
|
-
tocados.add(arquivo);
|
|
101
|
-
}
|
|
100
|
+
if (!tocados.has(arquivo)) tocados.set(arquivo, await copiaDeSeguranca(arquivo));
|
|
102
101
|
await writeFile(arquivo, trocarCitacao(texto, item, path.basename(arquivo) === 'crew.yaml'));
|
|
103
102
|
}
|
|
104
103
|
|
|
@@ -118,9 +117,10 @@ function guardaDeEscrita(raiz, crew) {
|
|
|
118
117
|
* Troca, nos arquivos da crew, cada caminho com sugestão única — só a citação que a coleta leu,
|
|
119
118
|
* nunca um pedaço de outro texto. O que a guarda de escrita barra não muda: `avisar` recebe uma
|
|
120
119
|
* linha por arquivo pulado, ou uma só quando a crew inteira é um link para fora do projeto.
|
|
120
|
+
* `gravado` recebe cada arquivo alterado e a cópia que ficou dele.
|
|
121
121
|
* @returns quantos caminhos foram gravados, em ao menos um arquivo
|
|
122
122
|
*/
|
|
123
|
-
export async function corrigir({ resultado, avisar = () => {} }) {
|
|
123
|
+
export async function corrigir({ resultado, avisar = () => {}, gravado = () => {} }) {
|
|
124
124
|
const { raiz, crew } = resultado;
|
|
125
125
|
const comSugestao = resultado.refs.filter((i) => i.sugestao && i.estado !== 'ok');
|
|
126
126
|
const podeGravar = comSugestao.length ? guardaDeEscrita(raiz, crew) : null;
|
|
@@ -128,7 +128,7 @@ export async function corrigir({ resultado, avisar = () => {} }) {
|
|
|
128
128
|
if (comSugestao.length) avisar(MSG.crewLigadaParaFora(crew));
|
|
129
129
|
return 0;
|
|
130
130
|
}
|
|
131
|
-
const tocados = new
|
|
131
|
+
const tocados = new Map();
|
|
132
132
|
const pulados = new Set();
|
|
133
133
|
let gravados = 0;
|
|
134
134
|
for (const item of comSugestao) {
|
|
@@ -138,19 +138,22 @@ export async function corrigir({ resultado, avisar = () => {} }) {
|
|
|
138
138
|
if (dentro.length) gravados += 1;
|
|
139
139
|
}
|
|
140
140
|
for (const arquivo of pulados) avisar(MSG.linkParaFora(relativoAoProjeto(raiz, arquivo)));
|
|
141
|
+
for (const [arquivo, copia] of tocados) gravado(relativoAoProjeto(raiz, arquivo), path.basename(copia));
|
|
141
142
|
return gravados;
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* --corrigir: troca o que tem sugestão única e diz cada arquivo alterado, com a cópia; o relatório
|
|
147
|
+
* sai uma vez só — o de depois, quando algo mudou. Diz também o que pulou e o que ficou sem correção.
|
|
148
|
+
*/
|
|
145
149
|
async function corrigirEAvisar(r, escrever) {
|
|
146
150
|
const pulados = [];
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
151
|
+
const feitos = [];
|
|
152
|
+
const n = await corrigir({ resultado: r, avisar: (linha) => pulados.push(linha), gravado: (arquivo, copia) => feitos.push(MSG.corrigido(arquivo, copia)) });
|
|
153
|
+
const atual = n ? await conferir({ raiz: r.raiz, crew: r.crew }) : r;
|
|
154
|
+
for (const linha of feitos) escrever(linha);
|
|
155
|
+
if (n) escrever('');
|
|
156
|
+
escrever(formatar(atual));
|
|
154
157
|
for (const linha of pulados) escrever(linha);
|
|
155
158
|
const semSugestao = atual.refs.filter((i) => i.estado === 'faltando' && !i.sugestao).length;
|
|
156
159
|
if (semSugestao) escrever(MSG.semCorrecaoAutomatica(semSugestao));
|
|
@@ -177,8 +180,11 @@ export async function main(argv, { cwd = process.cwd(), escrever = (s) => proces
|
|
|
177
180
|
}
|
|
178
181
|
try {
|
|
179
182
|
let r = await conferir({ raiz: cwd, crew });
|
|
180
|
-
escrever(formatar(r));
|
|
181
183
|
if (argv.includes('--corrigir')) r = await corrigirEAvisar(r, escrever);
|
|
184
|
+
else {
|
|
185
|
+
escrever(formatar(r));
|
|
186
|
+
if (r.refs.some((i) => i.sugestao && i.estado !== 'ok')) escrever(MSG.comoCorrigir);
|
|
187
|
+
}
|
|
182
188
|
escrever(`FONTES:${r.status}`);
|
|
183
189
|
return 0;
|
|
184
190
|
} catch (erroDeLeitura) {
|