@patricksardinha/agentkit-cli 0.2.0 → 0.4.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.
@@ -3,46 +3,146 @@ import type { Agent } from '../types/agent.js'
3
3
  export interface PlaybookInput {
4
4
  agents: Agent[]
5
5
  projectName: string
6
+ hasBlueprint: boolean
6
7
  }
7
8
 
8
- export function generatePlaybook({ agents, projectName }: PlaybookInput): string {
9
+ export function generatePlaybook({ agents, projectName, hasBlueprint }: PlaybookInput): string {
9
10
  const agentBlocks = agents.map((a) => agentBlock(a)).join('\n---\n\n')
10
11
 
12
+ const phase0 = hasBlueprint
13
+ ? `## Phase 0 — Agent Decomposition (run this first)
14
+
15
+ > A \`PROJECT_BLUEPRINT.md\` was provided.
16
+ > Claude Code reads it and decomposes the project into specialized agents
17
+ > before writing a single line of code.
18
+
19
+ **Read these files in order:**
20
+ 1. \`CLAUDE.md\`
21
+ 2. \`PROJECT_BLUEPRINT.md\`
22
+
23
+ **Then decompose the project into agents** following these rules:
24
+
25
+ - One agent = one coherent technical layer (never mix two layers)
26
+ - Each agent must have a runnable success criterion (\`npm test\`, \`cargo build\`…)
27
+ - Agents must be ordered by dependency (no feature without infra first)
28
+ - Maximum 6 agents — if you have more, group related ones
29
+ - Always respect this order:
30
+ 1. Infra & Configuration
31
+ 2. Data layer (DB schema, models, services)
32
+ 3. External integrations (auth, APIs, local services like Ollama)
33
+ 4. UI & pages
34
+ 5. Advanced features (RAG, export, realtime…)
35
+ 6. Build & release (CI/CD, packaging, installers)
36
+
37
+ **Write the result directly into \`AGENT_WORKFLOW.md\`** — replace its current
38
+ content with your decomposition.
39
+
40
+ **Then ask for human validation:**
41
+ > "I have decomposed the project into N agents: [list them].
42
+ > Should I proceed with execution?"
43
+
44
+ Wait for confirmation before moving to Phase 1.
45
+
46
+ ---
47
+
48
+ `
49
+ : `## Phase 0 — Project Discovery (run this first)
50
+
51
+ > No \`PROJECT_BLUEPRINT.md\` was provided.
52
+ > Before writing any code, Claude Code asks the user what they want to build,
53
+ > then decomposes the project into agents — exactly as if a blueprint had been provided.
54
+
55
+ **Ask the user these questions and wait for their answers:**
56
+
57
+ 1. What is this project? (one sentence describing the goal)
58
+ 2. What are the main features you want to build? (list them)
59
+ 3. Are there any tech constraints or architecture preferences?
60
+ (e.g. offline-only, specific DB, no auth, specific framework)
61
+
62
+ **Once you have the answers, decompose the project into agents**
63
+ following these rules:
64
+
65
+ - One agent = one coherent technical layer (never mix two layers)
66
+ - Each agent must have a runnable success criterion (\`npm test\`, \`cargo build\`…)
67
+ - Agents must be ordered by dependency (no feature without infra first)
68
+ - Maximum 6 agents — if you have more, group related ones
69
+ - Always respect this order:
70
+ 1. Infra & Configuration
71
+ 2. Data layer (DB schema, models, services)
72
+ 3. External integrations (auth, APIs, local services like Ollama)
73
+ 4. UI & pages
74
+ 5. Advanced features (RAG, export, realtime…)
75
+ 6. Build & release (CI/CD, packaging, installers)
76
+
77
+ **Write the result directly into \`AGENT_WORKFLOW.md\`** — replace its current
78
+ content with your decomposition.
79
+
80
+ **Then ask for human validation:**
81
+ > "I have decomposed the project into N agents: [list them].
82
+ > Should I proceed with execution?"
83
+
84
+ Wait for confirmation before moving to Phase 1.
85
+
86
+ ---
87
+
88
+ `
89
+
11
90
  return `# PLAYBOOK.md — ${projectName}
12
91
 
13
- > Donne cette instruction à Claude Code : 'Lis PLAYBOOK.md et exécute la procédure.'
92
+ > **One instruction to give Claude Code:**
93
+ > "Read PLAYBOOK.md and execute the procedure."
94
+ >
95
+ > Claude Code handles the rest autonomously — project discovery or blueprint reading,
96
+ > agent decomposition, execution, success validation, retries, and human escalation.
97
+ > No API key required. No additional cost beyond your LLM subscription.
14
98
 
15
- ## Règles d'exécution globales
99
+ ---
16
100
 
17
- Avant chaque agent :
18
- 1. Lire \`CLAUDE.md\`
19
- 2. Lire \`agents/agent-{N}-{slug}/skills.md\` (le fichier de l'agent courant)
101
+ ## Global Execution Rules
20
102
 
21
- Après chaque agent :
22
- - Exécuter le critère de succès
23
- - Si succès → annoncer "✅ Agent N terminé" et passer au suivant
24
- - Si échec analyser la cause racine, corriger, réexécuter (max 3 tentatives)
25
- - Après 3 échecs consécutifs → pause et demander validation humaine
26
- - **Ne jamais passer à l'agent suivant sans critère validé**
103
+ Before each agent:
104
+ 1. Read \`CLAUDE.md\`
105
+ 2. Read \`agents/agent-{N}-{slug}/skills.md\` (current agent's file)
106
+ 3. Read the agent's section in \`AGENT_WORKFLOW.md\`
27
107
 
28
- ## Agents
108
+ After each agent:
109
+ - Run the success criterion command
110
+ - ✅ Passes → announce "✅ Agent N complete" and move to the next
111
+ - ❌ Fails → analyze the root cause, fix, rerun (max 3 attempts)
112
+ - After 3 consecutive failures → stop and ask for human validation
113
+
114
+ **Never move to the next agent without a passing success criterion.**
115
+ **Stay strictly within your current agent's defined scope.**
116
+
117
+ ---
118
+
119
+ ${phase0}## Phase 1 — Execution
29
120
 
30
121
  ${agentBlocks}
31
122
 
32
- ## Itérations futures
123
+ ---
124
+
125
+ ## Future Iterations
33
126
 
34
- Lorsqu'un nouvel agent est ajouté via \`agentkit add --feature <description>\` :
35
- 1. Un nouveau bloc agent est ajouté à la fin de \`AGENT_WORKFLOW.md\`
36
- 2. Le dossier \`agents/agent-{N}-{slug}/\` est créé avec \`skills.md\` et \`context.md\`
37
- 3. Ce \`PLAYBOOK.md\` est régénéré automatiquement pour inclure le nouvel agent
38
- 4. L'exécution reprend à ce nouvel agent uniquementles agents précédents ne sont pas réexécutés
127
+ When a new agent is added via \`agentkit add --feature <description>\`:
128
+ 1. A new agent block is appended to \`AGENT_WORKFLOW.md\`
129
+ 2. The folder \`agents/agent-{N}-{slug}/\` is created with \`skills.md\`
130
+ 3. This \`PLAYBOOK.md\` is regenerated to include the new agent
131
+ 4. Execution resumes at the new agent onlycompleted agents are not rerun
39
132
 
40
- ## Validation humaine requise
133
+ When you receive the instruction to continue after an iteration:
134
+ > "Read PLAYBOOK.md and execute only the agents that haven't been completed yet."
41
135
 
42
- Les cas suivants nécessitent une pause et une confirmation humaine avant de continuer :
43
- - **3 échecs consécutifs** sur le critère de succès d'un agent
44
- - **Dépendance externe manquante** : clé API, variable d'environnement non définie, service tiers inaccessible
45
- - **Conflit** entre le blueprint fourni (\`--blueprint\`) et la stack détectée automatiquement
136
+ ---
137
+
138
+ ## Human Validation Required
139
+
140
+ Stop and wait for confirmation in these situations:
141
+ - **3 consecutive failures** on the same success criterion
142
+ - **Missing external dependency**: API key, env variable, unavailable service
143
+ - **Conflict** between the detected stack and the user's stated constraints
144
+ - **Destructive operation**: overwriting files not listed in deliverables
145
+ - **End of Phase 0**: agent decomposition must be validated before execution
46
146
  `
47
147
  }
48
148
 
@@ -51,26 +151,26 @@ function agentBlock(agent: Agent): string {
51
151
  const outputLines =
52
152
  agent.outputs.length > 0
53
153
  ? agent.outputs.map((o) => `- ${o}`).join('\n')
54
- : '- (voir skills.md pour le détail)'
154
+ : '- (see skills.md for details)'
55
155
 
56
- return `### ${agent.fullName}
156
+ return `### Agent ${agent.number} · ${agent.name}
57
157
 
58
- **Périmètre** : ${agent.scope}
158
+ **Scope**: ${agent.scope}
59
159
 
60
- **Skills** : \`${skillsPath}\`
160
+ **Skills**: \`${skillsPath}\`
61
161
 
62
- **Fichiers produits** :
162
+ **Deliverables**:
63
163
  ${outputLines}
64
164
 
65
- **Critère de succès** :
165
+ **Success criterion**:
66
166
  \`\`\`bash
67
167
  ${agent.criterion || 'npm run build && npm test'}
68
168
  \`\`\`
69
169
 
70
- **En cas d'échec** :
71
- 1. Analyser le message d'erreur complet
72
- 2. Corriger la cause racine (pas les symptômes)
73
- 3. Réexécuter le critère (max 3 tentatives)
74
- 4. Après 3 échecsdemander validation humaine
170
+ **On failure**:
171
+ 1. Read the full error output
172
+ 2. Fix the root cause not the symptoms
173
+ 3. Rerun the success criterion (max 3 attempts)
174
+ 4. After 3 failuresask for human validation
75
175
  `
76
- }
176
+ }
@@ -1,6 +1,4 @@
1
1
  import type { StackInfo } from '../detectors/stackDetector.js'
2
- import { parseBlueprint } from '../utils/blueprintParser.js'
3
- import { toSlug } from '../utils/agentParser.js'
4
2
  import * as react from '../templates/react.js'
5
3
  import * as nextjs from '../templates/nextjs.js'
6
4
  import * as tauri from '../templates/tauri.js'
@@ -9,8 +7,8 @@ import * as express from '../templates/express.js'
9
7
  import * as node from '../templates/node.js'
10
8
  import * as unknown from '../templates/unknown.js'
11
9
 
12
- export function generateWorkflow(stack: StackInfo, blueprintContent?: string): string {
13
- if (blueprintContent) return blueprintWorkflow(stack, blueprintContent)
10
+ export function generateWorkflow(stack: StackInfo, blueprintContent?: string, projectName?: string): string {
11
+ if (blueprintContent) return blueprintPlaceholder(projectName ?? stack.framework)
14
12
 
15
13
  switch (stack.framework) {
16
14
  case 'react': return react.workflow(stack)
@@ -23,40 +21,15 @@ export function generateWorkflow(stack: StackInfo, blueprintContent?: string): s
23
21
  }
24
22
  }
25
23
 
26
- function blueprintWorkflow(stack: StackInfo, blueprintContent: string): string {
27
- const features = parseBlueprint(blueprintContent)
24
+ function blueprintPlaceholder(projectName: string): string {
25
+ return `# AGENT_WORKFLOW.md — ${projectName}
28
26
 
29
- const agentBlocks = features.map((feature, i) => {
30
- const n = i + 1
31
- const slug = toSlug(feature.name)
32
- const outputLines =
33
- feature.items.length > 0
34
- ? feature.items.map((item) => ` - ${item}`).join('\n')
35
- : ` - src/${slug}/`
36
- return `### Agent ${n} · ${feature.name}
37
- Périmètre : Implémenter la fonctionnalité ${feature.name.toLowerCase()}
38
- Produit :
39
- ${outputLines}
40
- Critère : npm test (tests ${feature.name.toLowerCase()} passent)`
41
- })
27
+ > This file will be filled in by Claude Code during Phase 0.
28
+ > Claude Code will read PROJECT_BLUEPRINT.md, propose a decomposition,
29
+ > and replace this content after human validation.
42
30
 
43
- const ciN = features.length + 1
44
- agentBlocks.push(
45
- `### Agent ${ciN} · Tests & CI
46
- Périmètre : Couverture de tests complète et configuration du pipeline CI
47
- Produit :
48
- - tests/
49
- - .github/workflows/
50
- Critère : npm test passe, pipeline CI vert`,
51
- )
31
+ ---
52
32
 
53
- return `# Agent Workflow — ${stack.framework} (Blueprint)
54
-
55
- ## Stack détectée
56
- Framework: ${stack.framework} | Language: ${stack.language}
57
-
58
- ## Agents
59
-
60
- ${agentBlocks.join('\n\n')}
33
+ *Waiting for Phase 0 decomposition...*
61
34
  `
62
35
  }
@@ -116,7 +116,7 @@ describe('addFeatureToProject', () => {
116
116
  it('throws an error when AGENT_WORKFLOW.md is missing', async () => {
117
117
  tempDir = await mkdtemp(join(tmpdir(), 'agentkit-add-test-'))
118
118
  await expect(addFeatureToProject('Add feature', tempDir)).rejects.toThrow(
119
- 'AGENT_WORKFLOW.md introuvable',
119
+ 'AGENT_WORKFLOW.md not found in',
120
120
  )
121
121
  })
122
122
 
@@ -63,27 +63,30 @@ describe('generateClaudeMd with blueprint', () => {
63
63
  expect(generateClaudeMd(REACT_STACK)).toBe(generateClaudeMd(REACT_STACK, undefined))
64
64
  })
65
65
 
66
- it('includes a Features section when blueprint is provided', () => {
66
+ it('adds the blueprint note when blueprint is provided', () => {
67
67
  const result = generateClaudeMd(REACT_STACK, BLUEPRINT)
68
- expect(result).toContain('## Features (Blueprint)')
69
- expect(result).toContain('Authentication')
70
- expect(result).toContain('Dashboard')
71
- expect(result).toContain('API')
68
+ expect(result).toContain('PROJECT_BLUEPRINT.md is present')
69
+ expect(result).toContain('Phase 0')
72
70
  })
73
71
 
74
- it('includes blueprint sub-items', () => {
72
+ it('does NOT include a Features (Blueprint) section', () => {
75
73
  const result = generateClaudeMd(REACT_STACK, BLUEPRINT)
76
- expect(result).toContain('JWT tokens')
77
- expect(result).toContain('User statistics')
74
+ expect(result).not.toContain('## Features (Blueprint)')
78
75
  })
79
76
 
80
- it('places Features section before Conventions', () => {
77
+ it('does NOT include blueprint sub-items as feature bullets', () => {
81
78
  const result = generateClaudeMd(REACT_STACK, BLUEPRINT)
82
- const featIdx = result.indexOf('## Features (Blueprint)')
79
+ expect(result).not.toContain('JWT tokens')
80
+ expect(result).not.toContain('User statistics')
81
+ })
82
+
83
+ it('places blueprint note before Conventions', () => {
84
+ const result = generateClaudeMd(REACT_STACK, BLUEPRINT)
85
+ const noteIdx = result.indexOf('PROJECT_BLUEPRINT.md is present')
83
86
  const convIdx = result.indexOf('## Conventions')
84
- expect(featIdx).toBeGreaterThan(-1)
87
+ expect(noteIdx).toBeGreaterThan(-1)
85
88
  expect(convIdx).toBeGreaterThan(-1)
86
- expect(featIdx).toBeLessThan(convIdx)
89
+ expect(noteIdx).toBeLessThan(convIdx)
87
90
  })
88
91
 
89
92
  it('still contains stack-specific content', () => {
@@ -98,33 +101,35 @@ describe('generateWorkflow with blueprint', () => {
98
101
  expect(generateWorkflow(REACT_STACK)).toBe(generateWorkflow(REACT_STACK, undefined))
99
102
  })
100
103
 
101
- it('generates one agent per blueprint feature plus a CI agent', () => {
104
+ it('returns a Phase 0 placeholder instead of agent blocks', () => {
102
105
  const result = generateWorkflow(REACT_STACK, BLUEPRINT)
103
- expect(result).toContain('Agent 1 · Authentication')
104
- expect(result).toContain('Agent 2 · Dashboard')
105
- expect(result).toContain('Agent 3 · API')
106
- expect(result).toContain('Agent 4 · Tests & CI')
106
+ expect(result).toContain('AGENT_WORKFLOW.md')
107
+ expect(result).toContain('Phase 0')
108
+ expect(result).toContain('PROJECT_BLUEPRINT.md')
109
+ expect(result).toContain('Waiting for Phase 0 decomposition')
107
110
  })
108
111
 
109
- it('each agent block is parseable by extractAgentsFromWorkflow', () => {
112
+ it('does NOT generate agent blocks from blueprint sections', () => {
110
113
  const result = generateWorkflow(REACT_STACK, BLUEPRINT)
111
- const agents = extractAgentsFromWorkflow(result)
112
- expect(agents).toHaveLength(4)
113
- expect(agents[0].name).toBe('Authentication')
114
- expect(agents[0].slug).toBe('authentication')
115
- expect(agents[3].name).toBe('Tests & CI')
116
- expect(agents[3].slug).toBe('tests-ci')
114
+ expect(result).not.toContain('Agent 1')
115
+ expect(result).not.toContain('Authentication')
116
+ expect(result).not.toContain('Dashboard')
117
+ expect(result).not.toContain('Tests & CI')
117
118
  })
118
119
 
119
- it('includes blueprint feature items as outputs', () => {
120
+ it('does NOT include blueprint feature items as outputs', () => {
120
121
  const result = generateWorkflow(REACT_STACK, BLUEPRINT)
121
- expect(result).toContain('JWT tokens')
122
- expect(result).toContain('User statistics')
122
+ expect(result).not.toContain('JWT tokens')
123
+ expect(result).not.toContain('User statistics')
124
+ })
125
+
126
+ it('uses projectName in heading when provided', () => {
127
+ const result = generateWorkflow(REACT_STACK, BLUEPRINT, 'my-app')
128
+ expect(result).toContain('AGENT_WORKFLOW.md — my-app')
123
129
  })
124
130
 
125
- it('includes stack information in the header', () => {
131
+ it('falls back to framework name when projectName is omitted', () => {
126
132
  const result = generateWorkflow(REACT_STACK, BLUEPRINT)
127
- expect(result).toContain('react')
128
- expect(result).toContain('typescript')
133
+ expect(result).toContain('AGENT_WORKFLOW.md — react')
129
134
  })
130
135
  })
@@ -83,4 +83,52 @@ describe('generateClaudeMd', () => {
83
83
  expect(typeof result).toBe('string')
84
84
  expect(result.length).toBeGreaterThan(0)
85
85
  })
86
+
87
+ describe('blueprint note', () => {
88
+ const blueprint = `# My Project\n\n## Goal\nBuild something\n\n## Features\n- Auth\n- Dashboard\n`
89
+
90
+ it('adds the blueprint note when blueprintContent is provided', () => {
91
+ const result = generateClaudeMd(makeStack('react'), blueprint)
92
+ expect(result).toContain('PROJECT_BLUEPRINT.md is present')
93
+ expect(result).toContain('Phase 0')
94
+ })
95
+
96
+ it('does NOT list blueprint sections as features', () => {
97
+ const result = generateClaudeMd(makeStack('react'), blueprint)
98
+ expect(result).not.toContain('Features (Blueprint)')
99
+ expect(result).not.toContain('**Goal**')
100
+ expect(result).not.toContain('**Features**')
101
+ })
102
+
103
+ it('still contains the stack-based template content', () => {
104
+ const result = generateClaudeMd(makeStack('react'), blueprint)
105
+ expect(result).toContain('React')
106
+ expect(result).toContain('## Stack')
107
+ expect(result).toContain('## Commands')
108
+ })
109
+
110
+ it('adds blueprint note for unknown stack with blueprint', () => {
111
+ const result = generateClaudeMd(makeStack('unknown'), blueprint)
112
+ expect(result).toContain('PROJECT_BLUEPRINT.md is present')
113
+ expect(result).toContain('Phase 0')
114
+ })
115
+
116
+ it('returns base template unchanged when blueprintContent is absent', () => {
117
+ const withBlueprint = generateClaudeMd(makeStack('react'), blueprint)
118
+ const withoutBlueprint = generateClaudeMd(makeStack('react'))
119
+ expect(withBlueprint).not.toBe(withoutBlueprint)
120
+ expect(withoutBlueprint).not.toContain('PROJECT_BLUEPRINT.md is present')
121
+ })
122
+
123
+ it('adds blueprint note for every framework', () => {
124
+ const frameworks: StackInfo['framework'][] = [
125
+ 'react', 'nextjs', 'tauri', 'fastapi', 'express', 'node', 'unknown',
126
+ ]
127
+ for (const framework of frameworks) {
128
+ const result = generateClaudeMd(makeStack(framework), blueprint)
129
+ expect(result).toContain('PROJECT_BLUEPRINT.md is present')
130
+ expect(result).not.toContain('Features (Blueprint)')
131
+ }
132
+ })
133
+ })
86
134
  })