Documentation
¶
Overview ¶
Package devin adapts openPE to the Devin CLI's UserPromptSubmit hook.
Devin CLI uses a Claude-Code-compatible hook format, so the runtime contract here mirrors the codex adapter: decode the UserPromptSubmit payload, detect the manual `pe` trigger, run the enhancer, and block the original message while delivering the enhanced prompt (clipboard + stderr status). The Devin CLI is a terminal coding agent like Codex/Claude Code, so the implementation approach is intentionally the same.
Index ¶
- Constants
- func AdditionalContext(enhanced string) string
- func EncodeHookOutput(w io.Writer, output HookOutput) error
- func EncodeHookOutputOrFallback(w io.Writer, output HookOutput) int
- func HasUserOpenPEHookConfig() bool
- func HookCommand(bin string, envFile string) string
- func HookCommandForScope(bin string, envFile string, scope string) string
- func IsOpenPEHookCommand(command string) bool
- func LastPreviewPath() (string, error)
- func LastPromptPath() (string, error)
- func MarkdownPreview(enhanced string, language string) string
- func MergeConfigHooks(existing []byte, command string, timeout int) ([]byte, error)
- func MergeStandaloneHooks(existing []byte, command string, timeout int) ([]byte, error)
- func PreviewReason(cachePath string, language string) string
- func ProjectEnvFile(cwd string) string
- func ProjectHooksPath(cwd string) string
- func ReadLastPreview() (string, error)
- func ReadLastPrompt() (string, error)
- func SavePreview(enhanced string, language string) (string, error)
- func ShouldHandleHook(input HookInput, auto bool) (bool, bool)
- func UserConfigPath() string
- func WriteTerminalPreview(content string) error
- type HookInput
- type HookOptions
- type HookOutput
- func Block(reason string) HookOutput
- func BlockPreview(reason string, terminalPreview string, previewPrompt string) HookOutput
- func HandleHook(ctx context.Context, service *enhancer.Service, input HookInput, ...) (HookOutput, error)
- func HookError(manualTrigger bool, message string, language string) HookOutput
- func InjectionOutput(enhanced string, language string) HookOutput
- func Skip(message string) HookOutput
- func SkipOutput() HookOutput
- type HookSpecificOutput
- type Mode
Constants ¶
const ModePreview = manual.ModePreview
const UserPromptSubmit = "UserPromptSubmit"
UserPromptSubmit is the Devin CLI hook event fired when the user submits a message (Claude-Code-compatible name).
Variables ¶
This section is empty.
Functions ¶
func AdditionalContext ¶
func EncodeHookOutput ¶
func EncodeHookOutput(w io.Writer, output HookOutput) error
func EncodeHookOutputOrFallback ¶
func EncodeHookOutputOrFallback(w io.Writer, output HookOutput) int
func HasUserOpenPEHookConfig ¶
func HasUserOpenPEHookConfig() bool
HasUserOpenPEHookConfig reports whether the user-scope Devin config already contains an openPE hook (used to skip redundant project installs).
func HookCommand ¶
HookCommand builds the user-scope hook command (no scope suffix).
func HookCommandForScope ¶
HookCommandForScope builds the shell command Devin runs for the UserPromptSubmit hook. It mirrors the Codex adapter's delivery model — block the original message and copy the enhanced prompt to the clipboard for the user to paste/edit/resubmit — so the experience is consistent across clients. The transport is the same as Codex too: exit code 2 with the reason on stderr (the binary's --block-output default). Devin only documents exit-2 as the hook block channel and reads the reason from stderr since 3000.3.22; a stdout {"decision":"block"} on UserPromptSubmit is not honoured (2026-08-03 incident: an enhanced-and-"blocked" prompt reached the model).
func IsOpenPEHookCommand ¶
IsOpenPEHookCommand reports whether a hook command string is openPE's Devin hook, used for idempotent install and duplicate detection.
func LastPreviewPath ¶
func LastPromptPath ¶
func MarkdownPreview ¶
func MergeConfigHooks ¶
MergeConfigHooks merges the openPE hook into a Devin config file (`~/.config/devin/config.json` or `.devin/config.json`), where hooks live under the top-level "hooks" key. All other config keys are preserved.
func MergeStandaloneHooks ¶
MergeStandaloneHooks merges the openPE hook into a `.devin/hooks.v1.json` file, where the hooks object IS the entire file (no "hooks" wrapper key).
func PreviewReason ¶
func ProjectEnvFile ¶
ProjectEnvFile is the default project-scope dotenv file.
func ProjectHooksPath ¶
ProjectHooksPath is the project-scope standalone hooks file.
func ReadLastPreview ¶
func ReadLastPrompt ¶
func UserConfigPath ¶
func UserConfigPath() string
UserConfigPath is the user-scope Devin config (hooks under "hooks").
func WriteTerminalPreview ¶
Types ¶
type HookInput ¶
type HookInput struct {
HookEventName string `json:"hook_event_name"`
Prompt string `json:"prompt"`
CWD string `json:"cwd"`
}
HookInput is the Devin UserPromptSubmit stdin payload. The lifecycle docs only guarantee `prompt`; `cwd` is accepted for forward/Claude compatibility but the runtime falls back to DEVIN_PROJECT_DIR / process cwd when absent.
type HookOptions ¶
type HookOptions struct {
Client string
Mode string
Auto bool
CWD string
Prompt string
Language string
Timeout time.Duration
History []enhancer.Message
// Inject makes a manual `pe` trigger inject the enhanced prompt as
// additional context (exit 0, JSON hookSpecificOutput) instead of blocking
// the original message with a clipboard/preview handoff. Devin consumes
// additionalContext natively. (Codex CLI and Claude Code CLI also consume
// UserPromptSubmit additionalContext — so the unified inject switch covers
// them too; only Windsurf cannot, and there the switch is a no-op.) The
// default (false) preserves the review/clipboard behaviour for all clients.
Inject bool
// MaxContextTokens forwards the consumer-layer global token budget
// (config.Config.MaxContextTokens, sourced from OPENPE_MAX_CONTEXT_TOKENS)
// into enhancer.Request.Options. Zero means "no budget" so this field is
// purely additive.
MaxContextTokens int
CacheDir string
// SpecsDir / SpecMaxChars configure explicit user prompt-spec loading
// (`pe+<name> <task>`, config.Config.Specs). Empty dir means the per-user
// default ~/.config/openpe/specs; MaxChars <= 0 means the specs package
// default. Spec resolution failures BLOCK the enhancement (business
// contract D7: never silently drop a user-named spec).
SpecsDir string
SpecMaxChars int
}
type HookOutput ¶
type HookOutput struct {
Continue bool `json:"continue,omitempty"`
Decision string `json:"decision,omitempty"`
Reason string `json:"reason,omitempty"`
SystemMessage string `json:"systemMessage,omitempty"`
HookSpecificOutput *HookSpecificOutput `json:"hookSpecificOutput,omitempty"`
TerminalPreview string `json:"-"`
PreviewPrompt string `json:"-"`
// Warnings carries enhancer.Response.Warnings (language guard + content
// warnings) so the runner can fold them into the user-facing disclosure
// (block Reason / inject SystemMessage). Not part of the wire JSON.
Warnings []string `json:"-"`
// AppliedSpecs lists the user spec names appended to the enhanced prompt
// (`pe+<name>`), so the runner can disclose "applied specs: …" alongside
// the delivery status. Not part of the wire JSON.
AppliedSpecs []string `json:"-"`
}
func Block ¶
func Block(reason string) HookOutput
func BlockPreview ¶
func BlockPreview(reason string, terminalPreview string, previewPrompt string) HookOutput
func HandleHook ¶
func HandleHook(ctx context.Context, service *enhancer.Service, input HookInput, opts HookOptions) (HookOutput, error)
func InjectionOutput ¶
func InjectionOutput(enhanced string, language string) HookOutput
InjectionOutput builds the UserPromptSubmit output that injects an enhanced prompt as additional context (the non-preview success shape). It is exported so other adapters can render Devin-native output when the Devin CLI imports and runs their hooks: stdout hookSpecificOutput.additionalContext is the one documented Devin injection channel (blocks, by contrast, travel as exit-2 with the reason on stderr — see the runner's emitDevinBlock).
func Skip ¶
func Skip(message string) HookOutput
func SkipOutput ¶
func SkipOutput() HookOutput
SkipOutput is the no-op a de-duplication loser emits: it lets the host proceed with the original prompt without a second injection or a block, so only the single winning hook enhances the prompt.