@cjvana/claude-auto 0.1.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.
Files changed (159) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +435 -0
  4. package/dist/check-repo-6C4QI2M2.js +33 -0
  5. package/dist/check-repo-6C4QI2M2.js.map +1 -0
  6. package/dist/check-repo-SXWFIVO5.js +8 -0
  7. package/dist/check-repo-SXWFIVO5.js.map +1 -0
  8. package/dist/chunk-24PS2XSV.js +203 -0
  9. package/dist/chunk-24PS2XSV.js.map +1 -0
  10. package/dist/chunk-2D5E23XA.js +129 -0
  11. package/dist/chunk-2D5E23XA.js.map +1 -0
  12. package/dist/chunk-3NEANSUS.js +26 -0
  13. package/dist/chunk-3NEANSUS.js.map +1 -0
  14. package/dist/chunk-4I5UIASZ.js +71 -0
  15. package/dist/chunk-4I5UIASZ.js.map +1 -0
  16. package/dist/chunk-5LGOK52J.js +38 -0
  17. package/dist/chunk-5LGOK52J.js.map +1 -0
  18. package/dist/chunk-6RYMWH5M.js +35 -0
  19. package/dist/chunk-6RYMWH5M.js.map +1 -0
  20. package/dist/chunk-A6XWZPLY.js +56 -0
  21. package/dist/chunk-A6XWZPLY.js.map +1 -0
  22. package/dist/chunk-AWLSYOVF.js +61 -0
  23. package/dist/chunk-AWLSYOVF.js.map +1 -0
  24. package/dist/chunk-BY5YEOVG.js +75 -0
  25. package/dist/chunk-BY5YEOVG.js.map +1 -0
  26. package/dist/chunk-D4MBOIYQ.js +46 -0
  27. package/dist/chunk-D4MBOIYQ.js.map +1 -0
  28. package/dist/chunk-DVZC42TL.js +33 -0
  29. package/dist/chunk-DVZC42TL.js.map +1 -0
  30. package/dist/chunk-E3XVLTT4.js +13 -0
  31. package/dist/chunk-E3XVLTT4.js.map +1 -0
  32. package/dist/chunk-GLW7T4QE.js +116 -0
  33. package/dist/chunk-GLW7T4QE.js.map +1 -0
  34. package/dist/chunk-H2MUDYMW.js +23 -0
  35. package/dist/chunk-H2MUDYMW.js.map +1 -0
  36. package/dist/chunk-HF7PGQI3.js +69 -0
  37. package/dist/chunk-HF7PGQI3.js.map +1 -0
  38. package/dist/chunk-LBH6SLHH.js +543 -0
  39. package/dist/chunk-LBH6SLHH.js.map +1 -0
  40. package/dist/chunk-M53MPY3U.js +115 -0
  41. package/dist/chunk-M53MPY3U.js.map +1 -0
  42. package/dist/chunk-MI7OZ5XD.js +146 -0
  43. package/dist/chunk-MI7OZ5XD.js.map +1 -0
  44. package/dist/chunk-NB46PEG2.js +177 -0
  45. package/dist/chunk-NB46PEG2.js.map +1 -0
  46. package/dist/chunk-ORBF5IW3.js +60 -0
  47. package/dist/chunk-ORBF5IW3.js.map +1 -0
  48. package/dist/chunk-PFU5YLRH.js +131 -0
  49. package/dist/chunk-PFU5YLRH.js.map +1 -0
  50. package/dist/chunk-QLRCFKLU.js +34 -0
  51. package/dist/chunk-QLRCFKLU.js.map +1 -0
  52. package/dist/chunk-QQTIJN3S.js +167 -0
  53. package/dist/chunk-QQTIJN3S.js.map +1 -0
  54. package/dist/chunk-QRYCNVLT.js +72 -0
  55. package/dist/chunk-QRYCNVLT.js.map +1 -0
  56. package/dist/chunk-S6E67XMR.js +52 -0
  57. package/dist/chunk-S6E67XMR.js.map +1 -0
  58. package/dist/chunk-S6W4SURF.js +33 -0
  59. package/dist/chunk-S6W4SURF.js.map +1 -0
  60. package/dist/chunk-SMZYA6CY.js +121 -0
  61. package/dist/chunk-SMZYA6CY.js.map +1 -0
  62. package/dist/chunk-SNOA575X.js +12 -0
  63. package/dist/chunk-SNOA575X.js.map +1 -0
  64. package/dist/chunk-SZRIZBWI.js +44 -0
  65. package/dist/chunk-SZRIZBWI.js.map +1 -0
  66. package/dist/chunk-TAGHPCFT.js +47 -0
  67. package/dist/chunk-TAGHPCFT.js.map +1 -0
  68. package/dist/chunk-TGKCHHXT.js +34 -0
  69. package/dist/chunk-TGKCHHXT.js.map +1 -0
  70. package/dist/chunk-TORYFKPK.js +39 -0
  71. package/dist/chunk-TORYFKPK.js.map +1 -0
  72. package/dist/chunk-U35GRLBD.js +143 -0
  73. package/dist/chunk-U35GRLBD.js.map +1 -0
  74. package/dist/chunk-W2HBRERV.js +57 -0
  75. package/dist/chunk-W2HBRERV.js.map +1 -0
  76. package/dist/chunk-WYU476R2.js +119 -0
  77. package/dist/chunk-WYU476R2.js.map +1 -0
  78. package/dist/chunk-YMO45Z6G.js +69 -0
  79. package/dist/chunk-YMO45Z6G.js.map +1 -0
  80. package/dist/claude-auto-run.js +1717 -0
  81. package/dist/claude-auto-run.js.map +1 -0
  82. package/dist/claude-auto.js +186 -0
  83. package/dist/claude-auto.js.map +1 -0
  84. package/dist/cost-QGM3D4QW.js +72 -0
  85. package/dist/cost-QGM3D4QW.js.map +1 -0
  86. package/dist/cost-QKN3U7AG.js +11 -0
  87. package/dist/cost-QKN3U7AG.js.map +1 -0
  88. package/dist/create-T3BDDS6G.js +14 -0
  89. package/dist/create-T3BDDS6G.js.map +1 -0
  90. package/dist/create-U5WYKTD4.js +118 -0
  91. package/dist/create-U5WYKTD4.js.map +1 -0
  92. package/dist/crontab-CDMC2FDT.js +118 -0
  93. package/dist/crontab-CDMC2FDT.js.map +1 -0
  94. package/dist/crontab-MAJ52FOK.js +118 -0
  95. package/dist/crontab-MAJ52FOK.js.map +1 -0
  96. package/dist/crontab-PNEWANLW.js +12 -0
  97. package/dist/crontab-PNEWANLW.js.map +1 -0
  98. package/dist/edit-77E3ZQHM.js +134 -0
  99. package/dist/edit-77E3ZQHM.js.map +1 -0
  100. package/dist/edit-RVPRAAQ2.js +13 -0
  101. package/dist/edit-RVPRAAQ2.js.map +1 -0
  102. package/dist/index.d.ts +1137 -0
  103. package/dist/index.js +2049 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/launchd-7F27BIZB.js +166 -0
  106. package/dist/launchd-7F27BIZB.js.map +1 -0
  107. package/dist/launchd-HNZIWLNC.js +166 -0
  108. package/dist/launchd-HNZIWLNC.js.map +1 -0
  109. package/dist/launchd-LZGDP7BM.js +12 -0
  110. package/dist/launchd-LZGDP7BM.js.map +1 -0
  111. package/dist/list-OIGERGYJ.js +15 -0
  112. package/dist/list-OIGERGYJ.js.map +1 -0
  113. package/dist/list-T35RSQVU.js +73 -0
  114. package/dist/list-T35RSQVU.js.map +1 -0
  115. package/dist/logs-D5FNSCXE.js +12 -0
  116. package/dist/logs-D5FNSCXE.js.map +1 -0
  117. package/dist/logs-YVSFXBSB.js +40 -0
  118. package/dist/logs-YVSFXBSB.js.map +1 -0
  119. package/dist/pause-2YOLFMAR.js +12 -0
  120. package/dist/pause-2YOLFMAR.js.map +1 -0
  121. package/dist/pause-JB42JGTB.js +45 -0
  122. package/dist/pause-JB42JGTB.js.map +1 -0
  123. package/dist/pause-OJNUYBCJ.js +47 -0
  124. package/dist/pause-OJNUYBCJ.js.map +1 -0
  125. package/dist/remove-RXYKFYBI.js +12 -0
  126. package/dist/remove-RXYKFYBI.js.map +1 -0
  127. package/dist/remove-UASXZCOR.js +59 -0
  128. package/dist/remove-UASXZCOR.js.map +1 -0
  129. package/dist/report-CHAJH2SA.js +150 -0
  130. package/dist/report-CHAJH2SA.js.map +1 -0
  131. package/dist/report-IYGK5HTC.js +14 -0
  132. package/dist/report-IYGK5HTC.js.map +1 -0
  133. package/dist/resume-3ATNZP6D.js +13 -0
  134. package/dist/resume-3ATNZP6D.js.map +1 -0
  135. package/dist/resume-6WVGU6XW.js +48 -0
  136. package/dist/resume-6WVGU6XW.js.map +1 -0
  137. package/dist/resume-JVTR7OEX.js +50 -0
  138. package/dist/resume-JVTR7OEX.js.map +1 -0
  139. package/dist/schtasks-2EQAD3ES.js +11 -0
  140. package/dist/schtasks-2EQAD3ES.js.map +1 -0
  141. package/dist/schtasks-4V2IFD3A.js +142 -0
  142. package/dist/schtasks-4V2IFD3A.js.map +1 -0
  143. package/dist/schtasks-JGEPEKQS.js +142 -0
  144. package/dist/schtasks-JGEPEKQS.js.map +1 -0
  145. package/dist/tui-2DUPCX3Q.js +15 -0
  146. package/dist/tui-2DUPCX3Q.js.map +1 -0
  147. package/dist/tui-6LOGPILA.js +547 -0
  148. package/dist/tui-6LOGPILA.js.map +1 -0
  149. package/package.json +81 -0
  150. package/scripts/postinstall.mjs +65 -0
  151. package/scripts/preuninstall.mjs +33 -0
  152. package/skills/edit/SKILL.md +25 -0
  153. package/skills/list/SKILL.md +26 -0
  154. package/skills/logs/SKILL.md +33 -0
  155. package/skills/pause/SKILL.md +21 -0
  156. package/skills/remove/SKILL.md +22 -0
  157. package/skills/resume/SKILL.md +21 -0
  158. package/skills/setup/SKILL.md +195 -0
  159. package/skills/status/SKILL.md +27 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui/index.tsx","../src/tui/app.tsx","../src/tui/components/job-list.tsx","../src/tui/components/job-detail.tsx","../src/tui/components/run-log.tsx","../src/tui/components/status-bar.tsx","../src/tui/hooks/use-jobs.ts","../src/tui/hooks/use-keyboard.ts"],"sourcesContent":["import React from \"react\";\nimport { render } from \"ink\";\nimport { App } from \"./app.js\";\n\n/**\n * Launch the interactive TUI dashboard.\n * Renders the App component via ink and blocks until the user quits.\n */\nexport async function launchDashboard(): Promise<void> {\n\tconst { waitUntilExit } = render(<App />);\n\tawait waitUntilExit();\n}\n","import { useState, useCallback } from \"react\";\nimport { Box, Text } from \"ink\";\nimport { JobList } from \"./components/job-list.js\";\nimport { JobDetail } from \"./components/job-detail.js\";\nimport { RunLog } from \"./components/run-log.js\";\nimport { StatusBar } from \"./components/status-bar.js\";\nimport { useJobs } from \"./hooks/use-jobs.js\";\nimport { useKeyboard } from \"./hooks/use-keyboard.js\";\n\n/**\n * Root App component for the TUI dashboard.\n * Routes between list, detail, and log views.\n * Handles pause/resume via dynamic import of CLI commands.\n */\nexport function App() {\n\tconst { jobs, loading, error, refresh } = useJobs();\n\tconst { view, selectedIdx, scrollOffset, actions } = useKeyboard(jobs.length);\n\tconst [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n\t// Handle pause/resume action from keyboard\n\tconst jobIdToPauseResume = actions.pauseResume(jobs);\n\tif (jobIdToPauseResume) {\n\t\thandlePauseResume(jobIdToPauseResume);\n\t}\n\n\tasync function handlePauseResume(jobId: string): Promise<void> {\n\t\tconst job = jobs.find((j) => j.id === jobId);\n\t\tif (!job) return;\n\n\t\ttry {\n\t\t\tif (job.enabled) {\n\t\t\t\tconst { pauseCommand } = await import(\"../cli/commands/pause.js\");\n\t\t\t\tawait pauseCommand({ jobId });\n\t\t\t\tsetStatusMessage(`Paused: ${job.name}`);\n\t\t\t} else {\n\t\t\t\tconst { resumeCommand } = await import(\"../cli/commands/resume.js\");\n\t\t\t\tawait resumeCommand({ jobId });\n\t\t\t\tsetStatusMessage(`Resumed: ${job.name}`);\n\t\t\t}\n\t\t\trefresh();\n\t\t} catch (err: unknown) {\n\t\t\tsetStatusMessage(`Error: ${err instanceof Error ? err.message : String(err)}`);\n\t\t}\n\n\t\t// Clear status message after 3 seconds\n\t\tsetTimeout(() => setStatusMessage(null), 3000);\n\t}\n\n\tconst selectedJob = jobs.length > 0 && selectedIdx < jobs.length ? jobs[selectedIdx] : null;\n\n\treturn (\n\t\t<Box flexDirection=\"column\">\n\t\t\t{/* Header */}\n\t\t\t<Box paddingX={1}>\n\t\t\t\t<Text bold color=\"cyan\">claude-auto dashboard</Text>\n\t\t\t\t{loading && jobs.length > 0 && <Text dimColor> (refreshing...)</Text>}\n\t\t\t</Box>\n\n\t\t\t{/* Error display */}\n\t\t\t{error && (\n\t\t\t\t<Box paddingX={1}>\n\t\t\t\t\t<Text color=\"red\">Error: {error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{/* Status message */}\n\t\t\t{statusMessage && (\n\t\t\t\t<Box paddingX={1}>\n\t\t\t\t\t<Text color=\"yellow\">{statusMessage}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{/* Main content area */}\n\t\t\t{view === \"list\" && (\n\t\t\t\t<JobList jobs={jobs} selectedIdx={selectedIdx} loading={loading} />\n\t\t\t)}\n\t\t\t{view === \"detail\" && selectedJob && (\n\t\t\t\t<JobDetail job={selectedJob} />\n\t\t\t)}\n\t\t\t{view === \"logs\" && selectedJob && (\n\t\t\t\t<RunLog jobId={selectedJob.id} scrollOffset={scrollOffset} />\n\t\t\t)}\n\n\t\t\t{/* Footer */}\n\t\t\t<StatusBar view={view} />\n\t\t</Box>\n\t);\n}\n","import { Box, Text } from \"ink\";\nimport { Spinner } from \"@inkjs/ui\";\nimport type { JobWithMeta } from \"../hooks/use-jobs.js\";\nimport cronstrue from \"cronstrue\";\n\ninterface JobListProps {\n\tjobs: JobWithMeta[];\n\tselectedIdx: number;\n\tloading: boolean;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst now = Date.now();\n\tconst diffMs = date.getTime() - now;\n\tif (diffMs < 0) return \"overdue\";\n\n\tconst diffMin = Math.floor(diffMs / 60000);\n\tif (diffMin < 60) return `in ${diffMin}m`;\n\n\tconst diffHr = Math.floor(diffMin / 60);\n\tif (diffHr < 24) return `in ${diffHr}h`;\n\n\tconst diffDay = Math.floor(diffHr / 24);\n\treturn `in ${diffDay}d`;\n}\n\nfunction formatCost(cost: number): string {\n\treturn `$${cost.toFixed(2)}`;\n}\n\nfunction truncate(str: string, max: number): string {\n\tif (str.length <= max) return str;\n\treturn str.slice(0, max - 1) + \"\\u2026\";\n}\n\nfunction describeScheduleSafe(cron: string): string {\n\ttry {\n\t\treturn cronstrue.toString(cron, { use24HourTimeFormat: false });\n\t} catch {\n\t\treturn cron;\n\t}\n}\n\n/**\n * Job list table with columns: Name, Status, Schedule, Next Run, Cost.\n * Highlights the selected row with inverse/bold styling.\n */\nexport function JobList({ jobs, selectedIdx, loading }: JobListProps) {\n\tif (loading && jobs.length === 0) {\n\t\treturn (\n\t\t\t<Box padding={1}>\n\t\t\t\t<Spinner label=\"Loading jobs...\" />\n\t\t\t</Box>\n\t\t);\n\t}\n\n\tif (jobs.length === 0) {\n\t\treturn (\n\t\t\t<Box padding={1}>\n\t\t\t\t<Text dimColor>No jobs configured. Run &apos;claude-auto create&apos; to get started.</Text>\n\t\t\t</Box>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingTop={1}>\n\t\t\t{/* Header */}\n\t\t\t<Box>\n\t\t\t\t<Box width={22}><Text bold underline>Name</Text></Box>\n\t\t\t\t<Box width={10}><Text bold underline>Status</Text></Box>\n\t\t\t\t<Box width={27}><Text bold underline>Schedule</Text></Box>\n\t\t\t\t<Box width={14}><Text bold underline>Next Run</Text></Box>\n\t\t\t\t<Box width={10}><Text bold underline>Cost</Text></Box>\n\t\t\t</Box>\n\n\t\t\t{/* Rows */}\n\t\t\t{jobs.map((job, idx) => {\n\t\t\t\tconst isSelected = idx === selectedIdx;\n\t\t\t\tconst statusText = job.enabled ? \"active\" : \"paused\";\n\t\t\t\tconst statusColor = job.enabled ? \"green\" : \"yellow\";\n\t\t\t\tconst schedule = truncate(describeScheduleSafe(job.cron), 25);\n\t\t\t\tconst nextRun = job.nextRun ? formatRelativeTime(job.nextRun) : \"paused\";\n\t\t\t\tconst cost = formatCost(job.totalCost);\n\n\t\t\t\treturn (\n\t\t\t\t\t<Box key={job.id}>\n\t\t\t\t\t\t<Box width={22}>\n\t\t\t\t\t\t\t<Text inverse={isSelected} bold={isSelected}>\n\t\t\t\t\t\t\t\t{truncate(job.name, 20)}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t<Box width={10}>\n\t\t\t\t\t\t\t<Text inverse={isSelected} color={statusColor}>\n\t\t\t\t\t\t\t\t{statusText}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t<Box width={27}>\n\t\t\t\t\t\t\t<Text inverse={isSelected} dimColor={!isSelected}>\n\t\t\t\t\t\t\t\t{schedule}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t<Box width={14}>\n\t\t\t\t\t\t\t<Text inverse={isSelected} dimColor={!isSelected}>\n\t\t\t\t\t\t\t\t{nextRun}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t<Box width={10}>\n\t\t\t\t\t\t\t<Text inverse={isSelected} dimColor={!isSelected}>\n\t\t\t\t\t\t\t\t{cost}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t</Box>\n\t\t\t\t);\n\t\t\t})}\n\t\t</Box>\n\t);\n}\n","import { Box, Text } from \"ink\";\nimport type { JobWithMeta } from \"../hooks/use-jobs.js\";\nimport cronstrue from \"cronstrue\";\n\ninterface JobDetailProps {\n\tjob: JobWithMeta;\n}\n\nfunction describeScheduleSafe(cron: string): string {\n\ttry {\n\t\treturn cronstrue.toString(cron, { use24HourTimeFormat: false });\n\t} catch {\n\t\treturn cron;\n\t}\n}\n\nfunction formatDuration(ms: number): string {\n\tconst sec = Math.floor(ms / 1000);\n\tif (sec < 60) return `${sec}s`;\n\tconst min = Math.floor(sec / 60);\n\tif (min < 60) return `${min}m ${sec % 60}s`;\n\tconst hr = Math.floor(min / 60);\n\treturn `${hr}h ${min % 60}m`;\n}\n\n/**\n * Single job detail view showing config, recent run info, and cost.\n */\nexport function JobDetail({ job }: JobDetailProps) {\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingTop={1} paddingLeft={1}>\n\t\t\t<Text bold color=\"cyan\">{job.name}</Text>\n\t\t\t<Text> </Text>\n\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Repository:</Text></Box>\n\t\t\t\t<Text dimColor>{job.repoPath}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Branch:</Text></Box>\n\t\t\t\t<Text dimColor>{job.branch}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Schedule:</Text></Box>\n\t\t\t\t<Text dimColor>{job.cron} ({describeScheduleSafe(job.cron)})</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Timezone:</Text></Box>\n\t\t\t\t<Text dimColor>{job.timezone}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Focus:</Text></Box>\n\t\t\t\t<Text dimColor>{job.focus.join(\", \")}</Text>\n\t\t\t</Box>\n\t\t\t{job.model && (\n\t\t\t\t<Box>\n\t\t\t\t\t<Box width={16}><Text bold>Model:</Text></Box>\n\t\t\t\t\t<Text dimColor>{job.model}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Status:</Text></Box>\n\t\t\t\t<Text color={job.enabled ? \"green\" : \"yellow\"}>\n\t\t\t\t\t{job.enabled ? \"active\" : \"paused\"}\n\t\t\t\t</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Budget:</Text></Box>\n\t\t\t\t<Text dimColor>${job.maxBudgetUsd.toFixed(2)}/run</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Box width={16}><Text bold>Total Cost:</Text></Box>\n\t\t\t\t<Text dimColor>${job.totalCost.toFixed(2)}</Text>\n\t\t\t</Box>\n\n\t\t\t<Text> </Text>\n\t\t\t<Text bold>Last Run:</Text>\n\t\t\t{job.lastRun ? (\n\t\t\t\t<Box flexDirection=\"column\" paddingLeft={2}>\n\t\t\t\t\t<Box>\n\t\t\t\t\t\t<Box width={14}><Text bold>Date:</Text></Box>\n\t\t\t\t\t\t<Text dimColor>{new Date(job.lastRun.startedAt).toLocaleString()}</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t\t<Box>\n\t\t\t\t\t\t<Box width={14}><Text bold>Status:</Text></Box>\n\t\t\t\t\t\t<Text color={job.lastRun.status === \"success\" ? \"green\" : \"red\"}>\n\t\t\t\t\t\t\t{job.lastRun.status}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t\t<Box>\n\t\t\t\t\t\t<Box width={14}><Text bold>Duration:</Text></Box>\n\t\t\t\t\t\t<Text dimColor>{formatDuration(job.lastRun.durationMs)}</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t\t{job.lastRun.costUsd !== undefined && (\n\t\t\t\t\t\t<Box>\n\t\t\t\t\t\t\t<Box width={14}><Text bold>Cost:</Text></Box>\n\t\t\t\t\t\t\t<Text dimColor>${job.lastRun.costUsd.toFixed(4)}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t\t{job.lastRun.summary && (\n\t\t\t\t\t\t<Box>\n\t\t\t\t\t\t\t<Box width={14}><Text bold>Summary:</Text></Box>\n\t\t\t\t\t\t\t<Text dimColor>{job.lastRun.summary}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t\t{job.lastRun.prUrl && (\n\t\t\t\t\t\t<Box>\n\t\t\t\t\t\t\t<Box width={14}><Text bold>PR:</Text></Box>\n\t\t\t\t\t\t\t<Text dimColor>{job.lastRun.prUrl}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t</Box>\n\t\t\t) : (\n\t\t\t\t<Box paddingLeft={2}>\n\t\t\t\t\t<Text dimColor>No runs yet</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{job.nextRun && (\n\t\t\t\t<>\n\t\t\t\t\t<Text> </Text>\n\t\t\t\t\t<Box>\n\t\t\t\t\t\t<Box width={16}><Text bold>Next Run:</Text></Box>\n\t\t\t\t\t\t<Text dimColor>{job.nextRun.toLocaleString()}</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</Box>\n\t);\n}\n","import { useState, useEffect } from \"react\";\nimport { Box, Text } from \"ink\";\nimport { Spinner } from \"@inkjs/ui\";\nimport { listRunLogs } from \"../../runner/logger.js\";\nimport type { RunLogEntry } from \"../../runner/types.js\";\n\ninterface RunLogProps {\n\tjobId: string;\n\tscrollOffset?: number;\n}\n\nfunction formatDuration(ms: number): string {\n\tconst sec = Math.floor(ms / 1000);\n\tif (sec < 60) return `${sec}s`;\n\tconst min = Math.floor(sec / 60);\n\tif (min < 60) return `${min}m ${sec % 60}s`;\n\tconst hr = Math.floor(min / 60);\n\treturn `${hr}h ${min % 60}m`;\n}\n\nfunction statusColor(status: string): string {\n\tswitch (status) {\n\t\tcase \"success\":\n\t\t\treturn \"green\";\n\t\tcase \"no-changes\":\n\t\t\treturn \"yellow\";\n\t\tcase \"error\":\n\t\tcase \"git-error\":\n\t\t\treturn \"red\";\n\t\tcase \"locked\":\n\t\tcase \"paused\":\n\t\t\treturn \"gray\";\n\t\tcase \"budget-exceeded\":\n\t\t\treturn \"magenta\";\n\t\tcase \"merge-conflict\":\n\t\t\treturn \"red\";\n\t\tcase \"needs-human-review\":\n\t\t\treturn \"cyan\";\n\t\tdefault:\n\t\t\treturn \"white\";\n\t}\n}\n\nconst VISIBLE_ENTRIES = 10;\n\n/**\n * Scrollable run log history for a single job.\n * Displays date, status, duration, cost, and summary for each run.\n */\nexport function RunLog({ jobId, scrollOffset = 0 }: RunLogProps) {\n\tconst [logs, setLogs] = useState<RunLogEntry[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\n\t\tconst load = async () => {\n\t\t\ttry {\n\t\t\t\tconst entries = await listRunLogs(jobId);\n\t\t\t\tif (!cancelled) {\n\t\t\t\t\tsetLogs(entries);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t} catch (err: unknown) {\n\t\t\t\tif (!cancelled) {\n\t\t\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tload();\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, [jobId]);\n\n\tif (loading) {\n\t\treturn (\n\t\t\t<Box padding={1}>\n\t\t\t\t<Spinner label=\"Loading run logs...\" />\n\t\t\t</Box>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<Box padding={1}>\n\t\t\t\t<Text color=\"red\">Error loading logs: {error}</Text>\n\t\t\t</Box>\n\t\t);\n\t}\n\n\tif (logs.length === 0) {\n\t\treturn (\n\t\t\t<Box padding={1}>\n\t\t\t\t<Text dimColor>No run history for this job.</Text>\n\t\t\t</Box>\n\t\t);\n\t}\n\n\tconst clampedOffset = Math.min(scrollOffset, Math.max(0, logs.length - VISIBLE_ENTRIES));\n\tconst visibleLogs = logs.slice(clampedOffset, clampedOffset + VISIBLE_ENTRIES);\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingTop={1} paddingLeft={1}>\n\t\t\t<Text bold>Run History ({logs.length} runs, showing {clampedOffset + 1}-{clampedOffset + visibleLogs.length})</Text>\n\t\t\t<Text> </Text>\n\n\t\t\t{visibleLogs.map((log) => (\n\t\t\t\t<Box key={log.runId} flexDirection=\"column\" marginBottom={1}>\n\t\t\t\t\t<Box>\n\t\t\t\t\t\t<Text dimColor>{new Date(log.startedAt).toLocaleString()}</Text>\n\t\t\t\t\t\t<Text> </Text>\n\t\t\t\t\t\t<Text color={statusColor(log.status)} bold>{log.status}</Text>\n\t\t\t\t\t\t<Text> </Text>\n\t\t\t\t\t\t<Text dimColor>{formatDuration(log.durationMs)}</Text>\n\t\t\t\t\t\t{log.costUsd !== undefined && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<Text> </Text>\n\t\t\t\t\t\t\t\t<Text dimColor>${log.costUsd.toFixed(4)}</Text>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</Box>\n\t\t\t\t\t{log.summary && (\n\t\t\t\t\t\t<Box paddingLeft={2}>\n\t\t\t\t\t\t\t<Text dimColor>{log.summary}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t\t{log.error && (\n\t\t\t\t\t\t<Box paddingLeft={2}>\n\t\t\t\t\t\t\t<Text color=\"red\">{log.error}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t\t{log.prUrl && (\n\t\t\t\t\t\t<Box paddingLeft={2}>\n\t\t\t\t\t\t\t<Text dimColor>PR: {log.prUrl}</Text>\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t)}\n\t\t\t\t</Box>\n\t\t\t))}\n\n\t\t\t{logs.length > VISIBLE_ENTRIES && (\n\t\t\t\t<Text dimColor>Use j/k or up/down to scroll</Text>\n\t\t\t)}\n\t\t</Box>\n\t);\n}\n","import { Box, Text } from \"ink\";\nimport type { View } from \"../hooks/use-keyboard.js\";\n\ninterface StatusBarProps {\n\tview: View;\n}\n\nconst HINTS: Record<View, string> = {\n\tlist: \"up/down navigate | Enter detail | p pause/resume | l logs | q quit\",\n\tdetail: \"Esc back | p pause/resume | l logs | q quit\",\n\tlogs: \"Esc back | j/k scroll | q quit\",\n};\n\n/**\n * Bottom status bar with keybinding hints based on current view.\n */\nexport function StatusBar({ view }: StatusBarProps) {\n\treturn (\n\t\t<Box borderStyle=\"single\" borderTop borderBottom={false} borderLeft={false} borderRight={false} paddingX={1} marginTop={1}>\n\t\t\t<Text dimColor>{HINTS[view]}</Text>\n\t\t</Box>\n\t);\n}\n","import { useEffect, useState } from \"react\";\nimport { listJobs } from \"../../core/job-manager.js\";\nimport { getNextRuns } from \"../../core/schedule.js\";\nimport { type CostSummaryRow, getCostSummary } from \"../../runner/cost-tracker.js\";\nimport { listRunLogs } from \"../../runner/logger.js\";\nimport type { RunLogEntry } from \"../../runner/types.js\";\n\n/**\n * Extended job data combining config with runtime metadata.\n */\nexport interface JobWithMeta {\n\tid: string;\n\tname: string;\n\trepoPath: string;\n\tbranch: string;\n\tcron: string;\n\ttimezone: string;\n\tfocus: string[];\n\tenabled: boolean;\n\tmodel?: string;\n\tmaxBudgetUsd: number;\n\tlastRun: RunLogEntry | null;\n\tnextRun: Date | null;\n\ttotalCost: number;\n}\n\n/**\n * Load all jobs with combined metadata (cost, last run, next run).\n * Exported separately for direct testing without React hooks.\n */\nexport async function loadJobsWithMeta(): Promise<JobWithMeta[]> {\n\tconst jobs = await listJobs();\n\n\t// Load cost data (best-effort -- DB may not exist)\n\tconst costMap: Map<string, number> = new Map();\n\ttry {\n\t\tconst costRows = getCostSummary() as CostSummaryRow[];\n\t\tfor (const row of costRows) {\n\t\t\tcostMap.set(row.job_id, row.total_cost);\n\t\t}\n\t} catch {\n\t\t// Cost data unavailable -- default to 0\n\t}\n\n\tconst results: JobWithMeta[] = [];\n\n\tfor (const job of jobs) {\n\t\t// Get last run (best-effort)\n\t\tlet lastRun: RunLogEntry | null = null;\n\t\ttry {\n\t\t\tconst logs = await listRunLogs(job.id);\n\t\t\tif (logs.length > 0) {\n\t\t\t\tlastRun = logs[0]; // Already sorted newest first\n\t\t\t}\n\t\t} catch {\n\t\t\t// No logs available\n\t\t}\n\n\t\t// Get next run time (best-effort)\n\t\tlet nextRun: Date | null = null;\n\t\ttry {\n\t\t\tif (job.enabled) {\n\t\t\t\tconst runs = getNextRuns(job.schedule.cron, job.schedule.timezone, 1);\n\t\t\t\tif (runs.length > 0) {\n\t\t\t\t\tnextRun = runs[0];\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Invalid cron or timezone\n\t\t}\n\n\t\tresults.push({\n\t\t\tid: job.id,\n\t\t\tname: job.name,\n\t\t\trepoPath: job.repo.path,\n\t\t\tbranch: job.repo.branch,\n\t\t\tcron: job.schedule.cron,\n\t\t\ttimezone: job.schedule.timezone,\n\t\t\tfocus: job.focus,\n\t\t\tenabled: job.enabled,\n\t\t\tmodel: job.model,\n\t\t\tmaxBudgetUsd: job.guardrails.maxBudgetUsd,\n\t\t\tlastRun,\n\t\t\tnextRun,\n\t\t\ttotalCost: costMap.get(job.id) ?? 0,\n\t\t});\n\t}\n\n\treturn results;\n}\n\n/**\n * React hook that polls job data at a configurable interval.\n *\n * @param pollIntervalMs - Polling interval in milliseconds (default 3000)\n * @returns Jobs with metadata, loading state, and any error message\n */\nexport function useJobs(pollIntervalMs = 3000): {\n\tjobs: JobWithMeta[];\n\tloading: boolean;\n\terror: string | null;\n\trefresh: () => void;\n} {\n\tconst [jobs, setJobs] = useState<JobWithMeta[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [_refreshTick, setRefreshTick] = useState(0);\n\n\tconst refresh = () => setRefreshTick((t) => t + 1);\n\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\n\t\tconst load = async () => {\n\t\t\ttry {\n\t\t\t\tconst data = await loadJobsWithMeta();\n\t\t\t\tif (!cancelled) {\n\t\t\t\t\tsetJobs(data);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t\tsetError(null);\n\t\t\t\t}\n\t\t\t} catch (err: unknown) {\n\t\t\t\tif (!cancelled) {\n\t\t\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tload();\n\t\tconst timer = setInterval(load, pollIntervalMs);\n\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [pollIntervalMs]);\n\n\treturn { jobs, loading, error, refresh };\n}\n","import { useApp, useInput } from \"ink\";\nimport { useCallback, useState } from \"react\";\nimport type { JobWithMeta } from \"./use-jobs.js\";\n\n/**\n * Dashboard view states.\n */\nexport type View = \"list\" | \"detail\" | \"logs\";\n\n/**\n * Keyboard navigation state for the TUI dashboard.\n */\nexport interface KeyboardState {\n\tview: View;\n\tselectedIdx: number;\n\tscrollOffset: number;\n}\n\n/**\n * Actions that require caller-side mutation (not pure state changes).\n */\nexport interface KeyboardActions {\n\t/** Returns the selected job ID for pause/resume, or null if no job selected */\n\tpauseResume: (jobs: JobWithMeta[]) => string | null;\n\t/** Returns the selected job ID, or null if no jobs */\n\tgetSelectedJobId: (jobs: JobWithMeta[]) => string | null;\n}\n\n/**\n * Hook for keyboard navigation in the TUI dashboard.\n *\n * Handles arrow keys, Enter, Escape, p (pause/resume), l (logs), q (quit),\n * and j/k for scrolling in log view.\n *\n * @param jobCount - Total number of jobs (for bounds checking)\n * @returns Current navigation state and action helpers\n */\nexport function useKeyboard(jobCount: number): KeyboardState & { actions: KeyboardActions } {\n\tconst { exit } = useApp();\n\tconst [view, setView] = useState<View>(\"list\");\n\tconst [selectedIdx, setSelectedIdx] = useState(0);\n\tconst [scrollOffset, setScrollOffset] = useState(0);\n\tconst [pendingPauseResume, setPendingPauseResume] = useState(false);\n\n\tuseInput((input, key) => {\n\t\tif (input === \"q\") {\n\t\t\texit();\n\t\t\treturn;\n\t\t}\n\n\t\tif (key.upArrow) {\n\t\t\tif (view === \"logs\") {\n\t\t\t\tsetScrollOffset((o) => Math.max(0, o - 1));\n\t\t\t} else {\n\t\t\t\tsetSelectedIdx((i) => Math.max(0, i - 1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (key.downArrow) {\n\t\t\tif (view === \"logs\") {\n\t\t\t\tsetScrollOffset((o) => o + 1);\n\t\t\t} else {\n\t\t\t\tsetSelectedIdx((i) => Math.min(Math.max(0, jobCount - 1), i + 1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (key.return && view === \"list\") {\n\t\t\tsetView(\"detail\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (key.escape && view !== \"list\") {\n\t\t\tsetView(\"list\");\n\t\t\tsetScrollOffset(0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"p\" && view !== \"logs\") {\n\t\t\tsetPendingPauseResume(true);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"l\" && view !== \"logs\") {\n\t\t\tsetView(\"logs\");\n\t\t\tsetScrollOffset(0);\n\t\t\treturn;\n\t\t}\n\n\t\t// j/k scrolling in log view\n\t\tif (view === \"logs\") {\n\t\t\tif (input === \"j\") {\n\t\t\t\tsetScrollOffset((o) => o + 1);\n\t\t\t} else if (input === \"k\") {\n\t\t\t\tsetScrollOffset((o) => Math.max(0, o - 1));\n\t\t\t}\n\t\t}\n\t});\n\n\tconst getSelectedJobId = useCallback(\n\t\t(jobs: JobWithMeta[]): string | null => {\n\t\t\tif (jobs.length === 0 || selectedIdx >= jobs.length) return null;\n\t\t\treturn jobs[selectedIdx].id;\n\t\t},\n\t\t[selectedIdx],\n\t);\n\n\tconst pauseResume = useCallback(\n\t\t(jobs: JobWithMeta[]): string | null => {\n\t\t\tif (!pendingPauseResume) return null;\n\t\t\tsetPendingPauseResume(false);\n\t\t\treturn getSelectedJobId(jobs);\n\t\t},\n\t\t[pendingPauseResume, getSelectedJobId],\n\t);\n\n\treturn {\n\t\tview,\n\t\tselectedIdx,\n\t\tscrollOffset,\n\t\tactions: {\n\t\t\tpauseResume,\n\t\t\tgetSelectedJobId,\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["useState","Box","Text","statusColor","Box","Text","cronstrue","jsx","jsxs","describeScheduleSafe","Box","Text","Spinner","Fragment","jsx","jsxs","formatDuration","Box","Spinner","Text","Box","Text","jsx","useEffect","useState","useState","useEffect","useState","jsx","jsxs","useState","Box","Text","jsx"]}
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@cjvana/claude-auto",
3
+ "version": "0.1.0",
4
+ "description": "Autonomous Claude Code cron jobs for continuous codebase improvement",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.0.0"
8
+ },
9
+ "bin": {
10
+ "claude-auto": "./dist/claude-auto.js",
11
+ "claude-auto-run": "./dist/claude-auto-run.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist/",
21
+ ".claude-plugin/",
22
+ "skills/",
23
+ "scripts/postinstall.mjs",
24
+ "scripts/preuninstall.mjs",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsx watch src/index.ts",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "lint": "biome check .",
34
+ "lint:fix": "biome check --write .",
35
+ "typecheck": "tsc --noEmit",
36
+ "postinstall": "node scripts/postinstall.mjs",
37
+ "preuninstall": "node scripts/preuninstall.mjs"
38
+ },
39
+ "keywords": [
40
+ "claude",
41
+ "claude-code",
42
+ "autonomous",
43
+ "cron",
44
+ "ai"
45
+ ],
46
+ "license": "MIT",
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/cj-vana/claude-auto.git"
53
+ },
54
+ "dependencies": {
55
+ "@inkjs/ui": "^2.0.0",
56
+ "better-sqlite3": "^12.8.0",
57
+ "cron-parser": "^5.5.0",
58
+ "cronstrue": "^3.14.0",
59
+ "ink": "^6.8.0",
60
+ "nanoid": "^5.1.7",
61
+ "plist": "^3.1.0",
62
+ "proper-lockfile": "^4.1.2",
63
+ "react": "^19.2.4",
64
+ "write-file-atomic": "^7.0.1",
65
+ "yaml": "^2.8.3",
66
+ "zod": "^4.3.6"
67
+ },
68
+ "devDependencies": {
69
+ "@biomejs/biome": "^2.4.8",
70
+ "@types/better-sqlite3": "^7.6.13",
71
+ "@types/node": "^25.5.0",
72
+ "@types/plist": "^3.0.5",
73
+ "@types/proper-lockfile": "^4.1.4",
74
+ "@types/react": "^19.2.14",
75
+ "ink-testing-library": "^4.0.0",
76
+ "tsup": "^8.5.1",
77
+ "tsx": "^4.21.0",
78
+ "typescript": "^5.9.3",
79
+ "vitest": "^4.1.0"
80
+ }
81
+ }
@@ -0,0 +1,65 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+
5
+ const CLAUDE_DIR = join(homedir(), ".claude");
6
+ const PLUGINS_DIR = join(CLAUDE_DIR, "plugins");
7
+ const INSTALLED_FILE = join(PLUGINS_DIR, "installed_plugins.json");
8
+ const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
9
+ const PLUGIN_ID = "claude-auto";
10
+
11
+ // The plugin root is the package install location (where .claude-plugin/ lives)
12
+ const pluginRoot = join(import.meta.dirname, "..");
13
+ const now = new Date().toISOString();
14
+
15
+ try {
16
+ mkdirSync(PLUGINS_DIR, { recursive: true });
17
+
18
+ // 1. Register in installed_plugins.json
19
+ let installed = { version: 2, plugins: {} };
20
+ if (existsSync(INSTALLED_FILE)) {
21
+ try {
22
+ installed = JSON.parse(readFileSync(INSTALLED_FILE, "utf-8"));
23
+ } catch {
24
+ // Corrupted file — start fresh
25
+ }
26
+ }
27
+
28
+ installed.plugins = installed.plugins || {};
29
+ installed.plugins[PLUGIN_ID] = [
30
+ {
31
+ scope: "user",
32
+ installPath: pluginRoot,
33
+ version: "0.1.0",
34
+ installedAt: now,
35
+ lastUpdated: now,
36
+ isLocal: true,
37
+ },
38
+ ];
39
+
40
+ writeFileSync(INSTALLED_FILE, JSON.stringify(installed, null, 2) + "\n");
41
+
42
+ // 2. Enable in settings.json
43
+ mkdirSync(CLAUDE_DIR, { recursive: true });
44
+ let settings = {};
45
+ if (existsSync(SETTINGS_FILE)) {
46
+ try {
47
+ settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
48
+ } catch {
49
+ // Corrupted file — start fresh
50
+ }
51
+ }
52
+
53
+ settings.enabledPlugins = settings.enabledPlugins || {};
54
+ settings.enabledPlugins[PLUGIN_ID] = true;
55
+
56
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
57
+
58
+ console.log("claude-auto: Registered as Claude Code plugin");
59
+ console.log(
60
+ "claude-auto: Slash commands available — /claude-auto:setup, /claude-auto:list, etc.",
61
+ );
62
+ } catch (err) {
63
+ console.warn("claude-auto: Could not auto-register plugin:", err.message);
64
+ console.warn("claude-auto: Register manually — see README for instructions.");
65
+ }
@@ -0,0 +1,33 @@
1
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+
5
+ const CLAUDE_DIR = join(homedir(), ".claude");
6
+ const PLUGINS_DIR = join(CLAUDE_DIR, "plugins");
7
+ const INSTALLED_FILE = join(PLUGINS_DIR, "installed_plugins.json");
8
+ const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
9
+ const PLUGIN_ID = "claude-auto";
10
+
11
+ try {
12
+ // 1. Remove from installed_plugins.json
13
+ if (existsSync(INSTALLED_FILE)) {
14
+ const installed = JSON.parse(readFileSync(INSTALLED_FILE, "utf-8"));
15
+ if (installed.plugins && installed.plugins[PLUGIN_ID]) {
16
+ delete installed.plugins[PLUGIN_ID];
17
+ writeFileSync(INSTALLED_FILE, JSON.stringify(installed, null, 2) + "\n");
18
+ }
19
+ }
20
+
21
+ // 2. Remove from settings.json enabledPlugins
22
+ if (existsSync(SETTINGS_FILE)) {
23
+ const settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
24
+ if (settings.enabledPlugins && settings.enabledPlugins[PLUGIN_ID]) {
25
+ delete settings.enabledPlugins[PLUGIN_ID];
26
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
27
+ }
28
+ }
29
+
30
+ console.log("claude-auto: Plugin unregistered");
31
+ } catch {
32
+ // Best-effort: don't fail npm uninstall if cleanup fails
33
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: edit
3
+ description: Edit the configuration of an existing autonomous cron job. Use when the user wants to change a job's schedule, focus areas, branch, guardrails, or other settings.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ Edit an existing autonomous cron job. If the user hasn't specified which job,
8
+ show available jobs first:
9
+
10
+ ```bash
11
+ claude-auto list
12
+ ```
13
+
14
+ Ask what they want to change. Supported fields:
15
+
16
+ - **Name**: `claude-auto edit <jobId> --name "New Name"`
17
+ - **Schedule**: `claude-auto edit <jobId> --schedule "0 */8 * * *"`
18
+ - **Timezone**: `claude-auto edit <jobId> --timezone "America/New_York"`
19
+ - **Branch**: `claude-auto edit <jobId> --branch "develop"`
20
+ - **Max turns**: `claude-auto edit <jobId> --max-turns 100`
21
+ - **Max budget**: `claude-auto edit <jobId> --max-budget 10.00`
22
+ - **Focus areas**: `claude-auto edit <jobId> --focus "open-issues,features"`
23
+
24
+ Multiple flags can be combined in a single command. After editing, confirm
25
+ the changes and show the updated configuration.
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: list
3
+ description: List all configured autonomous cron jobs with their status, schedule, and last run info. Use when the user asks about their jobs, what's running, or wants an overview.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ List all configured autonomous cron jobs by running:
8
+
9
+ ```bash
10
+ claude-auto list
11
+ ```
12
+
13
+ Present the output in a readable format showing each job's name, schedule,
14
+ status (enabled/paused), and last run result.
15
+
16
+ If no jobs exist, suggest the user run `/claude-auto:setup` to create their
17
+ first autonomous job.
18
+
19
+ For more detailed information about a specific job, use:
20
+
21
+ ```bash
22
+ claude-auto list --json
23
+ ```
24
+
25
+ This returns structured JSON output that you can filter and format to answer
26
+ specific questions about individual jobs.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: logs
3
+ description: View run history and logs for an autonomous cron job. Use when the user wants to see what Claude has been doing or debug a job.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ View run history for an autonomous cron job. If the user hasn't specified
8
+ which job, show available jobs first:
9
+
10
+ ```bash
11
+ claude-auto list
12
+ ```
13
+
14
+ Show recent runs:
15
+
16
+ ```bash
17
+ claude-auto logs <jobId>
18
+ ```
19
+
20
+ For more history, use the limit flag:
21
+
22
+ ```bash
23
+ claude-auto logs <jobId> --limit 20
24
+ ```
25
+
26
+ For an aggregate view across all jobs, suggest:
27
+
28
+ ```bash
29
+ claude-auto report
30
+ ```
31
+
32
+ Present the log output in a readable format, highlighting successes,
33
+ failures, and any PRs that were created.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: pause
3
+ description: Pause an active autonomous cron job without losing its configuration. Use when the user wants to temporarily stop a job from running.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ Pause an autonomous cron job. If the user hasn't specified which job, show
8
+ available jobs first:
9
+
10
+ ```bash
11
+ claude-auto list
12
+ ```
13
+
14
+ Then pause the specified job:
15
+
16
+ ```bash
17
+ claude-auto pause $ARGUMENTS
18
+ ```
19
+
20
+ After pausing, confirm the job is paused and remind the user they can
21
+ resume it later with `/claude-auto:resume`.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: remove
3
+ description: Permanently remove an autonomous cron job and its schedule. Use when the user wants to delete a job they no longer need.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ Remove an autonomous cron job. This is destructive and cannot be undone.
8
+
9
+ If the user hasn't specified which job, show available jobs first:
10
+
11
+ ```bash
12
+ claude-auto list
13
+ ```
14
+
15
+ Before removing, confirm with the user that they want to permanently delete
16
+ the job. Ask if they want to keep the run logs:
17
+
18
+ - **Keep logs**: `claude-auto remove <jobId> --keep-logs`
19
+ - **Remove everything**: `claude-auto remove <jobId>`
20
+
21
+ After removal, confirm the job has been deleted and its scheduled runs
22
+ have been unregistered.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: resume
3
+ description: Resume a paused autonomous cron job. Use when the user wants to reactivate a previously paused job.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ Resume a paused autonomous cron job. If the user hasn't specified which job,
8
+ show paused jobs first:
9
+
10
+ ```bash
11
+ claude-auto list
12
+ ```
13
+
14
+ Then resume the specified job:
15
+
16
+ ```bash
17
+ claude-auto resume $ARGUMENTS
18
+ ```
19
+
20
+ After resuming, confirm the job is active again and show when the next
21
+ scheduled run will be.
@@ -0,0 +1,195 @@
1
+ ---
2
+ name: setup
3
+ description: Set up a new autonomous Claude cron job for continuous codebase improvement. Use when the user wants to create a scheduled job where Claude autonomously works on their repo.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ You are helping the user set up a new autonomous cron job where Claude will
8
+ continuously improve their codebase. Walk them through each step conversationally.
9
+ Be friendly, explain what each option does, and provide sensible defaults.
10
+
11
+ ## Step 1: Repository
12
+
13
+ Ask which repository to target. Accept either:
14
+ - A local filesystem path (e.g., `/Users/dev/my-project`)
15
+ - A GitHub identifier: `owner/repo` or a full URL
16
+
17
+ If they give a GitHub identifier, ask where they'd like it cloned locally.
18
+ Suggest `~/projects/<repo-name>` as the default location.
19
+
20
+ Validate the repository by running:
21
+
22
+ ```bash
23
+ claude-auto check-repo --path "$PATH"
24
+ ```
25
+
26
+ If the path doesn't exist and they provided a GitHub identifier, let them know
27
+ that `claude-auto create` will handle cloning automatically via the `--github-repo`
28
+ flag. Confirm the clone location before proceeding.
29
+
30
+ ## Step 2: Branch
31
+
32
+ Ask which branch the autonomous agent should target for pull requests.
33
+
34
+ - Default: `main`
35
+ - Common alternatives: `develop`, `dev`, `master`
36
+
37
+ Use whatever branch they use for merging PRs in their workflow.
38
+
39
+ ## Step 3: Schedule
40
+
41
+ Ask how often Claude should work on the repo. Accept natural language or raw cron:
42
+
43
+ - "every 6 hours"
44
+ - "twice a day" (translates to `0 */12 * * *`)
45
+ - "weekdays at 9am" (translates to `0 9 * * 1-5`)
46
+ - "once a day at midnight" (translates to `0 0 * * *`)
47
+ - Raw 5-field cron: `0 */6 * * *`
48
+
49
+ Convert natural language to a standard 5-field cron expression. Confirm with the
50
+ user: "So Claude will run [human-readable description]. Sound good?"
51
+
52
+ Also ask about timezone. Default to their local timezone. Common choices:
53
+ `America/New_York`, `America/Los_Angeles`, `Europe/London`, `UTC`.
54
+
55
+ ## Step 4: Focus Areas
56
+
57
+ Ask what Claude should focus on during each run. They can pick multiple:
58
+
59
+ - **open-issues** -- Work through open GitHub issues, prioritizing by labels
60
+ - **bug-discovery** -- Proactively scan the codebase for bugs and fix them
61
+ - **features** -- Suggest and implement small improvements
62
+ - **documentation** -- Update and improve docs, READMEs, and code comments
63
+
64
+ Default: `open-issues, bug-discovery`
65
+
66
+ Explain that Claude will prioritize in the order: issues > bugs > features > docs.
67
+
68
+ ## Step 5: System Prompt (The Personality)
69
+
70
+ This is the most important step. Help the user craft a system prompt that defines
71
+ how the autonomous Claude instance behaves. This prompt shapes every decision
72
+ Claude makes while working on the codebase.
73
+
74
+ Ask the user about each of these aspects:
75
+
76
+ **Coding style**
77
+ - Conservative (stick to existing patterns) or experimental (try modern approaches)?
78
+ - Verbose (explicit, well-commented) or concise (minimal, self-documenting)?
79
+ - Strong opinions on specific patterns? (e.g., prefer composition over inheritance)
80
+
81
+ **PR style**
82
+ - Big sweeping changes or small focused PRs?
83
+ - How detailed should PR descriptions be?
84
+ - Should Claude add tests with every change?
85
+
86
+ **Priorities**
87
+ - Tests first? Documentation first? Ship fast and iterate?
88
+ - Code quality vs speed tradeoff?
89
+ - Any specific areas of the codebase that need attention?
90
+
91
+ **Things to avoid**
92
+ - Specific directories or files that should not be touched?
93
+ - Patterns or libraries they dislike?
94
+ - Approaches that have caused problems before?
95
+
96
+ **Personality**
97
+ - Meticulous code reviewer who catches everything?
98
+ - Move-fast builder who ships features?
99
+ - Security hawk who locks things down?
100
+ - Documentation enthusiast who explains everything?
101
+
102
+ Based on their answers, **draft a complete system prompt** and show it to them.
103
+ For example:
104
+
105
+ > "You are a meticulous software engineer maintaining this TypeScript project.
106
+ > Focus on code quality and test coverage. Never add new dependencies without
107
+ > extremely good reason. Prefer small, focused PRs over large sweeping changes.
108
+ > Always run tests before committing. When fixing bugs, add a regression test.
109
+ > Avoid touching the legacy/ directory unless specifically asked."
110
+
111
+ Iterate on the prompt until the user is happy with it. This is a conversation --
112
+ refine based on their feedback.
113
+
114
+ Once finalized, write the system prompt to a temporary file:
115
+
116
+ ```bash
117
+ cat > /tmp/claude-auto-system-prompt.txt << 'PROMPT_EOF'
118
+ [the finalized system prompt text]
119
+ PROMPT_EOF
120
+ ```
121
+
122
+ This file will be passed to the create command in Step 8.
123
+
124
+ ## Step 6: Notifications (Optional)
125
+
126
+ Ask if the user wants to be notified when Claude creates PRs, encounters errors,
127
+ or finishes a run. This is optional -- skip if they say no.
128
+
129
+ Supported notification channels:
130
+ - **Discord**: Provide a webhook URL (`https://discord.com/api/webhooks/...`)
131
+ - **Slack**: Provide a webhook URL (`https://hooks.slack.com/services/...`)
132
+ - **Telegram**: Provide a bot token and chat ID
133
+
134
+ They can configure multiple channels. For each, ask which events to notify on:
135
+ success, failure, or both (default: both).
136
+
137
+ ## Step 7: Guardrails (Optional)
138
+
139
+ Ask if the user wants any restrictions on what Claude can do. This is optional --
140
+ default is full trust with sensible limits.
141
+
142
+ Available guardrails:
143
+ - **Max turns** per run (default: 50) -- how many tool-use steps Claude can take
144
+ - **Max budget** per run in USD (default: $5.00) -- spending limit per execution
145
+ - **No new dependencies** -- prevent Claude from adding packages
146
+ - **No architecture changes** -- keep Claude from restructuring the project
147
+ - **Bug fix only** -- restrict to fixing bugs, no new features
148
+ - **Restrict to paths** -- limit Claude to specific directories (comma-separated)
149
+
150
+ Default: max 50 turns, $5.00 budget, no other restrictions.
151
+
152
+ ## Step 8: Create the Job
153
+
154
+ Summarize all the gathered configuration and ask for final confirmation. Then
155
+ construct and run the create command:
156
+
157
+ ```bash
158
+ claude-auto create \
159
+ --name "$NAME" \
160
+ --repo "$REPO_PATH" \
161
+ --branch "$BRANCH" \
162
+ --schedule "$CRON_EXPRESSION" \
163
+ --timezone "$TIMEZONE" \
164
+ --focus "$FOCUS_CSV" \
165
+ --system-prompt-file /tmp/claude-auto-system-prompt.txt \
166
+ [--github-repo "$GITHUB_REPO"] \
167
+ [--notify-discord "$DISCORD_URL"] \
168
+ [--notify-slack "$SLACK_URL"] \
169
+ [--notify-telegram "$BOT_TOKEN:$CHAT_ID"] \
170
+ [--max-turns N] \
171
+ [--max-budget N] \
172
+ [--no-new-deps] \
173
+ [--no-arch-changes] \
174
+ [--bug-fix-only] \
175
+ [--restrict-paths "$PATHS_CSV"]
176
+ ```
177
+
178
+ After the command succeeds, present to the user:
179
+ - **Job ID** and name
180
+ - **Schedule** in human-readable form
181
+ - **Next run time**
182
+ - **Config file location** (so they know where to find it for manual edits)
183
+
184
+ Remind them about management commands:
185
+ - `/claude-auto:list` -- see all jobs
186
+ - `/claude-auto:pause` -- temporarily stop a job
187
+ - `/claude-auto:edit` -- change settings
188
+ - `/claude-auto:status` -- check job health
189
+ - `/claude-auto:logs` -- view run history
190
+
191
+ Clean up the temp file:
192
+
193
+ ```bash
194
+ rm -f /tmp/claude-auto-system-prompt.txt
195
+ ```
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: status
3
+ description: Show detailed status and last run information for an autonomous cron job. Use when the user wants to check if a job is healthy and working.
4
+ allowed-tools: Bash(claude-auto *)
5
+ ---
6
+
7
+ Show the status of an autonomous cron job. If the user hasn't specified
8
+ which job, show all jobs:
9
+
10
+ ```bash
11
+ claude-auto list --json
12
+ ```
13
+
14
+ For detailed status including last run information:
15
+
16
+ ```bash
17
+ claude-auto logs <jobId> --limit 1
18
+ ```
19
+
20
+ Present a summary including:
21
+ - Current status (enabled, paused, or errored)
22
+ - Schedule and next run time
23
+ - Last run result (success, failure, no changes)
24
+ - Number of PRs created in recent runs
25
+
26
+ If there are issues, suggest relevant management commands like
27
+ `/claude-auto:edit` or `/claude-auto:pause`.