11from __future__ import annotations
22
33import importlib .util
4+ import json
45import pkgutil
56import shutil
67from pathlib import Path
@@ -127,29 +128,63 @@ def _install_skills_to(
127128 return installed_count , removed_count
128129
129130
130- @click .command ()
131- @click .option (
132- "--install/--no-install" ,
133- is_flag = True ,
134- help = "Install skills to agent directories" ,
135- )
136- def skills (install : bool ) -> None :
137- """Install skills from Plain packages"""
131+ def _setup_session_hook (dest_dir : Path ) -> None :
132+ """Create or update settings.json with SessionStart hook."""
133+ settings_file = dest_dir / "settings.json"
138134
135+ # Load existing settings or start fresh
136+ if settings_file .exists ():
137+ settings = json .loads (settings_file .read_text ())
138+ else :
139+ settings = {}
140+
141+ # Ensure hooks structure exists
142+ if "hooks" not in settings :
143+ settings ["hooks" ] = {}
144+
145+ # Define the Plain hook - calls the agent context command directly
146+ plain_hook = {
147+ "matcher" : "startup|resume" ,
148+ "hooks" : [
149+ {
150+ "type" : "command" ,
151+ "command" : "uv run plain agent context 2>/dev/null || true" ,
152+ }
153+ ],
154+ }
155+
156+ # Get existing SessionStart hooks, remove any existing plain hook
157+ session_hooks = settings ["hooks" ].get ("SessionStart" , [])
158+ session_hooks = [h for h in session_hooks if "plain agent" not in str (h )]
159+ # Also remove old plain-context.md hooks for migration
160+ session_hooks = [h for h in session_hooks if "plain-context.md" not in str (h )]
161+ session_hooks .append (plain_hook )
162+ settings ["hooks" ]["SessionStart" ] = session_hooks
163+
164+ settings_file .write_text (json .dumps (settings , indent = 2 ) + "\n " )
165+
166+
167+ @click .group ()
168+ def agent () -> None :
169+ """AI agent integration for Plain projects"""
170+ pass
171+
172+
173+ @agent .command ()
174+ def context () -> None :
175+ """Output Plain framework context for AI agents"""
176+ click .echo ("This is a Plain project. Use the /plain-* skills for common tasks." )
177+
178+
179+ @agent .command ()
180+ def install () -> None :
181+ """Install skills and hooks to agent directories"""
139182 skills_by_package = _get_packages_with_skills ()
140183
141184 if not skills_by_package :
142185 click .echo ("No skills found in installed packages." )
143186 return
144187
145- if not install :
146- # Just list available skills
147- click .echo ("Available skills:" )
148- for pkg_name in sorted (skills_by_package .keys ()):
149- for skill_dir in skills_by_package [pkg_name ]:
150- click .echo (f" - { skill_dir .name } (from { pkg_name } )" )
151- return
152-
153188 # Find destinations based on what agent directories exist
154189 destinations = _get_skill_destinations ()
155190
@@ -163,10 +198,30 @@ def skills(install: bool) -> None:
163198 # Install to each destination
164199 for dest in destinations :
165200 installed_count , removed_count = _install_skills_to (dest , skills_by_package )
166- if installed_count > 0 or removed_count > 0 :
167- parts = []
168- if installed_count > 0 :
169- parts .append (f"installed { installed_count } " )
170- if removed_count > 0 :
171- parts .append (f"removed { removed_count } " )
172- click .echo (f"Skills: { ', ' .join (parts )} in { dest } /" )
201+
202+ # Setup hook in parent directory
203+ parent_dir = dest .parent # .claude/ or .codex/
204+ _setup_session_hook (parent_dir )
205+
206+ parts = []
207+ if installed_count > 0 :
208+ parts .append (f"installed { installed_count } skills" )
209+ if removed_count > 0 :
210+ parts .append (f"removed { removed_count } skills" )
211+ parts .append ("updated hooks" )
212+ click .echo (f"Agent: { ', ' .join (parts )} in { parent_dir } /" )
213+
214+
215+ @agent .command ()
216+ def skills () -> None :
217+ """List available skills from installed packages"""
218+ skills_by_package = _get_packages_with_skills ()
219+
220+ if not skills_by_package :
221+ click .echo ("No skills found in installed packages." )
222+ return
223+
224+ click .echo ("Available skills:" )
225+ for pkg_name in sorted (skills_by_package .keys ()):
226+ for skill_dir in skills_by_package [pkg_name ]:
227+ click .echo (f" - { skill_dir .name } (from { pkg_name } )" )
0 commit comments