guild-agents 0.0.1 → 0.2.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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -0
  3. package/bin/guild.js +95 -0
  4. package/package.json +58 -7
  5. package/src/commands/__tests__/doctor.test.js +85 -0
  6. package/src/commands/__tests__/list.test.js +82 -0
  7. package/src/commands/__tests__/new-agent.test.js +40 -0
  8. package/src/commands/__tests__/status.test.js +35 -0
  9. package/src/commands/doctor.js +99 -0
  10. package/src/commands/init.js +140 -0
  11. package/src/commands/list.js +82 -0
  12. package/src/commands/new-agent.js +92 -0
  13. package/src/commands/status.js +57 -0
  14. package/src/templates/agents/advisor.md +47 -0
  15. package/src/templates/agents/bugfix.md +50 -0
  16. package/src/templates/agents/code-reviewer.md +52 -0
  17. package/src/templates/agents/db-migration.md +50 -0
  18. package/src/templates/agents/developer.md +50 -0
  19. package/src/templates/agents/platform-expert.md +89 -0
  20. package/src/templates/agents/product-owner.md +51 -0
  21. package/src/templates/agents/qa.md +50 -0
  22. package/src/templates/agents/tech-lead.md +50 -0
  23. package/src/templates/skills/build-feature/SKILL.md +200 -0
  24. package/src/templates/skills/council/SKILL.md +145 -0
  25. package/src/templates/skills/dev-flow/SKILL.md +69 -0
  26. package/src/templates/skills/guild-specialize/SKILL.md +156 -0
  27. package/src/templates/skills/new-feature/SKILL.md +86 -0
  28. package/src/templates/skills/qa-cycle/SKILL.md +73 -0
  29. package/src/templates/skills/review/SKILL.md +66 -0
  30. package/src/templates/skills/session-end/SKILL.md +84 -0
  31. package/src/templates/skills/session-start/SKILL.md +83 -0
  32. package/src/templates/skills/status/SKILL.md +81 -0
  33. package/src/utils/files.js +103 -0
  34. package/src/utils/generators.js +111 -0
  35. package/src/utils/github.js +144 -0
  36. package/index.js +0 -1
@@ -0,0 +1,103 @@
1
+ /**
2
+ * files.js — Utilidades de sistema de archivos para Guild v1
3
+ */
4
+
5
+ import { mkdirSync, copyFileSync, existsSync, readdirSync, readFileSync } from 'fs';
6
+ import { join, dirname, resolve } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const TEMPLATES_DIR = join(__dirname, '..', 'templates');
11
+ const AGENTS_DIR = join('.claude', 'agents');
12
+ const SKILLS_DIR = join('.claude', 'skills');
13
+
14
+ /**
15
+ * Lista los nombres de los 9 agentes v1.
16
+ */
17
+ export function getAgentNames() {
18
+ return [
19
+ 'advisor',
20
+ 'product-owner',
21
+ 'tech-lead',
22
+ 'developer',
23
+ 'code-reviewer',
24
+ 'qa',
25
+ 'bugfix',
26
+ 'db-migration',
27
+ 'platform-expert',
28
+ ];
29
+ }
30
+
31
+ /**
32
+ * Copia los templates de agentes y skills al proyecto del usuario.
33
+ */
34
+ export async function copyTemplates() {
35
+ mkdirSync(AGENTS_DIR, { recursive: true });
36
+ mkdirSync(SKILLS_DIR, { recursive: true });
37
+
38
+ // Copy flat agent .md files
39
+ for (const name of getAgentNames()) {
40
+ const src = join(TEMPLATES_DIR, 'agents', `${name}.md`);
41
+ const dest = join(AGENTS_DIR, `${name}.md`);
42
+ if (existsSync(src)) {
43
+ copyFileSync(src, dest);
44
+ }
45
+ }
46
+
47
+ // Copy skill directories with SKILL.md
48
+ const skillsTemplate = join(TEMPLATES_DIR, 'skills');
49
+ if (existsSync(skillsTemplate)) {
50
+ const skills = readdirSync(skillsTemplate, { withFileTypes: true })
51
+ .filter(d => d.isDirectory())
52
+ .map(d => d.name);
53
+
54
+ for (const skill of skills) {
55
+ const skillDir = join(SKILLS_DIR, skill);
56
+ mkdirSync(skillDir, { recursive: true });
57
+
58
+ const src = join(skillsTemplate, skill, 'SKILL.md');
59
+ const dest = join(skillDir, 'SKILL.md');
60
+ if (existsSync(src)) {
61
+ copyFileSync(src, dest);
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Lee el contenido de PROJECT.md si existe.
69
+ */
70
+ export function readProjectMd() {
71
+ const path = 'PROJECT.md';
72
+ if (!existsSync(path)) return null;
73
+ return readFileSync(path, 'utf8');
74
+ }
75
+
76
+ /**
77
+ * Lee el contenido de SESSION.md si existe.
78
+ */
79
+ export function readSessionMd() {
80
+ const path = 'SESSION.md';
81
+ if (!existsSync(path)) return null;
82
+ return readFileSync(path, 'utf8');
83
+ }
84
+
85
+ /**
86
+ * Resuelve la raiz del proyecto Guild caminando hacia arriba desde startDir.
87
+ * Busca .claude/ o PROJECT.md como marcadores de un proyecto Guild.
88
+ * Retorna la ruta absoluta del proyecto o null si no se encuentra.
89
+ */
90
+ export function resolveProjectRoot(startDir = process.cwd()) {
91
+ let dir = resolve(startDir);
92
+ while (true) {
93
+ if (existsSync(join(dir, '.claude')) || existsSync(join(dir, 'PROJECT.md'))) {
94
+ return dir;
95
+ }
96
+ const parent = dirname(dir);
97
+ if (parent === dir) {
98
+ // Reached filesystem root without finding a project
99
+ return null;
100
+ }
101
+ dir = parent;
102
+ }
103
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * generators.js — Genera los archivos de estado del proyecto (v1)
3
+ */
4
+
5
+ import { writeFileSync } from 'fs';
6
+
7
+ /**
8
+ * Genera PROJECT.md con los datos del onboarding.
9
+ * V1: solo metadata cruda — CLAUDE.md tiene el contexto enriquecido.
10
+ */
11
+ export async function generateProjectMd(data) {
12
+ const date = new Date().toISOString().split('T')[0];
13
+
14
+ let content = `# PROJECT.md
15
+ > Generado por Guild v1 el ${date}
16
+
17
+ ## Proyecto
18
+ - **Nombre:** ${data.name}
19
+ - **Tipo:** ${data.type}
20
+ - **Stack:** ${data.stack}
21
+ - **Codigo existente:** ${data.hasExistingCode ? 'Si' : 'No'}
22
+ `;
23
+
24
+ if (data.github?.repoUrl) {
25
+ content += `
26
+ ## GitHub
27
+ - **Repositorio:** ${data.github.repoUrl}
28
+ `;
29
+ }
30
+
31
+ writeFileSync('PROJECT.md', content, 'utf8');
32
+ }
33
+
34
+ /**
35
+ * Genera CLAUDE.md — documento central con placeholders para guild-specialize.
36
+ */
37
+ export async function generateClaudeMd(data) {
38
+ const content = `# ${data.name}
39
+
40
+ ## Framework
41
+ Este proyecto usa Guild. Leer SESSION.md al inicio de cada sesion.
42
+
43
+ ## Stack
44
+ ${data.stack}
45
+
46
+ ## Estructura del proyecto
47
+ [PENDIENTE: guild-specialize]
48
+
49
+ ## Convenciones de codigo
50
+ [PENDIENTE: guild-specialize]
51
+
52
+ ## Patrones de arquitectura
53
+ [PENDIENTE: guild-specialize]
54
+
55
+ ## Variables de entorno
56
+ [PENDIENTE: guild-specialize]
57
+
58
+ ## Reglas globales
59
+ - No implementar sin plan aprobado
60
+ - Actualizar SESSION.md al cerrar cada sesion
61
+ - ESModules en todo el codigo
62
+ - path.join() siempre para construir paths
63
+
64
+ ## Subagent rules
65
+ - Guild agent roles (advisor, developer, tech-lead, etc.) are NOT Claude Code subagent_types
66
+ - Always use \`subagent_type: "general-purpose"\` when spawning agents via Task tool
67
+ - CLAUDE.md and SESSION.md changes must be committed separately from feature code
68
+ - No \`git stash\` in automated pipelines — use \`wip:\` commits instead
69
+ - Parallel agents must use git worktrees for isolation
70
+
71
+ ## Skills disponibles
72
+ - /guild-specialize — enriquecer CLAUDE.md explorando el proyecto real
73
+ - /build-feature — pipeline completo de desarrollo
74
+ - /new-feature — crear branch y scaffold para feature
75
+ - /council — debatir decisiones con multiples agentes
76
+ - /review — code review sobre el diff actual
77
+ - /qa-cycle — ciclo QA + bugfix
78
+ - /status — ver estado del proyecto
79
+ - /dev-flow — ver fase actual del pipeline
80
+ - /session-start — cargar contexto y retomar trabajo
81
+ - /session-end — guardar estado en SESSION.md
82
+ `;
83
+
84
+ writeFileSync('CLAUDE.md', content, 'utf8');
85
+ }
86
+
87
+ /**
88
+ * Genera SESSION.md inicial.
89
+ */
90
+ export async function generateSessionMd() {
91
+ const date = new Date().toISOString().split('T')[0];
92
+
93
+ const content = `# SESSION.md
94
+
95
+ ## Sesion activa
96
+ - **Fecha:** ${date}
97
+ - **Tarea en curso:** —
98
+ - **Agente activo:** —
99
+ - **Estado:** Proyecto recien inicializado con Guild v1
100
+
101
+ ## Contexto relevante
102
+ - Onboarding completado. Ver PROJECT.md para datos del proyecto.
103
+ - CLAUDE.md tiene placeholders — ejecutar /guild-specialize para enriquecer.
104
+
105
+ ## Proximos pasos
106
+ 1. Abrir Claude Code y ejecutar /guild-specialize
107
+ 2. Definir la primera feature con /build-feature
108
+ `;
109
+
110
+ writeFileSync('SESSION.md', content, 'utf8');
111
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * github.js — Integración con GitHub CLI (gh)
3
+ *
4
+ * Requiere que el usuario tenga gh instalado y autenticado.
5
+ * Todas las operaciones son no-bloqueantes — si gh no está disponible,
6
+ * Guild funciona normalmente sin integración GitHub.
7
+ *
8
+ * Uses execFileSync with array-based arguments to prevent shell injection
9
+ * through user-controlled strings (issue titles, bodies, labels).
10
+ */
11
+
12
+ import { execFileSync } from 'node:child_process';
13
+
14
+ const LABELS = [
15
+ { name: 'backlog', color: '8E8E8E', description: 'Tarea documentada, pendiente de iniciar' },
16
+ { name: 'in-progress', color: '0075CA', description: 'En implementación' },
17
+ { name: 'in-review', color: 'E4A800', description: 'En validación QA' },
18
+ { name: 'done', color: '2EA44F', description: 'Completada y mergeada' },
19
+ { name: 'bug', color: 'D73A4A', description: 'Bug reportado por QA' },
20
+ { name: 'blocked', color: 'E99695', description: 'Bloqueada por dependencia' },
21
+ ];
22
+
23
+ /**
24
+ * Verifica si gh CLI está instalado y autenticado.
25
+ */
26
+ export function isGhAvailable() {
27
+ try {
28
+ execFileSync('gh', ['auth', 'status'], { stdio: 'ignore' });
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Configura los labels de estado en el repositorio GitHub.
37
+ */
38
+ export async function setupGithubLabels(repoUrl) {
39
+ if (!isGhAvailable()) {
40
+ console.warn('gh CLI no disponible — saltando configuración de labels.');
41
+ return;
42
+ }
43
+
44
+ const repo = extractRepoFromUrl(repoUrl);
45
+ if (!repo) return;
46
+
47
+ for (const label of LABELS) {
48
+ try {
49
+ execFileSync('gh', [
50
+ 'label', 'create', label.name,
51
+ '--color', label.color,
52
+ '--description', label.description,
53
+ '--repo', repo,
54
+ '--force',
55
+ ], { stdio: 'ignore' });
56
+ } catch {
57
+ // Label ya existe o error no crítico
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Asigna un issue a @me y cambia su label de estado.
64
+ */
65
+ export function assignIssue(issueNumber, fromLabel, toLabel) {
66
+ if (!isGhAvailable()) return;
67
+
68
+ try {
69
+ execFileSync('gh', [
70
+ 'issue', 'assign', String(issueNumber), '--assignee', '@me',
71
+ ], { stdio: 'ignore' });
72
+ execFileSync('gh', [
73
+ 'issue', 'edit', String(issueNumber),
74
+ '--add-label', toLabel,
75
+ '--remove-label', fromLabel,
76
+ ], { stdio: 'ignore' });
77
+ } catch {
78
+ // Non-critical
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Agrega un comentario a un issue.
84
+ */
85
+ export function commentIssue(issueNumber, body) {
86
+ if (!isGhAvailable()) return;
87
+
88
+ try {
89
+ execFileSync('gh', [
90
+ 'issue', 'comment', String(issueNumber), '--body', body,
91
+ ], { stdio: 'ignore' });
92
+ } catch {
93
+ // Non-critical
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Cierra un issue con un comentario.
99
+ */
100
+ export function closeIssue(issueNumber, comment) {
101
+ if (!isGhAvailable()) return;
102
+
103
+ try {
104
+ execFileSync('gh', [
105
+ 'issue', 'close', String(issueNumber), '--comment', comment,
106
+ ], { stdio: 'ignore' });
107
+ } catch {
108
+ // Non-critical
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Crea un issue de bug referenciando una tarea padre.
114
+ */
115
+ export function createBugIssue(title, body, parentIssueNumber) {
116
+ if (!isGhAvailable()) return null;
117
+
118
+ try {
119
+ const result = execFileSync('gh', [
120
+ 'issue', 'create',
121
+ '--title', title,
122
+ '--body', body,
123
+ '--label', 'bug',
124
+ ], { encoding: 'utf8' });
125
+ const issueUrl = result.trim();
126
+ const issueNumber = issueUrl.split('/').pop();
127
+
128
+ if (parentIssueNumber) {
129
+ commentIssue(parentIssueNumber, `Bug encontrado: ${issueUrl}`);
130
+ }
131
+
132
+ return { number: issueNumber, url: issueUrl };
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Extrae "owner/repo" de una URL de GitHub.
140
+ */
141
+ function extractRepoFromUrl(url) {
142
+ const match = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
143
+ return match ? match[1] : null;
144
+ }
package/index.js DELETED
@@ -1 +0,0 @@
1
- // Guild Agents - coming soon