@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,166 @@
1
+ import {
2
+ execCommand
3
+ } from "./chunk-3NEANSUS.js";
4
+ import {
5
+ paths
6
+ } from "./chunk-H2MUDYMW.js";
7
+ import {
8
+ SchedulerError
9
+ } from "./chunk-AWLSYOVF.js";
10
+
11
+ // src/platform/launchd.ts
12
+ import { access, readdir, readFile, unlink, writeFile } from "fs/promises";
13
+ import { homedir } from "os";
14
+ import { dirname, join } from "path";
15
+ import { fileURLToPath } from "url";
16
+ import { CronExpressionParser } from "cron-parser";
17
+ import plist from "plist";
18
+ var LABEL_PREFIX = "com.claude-auto.";
19
+ function getLabel(jobId) {
20
+ return `${LABEL_PREFIX}${jobId}`;
21
+ }
22
+ function getUid() {
23
+ return process.getuid?.() ?? 501;
24
+ }
25
+ function getRunnerPath() {
26
+ try {
27
+ const currentDir = dirname(fileURLToPath(import.meta.url));
28
+ return join(currentDir, "..", "..", "dist", "claude-auto-run.js");
29
+ } catch {
30
+ return join(process.cwd(), "dist", "claude-auto-run.js");
31
+ }
32
+ }
33
+ function cronToCalendarIntervals(cronExpr) {
34
+ const interval = CronExpressionParser.parse(cronExpr);
35
+ const fields = interval.fields;
36
+ const minutes = [...fields.minute.values].map(Number);
37
+ const hours = [...fields.hour.values].map(Number);
38
+ const daysOfMonth = [...fields.dayOfMonth.values].map(Number);
39
+ const months = [...fields.month.values].map(Number);
40
+ const daysOfWeek = [...fields.dayOfWeek.values].map(Number);
41
+ const isAllHours = hours.length === 24;
42
+ const isAllDays = daysOfMonth.length === 31;
43
+ const isAllMonths = months.length === 12;
44
+ const isAllDow = daysOfWeek.length === 8;
45
+ if (isAllHours && isAllDays && isAllMonths && isAllDow && minutes.length > 1) {
46
+ const step = minutes[1] - minutes[0];
47
+ const isEvenStep = minutes.every((m, i) => i === 0 || m - minutes[i - 1] === step);
48
+ if (isEvenStep) {
49
+ return { startInterval: step * 60 };
50
+ }
51
+ }
52
+ const intervals = [];
53
+ const effectiveHours = isAllHours ? [void 0] : hours;
54
+ const effectiveDow = isAllDow ? [void 0] : daysOfWeek.filter((d) => d <= 6);
55
+ const effectiveDom = isAllDays ? [void 0] : daysOfMonth;
56
+ for (const minute of minutes) {
57
+ for (const hour of effectiveHours) {
58
+ for (const dow of effectiveDow) {
59
+ for (const dom of effectiveDom) {
60
+ const entry = { Minute: minute };
61
+ if (hour !== void 0) entry.Hour = hour;
62
+ if (dow !== void 0) entry.Weekday = dow;
63
+ if (dom !== void 0) entry.Day = dom;
64
+ intervals.push(entry);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ if (intervals.length > 50) {
70
+ throw new Error(
71
+ `Cron expression "${cronExpr}" would produce ${intervals.length} calendar intervals. launchd StartCalendarInterval is not efficient for complex schedules. Simplify the schedule or use a simpler pattern.`
72
+ );
73
+ }
74
+ return { calendarIntervals: intervals };
75
+ }
76
+ var LaunchdScheduler = class {
77
+ async register(job, env) {
78
+ const registered = await this.isRegistered(job.id);
79
+ if (registered) {
80
+ throw new SchedulerError("launchd", `Job "${job.id}" is already registered`);
81
+ }
82
+ const scheduling = cronToCalendarIntervals(job.schedule.cron);
83
+ const runnerPath = getRunnerPath();
84
+ const logPath = `${paths.jobLogs(job.id)}/launchd.log`;
85
+ const plistPath = paths.plistPath(job.id);
86
+ const obj = {
87
+ Label: getLabel(job.id),
88
+ ProgramArguments: [process.execPath, runnerPath, "--job-id", job.id],
89
+ StandardOutPath: logPath,
90
+ StandardErrorPath: logPath,
91
+ EnvironmentVariables: {
92
+ PATH: env?.PATH ?? process.env.PATH ?? "",
93
+ HOME: env?.HOME ?? homedir(),
94
+ ...env ? Object.fromEntries(Object.entries(env).filter(([k]) => k !== "PATH" && k !== "HOME")) : {}
95
+ },
96
+ RunAtLoad: false,
97
+ KeepAlive: false
98
+ };
99
+ if (scheduling.startInterval !== void 0) {
100
+ obj.StartInterval = scheduling.startInterval;
101
+ } else if (scheduling.calendarIntervals) {
102
+ obj.StartCalendarInterval = scheduling.calendarIntervals.length === 1 ? scheduling.calendarIntervals[0] : scheduling.calendarIntervals;
103
+ }
104
+ const xml = plist.build(obj);
105
+ await writeFile(plistPath, xml, "utf-8");
106
+ const uid = getUid();
107
+ await execCommand("launchctl", ["bootstrap", `gui/${uid}`, plistPath]);
108
+ }
109
+ async unregister(jobId) {
110
+ const uid = getUid();
111
+ const label = getLabel(jobId);
112
+ const plistPath = paths.plistPath(jobId);
113
+ try {
114
+ await execCommand("launchctl", ["bootout", `gui/${uid}/${label}`]);
115
+ } catch {
116
+ }
117
+ try {
118
+ await unlink(plistPath);
119
+ } catch {
120
+ }
121
+ }
122
+ async isRegistered(jobId) {
123
+ try {
124
+ await access(paths.plistPath(jobId));
125
+ return true;
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+ async list() {
131
+ const jobs = [];
132
+ try {
133
+ const files = await readdir(paths.plistDir);
134
+ const plistFiles = files.filter((f) => f.startsWith(LABEL_PREFIX) && f.endsWith(".plist"));
135
+ for (const file of plistFiles) {
136
+ try {
137
+ const filePath = join(paths.plistDir, file);
138
+ const content = await readFile(filePath, "utf-8");
139
+ const parsed = plist.parse(content);
140
+ const label = parsed.Label;
141
+ const jobId = label.replace(LABEL_PREFIX, "");
142
+ const progArgs = parsed.ProgramArguments ?? [];
143
+ let schedule = "";
144
+ if (parsed.StartInterval) {
145
+ schedule = `every ${parsed.StartInterval}s`;
146
+ } else if (parsed.StartCalendarInterval) {
147
+ schedule = "calendar";
148
+ }
149
+ jobs.push({
150
+ jobId,
151
+ schedule,
152
+ command: progArgs.join(" ")
153
+ });
154
+ } catch {
155
+ }
156
+ }
157
+ } catch {
158
+ }
159
+ return jobs;
160
+ }
161
+ };
162
+ export {
163
+ LaunchdScheduler,
164
+ cronToCalendarIntervals
165
+ };
166
+ //# sourceMappingURL=launchd-HNZIWLNC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/platform/launchd.ts"],"sourcesContent":["import { access, readdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { CronExpressionParser } from \"cron-parser\";\nimport plist from \"plist\";\nimport type { JobConfig } from \"../core/types.js\";\nimport { SchedulerError } from \"../util/errors.js\";\nimport { execCommand } from \"../util/exec.js\";\nimport { paths } from \"../util/paths.js\";\nimport type { RegisteredJob, Scheduler } from \"./scheduler.js\";\n\nexport interface CalendarInterval {\n\tMonth?: number;\n\tDay?: number;\n\tWeekday?: number;\n\tHour?: number;\n\tMinute?: number;\n}\n\nconst LABEL_PREFIX = \"com.claude-auto.\";\n\nfunction getLabel(jobId: string): string {\n\treturn `${LABEL_PREFIX}${jobId}`;\n}\n\nfunction getUid(): number {\n\treturn process.getuid?.() ?? 501;\n}\n\n/**\n * Resolve the runner script path relative to this module.\n */\nfunction getRunnerPath(): string {\n\ttry {\n\t\tconst currentDir = dirname(fileURLToPath(import.meta.url));\n\t\treturn join(currentDir, \"..\", \"..\", \"dist\", \"claude-auto-run.js\");\n\t} catch {\n\t\treturn join(process.cwd(), \"dist\", \"claude-auto-run.js\");\n\t}\n}\n\n/**\n * Convert a 5-field cron expression to launchd scheduling config.\n *\n * For high-frequency \"every N minutes\" patterns, returns { startInterval: seconds }.\n * For specific times, returns { calendarIntervals: CalendarInterval[] }.\n *\n * Throws if the expression would produce more than 50 calendar intervals.\n */\nexport function cronToCalendarIntervals(cronExpr: string): {\n\tcalendarIntervals?: CalendarInterval[];\n\tstartInterval?: number;\n} {\n\tconst interval = CronExpressionParser.parse(cronExpr);\n\tconst fields = interval.fields;\n\n\tconst minutes = [...fields.minute.values].map(Number);\n\tconst hours = [...fields.hour.values].map(Number);\n\tconst daysOfMonth = [...fields.dayOfMonth.values].map(Number);\n\tconst months = [...fields.month.values].map(Number);\n\tconst daysOfWeek = [...fields.dayOfWeek.values].map(Number);\n\n\tconst isAllHours = hours.length === 24;\n\tconst isAllDays = daysOfMonth.length === 31;\n\tconst isAllMonths = months.length === 12;\n\t// cron-parser returns 0-7 for dayOfWeek (8 values since 0 and 7 both mean Sunday)\n\tconst isAllDow = daysOfWeek.length === 8;\n\n\t// Detect \"every N minutes\" pattern: hours/days/months/dow all wildcard, minutes have even spacing\n\tif (isAllHours && isAllDays && isAllMonths && isAllDow && minutes.length > 1) {\n\t\tconst step = minutes[1] - minutes[0];\n\t\tconst isEvenStep = minutes.every((m, i) => i === 0 || m - minutes[i - 1] === step);\n\t\tif (isEvenStep) {\n\t\t\treturn { startInterval: step * 60 };\n\t\t}\n\t}\n\n\t// Build calendar interval combinations\n\tconst intervals: CalendarInterval[] = [];\n\tconst effectiveHours = isAllHours ? [undefined] : hours;\n\t// Filter out duplicate Sunday (7) -- launchd uses 0 for Sunday\n\tconst effectiveDow = isAllDow ? [undefined] : daysOfWeek.filter((d) => d <= 6);\n\tconst effectiveDom = isAllDays ? [undefined] : daysOfMonth;\n\n\tfor (const minute of minutes) {\n\t\tfor (const hour of effectiveHours) {\n\t\t\tfor (const dow of effectiveDow) {\n\t\t\t\tfor (const dom of effectiveDom) {\n\t\t\t\t\tconst entry: CalendarInterval = { Minute: minute };\n\t\t\t\t\tif (hour !== undefined) entry.Hour = hour;\n\t\t\t\t\tif (dow !== undefined) entry.Weekday = dow;\n\t\t\t\t\tif (dom !== undefined) entry.Day = dom;\n\t\t\t\t\tintervals.push(entry);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (intervals.length > 50) {\n\t\tthrow new Error(\n\t\t\t`Cron expression \"${cronExpr}\" would produce ${intervals.length} calendar intervals. ` +\n\t\t\t\t\"launchd StartCalendarInterval is not efficient for complex schedules. \" +\n\t\t\t\t\"Simplify the schedule or use a simpler pattern.\",\n\t\t);\n\t}\n\n\treturn { calendarIntervals: intervals };\n}\n\n/**\n * LaunchdScheduler implements the Scheduler interface for macOS.\n * Uses plist files in ~/Library/LaunchAgents/ and modern launchctl bootstrap/bootout.\n */\nexport class LaunchdScheduler implements Scheduler {\n\tasync register(job: JobConfig, env?: Record<string, string>): Promise<void> {\n\t\tconst registered = await this.isRegistered(job.id);\n\t\tif (registered) {\n\t\t\tthrow new SchedulerError(\"launchd\", `Job \"${job.id}\" is already registered`);\n\t\t}\n\n\t\tconst scheduling = cronToCalendarIntervals(job.schedule.cron);\n\t\tconst runnerPath = getRunnerPath();\n\t\tconst logPath = `${paths.jobLogs(job.id)}/launchd.log`;\n\t\tconst plistPath = paths.plistPath(job.id);\n\n\t\tconst obj: Record<string, unknown> = {\n\t\t\tLabel: getLabel(job.id),\n\t\t\tProgramArguments: [process.execPath, runnerPath, \"--job-id\", job.id],\n\t\t\tStandardOutPath: logPath,\n\t\t\tStandardErrorPath: logPath,\n\t\t\tEnvironmentVariables: {\n\t\t\t\tPATH: env?.PATH ?? process.env.PATH ?? \"\",\n\t\t\t\tHOME: env?.HOME ?? homedir(),\n\t\t\t\t...(env\n\t\t\t\t\t? Object.fromEntries(Object.entries(env).filter(([k]) => k !== \"PATH\" && k !== \"HOME\"))\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\tRunAtLoad: false,\n\t\t\tKeepAlive: false,\n\t\t};\n\n\t\tif (scheduling.startInterval !== undefined) {\n\t\t\tobj.StartInterval = scheduling.startInterval;\n\t\t} else if (scheduling.calendarIntervals) {\n\t\t\tobj.StartCalendarInterval =\n\t\t\t\tscheduling.calendarIntervals.length === 1\n\t\t\t\t\t? scheduling.calendarIntervals[0]\n\t\t\t\t\t: scheduling.calendarIntervals;\n\t\t}\n\n\t\tconst xml = plist.build(obj as unknown as plist.PlistValue);\n\t\tawait writeFile(plistPath, xml, \"utf-8\");\n\n\t\tconst uid = getUid();\n\t\tawait execCommand(\"launchctl\", [\"bootstrap\", `gui/${uid}`, plistPath]);\n\t}\n\n\tasync unregister(jobId: string): Promise<void> {\n\t\tconst uid = getUid();\n\t\tconst label = getLabel(jobId);\n\t\tconst plistPath = paths.plistPath(jobId);\n\n\t\ttry {\n\t\t\tawait execCommand(\"launchctl\", [\"bootout\", `gui/${uid}/${label}`]);\n\t\t} catch {\n\t\t\t// Service may already be unloaded -- continue to delete plist\n\t\t}\n\n\t\ttry {\n\t\t\tawait unlink(plistPath);\n\t\t} catch {\n\t\t\t// Plist may already be deleted\n\t\t}\n\t}\n\n\tasync isRegistered(jobId: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait access(paths.plistPath(jobId));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync list(): Promise<RegisteredJob[]> {\n\t\tconst jobs: RegisteredJob[] = [];\n\t\ttry {\n\t\t\tconst files = await readdir(paths.plistDir);\n\t\t\tconst plistFiles = files.filter((f) => f.startsWith(LABEL_PREFIX) && f.endsWith(\".plist\"));\n\n\t\t\tfor (const file of plistFiles) {\n\t\t\t\ttry {\n\t\t\t\t\tconst filePath = join(paths.plistDir, file);\n\t\t\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\t\t\tconst parsed = plist.parse(content) as Record<string, unknown>;\n\t\t\t\t\tconst label = parsed.Label as string;\n\t\t\t\t\tconst jobId = label.replace(LABEL_PREFIX, \"\");\n\t\t\t\t\tconst progArgs = (parsed.ProgramArguments as string[]) ?? [];\n\n\t\t\t\t\tlet schedule = \"\";\n\t\t\t\t\tif (parsed.StartInterval) {\n\t\t\t\t\t\tschedule = `every ${parsed.StartInterval}s`;\n\t\t\t\t\t} else if (parsed.StartCalendarInterval) {\n\t\t\t\t\t\tschedule = \"calendar\";\n\t\t\t\t\t}\n\n\t\t\t\t\tjobs.push({\n\t\t\t\t\t\tjobId,\n\t\t\t\t\t\tschedule,\n\t\t\t\t\t\tcommand: progArgs.join(\" \"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed plists\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Directory may not exist\n\t\t}\n\t\treturn jobs;\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,12 @@
1
+ import {
2
+ LaunchdScheduler,
3
+ cronToCalendarIntervals
4
+ } from "./chunk-QQTIJN3S.js";
5
+ import "./chunk-3NEANSUS.js";
6
+ import "./chunk-H2MUDYMW.js";
7
+ import "./chunk-YMO45Z6G.js";
8
+ export {
9
+ LaunchdScheduler,
10
+ cronToCalendarIntervals
11
+ };
12
+ //# sourceMappingURL=launchd-LZGDP7BM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,15 @@
1
+ import {
2
+ listCommand
3
+ } from "./chunk-HF7PGQI3.js";
4
+ import "./chunk-SMZYA6CY.js";
5
+ import "./chunk-ORBF5IW3.js";
6
+ import "./chunk-4I5UIASZ.js";
7
+ import "./chunk-D4MBOIYQ.js";
8
+ import "./chunk-24PS2XSV.js";
9
+ import "./chunk-E3XVLTT4.js";
10
+ import "./chunk-H2MUDYMW.js";
11
+ import "./chunk-YMO45Z6G.js";
12
+ export {
13
+ listCommand
14
+ };
15
+ //# sourceMappingURL=list-OIGERGYJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,73 @@
1
+ import {
2
+ formatRelativeTime,
3
+ formatTable
4
+ } from "./chunk-A6XWZPLY.js";
5
+ import {
6
+ listRunLogs
7
+ } from "./chunk-GLW7T4QE.js";
8
+ import "./chunk-4I5UIASZ.js";
9
+ import {
10
+ describeSchedule,
11
+ getNextRuns
12
+ } from "./chunk-TORYFKPK.js";
13
+ import {
14
+ listJobs
15
+ } from "./chunk-BY5YEOVG.js";
16
+ import "./chunk-2D5E23XA.js";
17
+ import "./chunk-E3XVLTT4.js";
18
+ import "./chunk-H2MUDYMW.js";
19
+ import "./chunk-AWLSYOVF.js";
20
+
21
+ // src/cli/commands/list.ts
22
+ function truncatePath(fullPath, segments = 2) {
23
+ const parts = fullPath.split("/").filter(Boolean);
24
+ if (parts.length <= segments) {
25
+ return fullPath;
26
+ }
27
+ return parts.slice(-segments).join("/");
28
+ }
29
+ async function listCommand(args) {
30
+ const jobs = await listJobs();
31
+ if (jobs.length === 0) {
32
+ if (args.json) {
33
+ console.log("[]");
34
+ } else {
35
+ console.log("No jobs configured.");
36
+ }
37
+ return;
38
+ }
39
+ const rows = [];
40
+ for (const job of jobs) {
41
+ const status = job.enabled ? "active" : "paused";
42
+ const schedule = describeSchedule(job.schedule.cron);
43
+ const logs = await listRunLogs(job.id);
44
+ const lastRun = logs.length > 0 ? formatRelativeTime(new Date(logs[0].startedAt)) : "never";
45
+ let nextRun;
46
+ if (!job.enabled) {
47
+ nextRun = "--";
48
+ } else {
49
+ const nextRuns = getNextRuns(job.schedule.cron, job.schedule.timezone, 1);
50
+ nextRun = nextRuns.length > 0 ? formatRelativeTime(nextRuns[0]) : "--";
51
+ }
52
+ rows.push([job.id, job.name, status, truncatePath(job.repo.path), schedule, lastRun, nextRun]);
53
+ }
54
+ if (args.json) {
55
+ const jsonOutput = jobs.map((job, i) => ({
56
+ id: job.id,
57
+ name: job.name,
58
+ status: job.enabled ? "active" : "paused",
59
+ repo: job.repo.path,
60
+ schedule: rows[i][4],
61
+ lastRun: rows[i][5],
62
+ nextRun: rows[i][6]
63
+ }));
64
+ console.log(JSON.stringify(jsonOutput, null, 2));
65
+ return;
66
+ }
67
+ const headers = ["ID", "Name", "Status", "Repo", "Schedule", "Last Run", "Next Run"];
68
+ console.log(formatTable(headers, rows));
69
+ }
70
+ export {
71
+ listCommand
72
+ };
73
+ //# sourceMappingURL=list-T35RSQVU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/list.ts"],"sourcesContent":["import { listJobs } from \"../../core/job-manager.js\";\nimport { describeSchedule, getNextRuns } from \"../../core/schedule.js\";\nimport { listRunLogs } from \"../../runner/logger.js\";\nimport { formatRelativeTime, formatTable } from \"../format.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Truncate a path to its last N segments for compact display.\n */\nfunction truncatePath(fullPath: string, segments = 2): string {\n\tconst parts = fullPath.split(\"/\").filter(Boolean);\n\tif (parts.length <= segments) {\n\t\treturn fullPath;\n\t}\n\treturn parts.slice(-segments).join(\"/\");\n}\n\n/**\n * List all configured jobs with enriched status info:\n * ID, Name, Status, Repo, Schedule, Last Run, Next Run.\n *\n * Satisfies JOB-01 (list with status/schedule/last run/next run)\n * and JOB-05 (multiple jobs visible as separate entries).\n */\nexport async function listCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobs = await listJobs();\n\n\tif (jobs.length === 0) {\n\t\tif (args.json) {\n\t\t\tconsole.log(\"[]\");\n\t\t} else {\n\t\t\tconsole.log(\"No jobs configured.\");\n\t\t}\n\t\treturn;\n\t}\n\n\tconst rows: string[][] = [];\n\n\tfor (const job of jobs) {\n\t\tconst status = job.enabled ? \"active\" : \"paused\";\n\t\tconst schedule = describeSchedule(job.schedule.cron);\n\n\t\t// Last run\n\t\tconst logs = await listRunLogs(job.id);\n\t\tconst lastRun = logs.length > 0 ? formatRelativeTime(new Date(logs[0].startedAt)) : \"never\";\n\n\t\t// Next run\n\t\tlet nextRun: string;\n\t\tif (!job.enabled) {\n\t\t\tnextRun = \"--\";\n\t\t} else {\n\t\t\tconst nextRuns = getNextRuns(job.schedule.cron, job.schedule.timezone, 1);\n\t\t\tnextRun = nextRuns.length > 0 ? formatRelativeTime(nextRuns[0]) : \"--\";\n\t\t}\n\n\t\trows.push([job.id, job.name, status, truncatePath(job.repo.path), schedule, lastRun, nextRun]);\n\t}\n\n\tif (args.json) {\n\t\tconst jsonOutput = jobs.map((job, i) => ({\n\t\t\tid: job.id,\n\t\t\tname: job.name,\n\t\t\tstatus: job.enabled ? \"active\" : \"paused\",\n\t\t\trepo: job.repo.path,\n\t\t\tschedule: rows[i][4],\n\t\t\tlastRun: rows[i][5],\n\t\t\tnextRun: rows[i][6],\n\t\t}));\n\t\tconsole.log(JSON.stringify(jsonOutput, null, 2));\n\t\treturn;\n\t}\n\n\tconst headers = [\"ID\", \"Name\", \"Status\", \"Repo\", \"Schedule\", \"Last Run\", \"Next Run\"];\n\tconsole.log(formatTable(headers, rows));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASA,SAAS,aAAa,UAAkB,WAAW,GAAW;AAC7D,QAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAChD,MAAI,MAAM,UAAU,UAAU;AAC7B,WAAO;AAAA,EACR;AACA,SAAO,MAAM,MAAM,CAAC,QAAQ,EAAE,KAAK,GAAG;AACvC;AASA,eAAsB,YAAY,MAA4C;AAC7E,QAAM,OAAO,MAAM,SAAS;AAE5B,MAAI,KAAK,WAAW,GAAG;AACtB,QAAI,KAAK,MAAM;AACd,cAAQ,IAAI,IAAI;AAAA,IACjB,OAAO;AACN,cAAQ,IAAI,qBAAqB;AAAA,IAClC;AACA;AAAA,EACD;AAEA,QAAM,OAAmB,CAAC;AAE1B,aAAW,OAAO,MAAM;AACvB,UAAM,SAAS,IAAI,UAAU,WAAW;AACxC,UAAM,WAAW,iBAAiB,IAAI,SAAS,IAAI;AAGnD,UAAM,OAAO,MAAM,YAAY,IAAI,EAAE;AACrC,UAAM,UAAU,KAAK,SAAS,IAAI,mBAAmB,IAAI,KAAK,KAAK,CAAC,EAAE,SAAS,CAAC,IAAI;AAGpF,QAAI;AACJ,QAAI,CAAC,IAAI,SAAS;AACjB,gBAAU;AAAA,IACX,OAAO;AACN,YAAM,WAAW,YAAY,IAAI,SAAS,MAAM,IAAI,SAAS,UAAU,CAAC;AACxE,gBAAU,SAAS,SAAS,IAAI,mBAAmB,SAAS,CAAC,CAAC,IAAI;AAAA,IACnE;AAEA,SAAK,KAAK,CAAC,IAAI,IAAI,IAAI,MAAM,QAAQ,aAAa,IAAI,KAAK,IAAI,GAAG,UAAU,SAAS,OAAO,CAAC;AAAA,EAC9F;AAEA,MAAI,KAAK,MAAM;AACd,UAAM,aAAa,KAAK,IAAI,CAAC,KAAK,OAAO;AAAA,MACxC,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI,UAAU,WAAW;AAAA,MACjC,MAAM,IAAI,KAAK;AAAA,MACf,UAAU,KAAK,CAAC,EAAE,CAAC;AAAA,MACnB,SAAS,KAAK,CAAC,EAAE,CAAC;AAAA,MAClB,SAAS,KAAK,CAAC,EAAE,CAAC;AAAA,IACnB,EAAE;AACF,YAAQ,IAAI,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAC/C;AAAA,EACD;AAEA,QAAM,UAAU,CAAC,MAAM,QAAQ,UAAU,QAAQ,YAAY,YAAY,UAAU;AACnF,UAAQ,IAAI,YAAY,SAAS,IAAI,CAAC;AACvC;","names":[]}
@@ -0,0 +1,12 @@
1
+ import {
2
+ logsCommand
3
+ } from "./chunk-5LGOK52J.js";
4
+ import "./chunk-SMZYA6CY.js";
5
+ import "./chunk-ORBF5IW3.js";
6
+ import "./chunk-4I5UIASZ.js";
7
+ import "./chunk-E3XVLTT4.js";
8
+ import "./chunk-H2MUDYMW.js";
9
+ export {
10
+ logsCommand
11
+ };
12
+ //# sourceMappingURL=logs-D5FNSCXE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,40 @@
1
+ import {
2
+ formatDuration,
3
+ formatRelativeTime,
4
+ formatTable
5
+ } from "./chunk-A6XWZPLY.js";
6
+ import {
7
+ listRunLogs
8
+ } from "./chunk-GLW7T4QE.js";
9
+ import "./chunk-4I5UIASZ.js";
10
+ import "./chunk-E3XVLTT4.js";
11
+ import "./chunk-H2MUDYMW.js";
12
+
13
+ // src/cli/commands/logs.ts
14
+ async function logsCommand(args) {
15
+ const jobId = args.jobId;
16
+ if (!jobId) {
17
+ console.error("Usage: claude-auto logs <job-id> [--limit N]");
18
+ return;
19
+ }
20
+ const limit = typeof args.limit === "number" ? args.limit : 10;
21
+ const logs = await listRunLogs(jobId);
22
+ const sliced = logs.slice(0, limit);
23
+ if (sliced.length === 0) {
24
+ console.log(`No runs found for job ${jobId}.`);
25
+ return;
26
+ }
27
+ const rows = sliced.map((entry) => {
28
+ const started = formatRelativeTime(new Date(entry.startedAt));
29
+ const duration = formatDuration(entry.durationMs);
30
+ const prUrl = entry.prUrl || "--";
31
+ const error = entry.error ? entry.error.length > 50 ? `${entry.error.slice(0, 47)}...` : entry.error : "--";
32
+ return [entry.runId, entry.status, started, duration, prUrl, error];
33
+ });
34
+ const headers = ["Run ID", "Status", "Started", "Duration", "PR URL", "Error"];
35
+ console.log(formatTable(headers, rows));
36
+ }
37
+ export {
38
+ logsCommand
39
+ };
40
+ //# sourceMappingURL=logs-YVSFXBSB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/logs.ts"],"sourcesContent":["import { listRunLogs } from \"../../runner/logger.js\";\nimport { formatDuration, formatRelativeTime, formatTable } from \"../format.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Show recent run history for a specific job.\n * Displays a table of: Run ID, Status, Started, Duration, PR URL, Error.\n */\nexport async function logsCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto logs <job-id> [--limit N]\");\n\t\treturn;\n\t}\n\n\tconst limit = typeof args.limit === \"number\" ? args.limit : 10;\n\tconst logs = await listRunLogs(jobId);\n\tconst sliced = logs.slice(0, limit);\n\n\tif (sliced.length === 0) {\n\t\tconsole.log(`No runs found for job ${jobId}.`);\n\t\treturn;\n\t}\n\n\tconst rows: string[][] = sliced.map((entry) => {\n\t\tconst started = formatRelativeTime(new Date(entry.startedAt));\n\t\tconst duration = formatDuration(entry.durationMs);\n\t\tconst prUrl = entry.prUrl || \"--\";\n\t\tconst error = entry.error\n\t\t\t? entry.error.length > 50\n\t\t\t\t? `${entry.error.slice(0, 47)}...`\n\t\t\t\t: entry.error\n\t\t\t: \"--\";\n\t\treturn [entry.runId, entry.status, started, duration, prUrl, error];\n\t});\n\n\tconst headers = [\"Run ID\", \"Status\", \"Started\", \"Duration\", \"PR URL\", \"Error\"];\n\tconsole.log(formatTable(headers, rows));\n}\n"],"mappings":";;;;;;;;;;;;;AAQA,eAAsB,YAAY,MAA4C;AAC7E,QAAM,QAAQ,KAAK;AAEnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,8CAA8C;AAC5D;AAAA,EACD;AAEA,QAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,QAAM,OAAO,MAAM,YAAY,KAAK;AACpC,QAAM,SAAS,KAAK,MAAM,GAAG,KAAK;AAElC,MAAI,OAAO,WAAW,GAAG;AACxB,YAAQ,IAAI,yBAAyB,KAAK,GAAG;AAC7C;AAAA,EACD;AAEA,QAAM,OAAmB,OAAO,IAAI,CAAC,UAAU;AAC9C,UAAM,UAAU,mBAAmB,IAAI,KAAK,MAAM,SAAS,CAAC;AAC5D,UAAM,WAAW,eAAe,MAAM,UAAU;AAChD,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,QAAQ,MAAM,QACjB,MAAM,MAAM,SAAS,KACpB,GAAG,MAAM,MAAM,MAAM,GAAG,EAAE,CAAC,QAC3B,MAAM,QACP;AACH,WAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,SAAS,UAAU,OAAO,KAAK;AAAA,EACnE,CAAC;AAED,QAAM,UAAU,CAAC,UAAU,UAAU,WAAW,YAAY,UAAU,OAAO;AAC7E,UAAQ,IAAI,YAAY,SAAS,IAAI,CAAC;AACvC;","names":[]}
@@ -0,0 +1,12 @@
1
+ import {
2
+ pauseCommand
3
+ } from "./chunk-SZRIZBWI.js";
4
+ import "./chunk-QLRCFKLU.js";
5
+ import "./chunk-24PS2XSV.js";
6
+ import "./chunk-E3XVLTT4.js";
7
+ import "./chunk-H2MUDYMW.js";
8
+ import "./chunk-YMO45Z6G.js";
9
+ export {
10
+ pauseCommand
11
+ };
12
+ //# sourceMappingURL=pause-2YOLFMAR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,45 @@
1
+ import {
2
+ createScheduler
3
+ } from "./chunk-S6W4SURF.js";
4
+ import {
5
+ readJob,
6
+ updateJob
7
+ } from "./chunk-NB46PEG2.js";
8
+ import "./chunk-H2MUDYMW.js";
9
+ import "./chunk-6RYMWH5M.js";
10
+
11
+ // src/cli/commands/pause.ts
12
+ async function pauseCommand(args) {
13
+ const jobId = args.jobId;
14
+ if (!jobId) {
15
+ console.error("Usage: claude-auto pause <job-id>");
16
+ throw new Error("Missing required argument: job-id");
17
+ }
18
+ let config;
19
+ try {
20
+ config = await readJob(jobId);
21
+ } catch (err) {
22
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
23
+ console.error(`Job ${jobId} not found.`);
24
+ throw err;
25
+ }
26
+ throw err;
27
+ }
28
+ if (!config.enabled) {
29
+ console.log(`Job ${jobId} is already paused.`);
30
+ return;
31
+ }
32
+ await updateJob(jobId, { enabled: false });
33
+ try {
34
+ const scheduler = await createScheduler();
35
+ await scheduler.unregister(jobId);
36
+ } catch {
37
+ }
38
+ console.log(
39
+ `Job ${jobId} paused. Configuration preserved -- resume with: claude-auto resume ${jobId}`
40
+ );
41
+ }
42
+ export {
43
+ pauseCommand
44
+ };
45
+ //# sourceMappingURL=pause-JB42JGTB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/pause.ts"],"sourcesContent":["import { readJob, updateJob } from \"../../core/job-manager.js\";\nimport type { JobConfig } from \"../../core/types.js\";\nimport { createScheduler } from \"../../platform/scheduler.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Pause a running job: sets enabled=false and unregisters from scheduler.\n * Idempotent: pausing an already-paused job prints a message and exits cleanly.\n */\nexport async function pauseCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto pause <job-id>\");\n\t\tthrow new Error(\"Missing required argument: job-id\");\n\t}\n\n\tlet config: JobConfig;\n\ttry {\n\t\tconfig = await readJob(jobId);\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tconsole.error(`Job ${jobId} not found.`);\n\t\t\tthrow err;\n\t\t}\n\t\tthrow err;\n\t}\n\n\tif (!config.enabled) {\n\t\tconsole.log(`Job ${jobId} is already paused.`);\n\t\treturn;\n\t}\n\n\tawait updateJob(jobId, { enabled: false });\n\n\t// Best-effort unregister from scheduler\n\ttry {\n\t\tconst scheduler = await createScheduler();\n\t\tawait scheduler.unregister(jobId);\n\t} catch {\n\t\t// Scheduler entry may already be missing -- best-effort\n\t}\n\n\tconsole.log(\n\t\t`Job ${jobId} paused. Configuration preserved -- resume with: claude-auto resume ${jobId}`,\n\t);\n}\n"],"mappings":";;;;;;;;;;;AASA,eAAsB,aAAa,MAA4C;AAC9E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,mCAAmC;AACjD,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,QAAQ,KAAK;AAAA,EAC7B,SAAS,KAAc;AACtB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC9F,cAAQ,MAAM,OAAO,KAAK,aAAa;AACvC,YAAM;AAAA,IACP;AACA,UAAM;AAAA,EACP;AAEA,MAAI,CAAC,OAAO,SAAS;AACpB,YAAQ,IAAI,OAAO,KAAK,qBAAqB;AAC7C;AAAA,EACD;AAEA,QAAM,UAAU,OAAO,EAAE,SAAS,MAAM,CAAC;AAGzC,MAAI;AACH,UAAM,YAAY,MAAM,gBAAgB;AACxC,UAAM,UAAU,WAAW,KAAK;AAAA,EACjC,QAAQ;AAAA,EAER;AAEA,UAAQ;AAAA,IACP,OAAO,KAAK,uEAAuE,KAAK;AAAA,EACzF;AACD;","names":[]}
@@ -0,0 +1,47 @@
1
+ import {
2
+ createScheduler
3
+ } from "./chunk-DVZC42TL.js";
4
+ import {
5
+ readJob,
6
+ updateJob
7
+ } from "./chunk-BY5YEOVG.js";
8
+ import "./chunk-2D5E23XA.js";
9
+ import "./chunk-E3XVLTT4.js";
10
+ import "./chunk-H2MUDYMW.js";
11
+ import "./chunk-AWLSYOVF.js";
12
+
13
+ // src/cli/commands/pause.ts
14
+ async function pauseCommand(args) {
15
+ const jobId = args.jobId;
16
+ if (!jobId) {
17
+ console.error("Usage: claude-auto pause <job-id>");
18
+ throw new Error("Missing required argument: job-id");
19
+ }
20
+ let config;
21
+ try {
22
+ config = await readJob(jobId);
23
+ } catch (err) {
24
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
25
+ console.error(`Job ${jobId} not found.`);
26
+ throw err;
27
+ }
28
+ throw err;
29
+ }
30
+ if (!config.enabled) {
31
+ console.log(`Job ${jobId} is already paused.`);
32
+ return;
33
+ }
34
+ await updateJob(jobId, { enabled: false });
35
+ try {
36
+ const scheduler = await createScheduler();
37
+ await scheduler.unregister(jobId);
38
+ } catch {
39
+ }
40
+ console.log(
41
+ `Job ${jobId} paused. Configuration preserved -- resume with: claude-auto resume ${jobId}`
42
+ );
43
+ }
44
+ export {
45
+ pauseCommand
46
+ };
47
+ //# sourceMappingURL=pause-OJNUYBCJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/pause.ts"],"sourcesContent":["import { readJob, updateJob } from \"../../core/job-manager.js\";\nimport type { JobConfig } from \"../../core/types.js\";\nimport { createScheduler } from \"../../platform/scheduler.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Pause a running job: sets enabled=false and unregisters from scheduler.\n * Idempotent: pausing an already-paused job prints a message and exits cleanly.\n */\nexport async function pauseCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto pause <job-id>\");\n\t\tthrow new Error(\"Missing required argument: job-id\");\n\t}\n\n\tlet config: JobConfig;\n\ttry {\n\t\tconfig = await readJob(jobId);\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tconsole.error(`Job ${jobId} not found.`);\n\t\t\tthrow err;\n\t\t}\n\t\tthrow err;\n\t}\n\n\tif (!config.enabled) {\n\t\tconsole.log(`Job ${jobId} is already paused.`);\n\t\treturn;\n\t}\n\n\tawait updateJob(jobId, { enabled: false });\n\n\t// Best-effort unregister from scheduler\n\ttry {\n\t\tconst scheduler = await createScheduler();\n\t\tawait scheduler.unregister(jobId);\n\t} catch {\n\t\t// Scheduler entry may already be missing -- best-effort\n\t}\n\n\tconsole.log(\n\t\t`Job ${jobId} paused. Configuration preserved -- resume with: claude-auto resume ${jobId}`,\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;AASA,eAAsB,aAAa,MAA4C;AAC9E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,mCAAmC;AACjD,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,QAAQ,KAAK;AAAA,EAC7B,SAAS,KAAc;AACtB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC9F,cAAQ,MAAM,OAAO,KAAK,aAAa;AACvC,YAAM;AAAA,IACP;AACA,UAAM;AAAA,EACP;AAEA,MAAI,CAAC,OAAO,SAAS;AACpB,YAAQ,IAAI,OAAO,KAAK,qBAAqB;AAC7C;AAAA,EACD;AAEA,QAAM,UAAU,OAAO,EAAE,SAAS,MAAM,CAAC;AAGzC,MAAI;AACH,UAAM,YAAY,MAAM,gBAAgB;AACxC,UAAM,UAAU,WAAW,KAAK;AAAA,EACjC,QAAQ;AAAA,EAER;AAEA,UAAQ;AAAA,IACP,OAAO,KAAK,uEAAuE,KAAK;AAAA,EACzF;AACD;","names":[]}
@@ -0,0 +1,12 @@
1
+ import {
2
+ removeCommand
3
+ } from "./chunk-W2HBRERV.js";
4
+ import "./chunk-QLRCFKLU.js";
5
+ import "./chunk-24PS2XSV.js";
6
+ import "./chunk-E3XVLTT4.js";
7
+ import "./chunk-H2MUDYMW.js";
8
+ import "./chunk-YMO45Z6G.js";
9
+ export {
10
+ removeCommand
11
+ };
12
+ //# sourceMappingURL=remove-RXYKFYBI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,59 @@
1
+ import {
2
+ createScheduler
3
+ } from "./chunk-DVZC42TL.js";
4
+ import {
5
+ deleteJob,
6
+ readJob
7
+ } from "./chunk-BY5YEOVG.js";
8
+ import "./chunk-2D5E23XA.js";
9
+ import "./chunk-E3XVLTT4.js";
10
+ import {
11
+ paths
12
+ } from "./chunk-H2MUDYMW.js";
13
+ import "./chunk-AWLSYOVF.js";
14
+
15
+ // src/cli/commands/remove.ts
16
+ import { cp, mkdir } from "fs/promises";
17
+ import { join } from "path";
18
+ async function removeCommand(args) {
19
+ const jobId = args.jobId;
20
+ if (!jobId) {
21
+ console.error("Usage: claude-auto remove <job-id> [--keep-logs]");
22
+ throw new Error("Missing required argument: job-id");
23
+ }
24
+ try {
25
+ await readJob(jobId);
26
+ } catch (err) {
27
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
28
+ console.error(`Job ${jobId} not found.`);
29
+ throw err;
30
+ }
31
+ throw err;
32
+ }
33
+ try {
34
+ const scheduler = await createScheduler();
35
+ await scheduler.unregister(jobId);
36
+ } catch {
37
+ }
38
+ let archivePath;
39
+ if (args.keepLogs) {
40
+ const logsDir = paths.jobLogs(jobId);
41
+ archivePath = join(paths.base, "archived-logs", jobId);
42
+ try {
43
+ await mkdir(archivePath, { recursive: true });
44
+ await cp(logsDir, archivePath, { recursive: true });
45
+ } catch {
46
+ archivePath = void 0;
47
+ }
48
+ }
49
+ await deleteJob(jobId);
50
+ if (archivePath) {
51
+ console.log(`Job ${jobId} removed. Run logs archived to ${archivePath}.`);
52
+ } else {
53
+ console.log(`Job ${jobId} removed.`);
54
+ }
55
+ }
56
+ export {
57
+ removeCommand
58
+ };
59
+ //# sourceMappingURL=remove-UASXZCOR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/remove.ts"],"sourcesContent":["import { cp, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { deleteJob, readJob } from \"../../core/job-manager.js\";\nimport { createScheduler } from \"../../platform/scheduler.js\";\nimport { paths } from \"../../util/paths.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Remove a job: unregisters from scheduler and deletes job directory.\n * With --keep-logs: archives run logs before deleting.\n */\nexport async function removeCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto remove <job-id> [--keep-logs]\");\n\t\tthrow new Error(\"Missing required argument: job-id\");\n\t}\n\n\ttry {\n\t\tawait readJob(jobId);\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tconsole.error(`Job ${jobId} not found.`);\n\t\t\tthrow err;\n\t\t}\n\t\tthrow err;\n\t}\n\n\t// Best-effort unregister from scheduler\n\ttry {\n\t\tconst scheduler = await createScheduler();\n\t\tawait scheduler.unregister(jobId);\n\t} catch {\n\t\t// Scheduler entry may already be missing -- best-effort\n\t}\n\n\tlet archivePath: string | undefined;\n\tif (args.keepLogs) {\n\t\t// Archive run logs before deleting\n\t\tconst logsDir = paths.jobLogs(jobId);\n\t\tarchivePath = join(paths.base, \"archived-logs\", jobId);\n\t\ttry {\n\t\t\tawait mkdir(archivePath, { recursive: true });\n\t\t\tawait cp(logsDir, archivePath, { recursive: true });\n\t\t} catch {\n\t\t\t// If logs dir doesn't exist, nothing to archive\n\t\t\tarchivePath = undefined;\n\t\t}\n\t}\n\n\tawait deleteJob(jobId);\n\n\tif (archivePath) {\n\t\tconsole.log(`Job ${jobId} removed. Run logs archived to ${archivePath}.`);\n\t} else {\n\t\tconsole.log(`Job ${jobId} removed.`);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,IAAI,aAAa;AAC1B,SAAS,YAAY;AAUrB,eAAsB,cAAc,MAA4C;AAC/E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,kDAAkD;AAChE,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AAEA,MAAI;AACH,UAAM,QAAQ,KAAK;AAAA,EACpB,SAAS,KAAc;AACtB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC9F,cAAQ,MAAM,OAAO,KAAK,aAAa;AACvC,YAAM;AAAA,IACP;AACA,UAAM;AAAA,EACP;AAGA,MAAI;AACH,UAAM,YAAY,MAAM,gBAAgB;AACxC,UAAM,UAAU,WAAW,KAAK;AAAA,EACjC,QAAQ;AAAA,EAER;AAEA,MAAI;AACJ,MAAI,KAAK,UAAU;AAElB,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,kBAAc,KAAK,MAAM,MAAM,iBAAiB,KAAK;AACrD,QAAI;AACH,YAAM,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,YAAM,GAAG,SAAS,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,IACnD,QAAQ;AAEP,oBAAc;AAAA,IACf;AAAA,EACD;AAEA,QAAM,UAAU,KAAK;AAErB,MAAI,aAAa;AAChB,YAAQ,IAAI,OAAO,KAAK,kCAAkC,WAAW,GAAG;AAAA,EACzE,OAAO;AACN,YAAQ,IAAI,OAAO,KAAK,WAAW;AAAA,EACpC;AACD;","names":[]}