#!/usr/bin/env bun /** * action-item-router * Routes action items into target systems using OpenAI. */ import { parseArgs } from "util"; import { existsSync, mkdirSync, appendFileSync, readFileSync } from "fs"; import { join, dirname, resolve } from "path"; type OutputFormat = "markdown" | "json"; type PriorityMode = "strict" | "balanced" | "light"; type RoutingTemplate = { items?: Array>; systems?: Array>; defaults?: Record; metadata?: Record; }; interface SkillOptions { rawItems: string; workspace?: string; defaultSystem?: string; priorityMode: PriorityMode; sla?: string; format: OutputFormat; model: string; output?: string; template?: RoutingTemplate; } interface OpenAIChatResponse { choices?: Array<{ message?: { content?: string | null; }; }>; error?: { message?: string; }; } const SKILL_SLUG = "action-item-router"; function ensureDir(path: string) { if (!existsSync(path)) { mkdirSync(path, { recursive: true }); } } function getPaths() { const sessionStamp = new Date().toISOString().replace(/[:.]/g, "_").replace(/-/g, "_"); const exportsRoot = process.env.SKILLS_EXPORTS_DIR || join(process.cwd(), ".skills", "exports"); const logsRoot = process.env.SKILLS_LOGS_DIR || join(process.cwd(), ".skills", "logs"); const skillExportsDir = join(exportsRoot, SKILL_SLUG); const skillLogsDir = join(logsRoot, SKILL_SLUG); ensureDir(skillExportsDir); ensureDir(skillLogsDir); return { sessionStamp, skillExportsDir, skillLogsDir, }; } function createLogger(logDir: string, sessionStamp: string) { const logFile = join(logDir, `log_${sessionStamp}.txt`); function write(level: "info" | "success" | "error", message: string) { const timestamp = new Date().toISOString(); const entry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`; appendFileSync(logFile, entry); const prefix = level === "success" ? "✅" : level === "error" ? "❌" : "â„šī¸"; console.log(`${prefix} ${message}`); } return { info: (message: string) => write("info", message), success: (message: string) => write("success", message), error: (message: string) => write("error", message), logFile, }; } function slugify(value: string): string { return value .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 40) || "action-items"; } function parseJsonTemplate(content: string): RoutingTemplate | undefined { try { const data = JSON.parse(content); if (typeof data === "object" && data !== null) { return data as RoutingTemplate; } } catch (error) { // treat as plain text if parsing fails } return undefined; } function parseOptions(): SkillOptions { const { values, positionals } = parseArgs({ args: Bun.argv.slice(2), options: { help: { type: "boolean", short: "h" }, text: { type: "string" }, workspace: { type: "string" }, "default-system": { type: "string" }, "priority-mode": { type: "string", default: "balanced" }, sla: { type: "string" }, format: { type: "string", default: "markdown" }, model: { type: "string", default: "gpt-4o-mini" }, output: { type: "string" }, }, allowPositionals: true, }); if (values.help) { console.log(` action-item-router - Route action items to target systems Usage: bun run skills/action-item-router/src/index.ts [options] [file] Options: -h, --help Show this help message --text Action items as text --workspace Workspace name --default-system Default target system (Asana, Jira, etc.) --priority-mode Priority mode: strict | balanced | light (default: balanced) --sla