@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.
@@ -10,7 +10,7 @@ const sampleAgents: Agent[] = [
10
10
  name: 'Infra & Setup',
11
11
  fullName: 'Agent 1 · Infra & Setup',
12
12
  slug: 'infra-setup',
13
- scope: 'scaffolding du projet',
13
+ scope: 'project scaffolding',
14
14
  outputs: ['package.json', 'tsconfig.json'],
15
15
  criterion: 'npm run build',
16
16
  },
@@ -19,76 +19,166 @@ const sampleAgents: Agent[] = [
19
19
  name: 'Components',
20
20
  fullName: 'Agent 2 · Components',
21
21
  slug: 'components',
22
- scope: 'composants UI réutilisables',
22
+ scope: 'reusable UI components',
23
23
  outputs: ['src/components/'],
24
24
  criterion: 'npm test',
25
25
  },
26
26
  ]
27
27
 
28
- describe('generatePlaybook', () => {
29
- it('returns a non-empty string', () => {
30
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
31
- expect(typeof result).toBe('string')
32
- expect(result.length).toBeGreaterThan(0)
28
+ // ─── Common behaviour (both modes) ────────────────────────────────────────────
29
+
30
+ describe('generatePlaybook (common)', () => {
31
+ for (const hasBlueprint of [true, false]) {
32
+ const label = hasBlueprint ? 'hasBlueprint: true' : 'hasBlueprint: false'
33
+
34
+ it(`[${label}] returns a non-empty string`, () => {
35
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
36
+ expect(typeof result).toBe('string')
37
+ expect(result.length).toBeGreaterThan(0)
38
+ })
39
+
40
+ it(`[${label}] contains the project name`, () => {
41
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
42
+ expect(result).toContain('my-app')
43
+ })
44
+
45
+ it(`[${label}] contains the one-instruction block`, () => {
46
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
47
+ expect(result).toContain('Read PLAYBOOK.md and execute the procedure')
48
+ })
49
+
50
+ it(`[${label}] contains global execution rules`, () => {
51
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
52
+ expect(result).toContain('Global Execution Rules')
53
+ expect(result).toContain('max 3 attempts')
54
+ expect(result).toContain('human validation')
55
+ expect(result).toContain('Never move to the next agent')
56
+ })
57
+
58
+ it(`[${label}] always contains Phase 0`, () => {
59
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
60
+ expect(result).toContain('Phase 0')
61
+ })
62
+
63
+ it(`[${label}] always contains Phase 1`, () => {
64
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
65
+ expect(result).toContain('Phase 1')
66
+ })
67
+
68
+ it(`[${label}] Phase 0 comes before Phase 1`, () => {
69
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
70
+ expect(result.indexOf('Phase 0')).toBeLessThan(result.indexOf('Phase 1'))
71
+ })
72
+
73
+ it(`[${label}] contains a block for each agent`, () => {
74
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
75
+ for (const agent of sampleAgents) {
76
+ expect(result).toContain(agent.fullName)
77
+ expect(result).toContain(agent.scope)
78
+ expect(result).toContain(`agents/agent-${agent.number}-${agent.slug}/skills.md`)
79
+ }
80
+ })
81
+
82
+ it(`[${label}] each agent block contains outputs and criterion`, () => {
83
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
84
+ expect(result).toContain('package.json')
85
+ expect(result).toContain('npm run build')
86
+ expect(result).toContain('src/components/')
87
+ expect(result).toContain('npm test')
88
+ })
89
+
90
+ it(`[${label}] contains the future iterations section`, () => {
91
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
92
+ expect(result).toContain('Future Iterations')
93
+ expect(result).toContain('agentkit add --feature')
94
+ })
95
+
96
+ it(`[${label}] contains the human validation section`, () => {
97
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint })
98
+ expect(result).toContain('Human Validation Required')
99
+ expect(result).toContain('3 consecutive failures')
100
+ expect(result).toContain('Missing external dependency')
101
+ expect(result).toContain('End of Phase 0')
102
+ })
103
+
104
+ it(`[${label}] handles an empty agents list gracefully`, () => {
105
+ const result = generatePlaybook({ agents: [], projectName: 'empty-project', hasBlueprint })
106
+ expect(typeof result).toBe('string')
107
+ expect(result).toContain('empty-project')
108
+ })
109
+ }
110
+ })
111
+
112
+ // ─── Phase 0 with blueprint (Decomposition mode) ──────────────────────────────
113
+
114
+ describe('generatePlaybook Phase 0 — Decomposition (hasBlueprint: true)', () => {
115
+ it('contains the decomposition title', () => {
116
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: true })
117
+ expect(result).toContain('Agent Decomposition')
118
+ })
119
+
120
+ it('references PROJECT_BLUEPRINT.md', () => {
121
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: true })
122
+ expect(result).toContain('PROJECT_BLUEPRINT.md')
123
+ })
124
+
125
+ it('contains decomposition rules', () => {
126
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: true })
127
+ expect(result).toContain('Maximum 6 agents')
128
+ expect(result).toContain('AGENT_WORKFLOW.md')
33
129
  })
34
130
 
35
- it('contains the project name', () => {
36
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
37
- expect(result).toContain('my-app')
131
+ it('contains the validation gate', () => {
132
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: true })
133
+ expect(result).toContain('Should I proceed')
38
134
  })
39
135
 
40
- it('starts with the unique instruction', () => {
41
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
42
- expect(result).toContain("Donne cette instruction à Claude Code")
43
- expect(result).toContain("Lis PLAYBOOK.md et exécute la procédure")
136
+ it('does NOT contain Project Discovery language', () => {
137
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: true })
138
+ expect(result).not.toContain('Project Discovery')
139
+ expect(result).not.toContain('Ask the user these questions')
44
140
  })
141
+ })
142
+
143
+ // ─── Phase 0 without blueprint (Discovery mode) ───────────────────────────────
45
144
 
46
- it('contains global execution rules', () => {
47
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
48
- expect(result).toContain('Règles d\'exécution globales')
49
- expect(result).toContain('3 tentatives')
50
- expect(result).toContain('validation humaine')
51
- expect(result).toContain('Ne jamais passer')
145
+ describe('generatePlaybook Phase 0 — Discovery (hasBlueprint: false)', () => {
146
+ it('contains the discovery title', () => {
147
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
148
+ expect(result).toContain('Project Discovery')
52
149
  })
53
150
 
54
- it('contains a block for each agent', () => {
55
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
56
- for (const agent of sampleAgents) {
57
- expect(result).toContain(agent.fullName)
58
- expect(result).toContain(agent.scope)
59
- expect(result).toContain(`agents/agent-${agent.number}-${agent.slug}/skills.md`)
60
- }
151
+ it('instructs Claude Code to ask the user questions', () => {
152
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
153
+ expect(result).toContain('Ask the user these questions')
154
+ expect(result).toContain('What is this project')
155
+ expect(result).toContain('What are the main features')
61
156
  })
62
157
 
63
- it('each agent block contains outputs and criterion', () => {
64
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
65
- expect(result).toContain('package.json')
66
- expect(result).toContain('npm run build')
67
- expect(result).toContain('src/components/')
68
- expect(result).toContain('npm test')
158
+ it('still contains decomposition rules', () => {
159
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
160
+ expect(result).toContain('Maximum 6 agents')
161
+ expect(result).toContain('AGENT_WORKFLOW.md')
69
162
  })
70
163
 
71
- it('contains the "Itérations futures" section', () => {
72
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
73
- expect(result).toContain('Itérations futures')
74
- expect(result).toContain('agentkit add --feature')
164
+ it('still contains the validation gate', () => {
165
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
166
+ expect(result).toContain('Should I proceed')
75
167
  })
76
168
 
77
- it('contains the human validation section', () => {
78
- const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
79
- expect(result).toContain('Validation humaine requise')
80
- expect(result).toContain('3 échecs consécutifs')
81
- expect(result).toContain('Dépendance externe manquante')
82
- expect(result).toContain('Conflit')
169
+ it('does NOT reference PROJECT_BLUEPRINT.md as a file to read', () => {
170
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
171
+ expect(result).not.toContain('A `PROJECT_BLUEPRINT.md` was provided')
83
172
  })
84
173
 
85
- it('handles an empty agents list gracefully', () => {
86
- const result = generatePlaybook({ agents: [], projectName: 'empty-project' })
87
- expect(typeof result).toBe('string')
88
- expect(result).toContain('empty-project')
174
+ it('does NOT contain Agent Decomposition title', () => {
175
+ const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app', hasBlueprint: false })
176
+ expect(result).not.toContain('Agent Decomposition (run this first)')
89
177
  })
90
178
  })
91
179
 
180
+ // ─── toSlug ───────────────────────────────────────────────────────────────────
181
+
92
182
  describe('toSlug', () => {
93
183
  it('converts name to lowercase hyphenated slug', () => {
94
184
  expect(toSlug('Infra & Setup')).toBe('infra-setup')
@@ -104,6 +194,8 @@ describe('toSlug', () => {
104
194
  })
105
195
  })
106
196
 
197
+ // ─── extractAgentsFromWorkflow ────────────────────────────────────────────────
198
+
107
199
  describe('extractAgentsFromWorkflow', () => {
108
200
  it('extracts agents from a workflow string matching template format', () => {
109
201
  const workflow = `# Agent Workflow — React Project
@@ -149,4 +241,4 @@ Critère : npm run build
149
241
  it('returns empty array for content with no agent blocks', () => {
150
242
  expect(extractAgentsFromWorkflow('# Just a title\nNo agents here.')).toEqual([])
151
243
  })
152
- })
244
+ })
@@ -81,4 +81,44 @@ describe('generateWorkflow', () => {
81
81
  expect(typeof result).toBe('string')
82
82
  expect(result.length).toBeGreaterThan(0)
83
83
  })
84
+
85
+ describe('blueprint placeholder', () => {
86
+ const blueprint = `# My Project\n\n## Goal\nBuild something\n\n## Features\n- Auth\n- Dashboard\n`
87
+
88
+ it('returns a placeholder when blueprintContent is provided', () => {
89
+ const result = generateWorkflow(makeStack('react'), blueprint)
90
+ expect(result).toContain('AGENT_WORKFLOW.md')
91
+ expect(result).toContain('Phase 0')
92
+ expect(result).toContain('PROJECT_BLUEPRINT.md')
93
+ expect(result).toContain('Waiting for Phase 0 decomposition')
94
+ })
95
+
96
+ it('uses projectName in heading when provided', () => {
97
+ const result = generateWorkflow(makeStack('react'), blueprint, 'my-app')
98
+ expect(result).toContain('AGENT_WORKFLOW.md — my-app')
99
+ })
100
+
101
+ it('falls back to framework name when projectName is omitted', () => {
102
+ const result = generateWorkflow(makeStack('react'), blueprint)
103
+ expect(result).toContain('AGENT_WORKFLOW.md — react')
104
+ })
105
+
106
+ it('does NOT parse blueprint sections as agents', () => {
107
+ const result = generateWorkflow(makeStack('react'), blueprint, 'my-app')
108
+ expect(result).not.toContain('Agent · Goal')
109
+ expect(result).not.toContain('Agent · Features')
110
+ expect(result).not.toContain('Agent 1')
111
+ })
112
+
113
+ it('returns the placeholder for every framework when blueprint is provided', () => {
114
+ const frameworks: StackInfo['framework'][] = [
115
+ 'react', 'nextjs', 'tauri', 'fastapi', 'express', 'node', 'unknown',
116
+ ]
117
+ for (const framework of frameworks) {
118
+ const result = generateWorkflow(makeStack(framework), blueprint, 'proj')
119
+ expect(result).toContain('Phase 0')
120
+ expect(result).not.toContain('Agent 1')
121
+ }
122
+ })
123
+ })
84
124
  })