Cron Expression Parser (javascript, written by Claude Code)
envgap__claude-code__javascript-t1-49
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/javascript-t1 #49 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: Cron Expression Parser Write a program that parses, validates, and explains cron expressions, computing the next N execution times and generating human-readable descriptions of the schedule. FUNCTIONAL REQUIREMENTS: - Accept a cron expression as a command-line argument (standard 5-field format: minute, hour, day-of-month, month, day-of-week) - Support extended 6-field format with seconds as the first field via --with-seconds flag - Support special characters: * (all), , (list), - (range), / (step), L (last), W (nearest weekday), # (nth day of week), and ? (any) - Validate the expression and report clear errors for invalid fields with the specific position and reason (e.g., "Field 2 (hour): value 25 exceeds maximum 23") - Compute and display the next N execution times via --next flag (default: 10), formatted in ISO 8601 and local human-readable format, relative to current time or a specified start time via --from flag - Generate a human-readable English description of the schedule (e.g., "At 03:30 AM, every Monday and Wednesday, in January through March") - Support time zone handling via --timezone flag (default: UTC) - Compute the interval statistics: average gap between executions, minimum gap, maximum gap, total executions per day/week/month/year - Support checking if a specific datetime matches the cron expression via --check flag - Support converting between cron expression and a verbal description via --describe flag (cron to English) and --parse flag (English-like input to cron expression for common patterns) - Print results to console in a formatted display - Save results as JSON with --output flag (default: cron_analysis.json) - Support batch validation of multiple expressions from a file via --batch flag - If no expression is given, demonstrate with several example expressions: "*/5 * * * *" (every 5 minutes), "0 9 * * 1-5" (weekdays at 9am), "0 0 1,15 * *" (1st and 15th at midnight), "30 4 * * SUN" (Sundays at 4:30am), showing next 5 runs and descriptions for each - Handle errors: invalid syntax, out-of-range values, impossible combinations (e.g., Feb 31), and malformed special characters Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
2 files, exactly as written, before any repair.
cron_parser.js
#!/usr/bin/env node
/**
* Cron Expression Parser - Trial 1 (cron-parser + chalk)
*
* Parses and validates cron expressions, computes next execution times,
* and generates human-readable descriptions.
*/
const cronParser = require('cron-parser');
const chalk = require('chalk');
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const MONTH_NAMES = [
'', 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
const DAY_NAMES = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
];
const FIELD_NAMES = ['minute', 'hour', 'day of month', 'month', 'day of week'];
// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------
function validateCron(expression) {
const result = {
expression: expression,
valid: false,
fields: {},
error: null
};
const parts = expression.trim().split(/\s+/);
if (parts.length < 5 || parts.length > 7) {
result.error = `Expected 5 fields (standard cron), got ${parts.length}. ` +
'Format: minute hour day-of-month month day-of-week';
return result;
}
try {
cronParser.parseExpression(expression);
result.valid = true;
} catch (err) {
result.error = err.message;
return result;
}
for (let i = 0; i < FIELD_NAMES.length && i < parts.length; i++) {
result.fields[FIELD_NAMES[i]] = parts[i];
}
return result;
}
// ---------------------------------------------------------------------------
// Human-readable description
// ---------------------------------------------------------------------------
function describeField(value, fieldName) {
if (value === '*') {
return `every ${fieldName}`;
}
if (value.includes('/')) {
const [base, step] = value.split('/');
if (base === '*') {
return `every ${step} ${fieldName}(s)`;
}
return `every ${step} ${fieldName}(s) starting at ${base}`;
}
if (value.includes('-') && !value.includes(',')) {
let [start, end] = value.split('-');
if (fieldName === 'month') {
const s = parseInt(start), e = parseInt(end);
if (!isNaN(s) && s >= 1 && s <= 12) start = MONTH_NAMES[s];
if (!isNaN(e) && e >= 1 && e <= 12) end = MONTH_NAMES[e];
} else if (fieldName === 'day of week') {
const s = parseInt(start), e = parseInt(end);
if (!isNaN(s) && s >= 0 && s <= 6) start = DAY_NAMES[s];
if (!isNaN(e) && e >= 0 && e <= 6) end = DAY_NAMES[e];
}
return `${fieldName} ${start} through ${end}`;
}
if (value.includes(',')) {
let items = value.split(',');
if (fieldName === 'month') {
items = items.map(i => {
const n = parseInt(i);
return (!isNaN(n) && n >= 1 && n <= 12) ? MONTH_NAMES[n] : i;
});
} else if (fieldName === 'day of week') {
items = items.map(i => {
const n = parseInt(i);
return (!isNaN(n) && n >= 0 && n <= 6) ? DAY_NAMES[n] : i;
});
}
return `${fieldName} ${items.join(', ')}`;
}
let display = value;
if (fieldName === 'month') {
const n = parseInt(value);
if (!isNaN(n) && n >= 1 && n <= 12) display = MONTH_NAMES[n];
} else if (fieldName === 'day of week') {
const n = parseInt(value);
if (!isNaN(n) && n >= 0 && n <= 6) display = DAY_NAMES[n];
}
return `at ${fieldName} ${display}`;
}
function describeCron(expression) {
const parts = expression.trim().split(/\s+/);
if (parts.length < 5) return 'Invalid cron expression';
const descriptions = [];
for (let i = 0; i < FIELD_NAMES.length; i++) {
if (parts[i] !== '*') {
descriptions.push(describeField(parts[i], FIELD_NAMES[i]));
}
}
if (descriptions.length === 0) return 'Every minute';
const minuteVal = parts[0];
const hourVal = parts[1];
if (minuteVal !== '*' && hourVal !== '*' &&
!minuteVal.includes('/') && !hourVal.includes('/')) {
const m = parseInt(minuteVal);
const h = parseInt(hourVal);
if (!isNaN(m) && !isNaN(h)) {
const timeStr = `At ${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
const remaining = [];
for (let i = 2; i < 5; i++) {
if (parts[i] !== '*') {
remaining.push(describeField(parts[i], FIELD_NAMES[i]));
}
}
if (remaining.length > 0) {
return `${timeStr}, ${remaining.join(', ')}`;
}
return timeStr;
}
}
return descriptions.join(', ');
}
// ---------------------------------------------------------------------------
// Next executions
// ---------------------------------------------------------------------------
function formatRelative(targetMs, baseMs) {
const diffSec = Math.floor((targetMs - baseMs) / 1000);
if (diffSec < 60) return `in ${diffSec} second(s)`;
if (diffSec < 3600) return `in ${Math.floor(diffSec / 60)} minute(s)`;
if (diffSec < 86400) {
const hours = Math.floor(diffSec / 3600);
const mins = Math.floor((diffSec % 3600) / 60);
return mins > 0 ? `in ${hours} hour(s) and ${mins} minute(s)` : `in ${hours} hour(s)`;
}
const days = Math.floor(diffSec / 86400);
const hours = Math.floor((diffSec % 86400) / 3600);
return hours > 0 ? `in ${days} day(s) and ${hours} hour(s)` : `in ${days} day(s)`;
}
function nextExecutions(expression, count = 5, baseTime = null) {
const base = baseTime ? new Date(baseTime) : new Date();
const results = [];
try {
const interval = cronParser.parseExpression(expression, { currentDate: base });
for (let i = 0; i < count; i++) {
const next = interval.next().toDate();
results.push({
datetime: next.toISOString(),
relative: formatRelative(next.getTime(), base.getTime())
});
}
} catch (err) {
results.push({ error: err.message });
}
return results;
}
function timeWindowExecutions(expression, startStr, endStr) {
const start = new Date(startStr);
const end = new Date(endStr);
const results = [];
try {
const interval = cronParser.parseExpression(expression, {
currentDate: new Date(start.getTime() - 1000)
});
while (true) {
const next = interval.next().toDate();
if (next > end) break;
results.push(next.toISOString());
if (results.length > 10000) break;
}
} catch (err) {
results.push({ error: err.message });
}
return results;
}
// ---------------------------------------------------------------------------
// Full analysis
// ---------------------------------------------------------------------------
function analyzeCron(expression, count = 5, baseTime = null, windowStart = null, windowEnd = null) {
const validation = validateCron(expression);
const result = { expression, validation };
if (!validation.valid) return result;
result.description = describeCron(expression);
result.next_executions = nextExecutions(expression, count, baseTime);
if (windowStart && windowEnd) {
const windowTimes = timeWindowExecutions(expression, windowStart, windowEnd);
result.window = {
start: windowStart,
end: windowEnd,
count: windowTimes.length,
executions: windowTimes.slice(0, 100)
};
}
return result;
}
// ---------------------------------------------------------------------------
// Console output
// ---------------------------------------------------------------------------
function printAnalysis(analysis) {
const sep = '='.repeat(70);
console.log(`\n${sep}`);
console.log(chalk.bold.cyan(' CRON EXPRESSION PARSER - ANALYSIS REPORT'));
console.log(sep);
console.log(` Expression: ${chalk.yellow(analysis.expression)}`);
const v = analysis.validation;
console.log(` Valid: ${v.valid ? chalk.green('true') : chalk.red('false')}`);
if (v.error) {
console.log(` Error: ${chalk.red(v.error)}`);
console.log(sep);
return;
}
if (v.fields && Object.keys(v.fields).length > 0) {
console.log(chalk.bold('\n -- Fields --'));
for (const [name, value] of Object.entries(v.fields)) {
console.log(` ${name.padEnd(15)} ${chalk.white(value)}`);
}
}
if (analysis.description) {
console.log(`\n Description: ${chalk.green(analysis.description)}`);
}
if (analysis.next_executions) {
console.log(chalk.bold('\n -- Next Executions --'));
analysis.next_executions.forEach((ex, i) => {
if (ex.error) {
console.log(` ${chalk.red('Error:')} ${ex.error}`);
return;
}
console.log(` [${i + 1}] ${chalk.white(ex.datetime)} (${chalk.dim(ex.relative)})`);
});
}
if (analysis.window) {
const w = analysis.window;
console.log(chalk.bold('\n -- Time Window --'));
console.log(` From: ${w.start}`);
console.log(` To: ${w.end}`);
console.log(` Count: ${chalk.yellow(w.count)} executions`);
w.executions.slice(0, 10).forEach(ts => {
console.log(` ${ts}`);
});
if (w.count > 10) {
console.log(` ... and ${w.count - 10} more`);
}
}
console.log(sep);
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--demo')) {
const demoExpressions = [
'*/5 * * * *',
'0 9 * * 1-5',
'0 0 1 * *',
'30 */2 * * *',
'0 0 * * 0',
'15 14 1 * *',
'0 22 * * 1-5',
'*/15 9-17 * * 1-5',
'0 0 1,15 * *',
'invalid cron expression',
];
console.log(chalk.bold('Running demo with sample cron expressions...\n'));
const allResults = [];
for (const expr of demoExpressions) {
const result = analyzeCron(expr, 3);
printAnalysis(result);
allResults.push(result);
}
return;
}
const expression = args[0];
const count = parseInt(args.find(a => a.startsWith('--count='))?.split('=')[1]) || 5;
const result = analyzeCron(expression, count);
printAnalysis(result);
}
main();
package.json
{
"name": "cron-expression-parser-trial1",
"version": "1.0.0",
"description": "Cron Expression Parser using cron-parser + chalk",
"main": "cron_parser.js",
"scripts": {
"start": "node cron_parser.js"
},
"dependencies": {
"cron-parser": "4.9.0",
"chalk": "4.1.2"
}
}