vibecoding-devtools 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +6 -5
  2. package/hooks/maestri-protocol.mjs +4 -3
  3. package/installer/cli.mjs +8 -0
  4. package/installer/codex-maestri.rules +13 -0
  5. package/installer/env-list.mjs +70 -0
  6. package/installer/execute.mjs +27 -4
  7. package/installer/json-merge.mjs +4 -4
  8. package/installer/plan.mjs +31 -1
  9. package/installer/team-checks.mjs +86 -0
  10. package/installer/team.mjs +260 -0
  11. package/npm-shrinkwrap.json +2 -2
  12. package/package.json +3 -1
  13. package/rules/maestri-usage.md +2 -0
  14. package/skills/maestri-agent-cli/SKILL.md +4 -2
  15. package/skills/maestri-agent-cli/references/antigravity.md +24 -0
  16. package/skills/maestri-agent-cli/references/codex.md +16 -0
  17. package/skills/maestri-protocol/SKILL.md +12 -7
  18. package/team/README.md +139 -0
  19. package/team/notes/project-context/acceptance-criteria.md +6 -0
  20. package/team/notes/project-context/code-standards.md +8 -0
  21. package/team/notes/project-context/contracts-integrations.md +10 -0
  22. package/team/notes/project-context/decisions.md +7 -0
  23. package/team/notes/project-context/design-system.md +8 -0
  24. package/team/notes/project-context/ops-runbook.md +10 -0
  25. package/team/notes/project-context/requirements.md +23 -0
  26. package/team/notes/project-context/risks-backlog.md +6 -0
  27. package/team/notes/team-roster.md +67 -0
  28. package/team/partitura/vibecoding-devtools.maestripartitura +4908 -0
  29. package/team/roles/_team-protocol.md +12 -0
  30. package/team/roles/appsec-auditor.md +45 -0
  31. package/team/roles/backend-architect.md +44 -0
  32. package/team/roles/backend-engineer.md +44 -0
  33. package/team/roles/code-reviewer.md +43 -0
  34. package/team/roles/devops-engineer.md +41 -0
  35. package/team/roles/frontend-engineer.md +45 -0
  36. package/team/roles/qa-engineer.md +41 -0
  37. package/team/roles/research-analyst.md +34 -0
  38. package/team/roles/task-runner.md +26 -0
  39. package/team/roles/team-maestro.md +80 -0
  40. package/team/roles/ux-ui-designer.md +49 -0
  41. package/team/roles/visual-asset-designer.md +45 -0
  42. package/team/roles/visual-qa-inspector.md +36 -0
  43. package/team/settings/task-runner-limited.json +55 -0
  44. package/team/team.json +59 -0
@@ -0,0 +1,260 @@
1
+ // Install the kit's Maestri team from team/team.json through the maestri CLI: roles, presets, agents, ropes, notes,
2
+ // portal and partitura. Maestri may run on another computer; this runs where the agent CLIs and `maestri` run.
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { execFileSync } from 'node:child_process';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { missingPrerequisites, realTools } from './team-checks.mjs';
9
+
10
+ export const TEAM_HELP = `Usage: vibecoding-devtools team [--roles-only] [--update-roles] [--dry-run]
11
+ Run inside a Maestri terminal in Maestro mode, on the machine where the agent CLIs run.
12
+ --roles-only Create the team's missing global roles only
13
+ --update-roles Also rewrite what differs from the kit: installed roles (restarts the agents that use them), the Maestro
14
+ preset command and the Task Runner settings file
15
+ --dry-run Print the full maestri commands without running them
16
+ Without --roles-only it also writes the Task Runner settings file to ~/.vibecoding-devtools/team/, creates the Maestro
17
+ preset, recruits the agents, notes and portal you do not reach yet, wires them, and saves the global partitura unless
18
+ one with its name exists.
19
+ Before any change, also with --dry-run, it checks read-only that the CLIs, the models in team.json and ECC in Claude
20
+ Code exist; missing items or conflicts stop the run with exit 1. If a run fails midway, run the same command again: it
21
+ skips what exists and finishes the rest.`;
22
+
23
+ const FLAGS = ['--roles-only', '--update-roles', '--dry-run'];
24
+
25
+ // `- "Name"` entries of `maestri role|preset|partitura list`.
26
+ export const listedNames = (output) => [...output.matchAll(/^\s*- "([^"]+)"/gm)].map((m) => m[1]);
27
+
28
+ // Top-level sections of `maestri list` (what the caller reaches); nested `⎿` lines belong to other agents' trees.
29
+ export function canvasEntries(output) {
30
+ const found = { agents: new Map(), notes: new Set(), portals: new Set(), ficharios: new Set() };
31
+ let section = null;
32
+ for (const line of output.split('\n')) {
33
+ if (/^\S/.test(line)) section = /^Connected (agents|notes|portals)/.exec(line)?.[1] ?? null;
34
+ else if (section && !line.trimStart().startsWith('⎿')) {
35
+ const fichario = /^\s*fichário "([^"]+)":/.exec(line);
36
+ const entry = /^\s*- name: "([^"]+)"(?:, role: "([^"]+)")?/.exec(line);
37
+ if (fichario) found.ficharios.add(fichario[1]);
38
+ else if (entry && section === 'agents') found.agents.set(entry[1], entry[2] ?? null);
39
+ else if (entry) found[section].add(entry[1]);
40
+ }
41
+ }
42
+ return found;
43
+ }
44
+
45
+ export const roleText = (role, protocol) => `${role.trimEnd()}\n\n${protocol.trimEnd()}`;
46
+
47
+ // `maestri preset show` prints `Command: ...` and `Maestro: on|off`.
48
+ export const presetShown = (output) => ({
49
+ command: output.match(/^Command: (.*)$/m)?.[1]?.trim(),
50
+ maestro: /^Maestro: on$/m.test(output),
51
+ });
52
+
53
+ // maestri decodes \n and \\ in note content; keep backslashes literal.
54
+ const noteText = (text) => text.trimEnd().replaceAll('\\', '\\\\');
55
+
56
+ // Session settings live in a stable file under the home folder, not in the kit's (npx cache) folder.
57
+ export const settingsPath = (home, file) => path.join(home, '.vibecoding-devtools', 'team', path.basename(file));
58
+
59
+ // One shell argument that POSIX shells and PowerShell read the same way.
60
+ function shellArgument(value) {
61
+ if (/["`$]/.test(value)) throw new Error(`The path ${value} has a character the agent command cannot quote.`);
62
+ return /^[\w./\\:@-]+$/.test(value) ? value : `"${value}"`;
63
+ }
64
+
65
+ function agentCommand(agent, home) {
66
+ if (!agent.settingsFile) return agent.command;
67
+ return agent.command.replace('{settings}', () => shellArgument(settingsPath(home, agent.settingsFile)));
68
+ }
69
+
70
+ // Files the team needs on the machine that runs the agents: [{ path, content }].
71
+ export const teamFiles = (manifest, read, home) => manifest.agents.filter((a) => a.settingsFile).map((a) => ({
72
+ path: settingsPath(home, a.settingsFile),
73
+ content: `${JSON.stringify(JSON.parse(read(a.settingsFile)), null, 2)}\n`,
74
+ }));
75
+
76
+ const presetDiffers = (found, preset) => found.command !== preset.command || found.maestro !== Boolean(preset.maestro);
77
+
78
+ const kitRoles = (manifest, read) => {
79
+ const protocol = read(manifest.protocol);
80
+ return manifest.agents.map(({ role, roleFile }) => ({ role, text: roleText(read(roleFile), protocol) }));
81
+ };
82
+
83
+ // Everything that would make the run touch something that is not the kit's: checked before any change.
84
+ export function teamConflicts(manifest, read, existing, options = {}) {
85
+ const { rolesOnly = false, updateRoles = false, home = os.homedir() } = options;
86
+ const conflicts = [];
87
+ for (const { role, text } of kitRoles(manifest, read)) {
88
+ if (existing.roles.has(role) && existing.roles.get(role) !== text && !updateRoles) {
89
+ conflicts.push(`role "${role}" exists with other text; rerun with --update-roles to replace it`);
90
+ }
91
+ }
92
+ if (rolesOnly) return conflicts;
93
+ for (const preset of manifest.presets) {
94
+ const found = existing.presets.get(preset.name);
95
+ if (found && presetDiffers(found, preset) && !updateRoles) {
96
+ conflicts.push(`preset "${preset.name}" exists with another command or Maestro setting; `
97
+ + 'rerun with --update-roles to replace it');
98
+ }
99
+ }
100
+ for (const { path: file, content } of teamFiles(manifest, read, home)) {
101
+ if (existing.files.has(file) && existing.files.get(file) !== content && !updateRoles) {
102
+ conflicts.push(`settings file ${file} exists with other content; rerun with --update-roles to replace it`);
103
+ }
104
+ }
105
+ const { agents, notes, portals, ficharios } = existing.canvas;
106
+ const kinds = { agent: agents, note: notes, portal: portals, fichário: ficharios };
107
+ const misused = (name, kind) => Object.entries(kinds).find(([k, set]) => k !== kind && set.has(name))?.[0];
108
+ for (const agent of manifest.agents) {
109
+ if (agents.has(agent.name) && agents.get(agent.name) !== agent.role) {
110
+ conflicts.push(`agent "${agent.name}" exists with role "${agents.get(agent.name)}", not "${agent.role}"`);
111
+ }
112
+ }
113
+ const named = [...manifest.agents.map((a) => [a.name, 'agent']), ...manifest.notes.map((n) => [n.name, 'note']),
114
+ ...manifest.fichario.pages.map((n) => [n.name, 'note']), [manifest.fichario.name, 'fichário'],
115
+ ...manifest.portals.map((x) => [x.name, 'portal'])];
116
+ for (const [name, kind] of named) {
117
+ const other = misused(name, kind);
118
+ if (other) conflicts.push(`${other} "${name}" you reach has a name the kit needs for its ${kind}`);
119
+ }
120
+ return conflicts;
121
+ }
122
+
123
+ // Pure plan: maestri argument lists, skipping what already exists.
124
+ export function teamCommands(manifest, read, existing, options = {}) {
125
+ const { rolesOnly = false, updateRoles = false, home = os.homedir() } = options;
126
+ const commands = [];
127
+ for (const { role, text } of kitRoles(manifest, read)) {
128
+ if (!existing.roles.has(role)) commands.push(['role', 'create', role, text, '--scope', 'global']);
129
+ else if (updateRoles && existing.roles.get(role) !== text) commands.push(['role', 'write', role, text]);
130
+ }
131
+ if (rolesOnly) return commands;
132
+
133
+ for (const preset of manifest.presets) {
134
+ const found = existing.presets.get(preset.name);
135
+ const maestro = preset.maestro ? 'on' : 'off';
136
+ const settings = ['--command', preset.command, '--maestro', maestro];
137
+ if (!found) commands.push(['preset', 'create', preset.name, ...settings, '--quick-start', 'off']);
138
+ else if (updateRoles && presetDiffers(found, preset)) commands.push(['preset', 'set', preset.name, ...settings]);
139
+ }
140
+ for (const agent of manifest.agents.filter((a) => !existing.canvas.agents.has(a.name))) {
141
+ const launch = agent.preset ? ['--preset', agent.preset] : ['--command', agentCommand(agent, home)];
142
+ commands.push(['recruit', agent.name, '--role', agent.role, '--color', agent.color, '--icon', agent.icon, ...launch]);
143
+ }
144
+ const others = manifest.agents.map((a) => a.name).filter((n) => n !== manifest.maestro);
145
+ for (const [a, b] of [...others.map((n) => [manifest.maestro, n]), ...manifest.peers]) commands.push(['connect', a, b]);
146
+ for (const note of manifest.notes) {
147
+ if (!existing.canvas.notes.has(note.name)) commands.push(['note', 'create', noteText(read(note.file)), '--name', note.name, '--color', note.color]);
148
+ for (const agent of note.connect) commands.push(['connect', note.name, agent]);
149
+ }
150
+ for (const page of manifest.fichario.pages.filter((p) => !existing.canvas.notes.has(p.name))) {
151
+ commands.push(['note', 'create', noteText(read(page.file)), '--name', page.name, '--stack', manifest.fichario.name]);
152
+ }
153
+ for (const agent of manifest.fichario.connect) commands.push(['connect', manifest.fichario.name, agent]);
154
+ for (const portal of manifest.portals) {
155
+ if (!existing.canvas.portals.has(portal.name)) commands.push(['portal', 'create', portal.url, portal.name]);
156
+ for (const agent of portal.connect) commands.push(['connect', agent, portal.name]);
157
+ }
158
+ const { partitura } = manifest;
159
+ if (!existing.partituras.has(partitura.name)) {
160
+ commands.push(['partitura', 'create', partitura.name, ...manifest.agents.map((a) => a.name), '--without-me',
161
+ '--scope', 'global', '--commands', 'on', '--note-text', 'on', '--chat-history', 'off',
162
+ '--description', partitura.description, '--icon', partitura.icon, '--color', partitura.color]);
163
+ }
164
+ return commands;
165
+ }
166
+
167
+ // Settings files to write: the ones missing, and with --update-roles the ones that differ. Never with --roles-only.
168
+ export function teamFileWrites(manifest, read, existing, options = {}) {
169
+ const { rolesOnly = false, updateRoles = false, home = os.homedir() } = options;
170
+ if (rolesOnly) return [];
171
+ return teamFiles(manifest, read, home).filter(({ path: file, content }) => !existing.files.has(file)
172
+ || (updateRoles && existing.files.get(file) !== content));
173
+ }
174
+
175
+ const quoted = (a) => (/^[\w./#@:-]+$/.test(a) ? a : JSON.stringify(a));
176
+ const shown = (args, full) => `maestri ${args.map((a) => quoted(full || a.length <= 60 ? a : `${a.slice(0, 57)}...`)).join(' ')}`;
177
+
178
+ const readIfExists = (file) => {
179
+ try {
180
+ return fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
181
+ } catch (e) {
182
+ if (e.code === 'ENOENT') return undefined;
183
+ throw e;
184
+ }
185
+ };
186
+
187
+ const writeTeamFile = ({ path: file, content }) => {
188
+ fs.mkdirSync(path.dirname(file), { recursive: true });
189
+ fs.writeFileSync(file, content);
190
+ };
191
+
192
+ // Everything read-only: prerequisites, then what Maestri and the home folder hold now. Throws before any change.
193
+ function planInstall({ manifest, read, run, bin, home, tools, options }) {
194
+ const missing = missingPrerequisites(manifest, tools, { maestri: bin, rolesOnly: options.rolesOnly });
195
+ if (missing.length) throw new Error(`Nothing changed. Missing prerequisites:\n- ${missing.join('\n- ')}`);
196
+ const list = run(bin, ['list']);
197
+ if (!/maestro: true/.test(list.split('\n\n')[0])) throw new Error('Run this from a Maestri terminal in Maestro mode.');
198
+ const roleNames = new Set(listedNames(run(bin, ['role', 'list'])));
199
+ const presetNames = new Set(listedNames(run(bin, ['preset', 'list'])));
200
+ const files = teamFiles(manifest, read, home).map((f) => [f.path, readIfExists(f.path)])
201
+ .filter(([, text]) => text !== undefined);
202
+ const existing = {
203
+ roles: new Map(manifest.agents.filter((a) => roleNames.has(a.role))
204
+ .map((a) => [a.role, run(bin, ['role', 'show', a.role]).replace(/^Role: [^\n]*\n\n/, '').trimEnd()])),
205
+ presets: new Map(manifest.presets.filter((p) => presetNames.has(p.name))
206
+ .map((p) => [p.name, presetShown(run(bin, ['preset', 'show', p.name]))])),
207
+ files: new Map(files),
208
+ canvas: canvasEntries(list),
209
+ partituras: new Set(listedNames(run(bin, ['partitura', 'list']))),
210
+ };
211
+ const conflicts = teamConflicts(manifest, read, existing, options);
212
+ if (conflicts.length) throw new Error(`Nothing changed. Conflicts:\n- ${conflicts.join('\n- ')}`);
213
+ return [
214
+ ...teamFileWrites(manifest, read, existing, options).map((file) => {
215
+ const text = `${existing.files.has(file.path) ? 'replace' : 'write'} ${file.path}`;
216
+ return { full: text, brief: text, go: () => writeTeamFile(file) };
217
+ }),
218
+ ...teamCommands(manifest, read, existing, options)
219
+ .map((args) => ({ full: shown(args, true), brief: shown(args, false), go: () => run(bin, args) })),
220
+ ];
221
+ }
222
+
223
+ export async function teamMain(argv, { kit = fileURLToPath(new URL('../', import.meta.url)), out = console.log,
224
+ err = console.error, env = process.env, home = os.homedir(), tools = realTools({ env }),
225
+ run = (bin, args) => execFileSync(bin, args, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }) } = {}) {
226
+ if (argv.includes('--help')) { out(TEAM_HELP); return 0; }
227
+ const unknown = argv.filter((a) => !FLAGS.includes(a));
228
+ if (unknown.length) { err(`Unknown argument: ${unknown[0]}\n${TEAM_HELP}`); return 2; }
229
+ const options = { rolesOnly: argv.includes('--roles-only'), updateRoles: argv.includes('--update-roles'), home };
230
+ const dryRun = argv.includes('--dry-run');
231
+ const dir = path.join(kit, 'team');
232
+ const read = (file) => fs.readFileSync(path.join(dir, file), 'utf8');
233
+ const bin = env.MAESTRI_CLI || 'maestri';
234
+ let steps;
235
+ try {
236
+ steps = planInstall({ manifest: JSON.parse(read('team.json')), read, run, bin, home, tools, options });
237
+ } catch (e) {
238
+ err(e.stderr?.toString().trim() || e.message);
239
+ return 1;
240
+ }
241
+ out('Prerequisites found.');
242
+ if (!steps.length) out('The team is already installed as the kit defines it.');
243
+ for (const [index, step] of steps.entries()) {
244
+ out(dryRun ? step.full : step.brief);
245
+ if (dryRun) continue;
246
+ try {
247
+ step.go();
248
+ } catch (e) {
249
+ err(e.stderr?.toString().trim() || e.message);
250
+ err(`Stopped at step ${index + 1} of ${steps.length} (${index} done). Run the same command again to resume: `
251
+ + 'it skips what exists and finishes the rest.');
252
+ return 1;
253
+ }
254
+ }
255
+ if (!options.rolesOnly && !dryRun) {
256
+ out('Next: answer each new agent\'s startup prompts as the maestri-agent-cli skill describes, sign in to Gemini '
257
+ + 'in the portal, and run the check in team/README.md (Check section).');
258
+ }
259
+ return 0;
260
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "vibecoding-devtools",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "vibecoding-devtools",
9
- "version": "1.0.2",
9
+ "version": "1.1.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@clack/prompts": "1.8.1"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecoding-devtools",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Rules, skills, hooks and templates for AI development with Claude Code, Codex and Antigravity.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,6 +16,7 @@
16
16
  "installer/*.mjs",
17
17
  "!installer/*.test.mjs",
18
18
  "installer/third-party.json",
19
+ "installer/codex-maestri.rules",
19
20
  "hooks/*.mjs",
20
21
  "!hooks/*.test.mjs",
21
22
  "!hooks/README.md",
@@ -24,6 +25,7 @@
24
25
  "rules/",
25
26
  "skills/",
26
27
  "templates/",
28
+ "team/",
27
29
  "README.md",
28
30
  "LICENSE",
29
31
  "npm-shrinkwrap.json"
@@ -10,3 +10,5 @@ Applies only when you are the Maestro in Maestri coordinating other agents. Deta
10
10
  managed, and only between tasks.
11
11
  - Before an unrelated task, clear the agent's context (command in its CLI reference). Never compact or clear an agent
12
12
  in the middle of a task.
13
+ - Do not ask the user a question while a delegated task can still report back: the agent's ask back lands in your
14
+ input and can overwrite the user's answer. Collect pending replies first, or warn the user that one may arrive.
@@ -137,8 +137,10 @@ Answering another agent's permission prompt approves an action on the user's beh
137
137
  ## 5. Configure, safest first
138
138
 
139
139
  1. **Launch flags**: model, effort and permission flags in the preset command, applied with
140
- `maestri recruit "Name" --command "..."` (or `--replace "Name"`) or a preset. Each reference's **Model and effort**
141
- says where the CLI lists its exact model ids and how to pass them: never guess an id.
140
+ `maestri recruit "Name" --command "..."` or a preset. On a running agent, `maestri recruit --replace "Name"
141
+ --command "..."` keeps its role, connections and position; it works although `maestri recruit --help` lists only
142
+ `--preset` and `--account` with `--replace` (verified 2026-11-06). Each reference's **Model and effort** says where
143
+ the CLI lists its exact model ids and how to pass them: never guess an id.
142
144
  2. **The CLI's settings file**, when the reference names it.
143
145
  3. **The TUI menu** (`/model`, `/config`...) through `--raw`, step by step.
144
146
 
@@ -71,6 +71,30 @@ See the [CLI permission reference](https://antigravity.google/docs/permissions#c
71
71
 
72
72
  `--dangerously-skip-permissions` auto-approves every tool request. Use only at the user's explicit request.
73
73
 
74
+ ### Verified limited agent
75
+
76
+ Verified on Antigravity CLI 1.5.2, Linux, Google AI Pro (2026-11-06), with the global `toolPermission` set to
77
+ `always-proceed`:
78
+
79
+ ```bash
80
+ agy --model <id> --effort <level> --mode accept-edits --add-dir ../../.. --settings <file>
81
+ ```
82
+
83
+ `<file>` is a JSON file with `"toolPermission": "request-review"` and the `permissions` lists; session settings
84
+ override the global value. Prefer a file over inline JSON: inline JSON needs different quoting in POSIX shells and
85
+ PowerShell. Keep the file at a stable path (the kit's `team` command writes it to
86
+ `~/.vibecoding-devtools/team/task-runner-limited.json`), not in a cache that can disappear. Verified with a file in a
87
+ throwaway folder: an allowed command (`ls`) ran, a denied one (`rm -f a.txt`) was refused with `Matches user-configured
88
+ deny rule` and the file stayed, and an unlisted one (`uname -s`) showed the command prompt (headless `-p` auto-denies
89
+ it). A complete allowlist for a helper agent is in `team/settings/task-runner-limited.json`.
90
+
91
+ ### Recruited with a role
92
+
93
+ - The first start in each role folder (`<workspace>/.maestri/roles/<id>/`) asks `Do you trust the contents of this
94
+ project?`; Enter trusts it and saves it in `trustedWorkspaces`. A folder Maestri just created holds only the role
95
+ files.
96
+ - Add `--add-dir ../../..` so the workspace root is part of the agent's workspace.
97
+
74
98
  ## Permission prompts
75
99
 
76
100
  File edit or creation:
@@ -48,6 +48,19 @@ Send the text, then `\n` in a separate `--raw` call.
48
48
  - Changing the permission mode lowers or raises what the agent may do on the user's behalf: ask the user first.
49
49
  - `/approve` approves one retry of a recent auto-review denial.
50
50
 
51
+ ## Recruited with a role
52
+
53
+ Verified on Codex 0.167.0, Linux (2026-11-06).
54
+
55
+ - A recruit with a role starts in `<workspace>/.maestri/roles/<id>/`, and workspace-write covers only that folder. Add
56
+ `--add-dir ../../..` to the command: `/status` then shows `Workspace [<workspace root>]`. A folder every Codex agent
57
+ may write goes in `~/.codex/config.toml` as `[sandbox_workspace_write]` `writable_roots = ["<path>"]`.
58
+ - `AGENTS.md` from the role folder and from the repository both load (`/status` → `Agents.md`).
59
+ - Startup can stop on **Hooks need review** (`1 hook is new or changed`). With the ECC plugin, its `SessionStart` hook
60
+ asked again at every start: Codex re-syncs the plugin cache on launch and the stored content hash no longer matches.
61
+ Esc skips it (that hook does not run; trusted hooks keep running); `t` in the review panel trusts it until the next
62
+ start. Trusting is a persistent choice: ask the user once, then repeat it at each start.
63
+
51
64
  ## Autonomous launch and command rules
52
65
 
53
66
  Verified on Codex 0.166.0, Windows (2026-11-06): local help, isolated rule checks and a live recruited agent.
@@ -185,6 +198,9 @@ Codex Document Control and Safety Settings. Maestri's skills in the list come fr
185
198
  - With `--no-daemon` the startup warning is:
186
199
  `Running without the shared background server: Bedrock sign-in requires embedded mode.`
187
200
 
201
+ - Startup warning `Codex could not find bubblewrap on PATH` on Linux: Codex falls back to its bundled bubblewrap.
202
+ Installing the OS package removes the warning.
203
+
188
204
  ## Not yet observed
189
205
 
190
206
  Limit-reached screen, logged-out screen, API or network errors.
@@ -34,8 +34,8 @@ were given, or a question that is not a task) starts with one header line:
34
34
  - Summary: one line, no secrets.
35
35
 
36
36
  Details, evidence and lists go on the following lines; in PowerShell, use real line breaks, not `\n`. Keep the whole
37
- message inside quotes and avoid double quotes, backticks and `$` in it, which break the shell command. Never send a bare
38
- `ok` or `done`. One report per task; post progress with `maestri floor status`.
37
+ message inside double quotes and avoid unescaped double quotes, backticks and `$` in it, which break the shell command.
38
+ Never send a bare `ok` or `done`. One report per task; post progress with `maestri floor status`.
39
39
 
40
40
  ```text
41
41
  maestri ask "Maestro" "~ Hawk | DONE | T-12 | login screen wired, tests green, PR #412 open"
@@ -46,18 +46,23 @@ maestri ask "Maestro" "~ Kite | ACK | design-spec | reply format adopted"
46
46
  ## Delegating a task
47
47
 
48
48
  Give each task a ref (a task id like `T-12`, or the PR, branch or decision it concerns) and end the prompt with this
49
- footer, filled in with your name and the ref. Do this in `--batch` prompts too, and never ask for a bare `ok` reply.
49
+ footer, filled in with your name (the address it reports to), the agent's name (who writes the report) and the ref.
50
+ Do this in `--batch` prompts too, and never ask for a bare `ok` reply.
50
51
 
51
52
  ```text
52
53
  ref: T-12
53
54
  When done, load the maestri-protocol skill and report back with:
54
- maestri ask "Your Name" "~ Your Name | STATUS | T-12 | one-line summary"
55
+ maestri ask "Your Name" "~ Agent Name | STATUS | T-12 | one-line summary"
55
56
  STATUS is one of DONE, PENDING, BLOCKED, ERROR, ACK. Details go on the following lines.
56
57
  ```
57
58
 
58
- Your name is under `You:` in `maestri list`; when the agent is in another workspace, use the `Name @ Workspace` address
59
- shown right under `You:` instead. The footer carries the whole format, so the agent can follow it even if it does not
60
- load this skill.
59
+ When the prompt itself is wrapped in double quotes, escape the footer's quotes (`\"`) or write that one line with
60
+ single quotes; the protocol hook accepts both. Your name is under `You:` in `maestri list`; when the agent is in another
61
+ workspace, use the `Name @ Workspace` address shown right under `You:` instead. The footer carries the whole format, so
62
+ the agent can follow it even if it does not load this skill.
63
+
64
+ Pass each message as a literal argument. The protocol hook cannot read a message built at run time, such as
65
+ `"$(cat task.md)"` or a variable, so such a message skips the check without warning.
61
66
 
62
67
  ## Receiving a message
63
68
 
package/team/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Web development team
2
+
3
+ A permanent multi-agent team for the Maestri canvas: one Maestro and twelve specialists for web apps, APIs,
4
+ automations, bots and services. This folder is the versioned source of the team. Maestri holds the live copy as global
5
+ roles and the global partitura `vibecoding-devtools`.
6
+
7
+ ## Layout
8
+
9
+ | Path | Content |
10
+ |---|---|
11
+ | `roles/<role>.md` | Role prompt of each agent, without the shared block |
12
+ | `roles/_team-protocol.md` | Block appended to every role when it is installed |
13
+ | `notes/team-roster.md` | Permanent `Team Roster` note: agents, pools, topology, approvals, contingencies |
14
+ | `notes/project-context/*.md` | Blank pages of the `Project Context` fichário, filled per project |
15
+ | `team.json` | Manifest: agents, launch commands, colors, ropes, notes, portal and partitura |
16
+ | `settings/task-runner-limited.json` | Antigravity session allowlist for the Task Runner; installed as a file in `~/.vibecoding-devtools/team/` |
17
+ | `partitura/vibecoding-devtools.maestripartitura` | Export of the partitura from the Maestri app: roles, commands, notes, portal, ropes |
18
+
19
+ The prompts rely on the kit rules (`base`, `risk-process`, `testing`, `maestri-messaging`) and the
20
+ `maestri-protocol`, `risk-triage`, `security-surfaces`, `maestri-agent-cli` and `maestri-budget` skills, installed for
21
+ every CLI by the kit installer; they do not repeat them.
22
+
23
+ ## Agents
24
+
25
+ | Agent | Role | Launch command |
26
+ |---|---|---|
27
+ | Regente | Team Maestro | preset `Team Maestro` (Maestro on): `claude --model claude-opus-5-5 --effort high --permission-mode auto --add-dir ../../..` |
28
+ | Vitral | Frontend Engineer | `claude --model claude-sonnet-5-5 --effort high --permission-mode auto --add-dir ../../..` |
29
+ | Bússola | UX/UI Designer | `claude --model claude-opus-5-5 --effort high --permission-mode auto --add-dir ../../..` |
30
+ | Alicerce | Backend Architect | `claude --model claude-sonnet-5-5 --effort high --permission-mode auto --add-dir ../../..` |
31
+ | Sentinela | AppSec Auditor | `claude --model claude-opus-5-5 --effort medium --permission-mode auto --add-dir ../../.. --disallowedTools Edit Write NotebookEdit` |
32
+ | Crivo | Code Reviewer | same as Sentinela |
33
+ | Guindaste | DevOps Engineer | `claude --model claude-sonnet-5-5 --effort medium --permission-mode auto --add-dir ../../..` |
34
+ | Forja | Backend Engineer | `codex --no-daemon --approve-for-me -m gpt-6.1-sol -c model_reasoning_effort=medium --add-dir ../../..` |
35
+ | Ensaio | QA Engineer | same as Forja |
36
+ | Aquarela | Visual Asset Designer | `codex --no-daemon --approve-for-me -m gpt-6.1-sol -c model_reasoning_effort=low --add-dir ../../..` |
37
+ | Retina | Visual QA Inspector | `agy --model gemini-4-argon-medium --effort medium --mode auto --add-dir ../../..` |
38
+ | Sonda | Research Analyst | `agy --model gemini-4-argon-high --effort high --mode auto --add-dir ../../..` |
39
+ | Formiga | Task Runner | `agy --model gemini-3.8-flash-medium --effort medium --mode accept-edits --add-dir ../../.. --settings <file>` |
40
+
41
+ - Recruits start in `<workspace>/.maestri/roles/<id>/`, so `--add-dir ../../..` always points at the workspace root.
42
+ Every Claude command has it too. It is variadic: keep it before `--disallowedTools` or last. The preset
43
+ `Team Maestro` (its own name, so the kit never creates or changes a generic preset such as `Maestro Opus High`)
44
+ carries it as well; start a Maestro with it from a role folder, since from the workspace root `../../..` points
45
+ three folders above it.
46
+ - `<file>` is `task-runner-limited.json` in `~/.vibecoding-devtools/team/` (a path, quoted if it has spaces), written by
47
+ `team` from `settings/task-runner-limited.json`: a stable path that does not depend on the npx cache and works in
48
+ POSIX shells and PowerShell. Commands outside its allowlist prompt.
49
+ - Model ids change: check `codex debug models` and `agy models` before reusing a command.
50
+
51
+ ## Topology
52
+
53
+ Regente connects to every agent. Peer ropes: Vitral with Bússola, Alicerce, Ensaio, Crivo and Retina; Bússola with
54
+ Retina and Aquarela; Alicerce with Forja, Guindaste and Sentinela; Forja with Ensaio, Crivo and Sentinela; Guindaste
55
+ with Sentinela and Crivo. Sonda and Formiga talk only to Regente. `Team Roster` is wired to Regente; the
56
+ `Project Context` fichário to Regente and every agent except Sonda, Aquarela and Formiga; the `Gemini` portal to
57
+ Aquarela.
58
+
59
+ ## Install
60
+
61
+ Run on the machine where the agent CLIs run, from a Maestri terminal in Maestro mode. Maestri itself may run on another
62
+ computer (see [Maestri on a remote machine](../docs/maestri-remote.md)).
63
+
64
+ ```bash
65
+ npx vibecoding-devtools team --dry-run # preview the maestri commands
66
+ npx vibecoding-devtools team --roles-only # create or update the global roles
67
+ npx vibecoding-devtools team # also preset, agents, ropes, notes, portal and partitura
68
+ ```
69
+
70
+ `team/team.json` is the manifest the command reads. Before running any `maestri` command, also with `--dry-run`, it
71
+ checks read-only that everything the launch commands need exists, lists what is missing together and stops with exit 1:
72
+
73
+ - each CLI on `PATH` (`maestri`, `claude`, `codex`, `agy`);
74
+ - each Codex model in `codex debug models` and each Antigravity model in `agy models`; Claude models are not checked,
75
+ since there is no offline list;
76
+ - ECC in Claude Code (`claude plugin list` shows `ecc@`), because the Team Maestro role uses `/ecc:plan`.
77
+
78
+ `--roles-only` launches nothing and needs only `maestri`. Then, before changing anything, it stops on conflicts: a role
79
+ with other text, a preset with another command or Maestro setting, a Task Runner settings file with other content (each
80
+ replaced with `--update-roles`), and an agent you reach with another role. It writes the settings file when missing,
81
+ leaves identical roles and files alone and skips agents, notes and the portal you already reach and a partitura that
82
+ already has the name. `--dry-run` prints `write <path>` for the file and writes nothing. Then answer each agent's
83
+ startup prompts and sign in to Gemini in the portal.
84
+
85
+ ### Resume after a failure
86
+
87
+ The run is safe to repeat. If a step fails midway (a recruit that errors, a lost connection), the command prints the
88
+ `maestri` error, the step it stopped at and `Run the same command again to resume`. Running the same command again
89
+ skips what exists (roles, preset, settings file, agents, notes, portal, partitura), `maestri connect` on an existing
90
+ rope prints `already connected` and exits 0, and the rest is created. A conflict or a missing prerequisite still stops
91
+ before any change.
92
+
93
+ Without the CLI, import `partitura/vibecoding-devtools.maestripartitura` in the Maestri app. The file carries the 13
94
+ role prompts with the team protocol, the launch commands, the notes and the portal, matching `roles/`, `notes/` and
95
+ `team.json`; it holds no chat history.
96
+
97
+ ## Check
98
+
99
+ After an install, as the Maestro (Regente) or the installer:
100
+
101
+ 1. `npx vibecoding-devtools team --dry-run` prints only `connect` lines: roles, preset, agents, notes, portal and
102
+ partitura match the kit.
103
+ 2. Each agent's screen shows its model and effort, and the expected mode (`auto mode on`, `Workspace [...] (Approve for
104
+ me)` in Codex `/status`, `auto` or `accept-edits` in Antigravity).
105
+ 3. Regente sends one `maestri ask --batch` with a ref to every agent asking, without changing files, for its role in
106
+ one line, its working folder and writable project root, the first line of `Requirements` when `Project Context` is
107
+ wired to it, and whom it reports to. Every answer matches its role and the topology above.
108
+ 4. One peer exchange works, for example Vitral asks Bússola where the design system lives and relays the answer.
109
+
110
+ ## Use in a project
111
+
112
+ Insert the partitura into the project's workspace, from the Maestri palette or as a Maestro:
113
+
114
+ ```bash
115
+ maestri partitura insert "vibecoding-devtools"
116
+ ```
117
+
118
+ Then fill the instance block of `Requirements` with Regente. The portal keeps the browser session of the computer
119
+ running Maestri; sign in to Gemini there once.
120
+
121
+ ## Update
122
+
123
+ Edit the role files or `team.json`, then run `npx vibecoding-devtools team --roles-only --update-roles` to rewrite the
124
+ installed roles that differ. A full `team --update-roles` also replaces the Maestro preset with `maestri preset set`
125
+ when its command changed, and the Task Runner settings file when its content changed.
126
+ Maestri restarts the agents that use a rewritten role, and their chat context is lost: run it between tasks.
127
+ Agents already recruited keep the command they were launched with (the preset command applies to new terminals only).
128
+ To apply a changed command, run `maestri recruit --replace "<agent>" --command "<command>"` for that agent between
129
+ tasks, with the command from the table above and, for Formiga, the path of the settings file. Save the partitura again
130
+ with `maestri partitura update "vibecoding-devtools" <agents> --without-me` when agents, ropes or notes change, then
131
+ export it again from the Maestri app over `partitura/vibecoding-devtools.maestripartitura`.
132
+
133
+ ## Known limits
134
+
135
+ - Maestri roles have no color; tier colors are listed in `Team Roster`.
136
+ - Sentinela and Crivo lose the Edit and Write tools only; the shell can still write, so read-only is role-enforced.
137
+ - Codex runs in the workspace-write sandbox without shell network by default; installs ask for approval.
138
+ - Gemini images captured through the portal are rendered pixels: no transparency, possibly below the original size.
139
+ Downloads from the portal land on the computer running Maestri, not on the server.
@@ -0,0 +1,6 @@
1
+ # Acceptance Criteria
2
+
3
+ Owner: Regente, with Ensaio. Each criterion is testable and has an id.
4
+
5
+ | Id | Criterion (given / when / then) | Verified by | Status | Evidence |
6
+ |---|---|---|---|---|
@@ -0,0 +1,8 @@
1
+ # Code Standards
2
+
3
+ Owner: Alicerce (backend) and Vitral (frontend); Crivo reviews against it. Canonical: repository `AGENTS.md` and
4
+ linters. Record here only what the linters do not enforce.
5
+
6
+ - Commands (lint, typecheck, test, build): see `AGENTS.md`.
7
+ - Risk map: see `AGENTS.md`.
8
+ - Project-specific rules:
@@ -0,0 +1,10 @@
1
+ # Contracts & Integrations
2
+
3
+ Owner: Alicerce. Canonical: OpenAPI and schema files in the repository. Changes are announced to Forja, Vitral and
4
+ Regente before they land.
5
+
6
+ | Contract | File | Version | Consumers |
7
+ |---|---|---|---|
8
+
9
+ | External service | Client folder | Official docs | Notes |
10
+ |---|---|---|---|
@@ -0,0 +1,7 @@
1
+ # Decisions
2
+
3
+ Owner: Regente. Architecture records live in repository `docs/adr/` (Alicerce); this log indexes every decision,
4
+ accepted risk and approved contingency.
5
+
6
+ | Date | Ref | Decision | Reason | Approved by |
7
+ |---|---|---|---|---|
@@ -0,0 +1,8 @@
1
+ # Design System
2
+
3
+ Owner: Bússola. Canonical: repository `DESIGN.md` (impeccable). This page only points to it.
4
+
5
+ - `DESIGN.md` path:
6
+ - Prototypes path:
7
+ - Breakpoints:
8
+ - Current tokens summary (color, type, spacing):
@@ -0,0 +1,10 @@
1
+ # Ops Runbook
2
+
3
+ Owner: Guindaste. Canonical: repository `docs/runbook/`.
4
+
5
+ | Environment | URL | Deploy method | Rollback | Health check |
6
+ |---|---|---|---|---|
7
+
8
+ - Backups (what, where, how often, restore test date):
9
+ - Secrets location (store name only, never values):
10
+ - Monitoring and alerts:
@@ -0,0 +1,23 @@
1
+ # Requirements
2
+
3
+ Owner: Regente. Canonical: repository `docs/requirements.md` when it exists; this page indexes it.
4
+
5
+ ## Instance (fill first)
6
+
7
+ - Project:
8
+ - Repository path / workspace:
9
+ - Default branch:
10
+ - Run locally (command, URL, ports):
11
+ - Artifacts path (screenshots, reports):
12
+
13
+ ## Goal
14
+
15
+ ## Users
16
+
17
+ ## In scope
18
+
19
+ ## Out of scope
20
+
21
+ ## Constraints (stack, deadline, budget, compliance)
22
+
23
+ ## Open questions