commandmate 0.3.1 → 0.3.3

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 (151) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +11 -11
  3. package/.next/app-path-routes-manifest.json +1 -1
  4. package/.next/build-manifest.json +4 -4
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/cache/config.json +3 -3
  7. package/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/.next/cache/webpack/client-production/1.pack +0 -0
  9. package/.next/cache/webpack/client-production/2.pack +0 -0
  10. package/.next/cache/webpack/client-production/index.pack +0 -0
  11. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  12. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  13. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  14. package/.next/cache/webpack/server-production/0.pack +0 -0
  15. package/.next/cache/webpack/server-production/index.pack +0 -0
  16. package/.next/next-server.js.nft.json +1 -1
  17. package/.next/prerender-manifest.json +1 -1
  18. package/.next/required-server-files.json +1 -1
  19. package/.next/routes-manifest.json +1 -1
  20. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/app/update-check/route.js +1 -1
  23. package/.next/server/app/api/external-apps/[id]/health/route.js +1 -1
  24. package/.next/server/app/api/external-apps/[id]/route.js +1 -1
  25. package/.next/server/app/api/external-apps/route.js +1 -1
  26. package/.next/server/app/api/hooks/claude-done/route.js +1 -1
  27. package/.next/server/app/api/ollama/models/route.js +1 -0
  28. package/.next/server/app/api/ollama/models/route.js.nft.json +1 -0
  29. package/.next/server/app/api/ollama/models.body +1 -0
  30. package/.next/server/app/api/ollama/models.meta +1 -0
  31. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  32. package/.next/server/app/api/repositories/clone/route.js +1 -1
  33. package/.next/server/app/api/repositories/excluded/route.js +7 -7
  34. package/.next/server/app/api/repositories/restore/route.js +3 -3
  35. package/.next/server/app/api/repositories/route.js +13 -11
  36. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  37. package/.next/server/app/api/repositories/scan/route.js +1 -1
  38. package/.next/server/app/api/repositories/sync/route.js +3 -3
  39. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  40. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  41. package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
  42. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  43. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  44. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -0
  45. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js.nft.json +1 -0
  46. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +9 -0
  47. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js.nft.json +1 -0
  48. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
  49. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  50. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  51. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  52. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  53. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
  54. package/.next/server/app/api/worktrees/[id]/logs/route.js +2 -2
  55. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
  56. package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
  57. package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
  58. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  59. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  60. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  61. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  62. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  63. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -0
  65. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js.nft.json +1 -0
  66. package/.next/server/app/api/worktrees/[id]/schedules/route.js +4 -0
  67. package/.next/server/app/api/worktrees/[id]/schedules/route.js.nft.json +1 -0
  68. package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
  69. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  70. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  71. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
  74. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
  76. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
  78. package/.next/server/app/api/worktrees/route.js +1 -1
  79. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  80. package/.next/server/app/login/page.js.nft.json +1 -1
  81. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/page.js +1 -1
  83. package/.next/server/app/page.js.nft.json +1 -1
  84. package/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/proxy/[...path]/route.js +1 -1
  86. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  87. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  88. package/.next/server/app/worktrees/[id]/page.js +8 -3
  89. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  90. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  91. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  92. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app-paths-manifest.json +10 -5
  94. package/.next/server/chunks/2314.js +1 -0
  95. package/.next/server/chunks/3860.js +1 -1
  96. package/.next/server/chunks/4559.js +1 -1
  97. package/.next/server/chunks/539.js +10 -10
  98. package/.next/server/chunks/5853.js +1 -1
  99. package/.next/server/chunks/6228.js +1 -0
  100. package/.next/server/chunks/7425.js +112 -37
  101. package/.next/server/chunks/7566.js +1 -1
  102. package/.next/server/chunks/8693.js +1 -1
  103. package/.next/server/chunks/9446.js +1 -0
  104. package/.next/server/functions-config-manifest.json +1 -1
  105. package/.next/server/middleware-build-manifest.js +1 -1
  106. package/.next/server/middleware-manifest.json +5 -5
  107. package/.next/server/pages/500.html +1 -1
  108. package/.next/server/server-reference-manifest.json +1 -1
  109. package/.next/static/chunks/8091-274bc0716106e7fc.js +1 -0
  110. package/.next/static/chunks/app/page-060057e02b841125.js +1 -0
  111. package/.next/static/chunks/app/worktrees/[id]/page-78580947c201d698.js +1 -0
  112. package/.next/static/chunks/{main-db79434ee4a6c931.js → main-2feda12a4d321111.js} +1 -1
  113. package/.next/static/css/e85de230ef5ddc40.css +3 -0
  114. package/.next/trace +5 -5
  115. package/.next/types/app/api/ollama/models/route.ts +343 -0
  116. package/.next/types/app/api/worktrees/[id]/execution-logs/[logId]/route.ts +343 -0
  117. package/.next/types/app/api/worktrees/[id]/execution-logs/route.ts +343 -0
  118. package/.next/types/app/api/worktrees/[id]/schedules/[scheduleId]/route.ts +343 -0
  119. package/.next/types/app/api/worktrees/[id]/schedules/route.ts +343 -0
  120. package/README.md +74 -76
  121. package/dist/cli/utils/docs-reader.d.ts.map +1 -1
  122. package/dist/cli/utils/docs-reader.js +1 -0
  123. package/dist/server/server.js +5 -0
  124. package/dist/server/src/config/cmate-constants.js +79 -0
  125. package/dist/server/src/config/schedule-config.js +60 -0
  126. package/dist/server/src/lib/auto-yes-manager.js +2 -2
  127. package/dist/server/src/lib/claude-executor.js +158 -0
  128. package/dist/server/src/lib/cli-patterns.js +73 -9
  129. package/dist/server/src/lib/cli-tools/gemini.js +81 -22
  130. package/dist/server/src/lib/cli-tools/manager.js +4 -2
  131. package/dist/server/src/lib/cli-tools/types.js +64 -2
  132. package/dist/server/src/lib/cli-tools/vibe-local.js +163 -0
  133. package/dist/server/src/lib/cmate-parser.js +262 -0
  134. package/dist/server/src/lib/db-instance.js +3 -0
  135. package/dist/server/src/lib/db-migrations.js +145 -2
  136. package/dist/server/src/lib/db.js +51 -1
  137. package/dist/server/src/lib/env-sanitizer.js +57 -0
  138. package/dist/server/src/lib/prompt-detector.js +4 -3
  139. package/dist/server/src/lib/response-poller.js +22 -11
  140. package/dist/server/src/lib/schedule-manager.js +401 -0
  141. package/dist/server/src/lib/selected-agents-validator.js +99 -0
  142. package/dist/server/src/types/cmate.js +6 -0
  143. package/dist/server/src/types/sidebar.js +9 -4
  144. package/package.json +2 -1
  145. package/.next/server/chunks/7536.js +0 -1
  146. package/.next/static/chunks/8091-925542bdfc843dce.js +0 -1
  147. package/.next/static/chunks/app/page-238b5a70d8c101e9.js +0 -1
  148. package/.next/static/chunks/app/worktrees/[id]/page-a556551ce5c69dec.js +0 -1
  149. package/.next/static/css/b9ea6a4fad17dc32.css +0 -3
  150. /package/.next/static/{hmAjbCPjxX_C0Os7rphI1 → O7EDFfAYQNe_HRbORxQAC}/_buildManifest.js +0 -0
  151. /package/.next/static/{hmAjbCPjxX_C0Os7rphI1 → O7EDFfAYQNe_HRbORxQAC}/_ssgManifest.js +0 -0
@@ -0,0 +1,343 @@
1
+ // File: /home/runner/work/CommandMate/CommandMate/src/app/api/worktrees/[id]/schedules/route.ts
2
+ import * as entry from '../../../../../../../src/app/api/worktrees/[id]/schedules/route.js'
3
+ import type { NextRequest } from 'next/server.js'
4
+
5
+ type TEntry = typeof import('../../../../../../../src/app/api/worktrees/[id]/schedules/route.js')
6
+
7
+ // Check that the entry is a valid entry
8
+ checkFields<Diff<{
9
+ GET?: Function
10
+ HEAD?: Function
11
+ OPTIONS?: Function
12
+ POST?: Function
13
+ PUT?: Function
14
+ DELETE?: Function
15
+ PATCH?: Function
16
+ config?: {}
17
+ generateStaticParams?: Function
18
+ revalidate?: RevalidateRange<TEntry> | false
19
+ dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
20
+ dynamicParams?: boolean
21
+ fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
22
+ preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
23
+ runtime?: 'nodejs' | 'experimental-edge' | 'edge'
24
+ maxDuration?: number
25
+
26
+ }, TEntry, ''>>()
27
+
28
+ // Check the prop type of the entry function
29
+ if ('GET' in entry) {
30
+ checkFields<
31
+ Diff<
32
+ ParamCheck<Request | NextRequest>,
33
+ {
34
+ __tag__: 'GET'
35
+ __param_position__: 'first'
36
+ __param_type__: FirstArg<MaybeField<TEntry, 'GET'>>
37
+ },
38
+ 'GET'
39
+ >
40
+ >()
41
+ checkFields<
42
+ Diff<
43
+ ParamCheck<PageParams>,
44
+ {
45
+ __tag__: 'GET'
46
+ __param_position__: 'second'
47
+ __param_type__: SecondArg<MaybeField<TEntry, 'GET'>>
48
+ },
49
+ 'GET'
50
+ >
51
+ >()
52
+
53
+ checkFields<
54
+ Diff<
55
+ {
56
+ __tag__: 'GET',
57
+ __return_type__: Response | void | never | Promise<Response | void | never>
58
+ },
59
+ {
60
+ __tag__: 'GET',
61
+ __return_type__: ReturnType<MaybeField<TEntry, 'GET'>>
62
+ },
63
+ 'GET'
64
+ >
65
+ >()
66
+ }
67
+ // Check the prop type of the entry function
68
+ if ('HEAD' in entry) {
69
+ checkFields<
70
+ Diff<
71
+ ParamCheck<Request | NextRequest>,
72
+ {
73
+ __tag__: 'HEAD'
74
+ __param_position__: 'first'
75
+ __param_type__: FirstArg<MaybeField<TEntry, 'HEAD'>>
76
+ },
77
+ 'HEAD'
78
+ >
79
+ >()
80
+ checkFields<
81
+ Diff<
82
+ ParamCheck<PageParams>,
83
+ {
84
+ __tag__: 'HEAD'
85
+ __param_position__: 'second'
86
+ __param_type__: SecondArg<MaybeField<TEntry, 'HEAD'>>
87
+ },
88
+ 'HEAD'
89
+ >
90
+ >()
91
+
92
+ checkFields<
93
+ Diff<
94
+ {
95
+ __tag__: 'HEAD',
96
+ __return_type__: Response | void | never | Promise<Response | void | never>
97
+ },
98
+ {
99
+ __tag__: 'HEAD',
100
+ __return_type__: ReturnType<MaybeField<TEntry, 'HEAD'>>
101
+ },
102
+ 'HEAD'
103
+ >
104
+ >()
105
+ }
106
+ // Check the prop type of the entry function
107
+ if ('OPTIONS' in entry) {
108
+ checkFields<
109
+ Diff<
110
+ ParamCheck<Request | NextRequest>,
111
+ {
112
+ __tag__: 'OPTIONS'
113
+ __param_position__: 'first'
114
+ __param_type__: FirstArg<MaybeField<TEntry, 'OPTIONS'>>
115
+ },
116
+ 'OPTIONS'
117
+ >
118
+ >()
119
+ checkFields<
120
+ Diff<
121
+ ParamCheck<PageParams>,
122
+ {
123
+ __tag__: 'OPTIONS'
124
+ __param_position__: 'second'
125
+ __param_type__: SecondArg<MaybeField<TEntry, 'OPTIONS'>>
126
+ },
127
+ 'OPTIONS'
128
+ >
129
+ >()
130
+
131
+ checkFields<
132
+ Diff<
133
+ {
134
+ __tag__: 'OPTIONS',
135
+ __return_type__: Response | void | never | Promise<Response | void | never>
136
+ },
137
+ {
138
+ __tag__: 'OPTIONS',
139
+ __return_type__: ReturnType<MaybeField<TEntry, 'OPTIONS'>>
140
+ },
141
+ 'OPTIONS'
142
+ >
143
+ >()
144
+ }
145
+ // Check the prop type of the entry function
146
+ if ('POST' in entry) {
147
+ checkFields<
148
+ Diff<
149
+ ParamCheck<Request | NextRequest>,
150
+ {
151
+ __tag__: 'POST'
152
+ __param_position__: 'first'
153
+ __param_type__: FirstArg<MaybeField<TEntry, 'POST'>>
154
+ },
155
+ 'POST'
156
+ >
157
+ >()
158
+ checkFields<
159
+ Diff<
160
+ ParamCheck<PageParams>,
161
+ {
162
+ __tag__: 'POST'
163
+ __param_position__: 'second'
164
+ __param_type__: SecondArg<MaybeField<TEntry, 'POST'>>
165
+ },
166
+ 'POST'
167
+ >
168
+ >()
169
+
170
+ checkFields<
171
+ Diff<
172
+ {
173
+ __tag__: 'POST',
174
+ __return_type__: Response | void | never | Promise<Response | void | never>
175
+ },
176
+ {
177
+ __tag__: 'POST',
178
+ __return_type__: ReturnType<MaybeField<TEntry, 'POST'>>
179
+ },
180
+ 'POST'
181
+ >
182
+ >()
183
+ }
184
+ // Check the prop type of the entry function
185
+ if ('PUT' in entry) {
186
+ checkFields<
187
+ Diff<
188
+ ParamCheck<Request | NextRequest>,
189
+ {
190
+ __tag__: 'PUT'
191
+ __param_position__: 'first'
192
+ __param_type__: FirstArg<MaybeField<TEntry, 'PUT'>>
193
+ },
194
+ 'PUT'
195
+ >
196
+ >()
197
+ checkFields<
198
+ Diff<
199
+ ParamCheck<PageParams>,
200
+ {
201
+ __tag__: 'PUT'
202
+ __param_position__: 'second'
203
+ __param_type__: SecondArg<MaybeField<TEntry, 'PUT'>>
204
+ },
205
+ 'PUT'
206
+ >
207
+ >()
208
+
209
+ checkFields<
210
+ Diff<
211
+ {
212
+ __tag__: 'PUT',
213
+ __return_type__: Response | void | never | Promise<Response | void | never>
214
+ },
215
+ {
216
+ __tag__: 'PUT',
217
+ __return_type__: ReturnType<MaybeField<TEntry, 'PUT'>>
218
+ },
219
+ 'PUT'
220
+ >
221
+ >()
222
+ }
223
+ // Check the prop type of the entry function
224
+ if ('DELETE' in entry) {
225
+ checkFields<
226
+ Diff<
227
+ ParamCheck<Request | NextRequest>,
228
+ {
229
+ __tag__: 'DELETE'
230
+ __param_position__: 'first'
231
+ __param_type__: FirstArg<MaybeField<TEntry, 'DELETE'>>
232
+ },
233
+ 'DELETE'
234
+ >
235
+ >()
236
+ checkFields<
237
+ Diff<
238
+ ParamCheck<PageParams>,
239
+ {
240
+ __tag__: 'DELETE'
241
+ __param_position__: 'second'
242
+ __param_type__: SecondArg<MaybeField<TEntry, 'DELETE'>>
243
+ },
244
+ 'DELETE'
245
+ >
246
+ >()
247
+
248
+ checkFields<
249
+ Diff<
250
+ {
251
+ __tag__: 'DELETE',
252
+ __return_type__: Response | void | never | Promise<Response | void | never>
253
+ },
254
+ {
255
+ __tag__: 'DELETE',
256
+ __return_type__: ReturnType<MaybeField<TEntry, 'DELETE'>>
257
+ },
258
+ 'DELETE'
259
+ >
260
+ >()
261
+ }
262
+ // Check the prop type of the entry function
263
+ if ('PATCH' in entry) {
264
+ checkFields<
265
+ Diff<
266
+ ParamCheck<Request | NextRequest>,
267
+ {
268
+ __tag__: 'PATCH'
269
+ __param_position__: 'first'
270
+ __param_type__: FirstArg<MaybeField<TEntry, 'PATCH'>>
271
+ },
272
+ 'PATCH'
273
+ >
274
+ >()
275
+ checkFields<
276
+ Diff<
277
+ ParamCheck<PageParams>,
278
+ {
279
+ __tag__: 'PATCH'
280
+ __param_position__: 'second'
281
+ __param_type__: SecondArg<MaybeField<TEntry, 'PATCH'>>
282
+ },
283
+ 'PATCH'
284
+ >
285
+ >()
286
+
287
+ checkFields<
288
+ Diff<
289
+ {
290
+ __tag__: 'PATCH',
291
+ __return_type__: Response | void | never | Promise<Response | void | never>
292
+ },
293
+ {
294
+ __tag__: 'PATCH',
295
+ __return_type__: ReturnType<MaybeField<TEntry, 'PATCH'>>
296
+ },
297
+ 'PATCH'
298
+ >
299
+ >()
300
+ }
301
+
302
+ // Check the arguments and return type of the generateStaticParams function
303
+ if ('generateStaticParams' in entry) {
304
+ checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
305
+ checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
306
+ }
307
+
308
+ type PageParams = any
309
+ export interface PageProps {
310
+ params?: any
311
+ searchParams?: any
312
+ }
313
+ export interface LayoutProps {
314
+ children?: React.ReactNode
315
+
316
+ params?: any
317
+ }
318
+
319
+ // =============
320
+ // Utility types
321
+ type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
322
+
323
+ // If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
324
+ type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
325
+ type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
326
+
327
+ type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
328
+ type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
329
+ type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
330
+
331
+ type ParamCheck<T> = {
332
+ __tag__: string
333
+ __param_position__: string
334
+ __param_type__: T
335
+ }
336
+
337
+ function checkFields<_ extends { [k in keyof any]: never }>() {}
338
+
339
+ // https://github.com/sindresorhus/type-fest
340
+ type Numeric = number | bigint
341
+ type Zero = 0 | 0n
342
+ type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
343
+ type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # CommandMate
2
2
 
3
+ [![GitHub Stars](https://img.shields.io/github/stars/Kewton/CommandMate?style=social)](https://github.com/Kewton/CommandMate)
3
4
  ![npm version](https://img.shields.io/npm/v/commandmate)
4
5
  ![npm downloads](https://img.shields.io/npm/dm/commandmate)
5
6
  ![license](https://img.shields.io/github/license/Kewton/CommandMate)
@@ -16,12 +17,17 @@
16
17
 
17
18
  Not a "remote control" — a **mobile dev cockpit**.
18
19
 
19
- AI writes code for you. That was supposed to mean freedom.
20
- Instead, you're watching a terminal, afraid to walk away.
21
- Your friends are at dinner. Your kid needs a bath. You just want ten minutes on the couch.
22
- But close the lid and every session dies. So you sit there — babysitting the machine that was supposed to babysit your code.
20
+ ```bash
21
+ npx commandmate
22
+ ```
23
+
24
+ **From install to mobile monitoring in 60 seconds.** macOS / Linux · Node.js v20+ · npm · git · tmux · openssl
23
25
 
24
- CommandMate changes that. Auto Yes keeps the agent moving. Sessions survive in the background. And a Web UI on your phone means you can check in, review diffs, edit instructions, and send screenshots — from anywhere.
26
+ ---
27
+
28
+ AI writes code for you — but you're stuck watching a terminal, afraid to walk away.
29
+ Close the lid and every session dies.
30
+ **CommandMate keeps it alive, and puts the controls on your phone.**
25
31
 
26
32
  Of course, it works great on desktop too — the two-column layout gives you a full overview of all sessions and worktrees at a glance.
27
33
 
@@ -29,83 +35,69 @@ Of course, it works great on desktop too — the two-column layout gives you a f
29
35
  <img src="./docs/images/demo-desktop.gif" alt="CommandMate desktop demo" width="600">
30
36
  </p>
31
37
 
32
- ```bash
33
- npx commandmate
34
- ```
35
-
36
38
  ---
37
39
 
38
- ## The 6 Pillars
40
+ ## Key Features
39
41
 
40
- | Pillar | What it does | Why it matters |
41
- |--------|-------------|----------------|
42
+ | Feature | What it does | Why it matters |
43
+ |---------|-------------|----------------|
42
44
  | **Auto Yes Mode** | Agent runs without stopping for confirmations | No babysitting — Claude Code keeps working while you're away |
43
45
  | **Git Worktree Sessions** | One session per worktree, parallel execution | Multiple tasks progress simultaneously |
44
46
  | **Mobile Web UI** | Full session control from any browser | Monitor and steer from your phone |
45
47
  | **File Viewer** | Browse worktree files from the browser | Review code changes without touching your PC |
46
48
  | **Markdown Editor** | Edit Markdown files in the browser | Update AI instructions on the go |
47
49
  | **Screenshot Instructions** | Attach images to your prompts | Snap a bug → "Fix this" — the agent sees the screenshot |
50
+ | **Token Authentication** | SHA-256 hashed token + HTTPS + rate limiting | Secure remote access — no credentials leaked, brute-force protected |
51
+ | **Scheduled Execution** | Cron-based auto-run via CMATE.md | Daily reviews, nightly tests — Claude Code works on a schedule |
48
52
 
49
53
  ---
50
54
 
51
- ## Quick Start
55
+ ## Use Cases
52
56
 
53
- **Prerequisites:** macOS / Linux, Node.js v20+, npm, git, tmux, openssl
54
-
55
- ```bash
56
- # Install & start in one command
57
- npx commandmate
58
-
59
- # Or install globally
60
- npm install -g commandmate
61
- commandmate init
62
- commandmate start --daemon
63
- ```
64
-
65
- Open http://localhost:3000 in your browser.
66
-
67
- See the [CLI Setup Guide](./docs/en/user-guide/cli-setup-guide.md) for details.
57
+ | Scenario | How CommandMate helps |
58
+ |----------|----------------------|
59
+ | **Couch coding** | Start a task on your PC, then monitor and steer from the sofa |
60
+ | **Commute review** | Review AI-generated code changes on the train |
61
+ | **Overnight runs** | Let Claude Code work all night — check progress from bed |
62
+ | **Visual bug fix** | Snap a UI bug on your phone, send it with "Fix this" |
63
+ | **Parallel tasks** | Run multiple worktree sessions, manage them all from one dashboard |
68
64
 
69
65
  ---
70
66
 
71
67
  ## Comparison
72
68
 
73
- | Feature | CommandMate | Happy Coder | claude-squad | Omnara |
74
- |---------|:-----------:|:-----------:|:------------:|:------:|
75
- | Auto Yes Mode | Yes | No | Yes (TUI only) | No |
76
- | Git Worktree Management | Yes | No | Yes (TUI only) | No |
77
- | Mobile Web UI | Yes | Yes | **No** | Yes |
78
- | File Viewer | Yes | No | No | No |
79
- | Markdown Editor | Yes | No | No | No |
80
- | Screenshot Instructions | Yes | No | Not possible | No |
81
- | Free / OSS | Yes | Free + Paid | Yes | $20/mo |
82
- | Runs 100% Locally | Yes | Server-routed | Yes | Cloud fallback |
69
+ | Feature | CommandMate | Remote Control (Official) | Happy Coder | claude-squad | Omnara |
70
+ |---------|:-----------:|:------------------------:|:-----------:|:------------:|:------:|
71
+ | Auto Yes Mode | Yes | No | No | Yes (TUI only) | No |
72
+ | Git Worktree Management | Yes | No | No | Yes (TUI only) | No |
73
+ | Parallel Sessions | Yes | **No (1 only)** | Yes | Yes | No |
74
+ | Mobile Web UI | Yes | Yes (claude.ai) | Yes | **No** | Yes |
75
+ | File Viewer | Yes | No | No | No | No |
76
+ | Markdown Editor | Yes | No | No | No | No |
77
+ | Screenshot Instructions | Yes | No | No | Not possible | No |
78
+ | Scheduled Execution | Yes | No | No | No | No |
79
+ | Survives Laptop Close | Yes (daemon) | **No (terminal must stay open)** | Yes | Yes | Yes |
80
+ | Token Authentication | Yes | N/A (Anthropic account) | N/A (app) | No | N/A (cloud) |
81
+ | Free / OSS | Yes | Requires Pro/Max | Free + Paid | Yes | $20/mo |
82
+ | Runs 100% Locally | Yes | Via Anthropic API | Server-routed | Yes | Cloud fallback |
83
83
 
84
84
  ---
85
85
 
86
- ## Workflow
86
+ ## Screenshots
87
87
 
88
- ```
89
- 1. Start tasks on your PC
90
- $ commandmate start --daemon
91
- → Claude Code begins working with Auto Yes
88
+ ### Desktop
92
89
 
93
- 2. Close your laptop and go
90
+ ![Desktop view](./docs/images/screenshot-desktop.png)
94
91
 
95
- 3. Check in from your phone
96
- → Web UI shows all sessions at a glance
92
+ ### Mobile
97
93
 
98
- 4. Review code changes
99
- → File Viewer lets you read diffs on mobile
94
+ | Top Page | Worktree (History) | Worktree (Terminal) |
95
+ |----------|-------------------|-------------------|
96
+ | ![Mobile view](./docs/images/screenshot-mobile.png) | ![Mobile - History](./docs/images/screenshot-worktree-mobile.png) | ![Mobile - Terminal](./docs/images/screenshot-worktree-mobile-terminal.png) |
100
97
 
101
- 5. Adjust direction
102
- → Edit a Markdown instruction file, or type a new prompt
98
+ ### Worktree Detail (Desktop)
103
99
 
104
- 6. Snap a bug
105
- → Screenshot Instructions: attach a photo and say "Fix this"
106
-
107
- 7. Claude Code sees the image and starts fixing
108
- ```
100
+ ![Desktop - Worktree detail](./docs/images/screenshot-worktree-desktop.png)
109
101
 
110
102
  ---
111
103
 
@@ -115,7 +107,7 @@ Runs **100% locally**. No external server, no cloud relay, no account required.
115
107
 
116
108
  - Fully open-source ([MIT License](./LICENSE))
117
109
  - Local database, local sessions
118
- - For remote access, use a VPN or authenticated reverse proxy
110
+ - For remote access, use a tunneling service ([Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), [ngrok](https://ngrok.com/), [Pinggy](https://pinggy.io/)), a VPN, or an authenticated reverse proxy
119
111
 
120
112
  See the [Security Guide](./docs/security-guide.md) and [Trust & Safety](./docs/en/TRUST_AND_SAFETY.md) for details.
121
113
 
@@ -136,6 +128,25 @@ Each Git worktree gets its own tmux session, so multiple tasks run in parallel w
136
128
 
137
129
  ---
138
130
 
131
+ <details>
132
+ <summary><strong>Quick Start (detailed)</strong></summary>
133
+
134
+ ```bash
135
+ # Install & start in one command
136
+ npx commandmate
137
+
138
+ # Or install globally
139
+ npm install -g commandmate
140
+ commandmate init
141
+ commandmate start --daemon
142
+ ```
143
+
144
+ Open http://localhost:3000 in your browser.
145
+
146
+ See the [CLI Setup Guide](./docs/en/user-guide/cli-setup-guide.md) for details.
147
+
148
+ </details>
149
+
139
150
  <details>
140
151
  <summary><strong>CLI Commands</strong></summary>
141
152
 
@@ -196,25 +207,6 @@ See `commandmate --help` for all options.
196
207
 
197
208
  </details>
198
209
 
199
- <details>
200
- <summary><strong>Screenshots</strong></summary>
201
-
202
- ### Desktop
203
-
204
- ![Desktop view](./docs/images/screenshot-desktop.png)
205
-
206
- ### Worktree Detail View (Message / Console / History)
207
-
208
- | Desktop | Mobile (History) | Mobile (Terminal) |
209
- |---------|-----------------|-------------------|
210
- | ![Desktop - Worktree detail](./docs/images/screenshot-worktree-desktop.png) | ![Mobile - History](./docs/images/screenshot-worktree-mobile.png) | ![Mobile - Terminal](./docs/images/screenshot-worktree-mobile-terminal.png) |
211
-
212
- ### Top Page (Mobile)
213
-
214
- ![Mobile view](./docs/images/screenshot-mobile.png)
215
-
216
- </details>
217
-
218
210
  <details>
219
211
  <summary><strong>Troubleshooting & FAQ</strong></summary>
220
212
 
@@ -258,7 +250,13 @@ Claude Code sets `CLAUDECODE=1` to prevent nesting. CommandMate removes this aut
258
250
  A: CommandMate runs a web server on your PC. To access it from your phone, your phone and PC must be on the same network (Wi-Fi). Run `commandmate init` and enable external access — this sets `CM_BIND=0.0.0.0`. Then open `http://<your-PC-IP>:3000` in your phone's browser.
259
251
 
260
252
  **Q: Can I access it from outside my home network?**
261
- A: Yes. Use a tunneling service like [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) to securely expose your local server without opening router ports. Alternatively, a VPN or an authenticated reverse proxy (Basic Auth, OIDC, etc.) also works. **Do not** expose the server directly to the internet without authentication.
253
+ A: Yes. Use a tunneling service to securely expose your local server without opening router ports:
254
+
255
+ - [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) — free, requires Cloudflare account
256
+ - [ngrok](https://ngrok.com/) — free tier available, easy setup
257
+ - [Pinggy](https://pinggy.io/) — no sign-up required, simple SSH-based tunnel
258
+
259
+ Alternatively, a VPN or an authenticated reverse proxy (Basic Auth, OIDC, etc.) also works. **Do not** expose the server directly to the internet without authentication.
262
260
 
263
261
  **Q: Does it work on iPhone / Android?**
264
262
  A: Yes. CommandMate's Web UI is responsive and works on any modern mobile browser (Safari, Chrome, etc.). No app install required.
@@ -1 +1 @@
1
- {"version":3,"file":"docs-reader.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/docs-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkDH;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0BvF"}
1
+ {"version":3,"file":"docs-reader.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/docs-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmDH;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0BvF"}
@@ -66,6 +66,7 @@ const SECTION_MAP = {
66
66
  'workflow-examples': 'docs/user-guide/workflow-examples.md',
67
67
  'cli-setup': 'docs/user-guide/cli-setup-guide.md',
68
68
  'agents': 'docs/user-guide/agents-guide.md',
69
+ 'cmate-schedules': 'docs/user-guide/cmate-schedules-guide.md',
69
70
  'architecture': 'docs/architecture.md',
70
71
  'readme': 'README.md',
71
72
  };
@@ -39,6 +39,7 @@ const worktrees_1 = require("./src/lib/worktrees");
39
39
  const db_instance_1 = require("./src/lib/db-instance");
40
40
  const response_poller_1 = require("./src/lib/response-poller");
41
41
  const auto_yes_manager_1 = require("./src/lib/auto-yes-manager");
42
+ const schedule_manager_1 = require("./src/lib/schedule-manager");
42
43
  const db_migrations_1 = require("./src/lib/db-migrations");
43
44
  const env_1 = require("./src/lib/env");
44
45
  const db_repository_1 = require("./src/lib/db-repository");
@@ -227,6 +228,8 @@ app.prepare().then(() => {
227
228
  console.log(`> WebSocket server ready`);
228
229
  // Initialize worktrees after server starts
229
230
  await initializeWorktrees();
231
+ // [S3-010] Initialize schedule manager AFTER worktrees are ready
232
+ (0, schedule_manager_1.initScheduleManager)();
230
233
  });
231
234
  // Graceful shutdown with timeout
232
235
  let isShuttingDown = false;
@@ -241,6 +244,8 @@ app.prepare().then(() => {
241
244
  (0, response_poller_1.stopAllPolling)();
242
245
  // Issue #138: Stop all auto-yes pollers
243
246
  (0, auto_yes_manager_1.stopAllAutoYesPolling)();
247
+ // Issue #294: Stop all scheduled executions (SIGKILL fire-and-forget)
248
+ (0, schedule_manager_1.stopAllSchedules)();
244
249
  // Close WebSocket connections immediately (don't wait)
245
250
  (0, ws_server_1.closeWebSocket)();
246
251
  // Force exit after 3 seconds if graceful shutdown fails
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * CMATE.md Shared Constants
4
+ * Issue #294: Constants shared between server-side parser and client-side validator
5
+ *
6
+ * This module has NO Node.js dependencies (no 'fs'), so it can be safely
7
+ * imported from both server and client code.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.MAX_SCHEDULE_ENTRIES = exports.MAX_CRON_EXPRESSION_LENGTH = exports.NAME_PATTERN = exports.CONTROL_CHAR_PATTERN = exports.CMATE_FILENAME = void 0;
11
+ exports.sanitizeContent = sanitizeContent;
12
+ exports.isValidCronExpression = isValidCronExpression;
13
+ // =============================================================================
14
+ // File
15
+ // =============================================================================
16
+ /** CMATE.md filename */
17
+ exports.CMATE_FILENAME = 'CMATE.md';
18
+ // =============================================================================
19
+ // Sanitization
20
+ // =============================================================================
21
+ /**
22
+ * Unicode control character regex for sanitization.
23
+ * Matches: C0 control chars (except \t \n \r), C1 control chars,
24
+ * zero-width characters, directional control characters.
25
+ *
26
+ * NOTE: No /g flag on the export — callers must use String.replace(pattern, '')
27
+ * with /g or String.replaceAll() to avoid lastIndex state issues.
28
+ *
29
+ * [S4-002] Strips potentially dangerous Unicode control characters
30
+ */
31
+ exports.CONTROL_CHAR_PATTERN =
32
+ // eslint-disable-next-line no-control-regex
33
+ /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F\x80-\x9F\u200B-\u200F\u2028-\u202F\uFEFF]/;
34
+ /**
35
+ * Remove Unicode control characters from a string.
36
+ * Preserves tabs (\t), newlines (\n), and carriage returns (\r).
37
+ *
38
+ * @param content - Raw string to sanitize
39
+ * @returns Sanitized string with control characters removed
40
+ */
41
+ function sanitizeContent(content) {
42
+ // Use RegExp constructor with /g to avoid lastIndex state on the shared pattern
43
+ return content.replace(new RegExp(exports.CONTROL_CHAR_PATTERN.source, 'g'), '');
44
+ }
45
+ // =============================================================================
46
+ // Name Validation
47
+ // =============================================================================
48
+ /**
49
+ * Name validation pattern.
50
+ * Allows: ASCII word chars, Japanese chars (CJK, Hiragana, Katakana, Symbols),
51
+ * spaces, and hyphens. Length: 1-100 characters.
52
+ *
53
+ * [S4-011] Prevents injection through name field
54
+ */
55
+ exports.NAME_PATTERN = /^[\w\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\uF900-\uFAFF\s-]{1,100}$/;
56
+ // =============================================================================
57
+ // Limits
58
+ // =============================================================================
59
+ /** Maximum cron expression length */
60
+ exports.MAX_CRON_EXPRESSION_LENGTH = 100;
61
+ /** Maximum number of schedule entries per worktree */
62
+ exports.MAX_SCHEDULE_ENTRIES = 100;
63
+ // =============================================================================
64
+ // Cron Validation
65
+ // =============================================================================
66
+ /**
67
+ * Validate a cron expression.
68
+ * Checks length and basic format (5-6 fields separated by spaces).
69
+ *
70
+ * @param expression - Cron expression to validate
71
+ * @returns true if the expression appears valid
72
+ */
73
+ function isValidCronExpression(expression) {
74
+ if (expression.length > exports.MAX_CRON_EXPRESSION_LENGTH) {
75
+ return false;
76
+ }
77
+ const parts = expression.trim().split(/\s+/);
78
+ return parts.length >= 5 && parts.length <= 6;
79
+ }