Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c5f1e96
feat: first-class agent-native runtime hooks for integrations
kanfil Jul 23, 2026
da6c20d
refactor: rework integration events per maintainer review
kanfil Jul 25, 2026
6ec91bb
fix(events): resolve ruff lint errors blocking CI
kanfil Jul 27, 2026
61f0a45
fix(events): make generated native hooks actually execute
kanfil Jul 27, 2026
0d546a2
fix(events): merge/teardown idempotency and data safety
kanfil Jul 27, 2026
2623ddb
fix(events): honor enabled flag, refresh on extension lifecycle, stri…
kanfil Jul 27, 2026
c5b9a93
revert: drop CHANGELOG.md/pyproject.toml version bumps from events fixes
kanfil Jul 27, 2026
aaab4d2
fix(events): compose --events into Copilot/Devin options() (#8, #9)
kanfil Jul 27, 2026
524c210
fix(events): Cursor version field, matcher grouping, Copilot cross-OS
kanfil Jul 27, 2026
ceee507
fix(events): anchor py scripts and prefix ps launcher in command runner
kanfil Jul 27, 2026
be43bc4
fix(events): skip-tracking on parse fail, drop dispatcher claim on re…
kanfil Jul 27, 2026
c74e5a4
test(extensions): update stale validation-message assertion
kanfil Jul 27, 2026
123e10a
fix(events): forced-teardown data safety, manifest-driven command res…
kanfil Jul 27, 2026
6761c94
fix(events): subprocess cwd, shell quoting, TOML matcher escaping, Ta…
kanfil Jul 27, 2026
ea33369
fix(events): POSIX dispatcher path constant + platform-agnostic tests
kanfil Jul 28, 2026
f7715a5
fix(events): override layer preservation, matcher validation, event c…
kanfil Jul 28, 2026
a51ff92
fix(events): protect shared dispatcher from stale cleanup, delete Cur…
kanfil Jul 28, 2026
cb66271
fix(events): host target uses POSIX quoting, Claude dispatcher double…
kanfil Jul 28, 2026
e113956
fix(events): opencode TS plugin resolves dispatcher from directory, e…
kanfil Jul 28, 2026
9b3f249
fix(events): Qwen ms timeout, Devin root-nested format, Copilot agent…
kanfil Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,11 @@ def version(
from .integrations._commands import register as _register_integration_cmds # noqa: E402
_register_integration_cmds(app)


# ===== Event Commands =====
from .commands.event import register as _register_event_cmds # noqa: E402
_register_event_cmds(app)

# Re-export selected helpers to preserve the public import surface.
from .integrations._helpers import ( # noqa: E402
_clear_init_options_for_integration as _clear_init_options_for_integration,
Expand Down
34 changes: 34 additions & 0 deletions src/specify_cli/commands/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""specify event * command handlers."""

from __future__ import annotations

from pathlib import Path
import sys
import typer

event_app = typer.Typer(
name="event",
help="Manage and execute event-driven commands",
add_completion=False,
)


@event_app.command("run")
def event_run(
command_name: str = typer.Argument(..., help="Name of the command to execute"),
event_name: str = typer.Argument(..., help="Canonical event name (e.g., session_start)"),
):
"""Resolve and run an event-driven command script with stdin payload."""
from ..events import resolve_and_run_event_command

# Read payload from stdin if available
payload = sys.stdin.read() if not sys.stdin.isatty() else "{}"

# Run the event command
project_root = Path.cwd() # The agent runs events from project root
exit_code = resolve_and_run_event_command(command_name, event_name, payload, project_root)
raise typer.Exit(code=exit_code)


def register(app: typer.Typer) -> None:
app.add_typer(event_app, name="event")
8 changes: 8 additions & 0 deletions src/specify_cli/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,20 @@ def init(
if extra:
integration_parsed_options.update(extra)

from ..events import resolve_events
events_map = resolve_events(
resolved_integration.key,
resolved_integration.config,
project_path,
integration_parsed_options or None,
)
resolved_integration.setup(
project_path,
manifest,
parsed_options=integration_parsed_options or None,
script_type=selected_script,
raw_options=integration_options,
events=events_map,
)
manifest.save()

Expand Down
Loading