mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
* feat(events): context injection for opencode and JSON-envelope agent hooks Adds first-class context injection to agent runtime events: 1. opencode: maps session_start to experimental.chat.system.transform (injects into system prompt) and user_prompt_submit to chat.message (injects synthetic TextPart). TS plugin captures runEvent stdout (stdio pipe, encoding utf-8) and pushes into output objects. Part IDs derive from output.parts[last].id to preserve OpenCode's prt_ brand and prevent session schema crashes. 2. JSON-envelope hook wrapping: adds events_context_envelope to IntegrationBase so agents that require JSON on stdout receive their target envelope via the dispatcher's 5th argument: - gemini, tabnine, qwen, devin: hookSpecificOutput.additionalContext on session_start/user_prompt_submit; suppress on non-injectable events (prevents systemMessage user-facing noise) - copilot: top-level additionalContext on session_start - cursor: top-level additional_context on session_start; suppress elsewhere - claude, codex: plain stdout passthrough (already injected) 3. Dispatcher template and resolve_and_run_event_command parse the 5th envelope arg and wrap stdout accordingly. Tests added for opencode TextPart schema, part ID derivation, envelope command generation, and dispatcher output wrapping. All 162 events/integration tests pass. * fix(events): address code review on #3934 - Qwen/Gemini/Tabnine/Devin: include native hookEventName inside hookSpecificOutput envelope (required by Qwen's hooks spec). Thread the native event name from the integration's CANONICAL_TO_NATIVE through _dispatcher_command as a 6th dispatcher argument, through the dispatcher template's main()/_run_inline()/_emit(), and through resolve_and_run_event_command()/_emit_event_stdout(). - Copilot: map user_prompt_submit to additionalContext (previously unmapped, breaking per-prompt context injection despite Copilot CLI supporting it via userPromptSubmitted). - OpenCode: guard experimental.chat.system.transform so canonical session_start handlers only run when input.sessionID is present — OpenCode fires this hook for non-session operations (e.g. agent generation) with no sessionID. Assisted-by: opencode (model: glm-5.2, supervised) * fix(events): address second Copilot review round on #3934 - Positional arg alignment: always emit default timeout (60s) as the 4th dispatcher argument even when timeout_seconds is omitted, so the envelope (5th) and native_event (6th) land in the correct argv slots. Previously, omitting timeout_seconds caused the envelope to be parsed as an invalid timeout, silently falling back to plain stdout. - OpenCode session_start caching: cache handler output per sessionID in the generated TS plugin so non-idempotent handlers (setup, telemetry, file-mutating scripts) run once per session instead of on every LLM request. Cache is evicted on session.deleted. - Updated PR description to reflect Copilot user_prompt_submit now maps to additionalContext (was documented as plain/unprocessed). Assisted-by: opencode (model: glm-5.2, supervised)
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
"""Tabnine CLI integration."""
|
|
|
|
from ..base import TomlIntegration
|
|
|
|
|
|
class TabnineIntegration(TomlIntegration):
|
|
key = "tabnine"
|
|
config = {
|
|
"name": "Tabnine CLI",
|
|
"folder": ".tabnine/agent/",
|
|
"commands_subdir": "commands",
|
|
"install_url": "https://docs.tabnine.com/main/getting-started/tabnine-cli",
|
|
"requires_cli": True,
|
|
}
|
|
registrar_config = {
|
|
"dir": ".tabnine/agent/commands",
|
|
"format": "toml",
|
|
"args": "{{args}}",
|
|
"extension": ".toml",
|
|
}
|
|
multi_install_safe = True
|
|
|
|
CANONICAL_TO_NATIVE = {
|
|
"session_start": "SessionStart",
|
|
"pre_tool_use": "BeforeTool",
|
|
"post_tool_use": "AfterTool",
|
|
"session_end": "SessionEnd",
|
|
# Tabnine's Gemini-compatible schema also provides BeforeAgent and
|
|
# AfterAgent (S7); mapping them so user_prompt_submit and stop
|
|
# extension handlers fire instead of being skipped.
|
|
"user_prompt_submit": "BeforeAgent",
|
|
"stop": "AfterAgent",
|
|
}
|
|
events_config_file = ".tabnine/agent/settings.json"
|
|
events_format = "json-nested"
|
|
# Tabnine is Gemini-hooks-compatible (JSON-only stdout) (C13).
|
|
events_context_envelope = {
|
|
"*": "suppress",
|
|
"session_start": "hookSpecificOutput",
|
|
"user_prompt_submit": "hookSpecificOutput",
|
|
}
|
|
# Tabnine mirrors Gemini's hook schema (BeforeTool/AfterTool) and, like
|
|
# Gemini, measures hook timeouts in milliseconds. Declaring the unit makes
|
|
# the shared formatter convert the 60s default to 60000ms instead of
|
|
# emitting timeout: 60 (60 ms), which would terminate the dispatcher
|
|
# before it starts (R5).
|
|
events_timeout_unit = "ms"
|