cc-os/plugins/os-context/hooks/inject.py

77 lines
2.3 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""SessionStart hook: compose prompts/session-start/*.md into one
additionalContext block.
Fires on startup/resume/clear, and on 'compact' (matcher includes it) so the
composed context survives a context-compaction pass, not just the first turn.
Prompt files live under prompts/<event>/*.md; only the session-start event is
implemented today (SessionEnd cannot inject context ADR-028). Files are
concatenated in filename sort order (e.g. 10-orchestration.md) with no added
separator between them, so a single prompt file present alone reproduces the
exact bytes it contributes.
"""
import glob
import json
import os
import sys
PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SESSION_START_DIR = os.path.join(PLUGIN_ROOT, "prompts", "session-start")
# Deterministic injected-context budget, in lines. Below WARN_LINE_BUDGET,
# inject normally. Between the two, still inject but warn on stderr. Above
# REFUSE_LINE_BUDGET, refuse to inject anything and warn on stderr instead.
WARN_LINE_BUDGET = 120
REFUSE_LINE_BUDGET = 240
def prompt_files(session_start_dir=None):
if session_start_dir is None:
session_start_dir = SESSION_START_DIR
return sorted(glob.glob(os.path.join(session_start_dir, "*.md")))
def compose(paths):
"""Concatenate prompt file contents in order with no added separators."""
parts = []
for path in paths:
with open(path) as f:
parts.append(f.read())
return "".join(parts)
def main():
paths = prompt_files()
if not paths:
return
context = compose(paths)
line_count = len(context.splitlines())
if line_count > REFUSE_LINE_BUDGET:
print(
f"os-context: session-start prompt budget exceeded "
f"({line_count} lines > {REFUSE_LINE_BUDGET}) — refusing to inject.",
file=sys.stderr,
)
return
if line_count > WARN_LINE_BUDGET:
print(
f"os-context: session-start prompt budget exceeded "
f"({line_count} lines > {WARN_LINE_BUDGET}) — injecting anyway.",
file=sys.stderr,
)
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": context,
}
}))
if __name__ == "__main__":
sys.exit(main())