ccsetup 1.1.0 → 1.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.
- package/README.md +100 -342
- package/bin/create-project.js +1616 -60
- package/bin/lib/claudeInterface.js +209 -0
- package/bin/lib/contextGenerator.js +287 -0
- package/bin/lib/scanner/index.js +28 -0
- package/bin/scan.js +367 -0
- package/lib/aiAgentSelector.js +155 -0
- package/lib/aiMergeHelper.js +112 -0
- package/lib/contextGenerator.js +574 -0
- package/lib/contextMerger.js +812 -0
- package/lib/progressReporter.js +88 -0
- package/lib/scanConfig.js +200 -0
- package/lib/scanner/fileAnalyzer.js +605 -0
- package/lib/scanner/index.js +164 -0
- package/lib/scanner/patterns.js +277 -0
- package/lib/scanner/projectDetector.js +147 -0
- package/lib/templates/README.md +176 -0
- package/lib/templates/catalog.js +230 -0
- package/lib/templates/filter.js +257 -0
- package/lib/templates/index.js +45 -0
- package/lib/templates/metadata/agents.json +413 -0
- package/lib/templates/metadata-extractor.js +329 -0
- package/lib/templates/search.js +356 -0
- package/package.json +11 -4
- package/template/{agents → .claude/agents}/checker.md +29 -0
- package/template/.claude/settings.json +15 -0
- package/template/.claude/skills/prd/SKILL.md +343 -0
- package/template/.claude/skills/ralph/SKILL.md +339 -0
- package/template/CLAUDE.md +39 -21
- package/template/CONTRIBUTING.md +37 -0
- package/template/GEMINI.md +126 -0
- package/template/agents/README.md +15 -171
- package/template/docs/ROADMAP.md +0 -36
- package/template/docs/agent-orchestration.md +24 -141
- package/template/hooks/workflow-selector/index.js +398 -0
- package/template/scripts/ralph/CLAUDE.md +174 -0
- package/template/scripts/ralph/ralph.sh +127 -0
- package/template/tickets/ticket-list.md +17 -68
- package/template/agents/ai-engineer.md +0 -31
- package/template/agents/api-documenter.md +0 -31
- package/template/agents/architect-review.md +0 -42
- package/template/agents/backend-architect.md +0 -29
- package/template/agents/business-analyst.md +0 -34
- package/template/agents/c-pro.md +0 -34
- package/template/agents/cloud-architect.md +0 -31
- package/template/agents/code-reviewer.md +0 -28
- package/template/agents/content-marketer.md +0 -34
- package/template/agents/context-manager.md +0 -63
- package/template/agents/cpp-pro.md +0 -37
- package/template/agents/customer-support.md +0 -34
- package/template/agents/data-engineer.md +0 -31
- package/template/agents/data-scientist.md +0 -28
- package/template/agents/database-admin.md +0 -31
- package/template/agents/database-optimizer.md +0 -31
- package/template/agents/debugger.md +0 -29
- package/template/agents/deployment-engineer.md +0 -31
- package/template/agents/devops-troubleshooter.md +0 -31
- package/template/agents/dx-optimizer.md +0 -62
- package/template/agents/error-detective.md +0 -31
- package/template/agents/frontend-developer.md +0 -30
- package/template/agents/golang-pro.md +0 -31
- package/template/agents/graphql-architect.md +0 -31
- package/template/agents/incident-responder.md +0 -73
- package/template/agents/javascript-pro.md +0 -34
- package/template/agents/legacy-modernizer.md +0 -31
- package/template/agents/ml-engineer.md +0 -31
- package/template/agents/mlops-engineer.md +0 -56
- package/template/agents/mobile-developer.md +0 -31
- package/template/agents/network-engineer.md +0 -31
- package/template/agents/payment-integration.md +0 -31
- package/template/agents/performance-engineer.md +0 -31
- package/template/agents/prompt-engineer.md +0 -58
- package/template/agents/python-pro.md +0 -31
- package/template/agents/quant-analyst.md +0 -31
- package/template/agents/risk-manager.md +0 -40
- package/template/agents/rust-pro.md +0 -34
- package/template/agents/sales-automator.md +0 -34
- package/template/agents/search-specialist.md +0 -58
- package/template/agents/security-auditor.md +0 -31
- package/template/agents/sql-pro.md +0 -34
- package/template/agents/terraform-specialist.md +0 -34
- package/template/agents/test-automator.md +0 -31
- /package/template/{agents → .claude/agents}/backend.md +0 -0
- /package/template/{agents → .claude/agents}/blockchain.md +0 -0
- /package/template/{agents → .claude/agents}/coder.md +0 -0
- /package/template/{agents → .claude/agents}/frontend.md +0 -0
- /package/template/{agents → .claude/agents}/planner.md +0 -0
- /package/template/{agents → .claude/agents}/researcher.md +0 -0
- /package/template/{agents → .claude/agents}/shadcn.md +0 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Environment variable toggle — exit early if not enabled
|
|
7
|
+
// Enable with: export CCSETUP_WORKFLOW=1
|
|
8
|
+
const enabled = process.env.CCSETUP_WORKFLOW;
|
|
9
|
+
if (!enabled || (enabled !== '1' && enabled.toLowerCase() !== 'true')) {
|
|
10
|
+
console.log('{}');
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Simple workflow selector that reads from agent-orchestration.md
|
|
15
|
+
class WorkflowSelector {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.workflows = this.loadWorkflows();
|
|
18
|
+
this.availableAgents = this.getAvailableAgents();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
loadWorkflows() {
|
|
22
|
+
const orchestrationPath = path.join(process.cwd(), 'docs', 'agent-orchestration.md');
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(orchestrationPath)) {
|
|
25
|
+
return this.getDefaultWorkflows();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// First try to use Claude to extract workflows intelligently
|
|
30
|
+
const workflows = this.loadWorkflowsWithClaude(orchestrationPath);
|
|
31
|
+
if (workflows && Object.keys(workflows).length > 0) {
|
|
32
|
+
return workflows;
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// Claude extraction failed, continue with regex
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fallback to regex-based extraction
|
|
39
|
+
return this.loadWorkflowsWithRegex(orchestrationPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
loadWorkflowsWithClaude(orchestrationPath) {
|
|
43
|
+
const { execSync } = require('child_process');
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const content = fs.readFileSync(orchestrationPath, 'utf8');
|
|
47
|
+
|
|
48
|
+
const extractPrompt = `
|
|
49
|
+
Extract all workflows from this agent orchestration document.
|
|
50
|
+
|
|
51
|
+
For each workflow, identify:
|
|
52
|
+
1. The workflow name
|
|
53
|
+
2. The sequence of agents used
|
|
54
|
+
3. The purpose/description
|
|
55
|
+
|
|
56
|
+
Return ONLY a JSON object with this structure:
|
|
57
|
+
{
|
|
58
|
+
"workflow_key": {
|
|
59
|
+
"name": "Workflow Name",
|
|
60
|
+
"agents": ["agent1", "agent2", "agent3"],
|
|
61
|
+
"purpose": "Brief description"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Document content:
|
|
66
|
+
${content}
|
|
67
|
+
|
|
68
|
+
Extract all workflows and return valid JSON only.
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const claudeResult = execSync(
|
|
72
|
+
`claude --print "${extractPrompt.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`,
|
|
73
|
+
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 1024 * 1024 * 10 }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Parse the JSON result
|
|
77
|
+
const parsed = JSON.parse(claudeResult);
|
|
78
|
+
|
|
79
|
+
// Validate and normalize the workflows
|
|
80
|
+
const workflows = {};
|
|
81
|
+
for (const [key, workflow] of Object.entries(parsed)) {
|
|
82
|
+
if (workflow.name && Array.isArray(workflow.agents) && workflow.agents.length > 0) {
|
|
83
|
+
// Normalize agent names (remove "agent" suffix, lowercase)
|
|
84
|
+
const normalizedAgents = workflow.agents.map(agent =>
|
|
85
|
+
agent.toLowerCase().replace(/\s*agent\s*$/, '').trim()
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
workflows[key] = {
|
|
89
|
+
name: workflow.name,
|
|
90
|
+
agents: normalizedAgents,
|
|
91
|
+
purpose: workflow.purpose || ''
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return workflows;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Claude extraction failed
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
loadWorkflowsWithRegex(orchestrationPath) {
|
|
104
|
+
try {
|
|
105
|
+
const content = fs.readFileSync(orchestrationPath, 'utf8');
|
|
106
|
+
const workflows = {};
|
|
107
|
+
|
|
108
|
+
// Enhanced regex patterns for better extraction
|
|
109
|
+
const workflowSections = this.extractWorkflowSections(content);
|
|
110
|
+
|
|
111
|
+
for (const section of workflowSections) {
|
|
112
|
+
const workflow = this.parseWorkflowSection(section);
|
|
113
|
+
if (workflow) {
|
|
114
|
+
const key = workflow.name.toLowerCase().replace(/\s+/g, '_');
|
|
115
|
+
workflows[key] = workflow;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If no workflows found, return defaults
|
|
120
|
+
return Object.keys(workflows).length > 0 ? workflows : this.getDefaultWorkflows();
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return this.getDefaultWorkflows();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
extractWorkflowSections(content) {
|
|
127
|
+
const sections = [];
|
|
128
|
+
|
|
129
|
+
// Split by workflow headers
|
|
130
|
+
const workflowRegex = /### \d+\.\s+(.+?)\s+Workflow([\s\S]*?)(?=### \d+\.|## |$)/g;
|
|
131
|
+
let match;
|
|
132
|
+
|
|
133
|
+
while ((match = workflowRegex.exec(content)) !== null) {
|
|
134
|
+
sections.push({
|
|
135
|
+
name: match[1].trim(),
|
|
136
|
+
content: match[2].trim()
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return sections;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
parseWorkflowSection(section) {
|
|
144
|
+
const workflow = {
|
|
145
|
+
name: section.name,
|
|
146
|
+
agents: []
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Look for the Flow section
|
|
150
|
+
const flowMatch = section.content.match(/\*\*Flow\*\*:?\s*([\s\S]*?)(?=\*\*|###|$)/);
|
|
151
|
+
if (!flowMatch) return null;
|
|
152
|
+
|
|
153
|
+
const flowContent = flowMatch[1];
|
|
154
|
+
|
|
155
|
+
// Extract agents from various formats
|
|
156
|
+
const agents = this.extractAgentsFromFlow(flowContent);
|
|
157
|
+
|
|
158
|
+
if (agents.length > 0) {
|
|
159
|
+
workflow.agents = agents;
|
|
160
|
+
return workflow;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
extractAgentsFromFlow(flowContent) {
|
|
167
|
+
const agents = [];
|
|
168
|
+
const lines = flowContent.split('\n');
|
|
169
|
+
|
|
170
|
+
for (const line of lines) {
|
|
171
|
+
// Pattern 1: "1. **Agent Name** → Description"
|
|
172
|
+
let match = line.match(/^\d+\.\s*\*\*([^*→]+?)(?:\s+Agent)?\*\*/);
|
|
173
|
+
|
|
174
|
+
// Pattern 2: "- Agent Name: Description"
|
|
175
|
+
if (!match) {
|
|
176
|
+
match = line.match(/^[-•]\s*\*\*([^*:]+?)(?:\s+Agent)?\*\*/);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Pattern 3: "Agent Name →"
|
|
180
|
+
if (!match) {
|
|
181
|
+
match = line.match(/^\s*([A-Za-z\s]+?)\s*(?:Agent\s*)?→/);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (match) {
|
|
185
|
+
const agent = match[1].toLowerCase()
|
|
186
|
+
.replace(/\s*agent\s*$/, '')
|
|
187
|
+
.trim();
|
|
188
|
+
|
|
189
|
+
if (agent && !agents.includes(agent) && agent.length > 0) {
|
|
190
|
+
agents.push(agent);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return agents;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getDefaultWorkflows() {
|
|
199
|
+
return {
|
|
200
|
+
feature_development: {
|
|
201
|
+
name: 'Feature Development',
|
|
202
|
+
agents: ['researcher', 'planner', 'coder', 'checker']
|
|
203
|
+
},
|
|
204
|
+
bug_fix: {
|
|
205
|
+
name: 'Bug Fix',
|
|
206
|
+
agents: ['researcher', 'coder', 'checker']
|
|
207
|
+
},
|
|
208
|
+
refactoring: {
|
|
209
|
+
name: 'Refactoring',
|
|
210
|
+
agents: ['researcher', 'planner', 'coder', 'checker']
|
|
211
|
+
},
|
|
212
|
+
api_development: {
|
|
213
|
+
name: 'API Development',
|
|
214
|
+
agents: ['planner', 'backend', 'frontend', 'checker']
|
|
215
|
+
},
|
|
216
|
+
ui_component: {
|
|
217
|
+
name: 'UI Component',
|
|
218
|
+
agents: ['frontend', 'shadcn', 'checker']
|
|
219
|
+
},
|
|
220
|
+
blockchain: {
|
|
221
|
+
name: 'Blockchain Development',
|
|
222
|
+
agents: ['planner', 'blockchain', 'checker']
|
|
223
|
+
},
|
|
224
|
+
qa: {
|
|
225
|
+
name: 'QA',
|
|
226
|
+
agents: ['researcher', 'checker', 'coder', 'checker']
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getAvailableAgents() {
|
|
232
|
+
const agentsDir = path.join(process.cwd(), '.claude', 'agents');
|
|
233
|
+
|
|
234
|
+
if (!fs.existsSync(agentsDir)) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
return fs.readdirSync(agentsDir)
|
|
240
|
+
.filter(file => file.endsWith('.md'))
|
|
241
|
+
.map(file => file.replace('.md', '').toLowerCase());
|
|
242
|
+
} catch (error) {
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async selectWorkflow(prompt) {
|
|
248
|
+
const { execSync } = require('child_process');
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
// First, let's use Claude to analyze the prompt and understand the task type
|
|
252
|
+
const analyzePrompt = `
|
|
253
|
+
Analyze this user prompt and determine the most appropriate workflow type.
|
|
254
|
+
User prompt: "${prompt}"
|
|
255
|
+
|
|
256
|
+
Available workflows:
|
|
257
|
+
${Object.entries(this.workflows).map(([key, workflow]) =>
|
|
258
|
+
`- ${workflow.name}: uses agents [${workflow.agents.join(', ')}]`
|
|
259
|
+
).join('\n')}
|
|
260
|
+
|
|
261
|
+
Consider:
|
|
262
|
+
1. What type of task is this? (feature, bug fix, refactoring, API work, UI work, testing, etc.)
|
|
263
|
+
2. What agents would be most helpful?
|
|
264
|
+
3. Match to the most appropriate workflow
|
|
265
|
+
|
|
266
|
+
Respond with ONLY the workflow key (e.g., 'feature_development', 'bug_fix', etc.)
|
|
267
|
+
`;
|
|
268
|
+
|
|
269
|
+
// Use Claude to analyze the prompt
|
|
270
|
+
const claudeAnalysis = execSync(
|
|
271
|
+
`claude --print "${analyzePrompt.replace(/"/g, '\\"')}"`,
|
|
272
|
+
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }
|
|
273
|
+
).trim().toLowerCase().replace(/['"]/g, '');
|
|
274
|
+
|
|
275
|
+
// Validate the response is a valid workflow key
|
|
276
|
+
if (this.workflows[claudeAnalysis]) {
|
|
277
|
+
return this.workflows[claudeAnalysis];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// If Claude's response isn't valid, fall back to enhanced keyword matching
|
|
281
|
+
return this.enhancedKeywordMatching(prompt);
|
|
282
|
+
|
|
283
|
+
} catch (error) {
|
|
284
|
+
// If Claude command fails, fall back to enhanced keyword matching
|
|
285
|
+
console.error('Claude analysis failed, using keyword matching:', error.message);
|
|
286
|
+
return this.enhancedKeywordMatching(prompt);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
enhancedKeywordMatching(prompt) {
|
|
291
|
+
const promptLower = prompt.toLowerCase();
|
|
292
|
+
|
|
293
|
+
// Score each workflow based on keyword matches
|
|
294
|
+
const scores = {};
|
|
295
|
+
|
|
296
|
+
// Define keyword weights for each workflow
|
|
297
|
+
const workflowKeywords = {
|
|
298
|
+
bug_fix: {
|
|
299
|
+
keywords: ['fix', 'bug', 'error', 'issue', 'broken', 'crash', 'fail', 'debug', 'problem', 'wrong'],
|
|
300
|
+
weight: 2
|
|
301
|
+
},
|
|
302
|
+
refactoring: {
|
|
303
|
+
keywords: ['refactor', 'improve', 'optimize', 'clean', 'restructure', 'reorganize', 'simplify', 'enhance'],
|
|
304
|
+
weight: 2
|
|
305
|
+
},
|
|
306
|
+
api_development: {
|
|
307
|
+
keywords: ['api', 'endpoint', 'rest', 'graphql', 'backend', 'server', 'route', 'request', 'response'],
|
|
308
|
+
weight: 2
|
|
309
|
+
},
|
|
310
|
+
ui_component: {
|
|
311
|
+
keywords: ['ui', 'component', 'frontend', 'react', 'vue', 'interface', 'button', 'form', 'page', 'view'],
|
|
312
|
+
weight: 2
|
|
313
|
+
},
|
|
314
|
+
qa: {
|
|
315
|
+
keywords: ['test', 'qa', 'quality', 'testing', 'unit', 'integration', 'e2e', 'coverage', 'assert'],
|
|
316
|
+
weight: 2
|
|
317
|
+
},
|
|
318
|
+
feature_development: {
|
|
319
|
+
keywords: ['add', 'create', 'implement', 'build', 'develop', 'feature', 'new', 'functionality'],
|
|
320
|
+
weight: 1
|
|
321
|
+
},
|
|
322
|
+
blockchain: {
|
|
323
|
+
keywords: ['blockchain', 'smart contract', 'web3', 'ethereum', 'solidity', 'defi', 'crypto', 'wallet'],
|
|
324
|
+
weight: 3
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Calculate scores for each workflow
|
|
329
|
+
Object.entries(workflowKeywords).forEach(([workflow, config]) => {
|
|
330
|
+
scores[workflow] = 0;
|
|
331
|
+
config.keywords.forEach(keyword => {
|
|
332
|
+
if (promptLower.includes(keyword)) {
|
|
333
|
+
scores[workflow] += config.weight;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Find the workflow with the highest score
|
|
339
|
+
let bestWorkflow = 'feature_development';
|
|
340
|
+
let highestScore = 0;
|
|
341
|
+
|
|
342
|
+
Object.entries(scores).forEach(([workflow, score]) => {
|
|
343
|
+
if (score > highestScore && this.workflows[workflow]) {
|
|
344
|
+
highestScore = score;
|
|
345
|
+
bestWorkflow = workflow;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
return this.workflows[bestWorkflow] || this.workflows.feature_development || Object.values(this.workflows)[0];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
filterAgentsByAvailability(agents) {
|
|
353
|
+
if (this.availableAgents.length === 0) {
|
|
354
|
+
return agents; // Return all if we can't check
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return agents.filter(agent => this.availableAgents.includes(agent));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Main execution - reads from stdin as Claude Code provides
|
|
362
|
+
if (require.main === module) {
|
|
363
|
+
let inputData = '';
|
|
364
|
+
|
|
365
|
+
process.stdin.on('data', (chunk) => {
|
|
366
|
+
inputData += chunk;
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
process.stdin.on('end', async () => {
|
|
370
|
+
try {
|
|
371
|
+
const input = JSON.parse(inputData);
|
|
372
|
+
const prompt = input.prompt || '';
|
|
373
|
+
|
|
374
|
+
if (!prompt) {
|
|
375
|
+
console.log('{}');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const selector = new WorkflowSelector();
|
|
380
|
+
const workflow = await selector.selectWorkflow(prompt);
|
|
381
|
+
const agents = selector.filterAgentsByAvailability(workflow.agents);
|
|
382
|
+
|
|
383
|
+
// Output suggestion — Claude should ask the user before applying
|
|
384
|
+
const output = {
|
|
385
|
+
workflow: workflow.name,
|
|
386
|
+
agents: agents,
|
|
387
|
+
message: `[Workflow Suggestion] Based on this prompt, the "${workflow.name}" workflow may be a good fit: ${agents.join(' → ')}. Ask the user if they'd like to follow this workflow or just proceed normally with Claude Code's default behavior.`
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
console.log(JSON.stringify(output));
|
|
391
|
+
} catch (error) {
|
|
392
|
+
// Silent fail - just return empty
|
|
393
|
+
console.log('{}');
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
module.exports = WorkflowSelector;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Ralph Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are an autonomous coding agent working on a software project. You have access to Claude Code's full toolset including subagents.
|
|
4
|
+
|
|
5
|
+
## Your Task
|
|
6
|
+
|
|
7
|
+
1. Read the PRD at `prd.json` (in the same directory as this file)
|
|
8
|
+
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
9
|
+
3. Check you're on the correct branch from PRD `branchName`. If not, check it out or create from main.
|
|
10
|
+
4. Pick the **highest priority** user story where `passes: false`
|
|
11
|
+
5. Read the story's `notes` field for file hints and relevant context
|
|
12
|
+
6. Implement that single user story
|
|
13
|
+
7. Run quality checks using the exact commands from `prd.json` → `qualityChecks`
|
|
14
|
+
8. **Spawn a reviewer subagent** to independently verify the implementation (see Verification below)
|
|
15
|
+
9. If reviewer approves: commit ALL changes with message `feat: [Story ID] - [Story Title]`
|
|
16
|
+
10. If reviewer rejects: fix the issues, re-run quality checks, and re-verify
|
|
17
|
+
11. Update the PRD to set `passes: true` and populate `notes` with what was actually done
|
|
18
|
+
12. Update CLAUDE.md files if you discover reusable patterns (see below)
|
|
19
|
+
13. Append your progress to `progress.txt`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quality Checks — Use Exact Commands
|
|
24
|
+
|
|
25
|
+
The PRD's `qualityChecks` field contains the exact commands for this project:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
"qualityChecks": {
|
|
29
|
+
"typecheck": "npm run typecheck",
|
|
30
|
+
"lint": "npm run lint",
|
|
31
|
+
"test": "npm test"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run **every command** listed in `qualityChecks` using the Bash tool. Do not guess commands — use exactly what's in the PRD.
|
|
36
|
+
|
|
37
|
+
If `qualityChecks` is missing (older PRD format), fall back to detecting commands from `package.json` scripts, `Makefile`, or equivalent config files.
|
|
38
|
+
|
|
39
|
+
**All checks must pass before proceeding to verification.**
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Verification — Spawn a Reviewer Subagent
|
|
44
|
+
|
|
45
|
+
After implementing a story and passing quality checks, you MUST spawn a **checker subagent** using the Agent tool to independently verify the implementation. The implementing agent (you) should not be the sole judge of its own work.
|
|
46
|
+
|
|
47
|
+
### How to verify
|
|
48
|
+
|
|
49
|
+
Use the Agent tool with `subagent_type: "checker"` and a prompt like:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Review the implementation of user story [Story ID]: "[Story Title]"
|
|
53
|
+
|
|
54
|
+
## Acceptance Criteria to verify:
|
|
55
|
+
[paste the acceptance criteria from the story]
|
|
56
|
+
|
|
57
|
+
## Quality commands to run:
|
|
58
|
+
[paste from prd.json qualityChecks]
|
|
59
|
+
|
|
60
|
+
## Instructions:
|
|
61
|
+
1. Run `git diff` to see all changes made in the working tree
|
|
62
|
+
2. For each acceptance criterion, verify it is actually met by the code changes — not just that the code compiles
|
|
63
|
+
3. Run each quality check command and confirm they pass
|
|
64
|
+
4. If any acceptance criterion references UI changes, check that the component renders correctly (use browser tools if available)
|
|
65
|
+
5. Look for: missing edge cases, broken imports, unused variables, incomplete implementations
|
|
66
|
+
|
|
67
|
+
Report back with:
|
|
68
|
+
- PASS or FAIL for each acceptance criterion (with brief reasoning)
|
|
69
|
+
- Overall verdict: APPROVED or CHANGES_REQUESTED
|
|
70
|
+
- If CHANGES_REQUESTED: specific issues to fix
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Acting on the review
|
|
74
|
+
|
|
75
|
+
- **APPROVED**: Proceed to commit
|
|
76
|
+
- **CHANGES_REQUESTED**: Fix each issue raised, re-run quality checks, then spawn the reviewer again. Maximum 3 review cycles per story — if still failing after 3, log the issues in progress.txt and move on (do NOT mark `passes: true`)
|
|
77
|
+
|
|
78
|
+
### Why subagent verification matters
|
|
79
|
+
|
|
80
|
+
The implementing agent has tunnel vision — it wrote the code and is biased toward thinking it works. A fresh subagent context catches:
|
|
81
|
+
- Acceptance criteria that look met but aren't (e.g., criterion says "default 'pending'" but code defaults to null)
|
|
82
|
+
- Quality regressions in files you didn't intend to change
|
|
83
|
+
- Missing integrations (e.g., new API route exists but nothing calls it)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Browser Testing
|
|
88
|
+
|
|
89
|
+
For any story with "Verify in browser using dev-browser skill" in its acceptance criteria:
|
|
90
|
+
|
|
91
|
+
1. Navigate to the relevant page
|
|
92
|
+
2. Verify the UI changes work as expected
|
|
93
|
+
3. Take a screenshot if helpful for the progress log
|
|
94
|
+
|
|
95
|
+
The reviewer subagent should also check UI stories if browser tools are available. If no browser tools are configured, note in your progress report that manual browser verification is needed.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Progress Report Format
|
|
100
|
+
|
|
101
|
+
APPEND to progress.txt (never replace, always append):
|
|
102
|
+
```
|
|
103
|
+
## [Date/Time] - [Story ID]
|
|
104
|
+
- What was implemented
|
|
105
|
+
- Files changed
|
|
106
|
+
- Review result: [APPROVED / CHANGES_REQUESTED → fixed → APPROVED]
|
|
107
|
+
- Review cycles: [1-3]
|
|
108
|
+
- **Learnings for future iterations:**
|
|
109
|
+
- Patterns discovered (e.g., "this codebase uses X for Y")
|
|
110
|
+
- Gotchas encountered (e.g., "don't forget to update Z when changing W")
|
|
111
|
+
- Useful context (e.g., "the evaluation panel is in component X")
|
|
112
|
+
- Reviewer catches (e.g., "reviewer caught missing default value on status column")
|
|
113
|
+
---
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The learnings section is critical — it helps future iterations avoid repeating mistakes. **Include what the reviewer caught** so future iterations avoid the same issues.
|
|
117
|
+
|
|
118
|
+
## Consolidate Patterns
|
|
119
|
+
|
|
120
|
+
If you discover a **reusable pattern** that future iterations should know, add it to the `## Codebase Patterns` section at the TOP of progress.txt (create it if it doesn't exist). This section should consolidate the most important learnings:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
## Codebase Patterns
|
|
124
|
+
- Example: Use `sql<number>` template for aggregations
|
|
125
|
+
- Example: Always use `IF NOT EXISTS` for migrations
|
|
126
|
+
- Example: Export types from actions.ts for UI components
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Only add patterns that are **general and reusable**, not story-specific details.
|
|
130
|
+
|
|
131
|
+
## Update CLAUDE.md Files
|
|
132
|
+
|
|
133
|
+
Before committing, check if any edited files have learnings worth preserving in nearby CLAUDE.md files:
|
|
134
|
+
|
|
135
|
+
1. **Identify directories with edited files** — Look at which directories you modified
|
|
136
|
+
2. **Check for existing CLAUDE.md** — Look for CLAUDE.md in those directories or parent directories
|
|
137
|
+
3. **Add valuable learnings** — If you discovered something future developers/agents should know:
|
|
138
|
+
- API patterns or conventions specific to that module
|
|
139
|
+
- Gotchas or non-obvious requirements
|
|
140
|
+
- Dependencies between files
|
|
141
|
+
- Testing approaches for that area
|
|
142
|
+
- Configuration or environment requirements
|
|
143
|
+
|
|
144
|
+
**Examples of good CLAUDE.md additions:**
|
|
145
|
+
- "When modifying X, also update Y to keep them in sync"
|
|
146
|
+
- "This module uses pattern Z for all API calls"
|
|
147
|
+
- "Tests require the dev server running on PORT 3000"
|
|
148
|
+
- "Field names must match the template exactly"
|
|
149
|
+
|
|
150
|
+
**Do NOT add:**
|
|
151
|
+
- Story-specific implementation details
|
|
152
|
+
- Temporary debugging notes
|
|
153
|
+
- Information already in progress.txt
|
|
154
|
+
|
|
155
|
+
Only update CLAUDE.md if you have **genuinely reusable knowledge** that would help future work in that directory.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Stop Condition
|
|
160
|
+
|
|
161
|
+
After completing a user story, check if ALL stories have `passes: true`.
|
|
162
|
+
|
|
163
|
+
If ALL stories are complete and passing, reply with:
|
|
164
|
+
<promise>COMPLETE</promise>
|
|
165
|
+
|
|
166
|
+
If there are still stories with `passes: false`, end your response normally (another iteration will pick up the next story).
|
|
167
|
+
|
|
168
|
+
## Important
|
|
169
|
+
|
|
170
|
+
- Work on ONE story per iteration
|
|
171
|
+
- Commit only after reviewer APPROVES
|
|
172
|
+
- Keep CI green — use the exact commands from `qualityChecks`
|
|
173
|
+
- Read the Codebase Patterns section in progress.txt before starting
|
|
174
|
+
- Read story `notes` for file hints before implementing
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Ralph Wiggum - Long-running AI agent loop
|
|
3
|
+
# Usage: ./ralph.sh [--tool amp|claude] [--model opus|sonnet|haiku] [max_iterations]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Parse arguments
|
|
8
|
+
TOOL="amp" # Default to amp for backwards compatibility
|
|
9
|
+
MODEL="" # Model for claude (opus, sonnet, haiku)
|
|
10
|
+
MAX_ITERATIONS=10
|
|
11
|
+
|
|
12
|
+
while [[ $# -gt 0 ]]; do
|
|
13
|
+
case $1 in
|
|
14
|
+
--tool)
|
|
15
|
+
TOOL="$2"
|
|
16
|
+
shift 2
|
|
17
|
+
;;
|
|
18
|
+
--tool=*)
|
|
19
|
+
TOOL="${1#*=}"
|
|
20
|
+
shift
|
|
21
|
+
;;
|
|
22
|
+
--model)
|
|
23
|
+
MODEL="$2"
|
|
24
|
+
shift 2
|
|
25
|
+
;;
|
|
26
|
+
--model=*)
|
|
27
|
+
MODEL="${1#*=}"
|
|
28
|
+
shift
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
# Assume it's max_iterations if it's a number
|
|
32
|
+
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
|
33
|
+
MAX_ITERATIONS="$1"
|
|
34
|
+
fi
|
|
35
|
+
shift
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# Validate tool choice
|
|
41
|
+
if [[ "$TOOL" != "amp" && "$TOOL" != "claude" ]]; then
|
|
42
|
+
echo "Error: Invalid tool '$TOOL'. Must be 'amp' or 'claude'."
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
46
|
+
PRD_FILE="$SCRIPT_DIR/prd.json"
|
|
47
|
+
PROGRESS_FILE="$SCRIPT_DIR/progress.txt"
|
|
48
|
+
ARCHIVE_DIR="$SCRIPT_DIR/archive"
|
|
49
|
+
LAST_BRANCH_FILE="$SCRIPT_DIR/.last-branch"
|
|
50
|
+
|
|
51
|
+
# Archive previous run if branch changed
|
|
52
|
+
if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then
|
|
53
|
+
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
|
54
|
+
LAST_BRANCH=$(cat "$LAST_BRANCH_FILE" 2>/dev/null || echo "")
|
|
55
|
+
|
|
56
|
+
if [ -n "$CURRENT_BRANCH" ] && [ -n "$LAST_BRANCH" ] && [ "$CURRENT_BRANCH" != "$LAST_BRANCH" ]; then
|
|
57
|
+
# Archive the previous run
|
|
58
|
+
DATE=$(date +%Y-%m-%d)
|
|
59
|
+
# Strip "ralph/" prefix from branch name for folder
|
|
60
|
+
FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||')
|
|
61
|
+
ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME"
|
|
62
|
+
|
|
63
|
+
echo "Archiving previous run: $LAST_BRANCH"
|
|
64
|
+
mkdir -p "$ARCHIVE_FOLDER"
|
|
65
|
+
[ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/"
|
|
66
|
+
[ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/"
|
|
67
|
+
echo " Archived to: $ARCHIVE_FOLDER"
|
|
68
|
+
|
|
69
|
+
# Reset progress file for new run
|
|
70
|
+
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
|
71
|
+
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
|
72
|
+
echo "---" >> "$PROGRESS_FILE"
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Track current branch
|
|
77
|
+
if [ -f "$PRD_FILE" ]; then
|
|
78
|
+
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
|
79
|
+
if [ -n "$CURRENT_BRANCH" ]; then
|
|
80
|
+
echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Initialize progress file if it doesn't exist
|
|
85
|
+
if [ ! -f "$PROGRESS_FILE" ]; then
|
|
86
|
+
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
|
87
|
+
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
|
88
|
+
echo "---" >> "$PROGRESS_FILE"
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
MODEL_DISPLAY="${MODEL:-default}"
|
|
92
|
+
echo "Starting Ralph - Tool: $TOOL - Model: $MODEL_DISPLAY - Max iterations: $MAX_ITERATIONS"
|
|
93
|
+
|
|
94
|
+
for i in $(seq 1 $MAX_ITERATIONS); do
|
|
95
|
+
echo ""
|
|
96
|
+
echo "==============================================================="
|
|
97
|
+
echo " Ralph Iteration $i of $MAX_ITERATIONS ($TOOL${MODEL:+ - $MODEL})"
|
|
98
|
+
echo "==============================================================="
|
|
99
|
+
|
|
100
|
+
# Run the selected tool with the ralph prompt
|
|
101
|
+
if [[ "$TOOL" == "amp" ]]; then
|
|
102
|
+
OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true
|
|
103
|
+
else
|
|
104
|
+
# Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output
|
|
105
|
+
MODEL_FLAG=""
|
|
106
|
+
if [[ -n "$MODEL" ]]; then
|
|
107
|
+
MODEL_FLAG="--model $MODEL"
|
|
108
|
+
fi
|
|
109
|
+
OUTPUT=$(claude $MODEL_FLAG --dangerously-skip-permissions --chrome --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Check for completion signal
|
|
113
|
+
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
|
|
114
|
+
echo ""
|
|
115
|
+
echo "Ralph completed all tasks!"
|
|
116
|
+
echo "Completed at iteration $i of $MAX_ITERATIONS"
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
echo "Iteration $i complete. Continuing..."
|
|
121
|
+
sleep 2
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
echo ""
|
|
125
|
+
echo "Ralph reached max iterations ($MAX_ITERATIONS) without completing all tasks."
|
|
126
|
+
echo "Check $PROGRESS_FILE for status."
|
|
127
|
+
exit 1
|