(cronExpr, claudeCommand)
| 121 | let schtasksArgs; |
| 122 | if (parts.join(' ') === '* * * * *') { |
| 123 | schtasksArgs = ['/Create', '/SC', 'MINUTE', '/MO', '1', '/TN', id, '/TR', invocation, '/F']; |
| 124 | } else if (/^\*\/\d+$/.test(minute) && hour === '*') { |
| 125 | const mo = minute.slice(2); |
| 126 | schtasksArgs = ['/Create', '/SC', 'MINUTE', '/MO', mo, '/TN', id, '/TR', invocation, '/F']; |
| 127 | } else if (minute === '0' && /^\*\/\d+$/.test(hour)) { |
| 128 | schtasksArgs = ['/Create', '/SC', 'HOURLY', '/MO', hour.slice(2), '/TN', id, '/TR', invocation, '/F']; |
| 129 | } else if (minute === '0' && hour === '*') { |
| 130 | schtasksArgs = ['/Create', '/SC', 'HOURLY', '/MO', '1', '/TN', id, '/TR', invocation, '/F']; |
| 131 | } else if (/^\d+$/.test(minute) && /^\d+$/.test(hour) && Number(hour) < 24 && Number(minute) < 60) { |
| 132 | schtasksArgs = ['/Create', '/SC', 'DAILY', '/ST', `${hour.padStart(2, '0')}:${minute.padStart(2, '0')}`, '/TN', id, '/TR', invocation, '/F']; |
| 133 | } else { |
| 134 | throw new Error(`Windows Task Scheduler mapping not supported for "${cronExpr}". Use: every {N}m, every {N}h, or daily at {H}.`); |
| 135 | } |
| 136 | saveJob(id, claudeCommand); |
| 137 | try { execFileSync('schtasks', schtasksArgs, { stdio: 'inherit' }); } |
| 138 | catch (error) { forgetJob(id); throw error; } |
| 139 | console.log(`Scheduled. ID: ${id}`); |
| 140 | console.log(`Remove with: node scripts/local-schedule.js remove ${id}`); |
no test coverage detected