ganbatte-os 0.2.34 → 0.2.36
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/.gos/agents/profiles/ganbatte-os-master.md +48 -8
- package/.gos/libraries/frontend/state-persistence-patterns.md +300 -0
- package/.gos/playbooks/plan-creation-playbook.md +34 -17
- package/.gos/scripts/integrations/check-plan.js +120 -0
- package/.gos/scripts/integrations/migrate-task-status.js +122 -0
- package/.gos/scripts/integrations/setup-ide-adapters.js +80 -66
- package/.gos/skills/execute-plan/SKILL.md +91 -20
- package/.gos/skills/plan-blueprint/SKILL.md +71 -6
- package/.gos/skills/plan-to-tasks/SKILL.md +13 -29
- package/.gos/skills/progress-tracker/SKILL.md +23 -3
- package/.gos/skills/registry.json +2 -1
- package/.gos/skills/validate-plan/SKILL.md +133 -0
- package/.gos/templates/planTemplate.md +50 -6
- package/.gos/templates/taskTemplate.md +6 -1
- package/CLAUDE.md +7 -4
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* migrate-task-status.js
|
|
4
|
+
*
|
|
5
|
+
* Migra task files do formato bugado (`## Status` no body, sem frontmatter)
|
|
6
|
+
* para o formato canonico (frontmatter YAML com `status:`).
|
|
7
|
+
*
|
|
8
|
+
* Origem do bug: plan-to-tasks/SKILL.md tinha template inline obsoleto sem
|
|
9
|
+
* `status:`. Tasks geradas ficavam travadas em `pendente` mesmo apos
|
|
10
|
+
* *execute-plan rodar codigo, porque progress-tracker procura no frontmatter
|
|
11
|
+
* e nao acha. Corrigido em commit subsequente; este script reabilita planos
|
|
12
|
+
* preexistentes.
|
|
13
|
+
*
|
|
14
|
+
* Uso:
|
|
15
|
+
* node migrate-task-status.js <plan-dir>
|
|
16
|
+
* node migrate-task-status.js <plan-dir> --infer-from-diff
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('node:fs');
|
|
20
|
+
const path = require('node:path');
|
|
21
|
+
const { execFileSync } = require('node:child_process');
|
|
22
|
+
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const planDir = args[0];
|
|
25
|
+
const inferFromDiff = args.includes('--infer-from-diff');
|
|
26
|
+
|
|
27
|
+
if (!planDir || !fs.existsSync(planDir)) {
|
|
28
|
+
console.error('uso: migrate-task-status.js <plan-dir> [--infer-from-diff]');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tasksDir = path.join(planDir, 'tasks');
|
|
33
|
+
if (!fs.existsSync(tasksDir)) {
|
|
34
|
+
console.error(`tasks dir nao encontrado: ${tasksDir}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const planFile = path.join(planDir, 'plan.md');
|
|
39
|
+
const planSlug = path.basename(planDir);
|
|
40
|
+
const planId = planSlug.split('-').slice(0, 2).join('-');
|
|
41
|
+
|
|
42
|
+
function gitLog(args) {
|
|
43
|
+
try {
|
|
44
|
+
return execFileSync('git', args, { encoding: 'utf8' }).trim();
|
|
45
|
+
} catch {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const taskFiles = fs.readdirSync(tasksDir)
|
|
51
|
+
.filter((f) => /^T-\d+.*\.md$/.test(f))
|
|
52
|
+
.map((f) => path.join(tasksDir, f));
|
|
53
|
+
|
|
54
|
+
let migrated = 0;
|
|
55
|
+
let skipped = 0;
|
|
56
|
+
let failed = 0;
|
|
57
|
+
|
|
58
|
+
for (const file of taskFiles) {
|
|
59
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
60
|
+
|
|
61
|
+
if (raw.startsWith('---\n')) {
|
|
62
|
+
skipped++;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const titleMatch = raw.match(/^# (T-\d+(?:-\d+)?)[ —-]+(.+?)$/m);
|
|
67
|
+
const statusMatch = raw.match(/^## Status\s*\n+`?(\w[\w-]*)`?/m);
|
|
68
|
+
|
|
69
|
+
if (!titleMatch) {
|
|
70
|
+
console.error(`falha: ${file} — sem heading T-NN`);
|
|
71
|
+
failed++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const taskId = titleMatch[1];
|
|
76
|
+
const title = titleMatch[2].trim();
|
|
77
|
+
const seqRaw = (taskId.match(/T-(\d+)(?:-(\d+))?/) || [])[2] || taskId.split('-')[1];
|
|
78
|
+
const bodyStatus = statusMatch ? statusMatch[1] : 'pendente';
|
|
79
|
+
|
|
80
|
+
let finalStatus = bodyStatus;
|
|
81
|
+
if (inferFromDiff && bodyStatus === 'pendente') {
|
|
82
|
+
const addCommit = gitLog(['log', '--diff-filter=A', '--format=%H', '--', planFile]);
|
|
83
|
+
const sinceCommit = addCommit ? addCommit.split('\n').pop() : '';
|
|
84
|
+
if (sinceCommit) {
|
|
85
|
+
const diff = gitLog(['log', `${sinceCommit}..HEAD`, '--name-only', '--pretty=format:']);
|
|
86
|
+
if (diff) finalStatus = 'validacao';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const bodyWithoutStatus = raw
|
|
91
|
+
.replace(/^## Status\s*\n+`?\w[\w-]*`?\s*\n+/m, '')
|
|
92
|
+
.replace(/^# T-\d+.*?\n+/m, '');
|
|
93
|
+
|
|
94
|
+
const frontmatter = `---
|
|
95
|
+
id: ${taskId}
|
|
96
|
+
plan_id: ${planId}
|
|
97
|
+
seq: ${parseInt(seqRaw, 10)}
|
|
98
|
+
title: ${title}
|
|
99
|
+
area: frontend
|
|
100
|
+
labels: [agent:dev]
|
|
101
|
+
priority: P1
|
|
102
|
+
estimate: "4h"
|
|
103
|
+
status: ${finalStatus}
|
|
104
|
+
valida_em: ""
|
|
105
|
+
depends_on_backend: []
|
|
106
|
+
interaction_target: []
|
|
107
|
+
override_target: []
|
|
108
|
+
assignees: []
|
|
109
|
+
links: []
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
# ${taskId} — ${title}
|
|
113
|
+
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(file, frontmatter + bodyWithoutStatus);
|
|
117
|
+
migrated++;
|
|
118
|
+
console.log(`migrado: ${path.basename(file)} → status=${finalStatus}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`\n[migrate-task-status] ${migrated} migrados | ${skipped} ja-validos | ${failed} falhas`);
|
|
122
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
@@ -22,15 +22,53 @@ function relativeTarget(fromFile, targetFile) {
|
|
|
22
22
|
return path.relative(path.dirname(fromFile), targetFile).replace(/\\/g, '/');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function agentSlug(id) {
|
|
26
|
+
return id.startsWith('gos-') ? id : `gos-${id}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cleanupStaleAdapters() {
|
|
30
|
+
// Remove arquivos .md soltos no root de skills/ (formato legado) em todas as IDEs.
|
|
31
|
+
// Codex/Qwen/Gemini/Opencode/Antigravity esperam <slug>/SKILL.md (diretorio), nao .md solto.
|
|
32
|
+
// .agent e o diretorio canonico do Antigravity (workspace scope: .agent/skills/, .agent/workflows/).
|
|
33
|
+
const allIdes = ['.codex', '.qwen', '.gemini', '.opencode', '.agent'];
|
|
34
|
+
for (const ide of allIdes) {
|
|
35
|
+
const skillsDir = path.join(root, ide, 'skills');
|
|
36
|
+
if (fs.existsSync(skillsDir)) {
|
|
37
|
+
for (const entry of fs.readdirSync(skillsDir)) {
|
|
38
|
+
const full = path.join(skillsDir, entry);
|
|
39
|
+
if (fs.statSync(full).isFile() && entry.endsWith('.md')) {
|
|
40
|
+
fs.unlinkSync(full);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const agentsDir = path.join(root, ide, 'agents');
|
|
45
|
+
if (fs.existsSync(agentsDir)) {
|
|
46
|
+
for (const entry of fs.readdirSync(agentsDir)) {
|
|
47
|
+
if (/^gos-gos-/.test(entry)) {
|
|
48
|
+
fs.unlinkSync(path.join(agentsDir, entry));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Legado: .antigravity/ era o diretorio errado (Antigravity nunca leu isso — usa .agent/).
|
|
55
|
+
// Remove silenciosamente para evitar drift entre IDEs.
|
|
56
|
+
const legacyAntigravity = path.join(root, '.antigravity');
|
|
57
|
+
if (fs.existsSync(legacyAntigravity)) {
|
|
58
|
+
fs.rmSync(legacyAntigravity, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
25
62
|
function agentWrapper(agentId, target) {
|
|
26
63
|
return `# ${agentId}\n\nFonte canonica: \`${target}\`\n\nLeia e siga o perfil em \`${target}\`.\nEste arquivo existe apenas como adapter fino para a IDE.`;
|
|
27
64
|
}
|
|
28
65
|
|
|
29
66
|
function skillWrapper(slug, target, description) {
|
|
30
|
-
const
|
|
67
|
+
const fullSlug = slug.startsWith('gos-') ? slug : `gos-${slug}`;
|
|
68
|
+
const body = `# ${fullSlug}\n\nFonte canonica: \`${target}\`\n\nLeia e siga a skill em \`${target}\`.\nEste arquivo existe apenas como adapter fino para a IDE.`;
|
|
31
69
|
if (!description) return body;
|
|
32
70
|
const desc = String(description).replace(/"/g, '\\"').replace(/\n/g, ' ').slice(0, 200);
|
|
33
|
-
return `---\nname: "
|
|
71
|
+
return `---\nname: "${fullSlug}"\ndescription: "${desc}"\n---\n\n${body}`;
|
|
34
72
|
}
|
|
35
73
|
|
|
36
74
|
function qwenCommandWrapper(name, description, target) {
|
|
@@ -38,10 +76,11 @@ function qwenCommandWrapper(name, description, target) {
|
|
|
38
76
|
return `---\ndescription: "${desc}"\n---\n\n# ${name} (Qwen Command Adapter)\n\n> Adapter para Qwen Code. Fonte canonica: \`${target}\`.\n\nCANONICAL-SOURCE: ${target}\n\n## Adapter Contract\n\n1. Leia o arquivo canonico em **CANONICAL-SOURCE** por completo.\n2. Execute as instrucoes desse arquivo como fonte primaria.\n3. Argumentos do usuario: {{args}}`;
|
|
39
77
|
}
|
|
40
78
|
|
|
41
|
-
function claudeCommandWrapper(name, description, target, argumentHint) {
|
|
79
|
+
function claudeCommandWrapper(name, description, target, argumentHint, ideLabel) {
|
|
42
80
|
const desc = (description || name).replace(/"/g, '\\"').replace(/\n/g, ' ').slice(0, 200);
|
|
43
81
|
const hint = argumentHint ? `\nargument-hint: "${argumentHint.replace(/"/g, '\\"')}"` : '\nargument-hint: "[argumentos opcionais]"';
|
|
44
|
-
|
|
82
|
+
const label = ideLabel || 'Claude';
|
|
83
|
+
return `---\ndescription: "${desc}"${hint}\n---\n\n# ${name} (${label} Adapter)\n\n> Adapter fino para ${label}. Fonte canonica: \`${target}\`.\n\nCANONICAL-SOURCE: ${target}\n\n## Adapter Contract\n\n1. Leia o arquivo canonico indicado em **CANONICAL-SOURCE** por completo.\n2. Execute as instrucoes desse arquivo como fonte primaria.\n3. Argumentos do usuario: $ARGUMENTS`;
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
function extractAgentDescription(filePath) {
|
|
@@ -86,32 +125,51 @@ function main() {
|
|
|
86
125
|
const agents = readJson(path.join(root, '.gos', 'agents', 'profiles', 'index.json')).profiles;
|
|
87
126
|
const skills = readJson(path.join(root, '.gos', 'skills', 'registry.json')).skills;
|
|
88
127
|
|
|
128
|
+
cleanupStaleAdapters();
|
|
129
|
+
|
|
89
130
|
for (const agent of agents) {
|
|
90
131
|
const agentProfilePath = path.join(root, '.gos', 'agents', 'profiles', agent.path);
|
|
91
132
|
const agentDesc = extractAgentDescription(agentProfilePath) || `${agent.id} agent`;
|
|
133
|
+
const aSlug = agentSlug(agent.id);
|
|
92
134
|
|
|
93
135
|
// Claude commands (requires YAML frontmatter with description)
|
|
94
136
|
const claudeFile = path.join(root, '.claude', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
95
|
-
writeFile(claudeFile, claudeCommandWrapper(
|
|
137
|
+
writeFile(claudeFile, claudeCommandWrapper(aSlug, agentDesc, relativeTarget(claudeFile, agentProfilePath), '', 'Claude'));
|
|
96
138
|
|
|
97
139
|
// Qwen commands (requires YAML frontmatter with description)
|
|
98
140
|
const qwenCmd = path.join(root, '.qwen', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
99
|
-
writeFile(qwenCmd, qwenCommandWrapper(
|
|
141
|
+
writeFile(qwenCmd, qwenCommandWrapper(aSlug, agentDesc, relativeTarget(qwenCmd, agentProfilePath)));
|
|
100
142
|
|
|
101
143
|
// Qwen sub-agents
|
|
102
|
-
const qwenAgent = path.join(root, '.qwen', 'agents',
|
|
144
|
+
const qwenAgent = path.join(root, '.qwen', 'agents', `${aSlug}.md`);
|
|
103
145
|
const agentTarget = relativeTarget(qwenAgent, agentProfilePath);
|
|
104
146
|
const safeDesc = agentDesc.replace(/"/g, '\\"').replace(/\n/g, ' ').slice(0, 200);
|
|
105
|
-
writeFile(qwenAgent, `---\nname: "
|
|
147
|
+
writeFile(qwenAgent, `---\nname: "${aSlug}"\ndescription: "${safeDesc}"\nmodel: inherit\ntools:\n - Read\n - Glob\n - Grep\n - Bash\n - Edit\n - Write\n---\n\nFonte canonica: \`${agentTarget}\`\nLeia e siga o perfil em \`${agentTarget}\`.`);
|
|
106
148
|
|
|
107
149
|
// Codex commands (slash commands no Codex IDE Extension)
|
|
108
150
|
const codexCmd = path.join(root, '.codex', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
109
|
-
writeFile(codexCmd, claudeCommandWrapper(
|
|
151
|
+
writeFile(codexCmd, claudeCommandWrapper(aSlug, agentDesc, relativeTarget(codexCmd, agentProfilePath), '', 'Codex'));
|
|
110
152
|
|
|
111
153
|
// Codex sub-agents (Codex IDE espera .codex/agents/<id>.md)
|
|
112
|
-
const codexAgent = path.join(root, '.codex', 'agents',
|
|
154
|
+
const codexAgent = path.join(root, '.codex', 'agents', `${aSlug}.md`);
|
|
113
155
|
const codexAgentTarget = relativeTarget(codexAgent, agentProfilePath);
|
|
114
|
-
writeFile(codexAgent, `---\nname: "
|
|
156
|
+
writeFile(codexAgent, `---\nname: "${aSlug}"\ndescription: "${safeDesc}"\nmodel: inherit\ntools:\n - Read\n - Glob\n - Grep\n - Bash\n - Edit\n - Write\n---\n\nFonte canonica: \`${codexAgentTarget}\`\nLeia e siga o perfil em \`${codexAgentTarget}\`.`);
|
|
157
|
+
|
|
158
|
+
// Codex / Antigravity (.agent/) / Gemini / Opencode usam namespace plano gos-<slug> em skills/.
|
|
159
|
+
// Agents aparecem no mesmo picker se emitidos como wrapper SKILL.md em skills/.
|
|
160
|
+
for (const ide of ['.codex', '.agent', '.gemini', '.opencode']) {
|
|
161
|
+
const ideAgentSkill = path.join(root, ide, 'skills', aSlug, 'SKILL.md');
|
|
162
|
+
writeFile(ideAgentSkill, skillWrapper(agent.id, relativeTarget(ideAgentSkill, agentProfilePath), agentDesc));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Antigravity workflows (.agent/workflows/<id>.md) — slash command invocation no picker.
|
|
166
|
+
const antigravityWorkflow = path.join(root, '.agent', 'workflows', `${agent.id}.md`);
|
|
167
|
+
const workflowTarget = relativeTarget(antigravityWorkflow, agentProfilePath);
|
|
168
|
+
const safeAgentDesc = agentDesc.replace(/"/g, '\\"').replace(/\n/g, ' ').slice(0, 200);
|
|
169
|
+
writeFile(
|
|
170
|
+
antigravityWorkflow,
|
|
171
|
+
`---\ndescription: "${safeAgentDesc}"\n---\n\n# /${agent.id} (Antigravity Workflow)\n\nFonte canonica: \`${workflowTarget}\`\n\nLeia o arquivo canonico apontado em CANONICAL-SOURCE e execute as instrucoes como fonte primaria.\n\nCANONICAL-SOURCE: ${workflowTarget}\n\nArgumentos do usuario seguem o prompt do agente.`
|
|
172
|
+
);
|
|
115
173
|
}
|
|
116
174
|
|
|
117
175
|
for (const skill of skills) {
|
|
@@ -119,8 +177,7 @@ function main() {
|
|
|
119
177
|
const canonicalPath = path.join(root, '.gos', skillTargetPath);
|
|
120
178
|
const claudeSkill = path.join(root, '.claude', 'commands', 'gos', 'skills', `${skill.slug}.md`);
|
|
121
179
|
const codexSkill = path.join(root, '.codex', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
122
|
-
const
|
|
123
|
-
const antigravitySkill = path.join(root, '.antigravity', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
180
|
+
const antigravitySkill = path.join(root, '.agent', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
124
181
|
const geminiSkill = path.join(root, '.gemini', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
125
182
|
const opencodeSkill = path.join(root, '.opencode', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
126
183
|
const qwenSkill = path.join(root, '.qwen', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
@@ -131,7 +188,6 @@ function main() {
|
|
|
131
188
|
const skillDesc = skillFm.description || skill.description || skill.name || skill.slug;
|
|
132
189
|
const skillArgHint = skillFm['argument-hint'] || '';
|
|
133
190
|
|
|
134
|
-
writeFile(claudeSkill, skillWrapper(skill.slug, relativeTarget(claudeSkill, canonicalPath), skillDesc));
|
|
135
191
|
writeFile(codexSkill, skillWrapper(skill.slug, relativeTarget(codexSkill, canonicalPath), skillDesc));
|
|
136
192
|
writeFile(antigravitySkill, skillWrapper(skill.slug, relativeTarget(antigravitySkill, canonicalPath), skillDesc));
|
|
137
193
|
writeFile(geminiSkill, skillWrapper(skill.slug, relativeTarget(geminiSkill, canonicalPath), skillDesc));
|
|
@@ -139,46 +195,13 @@ function main() {
|
|
|
139
195
|
writeFile(qwenSkill, skillWrapper(skill.slug, relativeTarget(qwenSkill, canonicalPath), skillDesc));
|
|
140
196
|
|
|
141
197
|
writeFile(qwenCmd, qwenCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(qwenCmd, canonicalPath)));
|
|
142
|
-
writeFile(
|
|
143
|
-
writeFile(claudeSkill, claudeCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(claudeSkill, canonicalPath), skillArgHint));
|
|
198
|
+
writeFile(claudeSkill, claudeCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(claudeSkill, canonicalPath), skillArgHint, 'Claude'));
|
|
144
199
|
}
|
|
145
200
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
'- `AGENTS.md`',
|
|
151
|
-
'- `CLAUDE.md`',
|
|
152
|
-
'- `.gos/docs/toolchain-map.md`',
|
|
153
|
-
'',
|
|
154
|
-
'Agentes disponiveis:',
|
|
155
|
-
...agents.map((agent) => `- ${agent.id}`),
|
|
156
|
-
'',
|
|
157
|
-
'Skills curadas:',
|
|
158
|
-
...skills.map((skill) => `- ${skill.slug}`),
|
|
159
|
-
'',
|
|
160
|
-
'## Como invocar Skills',
|
|
161
|
-
'',
|
|
162
|
-
'Para usar uma skill, leia o arquivo canonico e siga suas instrucoes.',
|
|
163
|
-
'Skills tambem disponiveis como adapters em `.antigravity/skills/`.',
|
|
164
|
-
'',
|
|
165
|
-
'| Skill | Arquivo canonico |',
|
|
166
|
-
'|-------|-----------------|',
|
|
167
|
-
...skills.map((skill) => `| \`gos-${skill.slug}\` | \`.gos/${skill.skillFile || skill.path}\` |`)
|
|
168
|
-
].join('\n');
|
|
169
|
-
|
|
170
|
-
writeFile(path.join(root, '.antigravity', 'instructions.md'), antigravityInstructions);
|
|
171
|
-
writeFile(
|
|
172
|
-
path.join(root, '.antigravity', 'config.json'),
|
|
173
|
-
JSON.stringify(
|
|
174
|
-
{
|
|
175
|
-
project: 'g-os',
|
|
176
|
-
instructions: ['instructions.md', '../AGENTS.md', '../CLAUDE.md']
|
|
177
|
-
},
|
|
178
|
-
null,
|
|
179
|
-
2
|
|
180
|
-
)
|
|
181
|
-
);
|
|
201
|
+
// Antigravity le AGENTS.md no root do workspace (ja existe no projeto).
|
|
202
|
+
// Skills vivem em .agent/skills/<slug>/SKILL.md (workspace scope, registrado acima no loop).
|
|
203
|
+
// Workflows (.agent/workflows/<id>.md) sao slash commands no picker da IDE.
|
|
204
|
+
// Rules opcionais em .agent/rules/ — nao geramos automaticamente (regras vivem no AGENTS.md).
|
|
182
205
|
|
|
183
206
|
// Codex IDE Extension — AGENTS.md + config.toml
|
|
184
207
|
// Codex e o ambiente de EXECUCAO (Opus planeja, Codex executa). Bloco abaixo garante
|
|
@@ -217,9 +240,9 @@ function main() {
|
|
|
217
240
|
'',
|
|
218
241
|
'## Como o Codex consome',
|
|
219
242
|
'',
|
|
220
|
-
'- Slash commands em `.codex/commands/gos/{agents,skills}/<id>.md` -> Codex carrega o canonico apontado em CANONICAL-SOURCE e executa.',
|
|
221
|
-
'- Subagents em `.codex/agents/gos-<id>.md` ->
|
|
222
|
-
'-
|
|
243
|
+
'- Slash commands em `.codex/commands/gos/{agents,skills}/<id>.md` -> unica superficie de skills/agents. Codex carrega o canonico apontado em CANONICAL-SOURCE e executa.',
|
|
244
|
+
'- Subagents em `.codex/agents/gos-<id>.md` -> declaracao de subagent (acessivel via Task tool e delegacao interna).',
|
|
245
|
+
'- Para invocar o orquestrador master, digite `/gos:agents:gos-master` no picker.',
|
|
223
246
|
''
|
|
224
247
|
].join('\n');
|
|
225
248
|
writeFile(path.join(root, '.codex', 'AGENTS.md'), codexAgentsMd);
|
|
@@ -229,14 +252,7 @@ function main() {
|
|
|
229
252
|
'# Edite os arquivos canonicos em .gos/ ao inves deste.',
|
|
230
253
|
'',
|
|
231
254
|
'project = "g-os"',
|
|
232
|
-
'',
|
|
233
|
-
'[instructions]',
|
|
234
|
-
'files = [',
|
|
235
|
-
' "AGENTS.md",',
|
|
236
|
-
' "../AGENTS.md",',
|
|
237
|
-
' "../CLAUDE.md",',
|
|
238
|
-
' "../.gos/docs/toolchain-map.md",',
|
|
239
|
-
']',
|
|
255
|
+
'instructions = "AGENTS.md"',
|
|
240
256
|
'',
|
|
241
257
|
'[execution]',
|
|
242
258
|
'primary_command = "*execute-plan"',
|
|
@@ -251,16 +267,14 @@ function main() {
|
|
|
251
267
|
// Evita regressoes silenciosas que ja quebraram a IDE no passado.
|
|
252
268
|
const codexFailures = [];
|
|
253
269
|
for (const agent of agents) {
|
|
254
|
-
const expectedAgent = path.join(root, '.codex', 'agents',
|
|
270
|
+
const expectedAgent = path.join(root, '.codex', 'agents', `${agentSlug(agent.id)}.md`);
|
|
255
271
|
const expectedCmd = path.join(root, '.codex', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
256
272
|
if (!fs.existsSync(expectedAgent)) codexFailures.push(expectedAgent);
|
|
257
273
|
if (!fs.existsSync(expectedCmd)) codexFailures.push(expectedCmd);
|
|
258
274
|
}
|
|
259
275
|
for (const skill of skills) {
|
|
260
276
|
const expectedSkill = path.join(root, '.codex', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
261
|
-
const expectedCmd = path.join(root, '.codex', 'commands', 'gos', 'skills', `${skill.slug}.md`);
|
|
262
277
|
if (!fs.existsSync(expectedSkill)) codexFailures.push(expectedSkill);
|
|
263
|
-
if (!fs.existsSync(expectedCmd)) codexFailures.push(expectedCmd);
|
|
264
278
|
}
|
|
265
279
|
if (!fs.existsSync(path.join(root, '.codex', 'AGENTS.md'))) codexFailures.push('.codex/AGENTS.md');
|
|
266
280
|
if (!fs.existsSync(path.join(root, '.codex', 'config.toml'))) codexFailures.push('.codex/config.toml');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: execute-plan
|
|
3
|
-
description: Executa um plano (PLAN-NNN-<slug>) task-a-task aplicando state machine + visual gate contra Storybook canonico antes de marcar validacao. Comando primario do ambiente Codex IDE.
|
|
4
|
-
argument-hint: "<PLAN-NNN-slug> [--task T-NNN-NN] [--skip-visual-gate]"
|
|
3
|
+
description: Executa um plano (PLAN-NNN-<slug>) task-a-task aplicando state machine + visual gate contra Storybook canonico antes de marcar validacao. Non-blocking em backend gaps (abre tasks ClickUp + segue). Comando primario do ambiente Codex IDE.
|
|
4
|
+
argument-hint: "<PLAN-NNN-slug> [--task T-NNN-NN] [--skip-visual-gate] [--skip-clickup]"
|
|
5
5
|
allowedTools: [Read, Glob, Grep, Bash, Write, Edit, Agent, AskUserQuestion]
|
|
6
6
|
sourceDocs:
|
|
7
7
|
- templates/taskTemplate.md
|
|
@@ -22,6 +22,15 @@ metadata:
|
|
|
22
22
|
|
|
23
23
|
Voce esta executando como **Executor de Planos** via skill `execute-plan`. No ambiente Codex IDE Extension este e o comando primario do ciclo `Opus(plan) -> Codex(execute)`.
|
|
24
24
|
|
|
25
|
+
## Contrato inviolavel — executar TODAS as tasks
|
|
26
|
+
|
|
27
|
+
`*execute-plan` e uma operacao **completa**: roda **TODAS** as tasks executaveis do plano em ordem de `seq`, sem parar no meio. Parar apos T-01 ou T-02 e **bug do executor**, NAO comportamento esperado. Regras vinculantes:
|
|
28
|
+
|
|
29
|
+
1. O loop so termina quando **toda** task tem `status` em `{validacao, concluido, bloqueada-backend}`. Tasks ainda em `pendente` ou `em-andamento` ao final = falha.
|
|
30
|
+
2. Falha em UMA task NAO encerra o loop. Registra em `T-NNN-NN.notes.md`, mantem task em `em-andamento`, e **continua** para a proxima task. Ao final, reporta lista de tasks com falha.
|
|
31
|
+
3. Se o executor for tentado a "pausar para usuario revisar" no meio do loop: NAO pausar. Concluir o loop e reportar tudo de uma vez no fechamento.
|
|
32
|
+
4. Verificacao final obrigatoria antes do resumo: `for f in <tasks>/T-*.md; do grep '^status:' $f; done` — listar status de cada task. Qualquer status `pendente` ou `em-andamento` apos o loop = abortar com erro `executor-incomplete: <lista>` e instruir re-rodada.
|
|
33
|
+
|
|
25
34
|
## Input
|
|
26
35
|
|
|
27
36
|
$ARGUMENTS
|
|
@@ -35,11 +44,16 @@ Formato esperado:
|
|
|
35
44
|
|
|
36
45
|
1. Resolver paths via `.gos-local/plan-paths.json`. Se ausente, abortar e instruir o usuario a rodar `*plan` primeiro.
|
|
37
46
|
2. Localizar `<dirs.planos>/<PLAN-NNN-slug>/plan.md`. Se ausente, abortar.
|
|
38
|
-
3. Ler `plan.md` por completo: frontmatter + Componentes mapeados + Componentes ausentes + Aderencia a stack + Plano de execucao + Checklist de aceite
|
|
47
|
+
3. Ler `plan.md` por completo: frontmatter + Componentes mapeados + Componentes ausentes + Aderencia a stack + Plano de execucao + Checklist de aceite + **Backend pendings**.
|
|
39
48
|
4. Validar `stack_ref` do frontmatter contra `<dirs.stack>` (`docs/stack.md`):
|
|
40
49
|
- Calcular sha-curto atual e comparar.
|
|
41
50
|
- Drift detectado: ABORTAR e instruir `*stack drift` + replanejar com `*stack refresh`.
|
|
42
51
|
5. Ler `<dirs.progress>` (progress.txt). Se aponta para outro plano ativo, perguntar se troca o foco antes de prosseguir.
|
|
52
|
+
6. **Backend pendings (non-blocking)**: ler tabela `## Backend pendings` do `plan.md`. Para cada linha:
|
|
53
|
+
- Sem `ClickUp ID` -> criar task via `mcp__clickup__clickup_create_task` (assignee `112010775` salvo override `ASSIGNEE` no plano, list de `clickup.backend_list_id` em `plan-paths.json`, titulo `[Backend] PLAN-NNN: <gap>`). Gravar ID retornado de volta na coluna `ClickUp ID` do `plan.md`.
|
|
54
|
+
- Com `ClickUp ID` -> consultar `mcp__clickup__clickup_get_task <ID>`. Atualizar coluna `Status`. Se ainda aberta, registrar em `progress.txt` como `blockers=<T-IDs>:<ClickUp-ID>:<gap-curto>` (uma linha por bloqueio ativo).
|
|
55
|
+
- `--skip-clickup` desliga MCP; nesse caso so registra warning visivel mantendo o que ja esta no plano.
|
|
56
|
+
- **Importante**: backend gap aberto NAO aborta o execute-plan. Tasks frontend que dependem do gap serao classificadas como `bloqueada-backend` no loop abaixo; tasks sem dependencia seguem normal.
|
|
43
57
|
|
|
44
58
|
## Pre-flight visual
|
|
45
59
|
|
|
@@ -52,11 +66,41 @@ Formato esperado:
|
|
|
52
66
|
- Se ausente: gerar task de criacao do componente ANTES das tasks de implementacao. Renumerar `seq` das tasks restantes.
|
|
53
67
|
4. Output do pre-flight: bloco em `progress.txt` campo `notes=` com numero de stories indexadas e tasks de criacao geradas.
|
|
54
68
|
|
|
69
|
+
## Pre-flight visual smoke (NOVO)
|
|
70
|
+
|
|
71
|
+
Antes da T-01 (depois do pre-flight de stories acima), gera comparacao visual entre pagina renderizada e frame Figma para capturar gaps grandes ANTES da execucao — evita o padrao PLAN-005 onde feedback iterativo gerou 26 rodadas no fim.
|
|
72
|
+
|
|
73
|
+
Ativacao:
|
|
74
|
+
- Storybook disponivel: usa `.stories.tsx` da pagina-completa quando existe (ex.: `ProjetosPage.stories.tsx`) e renderiza via `npm run storybook -- --static-build` ou screenshot ja gerado em CI.
|
|
75
|
+
- Playwright MCP disponivel (`mcp__plugin_playwright_playwright__*`): navega para localhost (rota da pagina + seed declarado em `## Mock strategy` ou seed nativo do projeto) e captura screenshot.
|
|
76
|
+
- Nenhum disponivel: pula com warning em `progress.txt` (`smoke=skipped: no storybook story for full page nor playwright MCP`). NAO bloqueia.
|
|
77
|
+
|
|
78
|
+
Comparacao:
|
|
79
|
+
1. Carrega frame Figma principal (`figma_url` do plano) via Figma MCP — extrai layout/secoes esperadas.
|
|
80
|
+
2. Confronta screenshot vs frame em 3 dimensoes basicas (sem refazer 4-dim por componente — isso fica no gate por task):
|
|
81
|
+
- **Secoes presentes**: KPI row, toolbar, table, drawer trigger, etc. — cada secao esperada esta no screenshot?
|
|
82
|
+
- **Layout grosseiro**: ordem vertical das secoes; colunas da table batem com Figma?
|
|
83
|
+
- **Cores/tokens primarios**: bg da pagina, accent color, contrast — sem inversao obvia.
|
|
84
|
+
3. Output: `<dirs.planos>/<PLAN-NNN-slug>/preflight-smoke.md` com lista de gaps detectados (secao faltando, layout invertido, cor errada).
|
|
85
|
+
4. Se gaps detectados: gerar tasks `T-000-XX` (prefixo `000` = pre-flight) com `priority: P0` e prepend no inicio da fila. Renumerar `seq` das tasks subsequentes.
|
|
86
|
+
|
|
87
|
+
Pre-flight smoke nao substitui o visual gate por task — ele captura gaps grandes (componente faltando, KPI row ausente) que viraram tasks novas em PLAN-004/PLAN-005.
|
|
88
|
+
|
|
55
89
|
## Loop por task
|
|
56
90
|
|
|
57
|
-
Iterar tasks em ordem de `seq`.
|
|
91
|
+
Iterar tasks em ordem de `seq`. Antes de executar cada task, **classificar**:
|
|
58
92
|
|
|
59
|
-
|
|
93
|
+
- Ler frontmatter `depends_on_backend:` da task (campo opcional, default `[]`). Cada item referencia uma `gap-key` da tabela `## Backend pendings` do plano (ex.: `migration-20260501150000`, `endpoint-projetos-fields`).
|
|
94
|
+
- Se a task referencia gap em aberto (status no ClickUp != `concluido`/`closed`):
|
|
95
|
+
- `*progress status T-NNN-NN bloqueada-backend` (transicao livre desde `pendente` ou `em-andamento`).
|
|
96
|
+
- Anotar em `tasks/T-NNN-NN.notes.md` linha `## Bloqueada backend <iso>` com ClickUp IDs.
|
|
97
|
+
- **PULAR** task — NAO falha o loop, segue pra proxima.
|
|
98
|
+
- Se a task tem `depends_on_backend: []` ou todas as dependencias estao `concluido` no ClickUp -> seguir fluxo normal abaixo.
|
|
99
|
+
|
|
100
|
+
Para cada task **executavel**:
|
|
101
|
+
|
|
102
|
+
0. **Pre-flight da task — gate de formato**: ler `tasks/T-NNN-NN*.md`. Confirmar que tem frontmatter YAML (`head -1 == "---"`) e contém `^status:` no frontmatter. Se ausente: ABORTAR a task com erro `task-malformada: T-NNN-NN sem frontmatter status — rodar plan-to-tasks regenerador OU migrate-task-status.js`. NUNCA tentar executar codigo sem frontmatter valido — sintoma do bug onde tasks ficam travadas em `pendente`.
|
|
103
|
+
1. **Mover para em-andamento**: invocar `*progress status T-NNN-NN em-andamento` (skill `progress-tracker`). **Pos-condicao obrigatoria**: ler T-NNN-NN.md de novo e confirmar `^status: em-andamento$` no frontmatter. Se nao mudou: ABORTAR e instruir humano. NAO seguir para implementacao com status pendente — esse e o bug central reportado.
|
|
60
104
|
2. **Despachar agent**: ler `labels: [agent:<slug>]` da task. Default: `dev`. Invocar via Agent tool com prompt completo (objetivo, plano de execucao, DoD, paths relevantes).
|
|
61
105
|
3. **Implementacao**: agent edita arquivos seguindo o plano de execucao da task. Stack como contrato — nada fora de `docs/stack.md` salvo se `arch_change=true` no frontmatter do plano pai.
|
|
62
106
|
4. **Visual gate** (antes de propor `validacao`):
|
|
@@ -64,29 +108,54 @@ Iterar tasks em ordem de `seq`. Para cada `tasks/T-NNN-NN-*.md`:
|
|
|
64
108
|
Para cada componente alterado/criado pela task:
|
|
65
109
|
|
|
66
110
|
a) Localizar `<Componente>.stories.tsx` em `<dirs.storybook>`.
|
|
67
|
-
b) Comparar implementacao vs story canonica em
|
|
111
|
+
b) Comparar implementacao vs story canonica em 5 dimensoes textuais:
|
|
68
112
|
- **Anatomia**: ordem de slots/elementos (header -> corpo -> footer; icones esquerda/direita; campos do form na ordem do design).
|
|
69
|
-
- **Tokens**: classes Tailwind/variaveis CSS batem com DS (cor, raio, espacamento, tipografia).
|
|
70
|
-
- **Variants**: props expostos cobrem variants da story.
|
|
71
|
-
- **Densidade**: padding/gap dentro de +-1 step da escala do DS.
|
|
113
|
+
- **Tokens**: classes Tailwind/variaveis CSS batem com DS (cor, raio, espacamento, tipografia). Quando o componente aparece em `## Page-level overrides` do plano: a divergencia cosmetica registrada NAO falha o gate — o gate confirma que o override foi APLICADO conforme decisao (a/b/c).
|
|
114
|
+
- **Variants**: props expostos cobrem variants da story. Decisao (b) em `## Page-level overrides`: confirmar que a nova variant existe na story canonica.
|
|
115
|
+
- **Densidade**: padding/gap dentro de +-1 step da escala do DS (ou conforme override registrado).
|
|
116
|
+
- **Comportamentos** (NOVO, heuristico via JSX + grep, sem E2E):
|
|
117
|
+
- Para cada `interaction_target:` declarado no frontmatter da task: existe handler implementado no diff? (ex.: `row.onClick` chamando `openDrawer`; `button.onClick` chamando mutation; `form.onSubmit` chamando server action).
|
|
118
|
+
- Estados visuais (skeleton/empty/error/loading) declarados em `## Interações & Estados` renderizam? grep por `isLoading|isPending|isError|empty|skeleton` nos arquivos tocados.
|
|
119
|
+
- Refetch apos mutation: `invalidateQueries` / `router.refresh` / `setState` observavel no diff?
|
|
120
|
+
- Para cada `override_target:` declarado: classes/props da decisao (a/b/c) presentes no diff? grep das classes declaradas em `## Page-level overrides`.
|
|
72
121
|
c) Para a tela como um todo: invocar Figma MCP no `figma_url` do plano e cruzar com o JSX renderizado em arvore (mesmo numero de secoes, mesma hierarquia, mesmas labels).
|
|
73
|
-
d) Output: relatorio curto em `tasks/T-NNN-NN.notes.md` (
|
|
74
|
-
e) Divergencia >= 1 item critico (anatomia ou
|
|
122
|
+
d) Output: relatorio curto em `tasks/T-NNN-NN.notes.md` (5 secoes: anatomia, tokens, variants, densidade, comportamentos + secao "Arvore vs Figma").
|
|
123
|
+
e) Divergencia >= 1 item critico (anatomia, tokens nao-overrideados, ou comportamento mapeado sem implementacao) -> falha o gate.
|
|
75
124
|
|
|
76
125
|
5. **Resultado do gate**:
|
|
77
|
-
- Sucesso -> `*progress status T-NNN-NN validacao`. Preparar arquivos staged (sem commit).
|
|
126
|
+
- Sucesso -> invocar `*progress status T-NNN-NN validacao`. **Pos-condicao obrigatoria**: ler T-NNN-NN.md, confirmar `^status: validacao$` no frontmatter. Se nao mudou: ABORTAR a task com erro `progress-tracker-falhou: T-NNN-NN`. Preparar arquivos staged (sem commit) somente apos confirmacao.
|
|
78
127
|
- Falha -> manter em `em-andamento`, gravar diff em `T-NNN-NN.notes.md`, retornar pra etapa 3.
|
|
79
128
|
|
|
129
|
+
**Invariante por task**: ao terminar a iteracao, T-NNN-NN.md DEVE ter status diferente de `pendente`. Se ainda for `pendente` apos a iteracao, registrar como bug do executor em `progress.txt` (`notes=executor-skipped-progress: T-NNN-NN`) — mas **NAO abortar o loop**: continuar para a proxima task. O abort vai acontecer no fechamento (verificacao global).
|
|
130
|
+
|
|
131
|
+
**Invariante de loop**: nunca encerrar o loop com tasks restantes nao processadas. Falha local em uma task = continuar; pausa para revisao humana no meio = proibida.
|
|
132
|
+
|
|
80
133
|
## Fechamento
|
|
81
134
|
|
|
82
|
-
Quando
|
|
135
|
+
Quando o loop terminar (toda task processada — em `validacao`, `concluido`, ou `bloqueada-backend`):
|
|
136
|
+
|
|
137
|
+
0. **Verificacao global obrigatoria** (antes de qualquer commit/resumo):
|
|
138
|
+
```bash
|
|
139
|
+
missed=$(for f in <dirs.planos>/<PLAN-NNN-slug>/tasks/T-*.md; do
|
|
140
|
+
awk '/^---/{c++; next} c==1 && /^status: (pendente|em-andamento)/{print FILENAME; exit}' "$f"
|
|
141
|
+
done)
|
|
142
|
+
```
|
|
143
|
+
Se `$missed` nao-vazio: ABORTAR com erro `executor-incomplete: <lista>` e mensagem orientando re-rodar `*execute-plan PLAN-NNN-<slug>` ou `--task T-NNN-NN` para as restantes. NAO preparar commit; NAO devolver "execucao concluida".
|
|
83
144
|
|
|
84
145
|
1. Listar arquivos modificados via `git diff --name-only`.
|
|
85
|
-
2. Preparar commit (NAO push). Mensagem segue Conventional Commits + referencia ao `PLAN-NNN-slug`.
|
|
86
|
-
3. Resumo final ao usuario:
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
|
|
146
|
+
2. Preparar commit (NAO push). Mensagem segue Conventional Commits + referencia ao `PLAN-NNN-slug`. Se houve tasks bloqueadas, citar na mensagem (ex.: `feat(checkout): PLAN-042 T-01..T-05 (T-06,T-07 bloqueadas backend CU-XXX)`).
|
|
147
|
+
3. Resumo final ao usuario em 4 blocos:
|
|
148
|
+
```
|
|
149
|
+
[execute-plan] PLAN-NNN-<slug>
|
|
150
|
+
|
|
151
|
+
tasks validacao: <N> (T-..., T-...)
|
|
152
|
+
bloqueada-backend: <K> (T-...:CU-..., ...)
|
|
153
|
+
backend pendings: <X> abertas no ClickUp / <Y> concluidas
|
|
154
|
+
visual gate: passou / falhou em (T-...)
|
|
155
|
+
|
|
156
|
+
proximo passo: *validate-plan PLAN-NNN-<slug>
|
|
157
|
+
```
|
|
158
|
+
4. Indicar que o turn seguinte e `*validate-plan PLAN-NNN-<slug>` (skill `validate-plan`, ambiente Opus 4.7). NAO marcar `concluido` automaticamente — `validate-plan` cuida disso.
|
|
90
159
|
|
|
91
160
|
## Ambiente Codex IDE — observacoes
|
|
92
161
|
|
|
@@ -98,8 +167,10 @@ Quando todas as tasks atingirem `validacao` E o checklist de aceite do plano est
|
|
|
98
167
|
|
|
99
168
|
- **Visual gate nao e opcional** salvo `--skip-visual-gate` explicito. Skill nao silencia o gate.
|
|
100
169
|
- **Sem push automatico**: commit fica preparado. Push e responsabilidade do humano.
|
|
101
|
-
- **State machine inviolavel**: transicao `concluido`
|
|
102
|
-
- **
|
|
170
|
+
- **State machine inviolavel**: transicao `concluido` ocorre via `*validate-plan` (auto-marca quando passa). Rollback humano: `*progress status T-NNN-NN pendente --rollback`.
|
|
171
|
+
- **Non-blocking em backend gaps**: gap aberto no ClickUp NAO aborta. Tasks dependentes viram `bloqueada-backend`, demais seguem.
|
|
172
|
+
- **Storybook como contrato base; Figma da pagina vence em conflito cosmetico**: componente sem `.stories.tsx` em `<dirs.storybook>` bloqueia a task ate ser criado. Divergencia cosmetica entre story e Figma da pagina que ESTA registrada em `## Page-level overrides` do plano: gate confirma aplicacao do override (decisao a/b/c). Divergencia NAO registrada no plano: gate falha — voltar pra `*plan` (ou registrar override no plan.md antes de prosseguir).
|
|
173
|
+
- **Comportamento mapeado e vinculante**: `interaction_target:` declarado na task DEVE ter handler/estado implementado e observavel no diff. Caso contrario, gate falha mesmo se anatomia + tokens passarem.
|
|
103
174
|
|
|
104
175
|
## Model guidance
|
|
105
176
|
|