2026-07-10 14:59:17 +02:00
#!/usr/bin/env node
// Enable the caveman and ponytail plugins in a Claude Code settings.json so
2026-10-30 15:23:32 +01:00
// their SessionStart hooks auto-activate both modes, register the autotune
2026-10-30 17:37:47 +01:00
// and dream reminder hooks, the democracy claim gate and the resource watch. Merges non-destructively: existing marketplaces,
2026-08-02 19:34:05 +02:00
// plugins and hooks are preserved, unparseable files are left untouched.
2026-07-10 14:59:17 +02:00
"use strict" ;
const fs = require ( "fs" );
2026-07-25 18:24:53 +02:00
const path = require ( "path" );
2026-07-10 14:59:17 +02:00
const settingsPath = process . argv [ 2 ];
if ( ! settingsPath ) {
console . error ( "usage: enable-plugins.js <settings.json>" );
process . exit ( 2 );
}
const MARKETPLACES = {
caveman : { source : { source : "github" , repo : "JuliusBrussee/caveman" } },
ponytail : { source : { source : "github" , repo : "DietrichGebert/ponytail" } },
};
const PLUGINS = { "caveman@caveman" : true , "ponytail@ponytail" : true };
2026-08-02 19:34:05 +02:00
const REMINDERS = [ "autotune" , "dream" ];
2026-07-10 14:59:17 +02:00
let data = {};
if ( fs . existsSync ( settingsPath )) {
const raw = fs . readFileSync ( settingsPath , "utf8" ). trim ();
if ( raw ) {
try {
data = JSON . parse ( raw );
} catch ( err ) {
console . error ( `skills: ${ settingsPath } is not valid JSON; leaving it untouched ( ${ err . message } ).` );
process . exit ( 0 );
}
}
}
data . extraKnownMarketplaces = data . extraKnownMarketplaces || {};
for ( const [ name , entry ] of Object . entries ( MARKETPLACES )) {
if ( ! data . extraKnownMarketplaces [ name ]) data . extraKnownMarketplaces [ name ] = entry ;
}
data . enabledPlugins = Object . assign ( data . enabledPlugins || {}, PLUGINS );
2026-07-25 18:24:53 +02:00
data . hooks = data . hooks || {};
data . hooks . UserPromptSubmit = data . hooks . UserPromptSubmit || [];
2026-08-02 19:34:05 +02:00
const alreadyRegistered = JSON . stringify ( data . hooks . UserPromptSubmit );
for ( const skill of REMINDERS ) {
if ( alreadyRegistered . includes ( ` ${ skill } /hooks/reminder.sh` )) continue ;
const reminder = path . join ( path . dirname ( settingsPath ), "skills" , skill , "hooks" , "reminder.sh" );
2026-07-25 18:24:53 +02:00
data . hooks . UserPromptSubmit . push ({
hooks : [{ type : "command" , command : `bash ${ JSON . stringify ( reminder ) } ` }],
});
}
2026-10-30 15:23:32 +01:00
data . hooks . PreToolUse = data . hooks . PreToolUse || [];
if ( ! JSON . stringify ( data . hooks . PreToolUse ). includes ( "democracy/scripts/council.py" )) {
const council = path . join ( path . dirname ( settingsPath ), "skills" , "democracy" , "scripts" , "council.py" );
data . hooks . PreToolUse . push ({
matcher : "Edit|Write|MultiEdit|NotebookEdit" ,
hooks : [{ type : "command" , command : `python3 ${ JSON . stringify ( council ) } gate` }],
});
}
2026-10-30 17:37:47 +01:00
const watch = path . join ( path . dirname ( settingsPath ), "skills" , "democracy" , "scripts" , "watch.py" );
const WATCH_HOOKS = [
[ "UserPromptSubmit" , null , "watch" ],
[ "PostToolUse" , "Bash" , "watch" ],
[ "PreToolUse" , "Bash" , "block" ],
];
for ( const [ event , matcher , sub ] of WATCH_HOOKS ) {
data . hooks [ event ] = data . hooks [ event ] || [];
const registered = data . hooks [ event ]. some (( entry ) =>
( entry . hooks || []). some (( hook ) =>
String ( hook . command ). includes ( "democracy/scripts/watch.py" ) && String ( hook . command ). endsWith ( ` ${ sub } ` )));
if ( registered ) continue ;
const entry = { hooks : [{ type : "command" , command : `python3 ${ JSON . stringify ( watch ) } ${ sub } ` }] };
data . hooks [ event ]. push ( matcher ? { matcher , ... entry } : entry );
}
2026-07-10 14:59:17 +02:00
fs . writeFileSync ( settingsPath , JSON . stringify ( data , null , 2 ) + "\n" );
2026-10-30 17:37:47 +01:00
console . log ( `skills: enabled caveman + ponytail plugins, the ${ REMINDERS . join ( " + " ) } reminders, the democracy claim gate and resource watch in ${ settingsPath } ` );