ganbatte-os 0.2.33 → 0.2.35
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 +69 -4
- package/.gos/integrations/registry.json +1 -1
- package/.gos/libraries/frontend/state-persistence-patterns.md +300 -0
- package/.gos/playbooks/plan-creation-playbook.md +60 -20
- 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 +155 -39
- package/.gos/skills/execute-plan/SKILL.md +169 -0
- package/.gos/skills/plan-blueprint/SKILL.md +121 -12
- package/.gos/skills/plan-to-tasks/SKILL.md +13 -29
- package/.gos/skills/progress-tracker/SKILL.md +23 -3
- package/.gos/skills/registry.json +3 -1
- package/.gos/skills/validate-plan/SKILL.md +125 -0
- package/.gos/templates/planTemplate.md +68 -3
- package/.gos/templates/taskTemplate.md +6 -0
- package/CLAUDE.md +15 -8
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-plan.js — barreira deterministica de criacao de plano.
|
|
4
|
+
*
|
|
5
|
+
* Roda como ULTIMA acao obrigatoria do *plan (apos plan-blueprint +
|
|
6
|
+
* plan-to-tasks). Valida que o plano e usavel pelo *execute-plan e
|
|
7
|
+
* *validate-plan. Falha = *plan nao terminou — usuario ve erro, nao
|
|
8
|
+
* "plano criado" ilusorio.
|
|
9
|
+
*
|
|
10
|
+
* Uso:
|
|
11
|
+
* node check-plan.js <plan-dir>
|
|
12
|
+
*
|
|
13
|
+
* Verificacoes:
|
|
14
|
+
* 1. plan.md existe e tem frontmatter YAML.
|
|
15
|
+
* 2. context.md existe.
|
|
16
|
+
* 3. tasks/ existe e contem >= 1 T-NN*.md.
|
|
17
|
+
* 4. Cada T-NN*.md: head -1 == "---" E frontmatter contem `status: pendente`
|
|
18
|
+
* E `id:`, `plan_id:`, `seq:`, `title:`.
|
|
19
|
+
* 5. Nenhum T-NN*.md tem secao `## Status` no body (formato bugado legado).
|
|
20
|
+
*
|
|
21
|
+
* Exit code:
|
|
22
|
+
* 0 = plano valido, usavel pelo pipeline
|
|
23
|
+
* 1 = falha — mensagem aponta exatamente o que esta errado
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
|
|
29
|
+
const planDir = process.argv[2];
|
|
30
|
+
|
|
31
|
+
if (!planDir) {
|
|
32
|
+
console.error('uso: check-plan.js <plan-dir>');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(planDir)) {
|
|
37
|
+
console.error(`[check-plan] FALHA: plan-dir nao existe: ${planDir}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const errors = [];
|
|
42
|
+
const warnings = [];
|
|
43
|
+
|
|
44
|
+
const planFile = path.join(planDir, 'plan.md');
|
|
45
|
+
const contextFile = path.join(planDir, 'context.md');
|
|
46
|
+
const tasksDir = path.join(planDir, 'tasks');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(planFile)) {
|
|
49
|
+
errors.push(`plan.md ausente em ${planDir}`);
|
|
50
|
+
} else {
|
|
51
|
+
const plan = fs.readFileSync(planFile, 'utf8');
|
|
52
|
+
if (!plan.startsWith('---\n')) {
|
|
53
|
+
errors.push(`plan.md sem frontmatter YAML (head -1 != "---")`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(contextFile)) {
|
|
58
|
+
warnings.push(`context.md ausente — recomendado, nao bloqueia`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(tasksDir)) {
|
|
62
|
+
errors.push(`tasks/ ausente em ${planDir} — *plan deve invocar plan-to-tasks`);
|
|
63
|
+
} else {
|
|
64
|
+
const taskFiles = fs.readdirSync(tasksDir)
|
|
65
|
+
.filter((f) => /^T-\d+.*\.md$/.test(f))
|
|
66
|
+
.map((f) => path.join(tasksDir, f));
|
|
67
|
+
|
|
68
|
+
if (taskFiles.length === 0) {
|
|
69
|
+
errors.push(`tasks/ vazio — nenhum T-NN*.md encontrado`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const file of taskFiles) {
|
|
73
|
+
const rel = path.relative(planDir, file);
|
|
74
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
75
|
+
|
|
76
|
+
if (!raw.startsWith('---\n')) {
|
|
77
|
+
errors.push(`${rel}: sem frontmatter YAML (head -1 != "---")`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const fmEnd = raw.indexOf('\n---', 4);
|
|
82
|
+
if (fmEnd === -1) {
|
|
83
|
+
errors.push(`${rel}: frontmatter aberto sem fechar ("---" final ausente)`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const frontmatter = raw.slice(4, fmEnd);
|
|
87
|
+
const body = raw.slice(fmEnd + 4);
|
|
88
|
+
|
|
89
|
+
const required = ['id', 'plan_id', 'seq', 'title', 'status'];
|
|
90
|
+
for (const key of required) {
|
|
91
|
+
if (!new RegExp(`^${key}:\\s*\\S`, 'm').test(frontmatter)) {
|
|
92
|
+
errors.push(`${rel}: frontmatter sem campo \`${key}:\``);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!/^status:\s*pendente\s*$/m.test(frontmatter)) {
|
|
97
|
+
errors.push(`${rel}: frontmatter status: deve ser exatamente "pendente" no plano recem-criado`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (/^## Status\s*$/m.test(body)) {
|
|
101
|
+
errors.push(`${rel}: contem secao "## Status" no body (formato legado bugado) — rodar migrate-task-status.js`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (warnings.length) {
|
|
107
|
+
for (const w of warnings) console.error(`[check-plan] aviso: ${w}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (errors.length) {
|
|
111
|
+
console.error(`\n[check-plan] FALHA — plano NAO usavel pelo *execute-plan:\n`);
|
|
112
|
+
for (const e of errors) console.error(` - ${e}`);
|
|
113
|
+
console.error(`\nproximo passo: regerar tasks via plan-to-tasks OU rodar migrate-task-status.js ${planDir}`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const taskCount = fs.readdirSync(tasksDir)
|
|
118
|
+
.filter((f) => /^T-\d+.*\.md$/.test(f)).length;
|
|
119
|
+
console.log(`[check-plan] OK — ${planDir}: plan.md + context.md + ${taskCount} tasks (todas com frontmatter status: pendente).`);
|
|
120
|
+
process.exit(0);
|
|
@@ -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,31 +125,59 @@ 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}\`.`);
|
|
148
|
+
|
|
149
|
+
// Codex commands (slash commands no Codex IDE Extension)
|
|
150
|
+
const codexCmd = path.join(root, '.codex', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
151
|
+
writeFile(codexCmd, claudeCommandWrapper(aSlug, agentDesc, relativeTarget(codexCmd, agentProfilePath), '', 'Codex'));
|
|
152
|
+
|
|
153
|
+
// Codex sub-agents (Codex IDE espera .codex/agents/<id>.md)
|
|
154
|
+
const codexAgent = path.join(root, '.codex', 'agents', `${aSlug}.md`);
|
|
155
|
+
const codexAgentTarget = relativeTarget(codexAgent, agentProfilePath);
|
|
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
|
+
);
|
|
106
173
|
}
|
|
107
174
|
|
|
108
175
|
for (const skill of skills) {
|
|
109
176
|
const skillTargetPath = skill.skillFile || skill.path;
|
|
110
177
|
const canonicalPath = path.join(root, '.gos', skillTargetPath);
|
|
111
178
|
const claudeSkill = path.join(root, '.claude', 'commands', 'gos', 'skills', `${skill.slug}.md`);
|
|
112
|
-
const codexSkill = path.join(root, '.
|
|
113
|
-
const antigravitySkill = path.join(root, '.
|
|
179
|
+
const codexSkill = path.join(root, '.codex', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
180
|
+
const antigravitySkill = path.join(root, '.agent', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
114
181
|
const geminiSkill = path.join(root, '.gemini', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
115
182
|
const opencodeSkill = path.join(root, '.opencode', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
116
183
|
const qwenSkill = path.join(root, '.qwen', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
@@ -121,7 +188,6 @@ function main() {
|
|
|
121
188
|
const skillDesc = skillFm.description || skill.description || skill.name || skill.slug;
|
|
122
189
|
const skillArgHint = skillFm['argument-hint'] || '';
|
|
123
190
|
|
|
124
|
-
writeFile(claudeSkill, skillWrapper(skill.slug, relativeTarget(claudeSkill, canonicalPath), skillDesc));
|
|
125
191
|
writeFile(codexSkill, skillWrapper(skill.slug, relativeTarget(codexSkill, canonicalPath), skillDesc));
|
|
126
192
|
writeFile(antigravitySkill, skillWrapper(skill.slug, relativeTarget(antigravitySkill, canonicalPath), skillDesc));
|
|
127
193
|
writeFile(geminiSkill, skillWrapper(skill.slug, relativeTarget(geminiSkill, canonicalPath), skillDesc));
|
|
@@ -129,47 +195,97 @@ function main() {
|
|
|
129
195
|
writeFile(qwenSkill, skillWrapper(skill.slug, relativeTarget(qwenSkill, canonicalPath), skillDesc));
|
|
130
196
|
|
|
131
197
|
writeFile(qwenCmd, qwenCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(qwenCmd, canonicalPath)));
|
|
132
|
-
writeFile(claudeSkill, claudeCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(claudeSkill, canonicalPath), skillArgHint));
|
|
198
|
+
writeFile(claudeSkill, claudeCommandWrapper(`gos-${skill.slug}`, skillDesc, relativeTarget(claudeSkill, canonicalPath), skillArgHint, 'Claude'));
|
|
133
199
|
}
|
|
134
200
|
|
|
135
|
-
|
|
136
|
-
|
|
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).
|
|
205
|
+
|
|
206
|
+
// Codex IDE Extension — AGENTS.md + config.toml
|
|
207
|
+
// Codex e o ambiente de EXECUCAO (Opus planeja, Codex executa). Bloco abaixo garante
|
|
208
|
+
// que slash commands e subagents estao disponiveis ao abrir o projeto no Codex.
|
|
209
|
+
const codexAgentsMd = [
|
|
210
|
+
'# G-OS no Codex IDE Extension',
|
|
211
|
+
'',
|
|
212
|
+
'Este arquivo e auto-gerado por `npm run sync:ides`. Nao edite a mao.',
|
|
213
|
+
'',
|
|
214
|
+
'Codex IDE Extension e o ambiente de EXECUCAO do G-OS. Opus 4.7 planeja em outra IDE/sessao;',
|
|
215
|
+
'Codex executa task-a-task com `*execute-plan`.',
|
|
137
216
|
'',
|
|
138
217
|
'Leia sempre:',
|
|
139
|
-
'-
|
|
140
|
-
'-
|
|
141
|
-
'-
|
|
218
|
+
'- `../AGENTS.md` (raiz do projeto)',
|
|
219
|
+
'- `../CLAUDE.md`',
|
|
220
|
+
'- `../.gos/docs/toolchain-map.md`',
|
|
221
|
+
'',
|
|
222
|
+
'## Execucao de planos (comando primario do Codex)',
|
|
223
|
+
'',
|
|
224
|
+
'```',
|
|
225
|
+
'*execute-plan PLAN-NNN-<slug>',
|
|
226
|
+
'```',
|
|
227
|
+
'',
|
|
228
|
+
'Ciclo: pre-flight visual -> loop por task com state machine -> visual gate -> validacao -> humano marca concluido.',
|
|
229
|
+
'Detalhes: `../.gos/skills/execute-plan/SKILL.md`.',
|
|
230
|
+
'',
|
|
231
|
+
'## Agents disponiveis',
|
|
232
|
+
'',
|
|
233
|
+
...agents.map((agent) => `- \`gos-${agent.id}\` -> \`../.gos/agents/profiles/${agent.path}\``),
|
|
142
234
|
'',
|
|
143
|
-
'
|
|
144
|
-
...agents.map((agent) => `- ${agent.id}`),
|
|
235
|
+
'## Skills curadas',
|
|
145
236
|
'',
|
|
146
|
-
'
|
|
147
|
-
|
|
237
|
+
'| Slug | Arquivo canonico |',
|
|
238
|
+
'|------|------------------|',
|
|
239
|
+
...skills.map((skill) => `| \`gos-${skill.slug}\` | \`../.gos/${skill.skillFile || skill.path}\` |`),
|
|
148
240
|
'',
|
|
149
|
-
'## Como
|
|
241
|
+
'## Como o Codex consome',
|
|
150
242
|
'',
|
|
151
|
-
'
|
|
152
|
-
'
|
|
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.',
|
|
246
|
+
''
|
|
247
|
+
].join('\n');
|
|
248
|
+
writeFile(path.join(root, '.codex', 'AGENTS.md'), codexAgentsMd);
|
|
249
|
+
|
|
250
|
+
const codexConfigToml = [
|
|
251
|
+
'# G-OS Codex IDE Extension config (auto-gerado por npm run sync:ides).',
|
|
252
|
+
'# Edite os arquivos canonicos em .gos/ ao inves deste.',
|
|
153
253
|
'',
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
|
|
254
|
+
'project = "g-os"',
|
|
255
|
+
'instructions = "AGENTS.md"',
|
|
256
|
+
'',
|
|
257
|
+
'[execution]',
|
|
258
|
+
'primary_command = "*execute-plan"',
|
|
259
|
+
'planning_command = "*plan"',
|
|
260
|
+
'progress_command = "*progress"',
|
|
261
|
+
'stack_command = "*stack"',
|
|
262
|
+
''
|
|
157
263
|
].join('\n');
|
|
264
|
+
writeFile(path.join(root, '.codex', 'config.toml'), codexConfigToml);
|
|
158
265
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
)
|
|
170
|
-
|
|
266
|
+
// Validacao final: garantir que os arquivos do Codex foram gerados.
|
|
267
|
+
// Evita regressoes silenciosas que ja quebraram a IDE no passado.
|
|
268
|
+
const codexFailures = [];
|
|
269
|
+
for (const agent of agents) {
|
|
270
|
+
const expectedAgent = path.join(root, '.codex', 'agents', `${agentSlug(agent.id)}.md`);
|
|
271
|
+
const expectedCmd = path.join(root, '.codex', 'commands', 'gos', 'agents', `${agent.id}.md`);
|
|
272
|
+
if (!fs.existsSync(expectedAgent)) codexFailures.push(expectedAgent);
|
|
273
|
+
if (!fs.existsSync(expectedCmd)) codexFailures.push(expectedCmd);
|
|
274
|
+
}
|
|
275
|
+
for (const skill of skills) {
|
|
276
|
+
const expectedSkill = path.join(root, '.codex', 'skills', `gos-${skill.slug}`, 'SKILL.md');
|
|
277
|
+
if (!fs.existsSync(expectedSkill)) codexFailures.push(expectedSkill);
|
|
278
|
+
}
|
|
279
|
+
if (!fs.existsSync(path.join(root, '.codex', 'AGENTS.md'))) codexFailures.push('.codex/AGENTS.md');
|
|
280
|
+
if (!fs.existsSync(path.join(root, '.codex', 'config.toml'))) codexFailures.push('.codex/config.toml');
|
|
281
|
+
if (codexFailures.length > 0) {
|
|
282
|
+
console.error(`[sync:ides] Codex IDE adapters incompletos. Faltando ${codexFailures.length} arquivos:`);
|
|
283
|
+
for (const f of codexFailures) console.error(` - ${path.relative(root, f)}`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
171
286
|
|
|
172
287
|
console.log(`Adapters generated for ${agents.length} agents and ${skills.length} skills.`);
|
|
288
|
+
console.log(`Codex IDE: ${agents.length} agents + ${skills.length} skills + AGENTS.md + config.toml.`);
|
|
173
289
|
}
|
|
174
290
|
|
|
175
291
|
main();
|