devin

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 13 Imported by: 0

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

View Source
const ModePreview = manual.ModePreview
View Source
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 AdditionalContext(enhanced string) string

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

func HookCommand(bin string, envFile string) string

HookCommand builds the user-scope hook command (no scope suffix).

func HookCommandForScope

func HookCommandForScope(bin string, envFile string, scope string) string

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

func IsOpenPEHookCommand(command string) bool

IsOpenPEHookCommand reports whether a hook command string is openPE's Devin hook, used for idempotent install and duplicate detection.

func LastPreviewPath

func LastPreviewPath() (string, error)

func LastPromptPath

func LastPromptPath() (string, error)

func MarkdownPreview

func MarkdownPreview(enhanced string, language string) string

func MergeConfigHooks

func MergeConfigHooks(existing []byte, command string, timeout int) ([]byte, error)

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

func MergeStandaloneHooks(existing []byte, command string, timeout int) ([]byte, error)

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 PreviewReason(cachePath string, language string) string

func ProjectEnvFile

func ProjectEnvFile(cwd string) string

ProjectEnvFile is the default project-scope dotenv file.

func ProjectHooksPath

func ProjectHooksPath(cwd string) string

ProjectHooksPath is the project-scope standalone hooks file.

func ReadLastPreview

func ReadLastPreview() (string, error)

func ReadLastPrompt

func ReadLastPrompt() (string, error)

func SavePreview

func SavePreview(enhanced string, language string) (string, error)

func ShouldHandleHook

func ShouldHandleHook(input HookInput, auto bool) (bool, bool)

func UserConfigPath

func UserConfigPath() string

UserConfigPath is the user-scope Devin config (hooks under "hooks").

func WriteTerminalPreview

func WriteTerminalPreview(content string) error

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.

func DecodeHookInput

func DecodeHookInput(r io.Reader) (HookInput, error)

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 HookError

func HookError(manualTrigger bool, message string, language string) HookOutput

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.

type HookSpecificOutput

type HookSpecificOutput struct {
	HookEventName     string `json:"hookEventName"`
	AdditionalContext string `json:"additionalContext,omitempty"`
}

type Mode

type Mode = manual.Mode

func ParseManualEnhance

func ParseManualEnhance(prompt string) (string, []string, Mode, bool)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL