@marlin-notes/scheduler-cli 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ var import_commander = require("commander");
29
29
  // src/summarizer.ts
30
30
  var import_fs = __toESM(require("fs"));
31
31
  var import_path = __toESM(require("path"));
32
+ var import_child_process = require("child_process");
32
33
  var import_gray_matter = __toESM(require("gray-matter"));
33
34
  var import_date_fns = require("date-fns");
34
35
 
@@ -71,7 +72,8 @@ async function summarize({
71
72
  tags,
72
73
  apiUrl,
73
74
  userId,
74
- baseDir
75
+ baseDir,
76
+ push
75
77
  }) {
76
78
  const now = /* @__PURE__ */ new Date();
77
79
  let startDate;
@@ -175,23 +177,48 @@ async function summarize({
175
177
  const summaryFilename = `summary-${frequency}-${now.toISOString().split("T")[0]}.md`;
176
178
  const summaryContent = `---
177
179
  date: ${now.getTime()}
178
- tags: ["#summary/${frequency}", "#auto"]
180
+ tags: ["summary", "${taskName}", "auto"]
179
181
  title: ${frequency} Summary - ${taskName}
182
+ frequency: ${frequency}
180
183
  ---
181
184
 
182
185
  ${result.summary}
183
186
  `;
184
187
  import_fs.default.writeFileSync(import_path.default.join(notesDir, summaryFilename), summaryContent);
185
188
  console.log(`[Scheduler] Summary saved to ${summaryFilename}`);
189
+ if (push) {
190
+ try {
191
+ console.log("[Scheduler] Committing and pushing changes...");
192
+ try {
193
+ (0, import_child_process.execSync)("git config user.name", { stdio: "ignore" });
194
+ } catch {
195
+ console.log("[Scheduler] Configuring git user...");
196
+ (0, import_child_process.execSync)('git config user.name "Marlin Scheduler"');
197
+ (0, import_child_process.execSync)('git config user.email "scheduler@marlin.app"');
198
+ }
199
+ const relativePath = import_path.default.relative(
200
+ process.cwd(),
201
+ import_path.default.join(notesDir, summaryFilename)
202
+ );
203
+ (0, import_child_process.execSync)(`git add "${relativePath}"`);
204
+ (0, import_child_process.execSync)(
205
+ `git commit -m "chore(scheduler): add ${frequency} summary for ${taskName}"`
206
+ );
207
+ (0, import_child_process.execSync)("git push");
208
+ console.log("[Scheduler] Successfully pushed to remote.");
209
+ } catch (error) {
210
+ console.error("[Scheduler] Failed to push changes:", error);
211
+ }
212
+ }
186
213
  }
187
214
 
188
215
  // src/index.ts
189
216
  var program = new import_commander.Command();
190
- program.name("marlin-scheduler").description("CLI for Marlin scheduled tasks").version("0.0.1");
217
+ program.name("marlin-scheduler").description("CLI for Marlin scheduled tasks").version("0.0.5");
191
218
  program.command("summarize").description("Run summarization task").option("--task-name <name>", "Name of the task").option("--frequency <freq>", "Frequency (weekly/monthly)").option("--tags <json>", "JSON string of tags").option("--api-url <url>", "Marlin API URL").option("--user-id <id>", "GitHub User ID").option(
192
219
  "--dir <path>",
193
220
  "Directory to search for notes (default: current dir)"
194
- ).action(async (options) => {
221
+ ).option("--push", "Commit and push the summary to the git repository").action(async (options) => {
195
222
  try {
196
223
  const taskName = options.taskName || process.env.INPUT_TASK_NAME || "Manual Task";
197
224
  const frequency = options.frequency || process.env.INPUT_FREQUENCY || "weekly";
@@ -199,6 +226,7 @@ program.command("summarize").description("Run summarization task").option("--tas
199
226
  const apiUrl = options.apiUrl || process.env.INPUT_API_URL || "https://marlinnotes.com/api/ai/summarize";
200
227
  const userId = options.userId || process.env.INPUT_USER_ID || process.env.GITHUB_ACTOR_ID;
201
228
  const baseDir = options.dir || process.env.INPUT_DIR;
229
+ const push = options.push || process.env.INPUT_PUSH === "true";
202
230
  if (!userId) {
203
231
  console.error(
204
232
  "Error: User ID is required. Pass via --user-id, set INPUT_USER_ID, or run in GitHub Actions (GITHUB_ACTOR_ID)."
@@ -217,7 +245,8 @@ program.command("summarize").description("Run summarization task").option("--tas
217
245
  tags,
218
246
  apiUrl,
219
247
  userId,
220
- baseDir
248
+ baseDir,
249
+ push
221
250
  });
222
251
  } catch (error) {
223
252
  console.error("Failed:", error);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/summarizer.ts","../src/oidc.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { summarize } from \"./summarizer\";\n\nconst program = new Command();\n\nprogram\n .name(\"marlin-scheduler\")\n .description(\"CLI for Marlin scheduled tasks\")\n .version(\"0.0.1\");\n\nprogram\n .command(\"summarize\")\n .description(\"Run summarization task\")\n .option(\"--task-name <name>\", \"Name of the task\")\n .option(\"--frequency <freq>\", \"Frequency (weekly/monthly)\")\n .option(\"--tags <json>\", \"JSON string of tags\")\n .option(\"--api-url <url>\", \"Marlin API URL\")\n .option(\"--user-id <id>\", \"GitHub User ID\")\n .option(\n \"--dir <path>\",\n \"Directory to search for notes (default: current dir)\"\n )\n .action(async (options) => {\n try {\n // Prioritize flags, then Env Vars (standard Action inputs often use INPUT_ prefix)\n const taskName =\n options.taskName || process.env.INPUT_TASK_NAME || \"Manual Task\";\n const frequency =\n options.frequency || process.env.INPUT_FREQUENCY || \"weekly\";\n const tagsJson = options.tags || process.env.INPUT_TAGS || \"[]\";\n // Default to production API if not specified\n const apiUrl =\n options.apiUrl ||\n process.env.INPUT_API_URL ||\n \"https://marlinnotes.com/api/ai/summarize\";\n\n // Resolve User ID: Flag -> Input -> GitHub Actor ID\n const userId =\n options.userId ||\n process.env.INPUT_USER_ID ||\n process.env.GITHUB_ACTOR_ID;\n const baseDir = options.dir || process.env.INPUT_DIR;\n\n if (!userId) {\n console.error(\n \"Error: User ID is required. Pass via --user-id, set INPUT_USER_ID, or run in GitHub Actions (GITHUB_ACTOR_ID).\"\n );\n process.exit(1);\n }\n\n let tags: string[] = [];\n try {\n tags = JSON.parse(tagsJson);\n } catch (e) {\n console.warn(\"Failed to parse tags JSON, assuming empty array:\", e);\n }\n\n await summarize({\n taskName,\n frequency: frequency as \"weekly\" | \"monthly\",\n tags,\n apiUrl,\n userId,\n baseDir,\n });\n } catch (error) {\n console.error(\"Failed:\", error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { subDays, subMonths, isBefore, isAfter } from \"date-fns\";\nimport { getGithubOidcToken } from \"./oidc\";\n\ninterface SummarizeOptions {\n taskName: string;\n frequency: \"weekly\" | \"monthly\";\n tags: string[];\n apiUrl: string;\n userId: string;\n baseDir?: string;\n}\n\nexport async function summarize({\n taskName,\n frequency,\n tags,\n apiUrl,\n userId,\n baseDir,\n}: SummarizeOptions) {\n // ... (existing date logic unchanged) ...\n const now = new Date();\n let startDate: Date;\n\n if (frequency === \"weekly\") {\n startDate = subDays(now, 7);\n } else if (frequency === \"monthly\") {\n startDate = subMonths(now, 1);\n } else {\n throw new Error(`Unknown frequency: ${frequency}`);\n }\n\n console.log(`[Scheduler] Running '${taskName}' (${frequency})`);\n console.log(`[Scheduler] Filtering from: ${startDate.toISOString()}`);\n console.log(`[Scheduler] Tags: ${tags.join(\", \") || \"(none)\"}`);\n\n const notesDir = baseDir\n ? path.resolve(process.cwd(), baseDir)\n : process.cwd();\n console.log(`[Scheduler] Searching in: ${notesDir}`);\n\n if (!fs.existsSync(notesDir)) {\n throw new Error(`Directory not found: ${notesDir}`);\n }\n\n // Use recursive search if available (Node 20+), otherwise shallow\n // We filter out node_modules and .git to avoid clutter\n const files = fs\n .readdirSync(notesDir, { recursive: true } as any)\n .map((f) => String(f)) // Ensure string\n .filter(\n (f) =>\n f.endsWith(\".md\") && !f.includes(\"node_modules\") && !f.includes(\".git\")\n );\n\n const matchedNotes = [];\n\n for (const file of files) {\n const filePath = path.join(notesDir, file);\n const content = fs.readFileSync(filePath, \"utf-8\");\n const { data, content: body } = matter(content);\n\n // 1. Date Check\n let noteDate: Date | null = null;\n\n // Check fields in priority: date -> updatedAt -> createdAt\n const dateFields = [data.date, data.updatedAt, data.createdAt];\n\n for (const field of dateFields) {\n if (!field) continue;\n\n if (field instanceof Date) {\n noteDate = field;\n } else {\n const d = new Date(field);\n if (!isNaN(d.getTime())) {\n noteDate = d;\n }\n }\n\n if (noteDate) break;\n }\n\n // Fallback to filename timestamp if no valid date found in frontmatter\n if (!noteDate) {\n const filenameTs = parseInt(file.replace(\".md\", \"\"));\n if (!isNaN(filenameTs) && filenameTs > 1000000000000) {\n noteDate = new Date(filenameTs);\n }\n }\n\n if (!noteDate || isNaN(noteDate.getTime())) continue;\n\n if (isBefore(noteDate, startDate) || isAfter(noteDate, now)) continue;\n\n // 2. Tag Check\n const noteTags: string[] = Array.isArray(data.tags) ? data.tags : [];\n const normalizedNoteTags = noteTags.map((t) =>\n String(t).replace(/^#/, \"\").toLowerCase()\n );\n const normalizedSearchTags = tags.map((t) =>\n t.replace(/^#/, \"\").toLowerCase()\n );\n\n const hasAllTags = normalizedSearchTags.every((t) =>\n normalizedNoteTags.includes(t)\n );\n\n if (hasAllTags || tags.length === 0) {\n matchedNotes.push({\n title: data.title || file.replace(\".md\", \"\"),\n content: body,\n date: noteDate.toISOString(),\n tags: normalizedNoteTags,\n });\n }\n }\n\n console.log(`[Scheduler] Found ${matchedNotes.length} matching notes.`);\n\n if (matchedNotes.length === 0) {\n console.log(\"No notes to summarize.\");\n return;\n }\n\n // 3. API Call\n console.log(`[Scheduler] Sending to API: ${apiUrl}`);\n\n // Try to get OIDC token\n const oidcToken = await getGithubOidcToken(\"marlin-api\");\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-Marlin-User-Id\": userId,\n };\n\n if (oidcToken) {\n console.log(\"[Scheduler] Authenticating with GitHub OIDC\");\n headers[\"Authorization\"] = `Bearer ${oidcToken}`;\n } else {\n // Fallback or Error\n // We allow running without OIDC if strictly testing, but warn loudly.\n // In production, the API will likely reject it.\n console.warn(\n \"[Scheduler] WARNING: No OIDC token found. Ensure 'id-token: write' permission is set in the workflow.\"\n );\n }\n\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n notes: matchedNotes,\n taskName,\n period: frequency,\n }),\n });\n\n if (!response.ok) {\n const errText = await response.text();\n throw new Error(`API Error ${response.status}: ${errText}`);\n }\n\n const result = (await response.json()) as { summary: string };\n\n // 4. Save Summary\n const summaryFilename = `summary-${frequency}-${now.toISOString().split(\"T\")[0]}.md`;\n\n const summaryContent = `---\ndate: ${now.getTime()}\ntags: [\"#summary/${frequency}\", \"#auto\"]\ntitle: ${frequency} Summary - ${taskName}\n---\n\n${result.summary}\n`;\n\n fs.writeFileSync(path.join(notesDir, summaryFilename), summaryContent);\n console.log(`[Scheduler] Summary saved to ${summaryFilename}`);\n}\n","export async function getGithubOidcToken(audience?: string): Promise<string | null> {\n const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;\n const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;\n\n if (!requestUrl || !requestToken) {\n // Not running in GitHub Actions or permissions not set\n return null;\n }\n\n try {\n const url = new URL(requestUrl);\n if (audience) {\n url.searchParams.append(\"audience\", audience);\n }\n\n const response = await fetch(url.toString(), {\n headers: {\n Authorization: `Bearer ${requestToken}`,\n Accept: \"application/json; api-version=2.0\",\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) {\n const text = await response.text();\n console.warn(`[OIDC] Failed to fetch token: ${response.status} ${text}`);\n return null;\n }\n\n const data = (await response.json()) as { value: string };\n return data.value;\n } catch (error) {\n console.warn(\"[OIDC] Error fetching token:\", error);\n return null;\n }\n}\n"],"mappings":"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","names":["path","fs","matter"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/summarizer.ts","../src/oidc.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { summarize } from \"./summarizer\";\n\nconst program = new Command();\n\nprogram\n .name(\"marlin-scheduler\")\n .description(\"CLI for Marlin scheduled tasks\")\n .version(\"0.0.5\");\n\nprogram\n .command(\"summarize\")\n .description(\"Run summarization task\")\n .option(\"--task-name <name>\", \"Name of the task\")\n .option(\"--frequency <freq>\", \"Frequency (weekly/monthly)\")\n .option(\"--tags <json>\", \"JSON string of tags\")\n .option(\"--api-url <url>\", \"Marlin API URL\")\n .option(\"--user-id <id>\", \"GitHub User ID\")\n .option(\n \"--dir <path>\",\n \"Directory to search for notes (default: current dir)\"\n )\n .option(\"--push\", \"Commit and push the summary to the git repository\")\n .action(async (options) => {\n try {\n // Prioritize flags, then Env Vars (standard Action inputs often use INPUT_ prefix)\n const taskName =\n options.taskName || process.env.INPUT_TASK_NAME || \"Manual Task\";\n const frequency =\n options.frequency || process.env.INPUT_FREQUENCY || \"weekly\";\n const tagsJson = options.tags || process.env.INPUT_TAGS || \"[]\";\n // Default to production API if not specified\n const apiUrl =\n options.apiUrl ||\n process.env.INPUT_API_URL ||\n \"https://marlinnotes.com/api/ai/summarize\";\n\n // Resolve User ID: Flag -> Input -> GitHub Actor ID\n const userId =\n options.userId ||\n process.env.INPUT_USER_ID ||\n process.env.GITHUB_ACTOR_ID;\n const baseDir = options.dir || process.env.INPUT_DIR;\n const push = options.push || process.env.INPUT_PUSH === \"true\";\n\n if (!userId) {\n console.error(\n \"Error: User ID is required. Pass via --user-id, set INPUT_USER_ID, or run in GitHub Actions (GITHUB_ACTOR_ID).\"\n );\n process.exit(1);\n }\n\n let tags: string[] = [];\n try {\n tags = JSON.parse(tagsJson);\n } catch (e) {\n console.warn(\"Failed to parse tags JSON, assuming empty array:\", e);\n }\n\n await summarize({\n taskName,\n frequency: frequency as \"weekly\" | \"monthly\",\n tags,\n apiUrl,\n userId,\n baseDir,\n push,\n });\n } catch (error) {\n console.error(\"Failed:\", error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\nimport matter from \"gray-matter\";\nimport { subDays, subMonths, isBefore, isAfter } from \"date-fns\";\nimport { getGithubOidcToken } from \"./oidc\";\n\ninterface SummarizeOptions {\n taskName: string;\n frequency: \"weekly\" | \"monthly\";\n tags: string[];\n apiUrl: string;\n userId: string;\n baseDir?: string;\n push?: boolean;\n}\n\nexport async function summarize({\n taskName,\n frequency,\n tags,\n apiUrl,\n userId,\n baseDir,\n push,\n}: SummarizeOptions) {\n // ... (existing date logic unchanged) ...\n const now = new Date();\n let startDate: Date;\n\n if (frequency === \"weekly\") {\n startDate = subDays(now, 7);\n } else if (frequency === \"monthly\") {\n startDate = subMonths(now, 1);\n } else {\n throw new Error(`Unknown frequency: ${frequency}`);\n }\n\n console.log(`[Scheduler] Running '${taskName}' (${frequency})`);\n console.log(`[Scheduler] Filtering from: ${startDate.toISOString()}`);\n console.log(`[Scheduler] Tags: ${tags.join(\", \") || \"(none)\"}`);\n\n const notesDir = baseDir\n ? path.resolve(process.cwd(), baseDir)\n : process.cwd();\n console.log(`[Scheduler] Searching in: ${notesDir}`);\n\n if (!fs.existsSync(notesDir)) {\n throw new Error(`Directory not found: ${notesDir}`);\n }\n\n // Use recursive search if available (Node 20+), otherwise shallow\n // We filter out node_modules and .git to avoid clutter\n const files = fs\n .readdirSync(notesDir, { recursive: true } as any)\n .map((f) => String(f)) // Ensure string\n .filter(\n (f) =>\n f.endsWith(\".md\") && !f.includes(\"node_modules\") && !f.includes(\".git\")\n );\n\n const matchedNotes = [];\n\n for (const file of files) {\n const filePath = path.join(notesDir, file);\n const content = fs.readFileSync(filePath, \"utf-8\");\n const { data, content: body } = matter(content);\n\n // 1. Date Check\n let noteDate: Date | null = null;\n\n // Check fields in priority: date -> updatedAt -> createdAt\n const dateFields = [data.date, data.updatedAt, data.createdAt];\n\n for (const field of dateFields) {\n if (!field) continue;\n\n if (field instanceof Date) {\n noteDate = field;\n } else {\n const d = new Date(field);\n if (!isNaN(d.getTime())) {\n noteDate = d;\n }\n }\n\n if (noteDate) break;\n }\n\n // Fallback to filename timestamp if no valid date found in frontmatter\n if (!noteDate) {\n const filenameTs = parseInt(file.replace(\".md\", \"\"));\n if (!isNaN(filenameTs) && filenameTs > 1000000000000) {\n noteDate = new Date(filenameTs);\n }\n }\n\n if (!noteDate || isNaN(noteDate.getTime())) continue;\n\n if (isBefore(noteDate, startDate) || isAfter(noteDate, now)) continue;\n\n // 2. Tag Check\n const noteTags: string[] = Array.isArray(data.tags) ? data.tags : [];\n const normalizedNoteTags = noteTags.map((t) =>\n String(t).replace(/^#/, \"\").toLowerCase()\n );\n const normalizedSearchTags = tags.map((t) =>\n t.replace(/^#/, \"\").toLowerCase()\n );\n\n const hasAllTags = normalizedSearchTags.every((t) =>\n normalizedNoteTags.includes(t)\n );\n\n if (hasAllTags || tags.length === 0) {\n matchedNotes.push({\n title: data.title || file.replace(\".md\", \"\"),\n content: body,\n date: noteDate.toISOString(),\n tags: normalizedNoteTags,\n });\n }\n }\n\n console.log(`[Scheduler] Found ${matchedNotes.length} matching notes.`);\n\n if (matchedNotes.length === 0) {\n console.log(\"No notes to summarize.\");\n return;\n }\n\n // 3. API Call\n console.log(`[Scheduler] Sending to API: ${apiUrl}`);\n\n // Try to get OIDC token\n const oidcToken = await getGithubOidcToken(\"marlin-api\");\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-Marlin-User-Id\": userId,\n };\n\n if (oidcToken) {\n console.log(\"[Scheduler] Authenticating with GitHub OIDC\");\n headers[\"Authorization\"] = `Bearer ${oidcToken}`;\n } else {\n // Fallback or Error\n // We allow running without OIDC if strictly testing, but warn loudly.\n // In production, the API will likely reject it.\n console.warn(\n \"[Scheduler] WARNING: No OIDC token found. Ensure 'id-token: write' permission is set in the workflow.\"\n );\n }\n\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n notes: matchedNotes,\n taskName,\n period: frequency,\n }),\n });\n\n if (!response.ok) {\n const errText = await response.text();\n throw new Error(`API Error ${response.status}: ${errText}`);\n }\n\n const result = (await response.json()) as { summary: string };\n\n // 4. Save Summary\n const summaryFilename = `summary-${frequency}-${now.toISOString().split(\"T\")[0]}.md`;\n\n const summaryContent = `---\ndate: ${now.getTime()}\ntags: [\"summary\", \"${taskName}\", \"auto\"]\ntitle: ${frequency} Summary - ${taskName}\nfrequency: ${frequency}\n---\n\n${result.summary}\n`;\n\n fs.writeFileSync(path.join(notesDir, summaryFilename), summaryContent);\n console.log(`[Scheduler] Summary saved to ${summaryFilename}`);\n\n if (push) {\n try {\n console.log(\"[Scheduler] Committing and pushing changes...\");\n try {\n execSync(\"git config user.name\", { stdio: \"ignore\" });\n } catch {\n console.log(\"[Scheduler] Configuring git user...\");\n execSync('git config user.name \"Marlin Scheduler\"');\n execSync('git config user.email \"scheduler@marlin.app\"');\n }\n\n const relativePath = path.relative(\n process.cwd(),\n path.join(notesDir, summaryFilename)\n );\n execSync(`git add \"${relativePath}\"`);\n execSync(\n `git commit -m \"chore(scheduler): add ${frequency} summary for ${taskName}\"`\n );\n execSync(\"git push\");\n console.log(\"[Scheduler] Successfully pushed to remote.\");\n } catch (error) {\n console.error(\"[Scheduler] Failed to push changes:\", error);\n }\n }\n}\n","export async function getGithubOidcToken(audience?: string): Promise<string | null> {\n const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;\n const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;\n\n if (!requestUrl || !requestToken) {\n // Not running in GitHub Actions or permissions not set\n return null;\n }\n\n try {\n const url = new URL(requestUrl);\n if (audience) {\n url.searchParams.append(\"audience\", audience);\n }\n\n const response = await fetch(url.toString(), {\n headers: {\n Authorization: `Bearer ${requestToken}`,\n Accept: \"application/json; api-version=2.0\",\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) {\n const text = await response.text();\n console.warn(`[OIDC] Failed to fetch token: ${response.status} ${text}`);\n return null;\n }\n\n const data = (await response.json()) as { value: string };\n return data.value;\n } catch (error) {\n console.warn(\"[OIDC] Error fetching token:\", error);\n return null;\n }\n}\n"],"mappings":"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","names":["path","fs","matter"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marlin-notes/scheduler-cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "CLI tool for Marlin scheduled tasks",
5
5
  "bin": {
6
6
  "marlin-scheduler": "./dist/index.js"
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ const program = new Command();
6
6
  program
7
7
  .name("marlin-scheduler")
8
8
  .description("CLI for Marlin scheduled tasks")
9
- .version("0.0.1");
9
+ .version("0.0.5");
10
10
 
11
11
  program
12
12
  .command("summarize")
@@ -20,6 +20,7 @@ program
20
20
  "--dir <path>",
21
21
  "Directory to search for notes (default: current dir)"
22
22
  )
23
+ .option("--push", "Commit and push the summary to the git repository")
23
24
  .action(async (options) => {
24
25
  try {
25
26
  // Prioritize flags, then Env Vars (standard Action inputs often use INPUT_ prefix)
@@ -40,6 +41,7 @@ program
40
41
  process.env.INPUT_USER_ID ||
41
42
  process.env.GITHUB_ACTOR_ID;
42
43
  const baseDir = options.dir || process.env.INPUT_DIR;
44
+ const push = options.push || process.env.INPUT_PUSH === "true";
43
45
 
44
46
  if (!userId) {
45
47
  console.error(
@@ -62,6 +64,7 @@ program
62
64
  apiUrl,
63
65
  userId,
64
66
  baseDir,
67
+ push,
65
68
  });
66
69
  } catch (error) {
67
70
  console.error("Failed:", error);
package/src/summarizer.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { execSync } from "child_process";
3
4
  import matter from "gray-matter";
4
5
  import { subDays, subMonths, isBefore, isAfter } from "date-fns";
5
6
  import { getGithubOidcToken } from "./oidc";
@@ -11,6 +12,7 @@ interface SummarizeOptions {
11
12
  apiUrl: string;
12
13
  userId: string;
13
14
  baseDir?: string;
15
+ push?: boolean;
14
16
  }
15
17
 
16
18
  export async function summarize({
@@ -20,6 +22,7 @@ export async function summarize({
20
22
  apiUrl,
21
23
  userId,
22
24
  baseDir,
25
+ push,
23
26
  }: SummarizeOptions) {
24
27
  // ... (existing date logic unchanged) ...
25
28
  const now = new Date();
@@ -170,8 +173,9 @@ export async function summarize({
170
173
 
171
174
  const summaryContent = `---
172
175
  date: ${now.getTime()}
173
- tags: ["#summary/${frequency}", "#auto"]
176
+ tags: ["summary", "${taskName}", "auto"]
174
177
  title: ${frequency} Summary - ${taskName}
178
+ frequency: ${frequency}
175
179
  ---
176
180
 
177
181
  ${result.summary}
@@ -179,4 +183,30 @@ ${result.summary}
179
183
 
180
184
  fs.writeFileSync(path.join(notesDir, summaryFilename), summaryContent);
181
185
  console.log(`[Scheduler] Summary saved to ${summaryFilename}`);
186
+
187
+ if (push) {
188
+ try {
189
+ console.log("[Scheduler] Committing and pushing changes...");
190
+ try {
191
+ execSync("git config user.name", { stdio: "ignore" });
192
+ } catch {
193
+ console.log("[Scheduler] Configuring git user...");
194
+ execSync('git config user.name "Marlin Scheduler"');
195
+ execSync('git config user.email "scheduler@marlin.app"');
196
+ }
197
+
198
+ const relativePath = path.relative(
199
+ process.cwd(),
200
+ path.join(notesDir, summaryFilename)
201
+ );
202
+ execSync(`git add "${relativePath}"`);
203
+ execSync(
204
+ `git commit -m "chore(scheduler): add ${frequency} summary for ${taskName}"`
205
+ );
206
+ execSync("git push");
207
+ console.log("[Scheduler] Successfully pushed to remote.");
208
+ } catch (error) {
209
+ console.error("[Scheduler] Failed to push changes:", error);
210
+ }
211
+ }
182
212
  }