1+ // Oxlint: This distributable plugin intentionally embeds Ponytail's instructions and uses Amp's async APIs.
2+ // oxlint-disable eslint/max-lines, eslint/max-lines-per-function, eslint/max-statements, oxc/no-async-await
13import type { PluginAPI , PluginCommandContext , ThreadID } from "@ampcode/plugin" ;
24
35export const description =
@@ -15,9 +17,12 @@ export type PonytailCommand =
1517const DEFAULT_MODE : PonytailMode = "full" ;
1618const DEFAULT_MODE_KEY = "ponytail.defaultMode" ;
1719const PONYTAIL_URL = "https://github.com/dietrichgebert/ponytail" ;
20+ const PONYTAIL_COMMAND_PATTERN = / ^ (?: [ / @ $ ] ) ? p o n y t a i l (?: : p o n y t a i l ) ? (?: \s + (?< arguments > .* ) ) ? $ / u;
21+ const TABLE_MODE_PATTERN = / ^ \| \s * \* \* (?< mode > .+ ?) \* \* \s * \| / u;
22+ const EXAMPLE_MODE_PATTERN = / ^ - \s * (?< mode > [ ^ : ] + ) : \s * " / u;
1823
19- // Adapted from Ponytail's canonical skills/ponytail/SKILL.md. Keeping the
20- // complete body here makes the installed, single-file Amp plugin work offline.
24+ // Adapted from Ponytail's canonical skills/ponytail/SKILL.md.
25+ // Keeping the complete body here makes the installed, single-file Amp plugin work offline.
2126const PONYTAIL_SKILL_BODY = `# Ponytail
2227
2328You are a lazy senior developer. Lazy means efficient, not careless. You have
@@ -120,102 +125,132 @@ changed or session end.
120125
121126The shortest path to done is the right path.` ;
122127
123- export function normalizeMode ( value : unknown ) : PonytailMode | null {
124- if ( typeof value !== "string" ) return null ;
128+ export const normalizeMode = ( value : unknown ) : PonytailMode | undefined => {
129+ if ( typeof value !== "string" ) {
130+ return ;
131+ }
125132
126133 const normalized = value . trim ( ) . toLowerCase ( ) ;
127- return PONYTAIL_MODES . includes ( normalized as PonytailMode ) ? ( normalized as PonytailMode ) : null ;
128- }
134+ if ( ! PONYTAIL_MODES . includes ( normalized as PonytailMode ) ) {
135+ return ;
136+ }
137+
138+ return normalized as PonytailMode ;
139+ } ;
129140
130- export function isDeactivationCommand ( value : unknown ) : boolean {
141+ export const isDeactivationCommand = ( value : unknown ) : boolean => {
131142 const normalized = String ( value ?? "" )
132143 . trim ( )
133144 . toLowerCase ( )
134- . replace ( / [ . ! ? \s ] + $ / , "" ) ;
145+ . replace ( / [ . ! ? \s ] + $ / u , "" ) ;
135146
136147 return normalized === "stop ponytail" || normalized === "normal mode" ;
137- }
148+ } ;
138149
139- export function parsePonytailCommand ( value : unknown ) : PonytailCommand | null {
150+ export const parsePonytailCommand = ( value : unknown ) : PonytailCommand | undefined => {
140151 if ( isDeactivationCommand ( value ) ) {
141- return { type : "set-mode " , mode : "off " } ;
152+ return { mode : "off " , type : "set-mode " } ;
142153 }
143154
144155 const command = String ( value ?? "" )
145156 . trim ( )
146157 . toLowerCase ( )
147- . match ( / ^ (?: [ / @ $ ] ) ? p o n y t a i l (?: : p o n y t a i l ) ? (?: \s + ( .* ) ) ? $ / ) ;
158+ . match ( PONYTAIL_COMMAND_PATTERN ) ;
159+ if ( ! command || ! command . groups ) {
160+ return ;
161+ }
148162
149- if ( ! command ) return null ;
163+ const { arguments : argumentText } = command . groups ;
164+ let argumentsValue = "" ;
165+ if ( argumentText ) {
166+ argumentsValue = argumentText . trim ( ) ;
167+ }
168+ if ( ! argumentsValue || argumentsValue === "status" ) {
169+ return { type : "status" } ;
170+ }
150171
151- const args = command [ 1 ] ?. trim ( ) ;
152- if ( ! args || args === "status" ) return { type : "status" } ;
172+ const [ primary , secondary , extra ] = argumentsValue . split ( / \s + / u) ;
173+ if ( primary === "default" ) {
174+ if ( ! extra ) {
175+ const mode = normalizeMode ( secondary ) ;
176+ if ( mode ) {
177+ return { mode, type : "set-default" } ;
178+ }
179+ }
153180
154- const parts = args . split ( / \s + / ) ;
155- if ( parts [ 0 ] === "default" ) {
156- const mode = parts . length === 2 ? normalizeMode ( parts [ 1 ] ) : null ;
157- return mode ? { type : "set-default" , mode } : { type : "invalid" } ;
181+ return { type : "invalid" } ;
182+ }
183+
184+ if ( ! secondary ) {
185+ const mode = normalizeMode ( primary ) ;
186+ if ( mode ) {
187+ return { mode, type : "set-mode" } ;
188+ }
158189 }
159190
160- const mode = parts . length === 1 ? normalizeMode ( parts [ 0 ] ) : null ;
161- return mode ? { type : "set-mode" , mode } : { type : "invalid" } ;
162- }
191+ return { type : "invalid" } ;
192+ } ;
163193
164- export function filterSkillBodyForMode ( body : string , mode : PonytailMode ) : string {
165- return body
166- . split ( / \r ? \n / )
194+ const modeFromMatch = ( match : RegExpMatchArray | null ) : PonytailMode | undefined => {
195+ if ( ! match || ! match . groups ) {
196+ return ;
197+ }
198+ return normalizeMode ( match . groups . mode ) ;
199+ } ;
200+
201+ export const filterSkillBodyForMode = ( body : string , mode : PonytailMode ) : string =>
202+ body
203+ . split ( / \r ? \n / u)
167204 . filter ( ( line ) => {
168- const tableLabel = line . match ( / ^ \| \s * \* \* ( .+ ?) \* \* \s * \| / ) ;
169- if ( tableLabel ) {
170- const labelMode = normalizeMode ( tableLabel [ 1 ] ) ;
171- if ( labelMode && labelMode !== "off" ) return labelMode === mode ;
205+ const tableMode = modeFromMatch ( line . match ( TABLE_MODE_PATTERN ) ) ;
206+ if ( tableMode && tableMode !== "off" ) {
207+ return tableMode === mode ;
172208 }
173209
174- const exampleLabel = line . match ( / ^ - \s * ( [ ^ : ] + ) : \s * " / ) ;
175- if ( exampleLabel ) {
176- const labelMode = normalizeMode ( exampleLabel [ 1 ] ) ;
177- if ( labelMode && labelMode !== "off" ) return labelMode === mode ;
210+ const exampleMode = modeFromMatch ( line . match ( EXAMPLE_MODE_PATTERN ) ) ;
211+ if ( exampleMode && exampleMode !== "off" ) {
212+ return exampleMode === mode ;
178213 }
179214
180215 return true ;
181216 } )
182217 . join ( "\n" ) ;
183- }
184218
185- export function getPonytailInstructions ( mode : PonytailMode ) : string {
186- if ( mode === "off" ) return "" ;
219+ export const getPonytailInstructions = ( mode : PonytailMode ) : string => {
220+ if ( mode === "off" ) {
221+ return "" ;
222+ }
187223
188224 return `PONYTAIL MODE ACTIVE — level: ${ mode } \n\n${ filterSkillBodyForMode ( PONYTAIL_SKILL_BODY , mode ) } ` ;
189- }
225+ } ;
190226
191- function effectiveDefaultMode ( config : Record < string , unknown > ) : PonytailMode {
192- return (
193- normalizeMode ( process . env . PONYTAIL_DEFAULT_MODE ) ??
194- normalizeMode ( config [ DEFAULT_MODE_KEY ] ) ??
195- DEFAULT_MODE
196- ) ;
197- }
227+ const effectiveDefaultMode = ( config : Record < string , unknown > ) : PonytailMode =>
228+ normalizeMode ( process . env . PONYTAIL_DEFAULT_MODE ) ??
229+ normalizeMode ( config [ DEFAULT_MODE_KEY ] ) ??
230+ DEFAULT_MODE ;
198231
199- function requireThread ( ctx : PluginCommandContext ) : ctx is PluginCommandContext & {
232+ const requireThread = (
233+ ctx : PluginCommandContext ,
234+ ) : ctx is PluginCommandContext & {
200235 thread : NonNullable < PluginCommandContext [ "thread" ] > ;
201- } {
202- return Boolean ( ctx . thread ) ;
203- }
236+ } => Boolean ( ctx . thread ) ;
204237
205- export default function ponytailPlugin ( amp : PluginAPI ) {
238+ const ponytailPlugin = ( amp : PluginAPI ) : void => {
206239 const threadModes = new Map < ThreadID , PonytailMode > ( ) ;
207240 let configuredDefaultMode : PonytailMode =
208241 normalizeMode ( process . env . PONYTAIL_DEFAULT_MODE ) ?? DEFAULT_MODE ;
209242 let receivedConfigurationUpdate = false ;
210243
211- const applyConfiguration = ( config : Record < string , unknown > ) => {
244+ const applyConfiguration = ( config : Record < string , unknown > ) : void => {
212245 configuredDefaultMode = effectiveDefaultMode ( config ) ;
213246 } ;
214247
215248 const loadConfiguration = amp . configuration
216249 . get ( )
217250 . then ( ( config ) => {
218- if ( ! receivedConfigurationUpdate ) applyConfiguration ( config ) ;
251+ if ( ! receivedConfigurationUpdate ) {
252+ applyConfiguration ( config ) ;
253+ }
219254 } )
220255 . catch ( ( error : unknown ) => {
221256 amp . logger . log ( "Unable to read Ponytail configuration; using the fallback default." , error ) ;
@@ -229,24 +264,27 @@ export default function ponytailPlugin(amp: PluginAPI) {
229264 configurationSubscription . unsubscribe ( ) ;
230265 } ) ;
231266
232- const getThreadMode = async ( threadID : ThreadID ) => {
267+ const getThreadMode = async ( threadID : ThreadID ) : Promise < PonytailMode > => {
233268 await loadConfiguration ;
234- if ( ! threadModes . has ( threadID ) ) {
235- threadModes . set ( threadID , configuredDefaultMode ) ;
269+ const currentMode = threadModes . get ( threadID ) ;
270+ if ( currentMode ) {
271+ return currentMode ;
236272 }
237- return threadModes . get ( threadID ) as PonytailMode ;
273+
274+ threadModes . set ( threadID , configuredDefaultMode ) ;
275+ return configuredDefaultMode ;
238276 } ;
239277
240- const setThreadMode = ( threadID : ThreadID , mode : PonytailMode ) => {
278+ const setThreadMode = ( threadID : ThreadID , mode : PonytailMode ) : void => {
241279 threadModes . set ( threadID , mode ) ;
242280 } ;
243281
244- const setDefaultMode = async ( mode : PonytailMode ) => {
282+ const setDefaultMode = async ( mode : PonytailMode ) : Promise < void > => {
245283 await amp . configuration . update ( { [ DEFAULT_MODE_KEY ] : mode } , "global" ) ;
246284 configuredDefaultMode = normalizeMode ( process . env . PONYTAIL_DEFAULT_MODE ) ?? mode ;
247285 } ;
248286
249- const notify = async ( message : string , ui : PluginCommandContext [ "ui" ] ) => {
287+ const notify = async ( message : string , ui : PluginCommandContext [ "ui" ] ) : Promise < void > => {
250288 try {
251289 await ui . notify ( message ) ;
252290 } catch ( error ) {
@@ -265,37 +303,44 @@ export default function ponytailPlugin(amp: PluginAPI) {
265303 const command = parsePonytailCommand ( event . message ) ;
266304 let commandResult = "" ;
267305
268- if ( command ?. type === "set-mode" ) {
269- currentMode = command . mode ;
270- setThreadMode ( event . thread . id , currentMode ) ;
271- commandResult = `Ponytail mode changed to ${ currentMode } for this thread.` ;
272- } else if ( command ?. type === "set-default" ) {
273- await setDefaultMode ( command . mode ) ;
274- commandResult =
275- configuredDefaultMode === command . mode
276- ? `Default Ponytail mode set to ${ command . mode } . The current thread remains ${ currentMode } .`
277- : `Saved default ${ command . mode } , but PONYTAIL_DEFAULT_MODE keeps the effective default at ${ configuredDefaultMode } . The current thread remains ${ currentMode } .` ;
278- } else if ( command ?. type === "status" ) {
279- commandResult = `Ponytail status: current ${ currentMode } ; default ${ configuredDefaultMode } .` ;
280- } else if ( command ?. type === "invalid" ) {
281- commandResult =
282- "Unknown Ponytail mode. Use off, lite, full, ultra, status, or default <mode>." ;
306+ if ( command ) {
307+ if ( command . type === "set-mode" ) {
308+ currentMode = command . mode ;
309+ setThreadMode ( event . thread . id , currentMode ) ;
310+ commandResult = `Ponytail mode changed to ${ currentMode } for this thread.` ;
311+ } else if ( command . type === "set-default" ) {
312+ await setDefaultMode ( command . mode ) ;
313+ commandResult = `Saved default ${ command . mode } , but PONYTAIL_DEFAULT_MODE keeps the effective default at ${ configuredDefaultMode } . The current thread remains ${ currentMode } .` ;
314+ if ( configuredDefaultMode === command . mode ) {
315+ commandResult = `Default Ponytail mode set to ${ command . mode } . The current thread remains ${ currentMode } .` ;
316+ }
317+ } else if ( command . type === "status" ) {
318+ commandResult = `Ponytail status: current ${ currentMode } ; default ${ configuredDefaultMode } .` ;
319+ } else {
320+ commandResult =
321+ "Unknown Ponytail mode. Use off, lite, full, ultra, status, or default <mode>." ;
322+ }
283323 }
284324
285- if ( commandResult ) await notify ( commandResult , ctx . ui ) ;
325+ if ( commandResult ) {
326+ await notify ( commandResult , ctx . ui ) ;
327+ }
286328
287329 const instructions = getPonytailInstructions ( currentMode ) ;
288330 const content = [ commandResult , instructions ] . filter ( Boolean ) . join ( "\n\n" ) ;
289331
290- return content ? { message : { content } } : undefined ;
332+ if ( content ) {
333+ return { message : { content } } ;
334+ }
335+ return { } ;
291336 } ) ;
292337
293338 amp . registerCommand (
294339 "ponytail-mode" ,
295340 {
296- title : "Change mode" ,
297341 category : "ponytail" ,
298342 description : "Set Ponytail intensity for the active thread." ,
343+ title : "Change mode" ,
299344 } ,
300345 async ( ctx ) => {
301346 if ( ! requireThread ( ctx ) ) {
@@ -305,14 +350,16 @@ export default function ponytailPlugin(amp: PluginAPI) {
305350
306351 const currentMode = await getThreadMode ( ctx . thread . id ) ;
307352 const selected = await ctx . ui . select ( {
308- title : "Ponytail mode" ,
353+ initialValue : currentMode ,
309354 message :
310355 "lite suggests the lazier option; full enforces the ladder; ultra challenges unnecessary work." ,
311356 options : [ ...PONYTAIL_MODES ] ,
312- initialValue : currentMode ,
357+ title : "Ponytail mode" ,
313358 } ) ;
314359 const mode = normalizeMode ( selected ) ;
315- if ( ! mode ) return ;
360+ if ( ! mode ) {
361+ return ;
362+ }
316363
317364 setThreadMode ( ctx . thread . id , mode ) ;
318365 await ctx . ui . notify ( `Ponytail mode set to ${ mode } for this thread.` ) ;
@@ -322,57 +369,64 @@ export default function ponytailPlugin(amp: PluginAPI) {
322369 amp . registerCommand (
323370 "ponytail-status" ,
324371 {
325- title : "Show status" ,
326372 category : "ponytail" ,
327373 description : "Show the active thread mode and configured default." ,
374+ title : "Show status" ,
328375 } ,
329376 async ( ctx ) => {
330377 await loadConfiguration ;
331- const current = ctx . thread ? await getThreadMode ( ctx . thread . id ) : "(no active thread)" ;
378+ let current : PonytailMode | "(no active thread)" = "(no active thread)" ;
379+ if ( ctx . thread ) {
380+ current = await getThreadMode ( ctx . thread . id ) ;
381+ }
332382 await ctx . ui . notify ( `Ponytail: current ${ current } ; default ${ configuredDefaultMode } .` ) ;
333383 } ,
334384 ) ;
335385
336386 amp . registerCommand (
337387 "ponytail-default-mode" ,
338388 {
339- title : "Set default mode" ,
340389 category : "ponytail" ,
341390 description : "Set the Ponytail mode used by new Amp threads." ,
391+ title : "Set default mode" ,
342392 } ,
343393 async ( ctx ) => {
344394 await loadConfiguration ;
345395 const selected = await ctx . ui . select ( {
346- title : "Default Ponytail mode" ,
396+ initialValue : configuredDefaultMode ,
347397 message : "This applies to new threads and is saved in Amp settings." ,
348398 options : [ ...PONYTAIL_MODES ] ,
349- initialValue : configuredDefaultMode ,
399+ title : "Default Ponytail mode" ,
350400 } ) ;
351401 const mode = normalizeMode ( selected ) ;
352- if ( ! mode ) return ;
402+ if ( ! mode ) {
403+ return ;
404+ }
353405
354406 await setDefaultMode ( mode ) ;
355407 const overridden = configuredDefaultMode !== mode ;
356- await ctx . ui . notify (
357- overridden
358- ? `Saved ${ mode } , but PONYTAIL_DEFAULT_MODE keeps the effective default at ${ configuredDefaultMode } .`
359- : `Default Ponytail mode set to ${ mode } .` ,
360- ) ;
408+ let message = `Default Ponytail mode set to ${ mode } .` ;
409+ if ( overridden ) {
410+ message = `Saved ${ mode } , but PONYTAIL_DEFAULT_MODE keeps the effective default at ${ configuredDefaultMode } .` ;
411+ }
412+ await ctx . ui . notify ( message ) ;
361413 } ,
362414 ) ;
363415
364416 amp . registerCommand (
365417 "ponytail-help" ,
366418 {
367- title : "Open documentation" ,
368419 category : "ponytail" ,
369420 description : "Open the Ponytail documentation on GitHub." ,
421+ title : "Open documentation" ,
370422 } ,
371423 async ( ctx ) => {
372424 await ctx . system . open ( PONYTAIL_URL ) ;
373425 } ,
374426 ) ;
375- }
427+ } ;
428+
429+ export default ponytailPlugin ;
376430
377431/*
378432MIT License
0 commit comments