@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.
- package/AGENT_WORKFLOW.md +129 -55
- package/CLAUDE.md +59 -34
- package/PLAYBOOK.md +192 -0
- package/PROJECT_BLUEPRINT.md +52 -0
- package/README.md +248 -167
- package/agents/agent-1-infra/skills.md +46 -0
- package/agents/agent-2-detectors/skills.md +38 -0
- package/agents/agent-3-generators/skills.md +43 -0
- package/agents/agent-4-commands/skills.md +37 -0
- package/dist/cli.cjs +208 -156
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +208 -156
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/add.ts +22 -15
- package/src/commands/init.ts +2 -2
- package/src/generators/claudeMdGenerator.ts +3 -14
- package/src/generators/playbookGenerator.ts +136 -36
- package/src/generators/workflowGenerator.ts +9 -36
- package/tests/commands/add.test.ts +1 -1
- package/tests/generators/blueprintSupport.test.ts +35 -30
- package/tests/generators/claudeMdGenerator.test.ts +48 -0
- package/tests/generators/playbookGenerator.test.ts +140 -48
- package/tests/generators/workflowGenerator.test.ts +40 -0
|
@@ -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
|
|
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: '
|
|
22
|
+
scope: 'reusable UI components',
|
|
23
23
|
outputs: ['src/components/'],
|
|
24
24
|
criterion: 'npm test',
|
|
25
25
|
},
|
|
26
26
|
]
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
36
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
37
|
-
expect(result).toContain('
|
|
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('
|
|
41
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
42
|
-
expect(result).toContain(
|
|
43
|
-
expect(result).toContain(
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
expect(result).toContain('
|
|
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('
|
|
55
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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('
|
|
64
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
65
|
-
expect(result).toContain('
|
|
66
|
-
expect(result).toContain('
|
|
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
|
|
72
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
73
|
-
expect(result).toContain('
|
|
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('
|
|
78
|
-
const result = generatePlaybook({ agents: sampleAgents, projectName: 'my-app' })
|
|
79
|
-
expect(result).toContain('
|
|
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('
|
|
86
|
-
const result = generatePlaybook({ agents:
|
|
87
|
-
expect(
|
|
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
|
})
|