@houston-ai/routines 0.2.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.
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Picker fields used by ScheduleBuilder — time, day-of-week, day-of-month.
3
+ */
4
+ import { cn } from "@houston-ai/core"
5
+
6
+ const inputClass = cn(
7
+ "px-3 py-2 rounded-xl border border-border bg-background",
8
+ "text-sm text-foreground",
9
+ "focus:outline-none focus:border-border/80 transition-colors",
10
+ )
11
+
12
+ const labelClass = "text-xs font-medium text-muted-foreground mb-1.5 block"
13
+
14
+ const DAYS_OF_WEEK = [
15
+ { value: 0, label: "Sun" },
16
+ { value: 1, label: "Mon" },
17
+ { value: 2, label: "Tue" },
18
+ { value: 3, label: "Wed" },
19
+ { value: 4, label: "Thu" },
20
+ { value: 5, label: "Fri" },
21
+ { value: 6, label: "Sat" },
22
+ ]
23
+
24
+ export function TimePicker({
25
+ value,
26
+ onChange,
27
+ }: {
28
+ value: string
29
+ onChange: (time: string) => void
30
+ }) {
31
+ return (
32
+ <div>
33
+ <label className={labelClass}>Time</label>
34
+ <input
35
+ type="time"
36
+ value={value}
37
+ onChange={(e) => onChange(e.target.value)}
38
+ className={cn(inputClass, "w-full")}
39
+ />
40
+ </div>
41
+ )
42
+ }
43
+
44
+ export function DayOfWeekPicker({
45
+ value,
46
+ onChange,
47
+ }: {
48
+ value: number
49
+ onChange: (day: number) => void
50
+ }) {
51
+ return (
52
+ <div>
53
+ <label className={labelClass}>Day</label>
54
+ <div className="flex gap-1">
55
+ {DAYS_OF_WEEK.map((day) => (
56
+ <button
57
+ key={day.value}
58
+ onClick={() => onChange(day.value)}
59
+ className={cn(
60
+ "size-8 rounded-lg text-xs font-medium transition-colors",
61
+ value === day.value
62
+ ? "bg-primary text-primary-foreground"
63
+ : "border border-border text-muted-foreground hover:bg-secondary",
64
+ )}
65
+ >
66
+ {day.label}
67
+ </button>
68
+ ))}
69
+ </div>
70
+ </div>
71
+ )
72
+ }
73
+
74
+ export function DayOfMonthPicker({
75
+ value,
76
+ onChange,
77
+ }: {
78
+ value: number
79
+ onChange: (day: number) => void
80
+ }) {
81
+ return (
82
+ <div>
83
+ <label className={labelClass}>Day of month</label>
84
+ <input
85
+ type="number"
86
+ min={1}
87
+ max={31}
88
+ value={value}
89
+ onChange={(e) => onChange(Number(e.target.value))}
90
+ className={cn(inputClass, "w-24")}
91
+ />
92
+ </div>
93
+ )
94
+ }
95
+
96
+ export function CronInput({
97
+ value,
98
+ onChange,
99
+ }: {
100
+ value: string
101
+ onChange: (cron: string) => void
102
+ }) {
103
+ return (
104
+ <div>
105
+ <label className={labelClass}>Cron Expression</label>
106
+ <input
107
+ type="text"
108
+ value={value}
109
+ onChange={(e) => onChange(e.target.value)}
110
+ placeholder="* * * * *"
111
+ className={cn(inputClass, "w-full font-mono")}
112
+ />
113
+ <p className="text-[11px] text-muted-foreground mt-1">
114
+ Format: minute hour day-of-month month day-of-week
115
+ </p>
116
+ </div>
117
+ )
118
+ }
package/src/styles.css ADDED
@@ -0,0 +1 @@
1
+ @source ".";
package/src/types.ts ADDED
@@ -0,0 +1,105 @@
1
+ // Generic routine types — mirrors Houston's Routine model without Tauri/backend coupling.
2
+
3
+ export type TriggerType = "on_approval" | "scheduled" | "periodic" | "manual"
4
+ export type RoutineStatus = "active" | "paused" | "needs_setup" | "error"
5
+ export type ApprovalMode = "manual" | "auto_approve"
6
+
7
+ export type RunStatus =
8
+ | "running"
9
+ | "completed"
10
+ | "failed"
11
+ | "approved"
12
+ | "needs_you"
13
+ | "done"
14
+ | "error"
15
+
16
+ export interface Routine {
17
+ id: string
18
+ project_id: string
19
+ goal_id: string | null
20
+ skill_id: string | null
21
+ name: string
22
+ description: string
23
+ trigger_type: TriggerType
24
+ trigger_config: string
25
+ status: RoutineStatus
26
+ approval_mode: ApprovalMode
27
+ context: string
28
+ run_count: number
29
+ approval_count: number
30
+ last_run_at: string | null
31
+ created_at: string
32
+ updated_at: string
33
+ }
34
+
35
+ export interface RoutineRun {
36
+ id: string
37
+ routine_id: string
38
+ project_id: string
39
+ status: RunStatus
40
+ session_id: string | null
41
+ claude_session_id: string | null
42
+ output_files: string | null
43
+ cost_usd: number | null
44
+ duration_ms: number | null
45
+ output_title: string | null
46
+ output_summary: string | null
47
+ feedback_text: string | null
48
+ is_test_run: boolean
49
+ created_at: string
50
+ completed_at: string | null
51
+ approved_at: string | null
52
+ }
53
+
54
+ export interface Skill {
55
+ id: string
56
+ name: string
57
+ description: string
58
+ }
59
+
60
+ export const TRIGGER_LABELS: Record<TriggerType, string> = {
61
+ on_approval: "On approval",
62
+ scheduled: "Scheduled",
63
+ periodic: "Periodic",
64
+ manual: "Manual",
65
+ }
66
+
67
+ /** Form state for the routine edit form */
68
+ export interface RoutineFormState {
69
+ name: string
70
+ description: string
71
+ context: string
72
+ triggerType: TriggerType
73
+ approvalMode: ApprovalMode
74
+ skillId: string | null
75
+ }
76
+
77
+ // --- Heartbeat & schedule types ---
78
+
79
+ export interface HeartbeatConfig {
80
+ enabled: boolean
81
+ intervalMinutes: number
82
+ prompt: string
83
+ activeHoursStart?: string
84
+ activeHoursEnd?: string
85
+ suppressionToken: string
86
+ }
87
+
88
+ export type SchedulePreset =
89
+ | "every_30min"
90
+ | "hourly"
91
+ | "daily"
92
+ | "weekdays"
93
+ | "weekly"
94
+ | "monthly"
95
+ | "custom"
96
+
97
+ export const SCHEDULE_PRESET_LABELS: Record<SchedulePreset, string> = {
98
+ every_30min: "Every 30 minutes",
99
+ hourly: "Every hour",
100
+ daily: "Daily",
101
+ weekdays: "Weekdays only",
102
+ weekly: "Weekly",
103
+ monthly: "Monthly",
104
+ custom: "Custom (cron)",
105
+ }