ccsetup 1.1.1 → 1.2.1
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 +144 -342
- package/bin/create-project.js +1246 -90
- package/bin/lib/claudeInterface.js +209 -0
- package/lib/aiAgentSelector.js +155 -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 +13 -5
- package/template/{agents → .claude/agents}/checker.md +29 -0
- package/template/.claude/settings.json +32 -0
- package/template/.claude/skills/codex-review/SKILL.md +139 -0
- package/template/.claude/skills/prd/SKILL.md +343 -0
- package/template/.claude/skills/ralph/SKILL.md +339 -0
- package/template/.claude/skills/secops/SKILL.md +259 -0
- package/template/.codex/skills/codex-review/SKILL.md +139 -0
- package/template/.codex/skills/prd/SKILL.md +343 -0
- package/template/.codex/skills/ralph/SKILL.md +339 -0
- package/template/AGENTS.md +43 -0
- package/template/CLAUDE.md +141 -21
- package/template/CONTRIBUTING.md +37 -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/docs/codex-setup.md +32 -0
- package/template/hooks/codex-review/index.js +105 -0
- package/template/hooks/workflow-selector/index.js +398 -0
- package/template/scripts/codex-review/codex-review.sh +266 -0
- package/template/scripts/ralph/CLAUDE.md +174 -0
- package/template/scripts/ralph/CODEX.md +76 -0
- package/template/scripts/ralph/ralph.sh +150 -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,266 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# codex-review.sh — Review plans, implementations, or code changes via Codex CLI
|
|
3
|
+
# Usage: codex-review.sh [plan-file-or--] [--model <model>]
|
|
4
|
+
# No arguments: reviews git changes (code review)
|
|
5
|
+
# With plan file: reviews plan, or plan+implementation if git changes exist
|
|
6
|
+
#
|
|
7
|
+
# Exit codes:
|
|
8
|
+
# 0 = success
|
|
9
|
+
# 1 = codex CLI not installed / nothing to review
|
|
10
|
+
# 2 = auth/API error
|
|
11
|
+
# 3 = timeout
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
PLAN_FILE=""
|
|
16
|
+
PLAN_CONTENT=""
|
|
17
|
+
DIFF_CONTENT=""
|
|
18
|
+
MODEL="${CODEX_REVIEW_MODEL:-}"
|
|
19
|
+
TIMEOUT=120
|
|
20
|
+
MAX_DIFF_CHARS=50000
|
|
21
|
+
|
|
22
|
+
if command -v timeout &>/dev/null; then
|
|
23
|
+
TIMEOUT_CMD="timeout"
|
|
24
|
+
elif command -v gtimeout &>/dev/null; then
|
|
25
|
+
TIMEOUT_CMD="gtimeout"
|
|
26
|
+
else
|
|
27
|
+
TIMEOUT_CMD=""
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case $1 in
|
|
32
|
+
--model)
|
|
33
|
+
MODEL="$2"
|
|
34
|
+
shift 2
|
|
35
|
+
;;
|
|
36
|
+
--model=*)
|
|
37
|
+
MODEL="${1#*=}"
|
|
38
|
+
shift
|
|
39
|
+
;;
|
|
40
|
+
-)
|
|
41
|
+
PLAN_FILE="-"
|
|
42
|
+
shift
|
|
43
|
+
;;
|
|
44
|
+
*)
|
|
45
|
+
if [[ -z "$PLAN_FILE" ]]; then
|
|
46
|
+
PLAN_FILE="$1"
|
|
47
|
+
fi
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
esac
|
|
51
|
+
done
|
|
52
|
+
|
|
53
|
+
# Check prerequisites
|
|
54
|
+
if ! command -v codex &>/dev/null; then
|
|
55
|
+
echo "Error: codex CLI is not installed. Install it with: npm install -g @openai/codex" >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Read plan content (optional — only when a plan file is provided)
|
|
60
|
+
if [[ "$PLAN_FILE" == "-" ]]; then
|
|
61
|
+
PLAN_CONTENT=$(cat)
|
|
62
|
+
elif [[ -n "$PLAN_FILE" && -f "$PLAN_FILE" ]]; then
|
|
63
|
+
PLAN_CONTENT=$(cat "$PLAN_FILE")
|
|
64
|
+
elif [[ -n "$PLAN_FILE" ]]; then
|
|
65
|
+
echo "Error: Plan file not found: $PLAN_FILE" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Gather git diff (silently skips if git unavailable or not in a repo)
|
|
70
|
+
gather_git_diff() {
|
|
71
|
+
if ! command -v git &>/dev/null; then return; fi
|
|
72
|
+
if ! git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then return; fi
|
|
73
|
+
|
|
74
|
+
DIFF_CONTENT=$(git diff HEAD 2>/dev/null || true)
|
|
75
|
+
|
|
76
|
+
if [[ -z "$DIFF_CONTENT" ]]; then
|
|
77
|
+
DIFF_CONTENT=$(git diff HEAD~1..HEAD 2>/dev/null || true)
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Fallback for initial commit (no HEAD yet) or staged-only changes
|
|
81
|
+
if [[ -z "$DIFF_CONTENT" ]]; then
|
|
82
|
+
DIFF_CONTENT=$(git diff --cached 2>/dev/null || true)
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [[ -n "$DIFF_CONTENT" && ${#DIFF_CONTENT} -gt $MAX_DIFF_CHARS ]]; then
|
|
86
|
+
DIFF_CONTENT="${DIFF_CONTENT:0:$MAX_DIFF_CHARS}
|
|
87
|
+
|
|
88
|
+
[... diff truncated at ${MAX_DIFF_CHARS} characters ...]"
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
gather_git_diff
|
|
93
|
+
|
|
94
|
+
# Must have at least a plan or git changes to review
|
|
95
|
+
if [[ -z "$PLAN_CONTENT" && -z "$DIFF_CONTENT" ]]; then
|
|
96
|
+
echo "Error: No plan file or git changes found. Nothing to review." >&2
|
|
97
|
+
echo "Usage: codex-review.sh [plan-file-or--] [--model <model>]" >&2
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
CMD_ARGS=()
|
|
102
|
+
if [[ -n "$MODEL" ]]; then
|
|
103
|
+
CMD_ARGS+=(--model "$MODEL")
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Build prompt based on available inputs
|
|
107
|
+
if [[ -n "$PLAN_CONTENT" && -n "$DIFF_CONTENT" ]]; then
|
|
108
|
+
# Implementation review: validate code changes against the plan
|
|
109
|
+
REVIEW_PROMPT="You are a senior architect reviewing an implementation against its plan. Validate that the code changes correctly fulfill the plan requirements.
|
|
110
|
+
|
|
111
|
+
## Plan
|
|
112
|
+
|
|
113
|
+
$PLAN_CONTENT
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Implementation (git diff)
|
|
118
|
+
|
|
119
|
+
$DIFF_CONTENT
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
Provide a structured review covering:
|
|
124
|
+
|
|
125
|
+
## Plan Compliance
|
|
126
|
+
- Which plan requirements are correctly implemented?
|
|
127
|
+
- Which plan requirements are missing or incomplete?
|
|
128
|
+
- Any divergence from the planned approach?
|
|
129
|
+
|
|
130
|
+
## Acceptance Criteria
|
|
131
|
+
- For each acceptance criterion in the plan, is it met by the implementation?
|
|
132
|
+
- List any unmet criteria explicitly
|
|
133
|
+
|
|
134
|
+
## Code Quality
|
|
135
|
+
- Are there bugs or logic errors in the implementation?
|
|
136
|
+
- Security concerns in the changed code?
|
|
137
|
+
- Performance issues?
|
|
138
|
+
|
|
139
|
+
## Suggestions
|
|
140
|
+
- Specific issues to fix before merging
|
|
141
|
+
- Missing tests or validation
|
|
142
|
+
- Improvements to better match the plan
|
|
143
|
+
|
|
144
|
+
Be direct and specific. Reference exact file paths and line ranges from the diff."
|
|
145
|
+
|
|
146
|
+
elif [[ -n "$PLAN_CONTENT" ]]; then
|
|
147
|
+
# Plan review: architectural review of the plan itself
|
|
148
|
+
REVIEW_PROMPT="You are a senior architect reviewing this plan. Provide a structured review covering:
|
|
149
|
+
|
|
150
|
+
## Architecture Review
|
|
151
|
+
- Are the technical choices sound?
|
|
152
|
+
- Are there simpler alternatives?
|
|
153
|
+
- Any missing dependencies or integration concerns?
|
|
154
|
+
|
|
155
|
+
## Risk Assessment
|
|
156
|
+
- What could go wrong?
|
|
157
|
+
- What edge cases are unhandled?
|
|
158
|
+
- Any security or performance concerns?
|
|
159
|
+
|
|
160
|
+
## Suggestions
|
|
161
|
+
- Specific improvements with rationale
|
|
162
|
+
- Missing acceptance criteria
|
|
163
|
+
- Implementation order concerns
|
|
164
|
+
|
|
165
|
+
Be direct and specific. Reference exact sections of the plan.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
Plan to review:
|
|
170
|
+
|
|
171
|
+
$PLAN_CONTENT"
|
|
172
|
+
|
|
173
|
+
else
|
|
174
|
+
# Code review: standalone review of git changes
|
|
175
|
+
REVIEW_PROMPT="You are a senior engineer performing a code review. Review the following code changes for quality, correctness, and best practices.
|
|
176
|
+
|
|
177
|
+
## Code Changes (git diff)
|
|
178
|
+
|
|
179
|
+
$DIFF_CONTENT
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
Provide a structured review covering:
|
|
184
|
+
|
|
185
|
+
## Bugs and Correctness
|
|
186
|
+
- Logic errors or incorrect behavior
|
|
187
|
+
- Missing null/error handling
|
|
188
|
+
- Off-by-one errors or boundary conditions
|
|
189
|
+
|
|
190
|
+
## Security
|
|
191
|
+
- Injection vulnerabilities
|
|
192
|
+
- Exposed secrets or credentials
|
|
193
|
+
- Missing input validation
|
|
194
|
+
|
|
195
|
+
## Performance
|
|
196
|
+
- Unnecessary computations or allocations
|
|
197
|
+
- Inefficient patterns
|
|
198
|
+
- Missing caching opportunities
|
|
199
|
+
|
|
200
|
+
## Code Quality
|
|
201
|
+
- Naming and readability
|
|
202
|
+
- Adherence to existing code conventions
|
|
203
|
+
- Dead code or unnecessary complexity
|
|
204
|
+
|
|
205
|
+
## Suggestions
|
|
206
|
+
- Specific improvements with rationale
|
|
207
|
+
- Missing tests
|
|
208
|
+
- Documentation gaps
|
|
209
|
+
|
|
210
|
+
Be direct and specific. Reference exact file paths and line ranges from the diff."
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Run codex exec with timeout (if available)
|
|
214
|
+
# Temporarily disable exit-on-error to capture the actual exit code before
|
|
215
|
+
# checking it. Using "if ! OUTPUT=$(cmd)" sets $? to 0 inside the then-block
|
|
216
|
+
# (the negated result), making timeout detection (exit 124) impossible.
|
|
217
|
+
set +e
|
|
218
|
+
if [[ -n "$TIMEOUT_CMD" ]]; then
|
|
219
|
+
OUTPUT=$($TIMEOUT_CMD "${TIMEOUT}s" codex exec ${CMD_ARGS[@]+"${CMD_ARGS[@]}"} "$REVIEW_PROMPT" 2>&1)
|
|
220
|
+
else
|
|
221
|
+
OUTPUT=$(codex exec ${CMD_ARGS[@]+"${CMD_ARGS[@]}"} "$REVIEW_PROMPT" 2>&1)
|
|
222
|
+
fi
|
|
223
|
+
EXIT_CODE=$?
|
|
224
|
+
set -e
|
|
225
|
+
|
|
226
|
+
if [[ $EXIT_CODE -ne 0 ]]; then
|
|
227
|
+
if [[ $EXIT_CODE -eq 124 ]]; then
|
|
228
|
+
echo "Error: Codex review timed out after ${TIMEOUT}s. Try a shorter plan or increase TIMEOUT." >&2
|
|
229
|
+
exit 3
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
if echo "$OUTPUT" | grep -qi "login\|log in\|sign in\|authenticate first"; then
|
|
233
|
+
echo "Error: Codex CLI requires login. Run 'codex login' first." >&2
|
|
234
|
+
exit 2
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
if echo "$OUTPUT" | grep -qi "auth\|unauthorized\|api.key\|invalid.*key\|forbidden\|permission denied"; then
|
|
238
|
+
echo "Error: Codex authentication failed. Check your OpenAI API key." >&2
|
|
239
|
+
echo "$OUTPUT" >&2
|
|
240
|
+
exit 2
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
if echo "$OUTPUT" | grep -qi "rate.limit\|too many requests\|429\|quota\|exceeded.*limit"; then
|
|
244
|
+
echo "Error: Rate limited by OpenAI API. Wait a moment and try again." >&2
|
|
245
|
+
echo "$OUTPUT" >&2
|
|
246
|
+
exit 2
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
if echo "$OUTPUT" | grep -qi "network\|connect\|ECONNREFUSED\|ENOTFOUND\|DNS\|resolve\|unreachable\|timed out"; then
|
|
250
|
+
echo "Error: Network error. Check your internet connection." >&2
|
|
251
|
+
echo "$OUTPUT" >&2
|
|
252
|
+
exit 2
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
if echo "$OUTPUT" | grep -qi "model.*not found\|does not exist\|invalid.*model\|unknown model"; then
|
|
256
|
+
echo "Error: Invalid model '${MODEL:-default}'. Check available models with 'codex --help'." >&2
|
|
257
|
+
echo "$OUTPUT" >&2
|
|
258
|
+
exit 2
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
echo "Error: Codex review failed (exit code $EXIT_CODE)" >&2
|
|
262
|
+
echo "$OUTPUT" >&2
|
|
263
|
+
exit 2
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
echo "$OUTPUT"
|