feat: 1Password vault plugin, Codex hook, native session-ensure --hook, initflow decomposition - #7
Merged
Merged
Conversation
Built-in 1Password vault plugin that retrieves secrets through the op CLI. Detected by locksmith init when op is on PATH; falls back to manual selection otherwise. Includes parsePath for three reference forms (op://full, item/field, item with default field), op-stderr error classification into typed SDK errors (NotFound, PermissionDenied, Unauthenticated, InvalidArgument), explicit env allowlist that excludes OP_SERVICE_ACCOUNT_TOKEN, HealthCheck via 'op --version' + 'op vault list', and >=90% test coverage with no real-op shellouts. Adds the vaults.<name>.vault config field, delivered to plugins as opts["vault"], so paths like "Item/field" can be qualified against a default 1Password vault. Docs: new plugins/onepassword/README.md, 1password section in docs/configuration.md, vault row in README.md.
…smith:*) Two coupled changes to make locksmith init re-runnable and friendly to Claude Code's Bash-permission UX: - Every agent template (Claude/Codex/Gemini/OpenCode and the generic agent file) is wrapped in <!-- LOCKSMITH_START --> / <!-- LOCKSMITH_END --> markers. The init wizard upserts the locksmith-managed block in place on every run, preserving any user content outside the markers. - locksmith init now adds Bash(locksmith:*) to permissions.allow in ~/.claude/settings.json so the agent can call locksmith directly without per-call approval. Idempotent: re-running does not duplicate the rule. The two were originally split across two commits (template-marker support + permission wiring); they ship together so an upgrade gets both behaviours in one go.
…strap Three coupled additions building the daemon-management surface: - internal/daemon: lifecycle helpers (IsRunning, Start, Stop, Restart, DefaultSocketPath) plus per-platform peerPID via SO_PEERCRED on Linux and LOCAL_PEERPID on darwin. _autostart now delegates to these helpers; the cli has no inline probe/spawn logic left. - internal/cli/restart_cmd.go: public 'locksmith restart' command with --timeout (5s default) and --no-start flags. Stops the running daemon (SIGTERM, escalates to SIGKILL after grace) and starts a fresh one. See docs/restart.md. - internal/shellhook/pathhook.go: per-shell PATH-snippet helpers (bash/zsh/ash/posix use case-style; fish uses 'not contains'). internal/initflow/flow.go::applyPathHook wires it in so init appends the snippet to the user's rc file when the locksmith binary directory is not already on PATH. Never creates a missing rc file.
…emon auto-restart
Replace the shell-script Claude Code UserPromptSubmit hook with a
native subcommand and migrate existing installs.
- locksmith session ensure --hook emits {\"env\":{\"LOCKSMITH_SESSION\":\"...\"}}
on stdout via encoding/json. No printf, no \$(...) - eliminates
the quoting drift that surfaced as Bash(export LOCKSMITH...) lines
in agent logs. Exits 0 silently when the daemon is unreachable so
the hook never blocks a prompt; mutually exclusive with --quiet.
- ClaudeHookInstaller switches the registered command to
'locksmith session ensure --hook'. The previously added
Bash(<hookCmd>) permission rule is gone; hooks run through
Claude's hook subsystem, not Bash, so the rule was redundant.
- Legacy installs with command pointing at the deprecated
~/.config/locksmith/agent-hook.sh script are migrated
transparently: the command is rewritten, the matching
Bash(.../agent-hook.sh) permission rule is dropped, and the
on-disk script is removed.
- applyDaemonRestart at end of applyInit restarts a running daemon
so plugin/config changes take effect immediately. Best-effort:
warning on failure, init does not fail.
- Cross-package lint fixes from the new wiring.
The earlier '_agent-hook' subcommand attempt was discarded; this
flag-on-existing-subcommand approach uses the existing 'session
ensure' surface and is cleaner.
…ame> subpackages
Split internal/initflow/flow.go (1002 lines) into focused files in
the same package and move per-agent / per-hook logic into
subpackages.
Top-level initflow files:
- flow.go (now ~170 lines, just RunInit + applyInit)
- types.go (InitOptions, InitResult, Prompter)
- prompter.go (huhPrompter implementation)
- apply.go (applyXxx helpers)
- select.go (selectXxx + consent helpers)
- agents.go (AgentWriter dispatcher)
New subpackages:
- internal/initflow/agents/marker - LOCKSMITH_START/END constants
and Upsert helper.
- internal/initflow/agents/{claude,codex,gemini,opencode,generic} -
per-agent install logic with embedded templates. Each exports
Install(homeDir, configDir) and ReadTemplateForTest. Gemini was
dead code (template existed, no dispatch); now wired up.
- internal/initflow/hooks - shared JSON-settings helpers
(ReadSettings, WriteSettings, FindStringInAllow, ErrMalformed).
- internal/initflow/hooks/claude - ClaudeHookInstaller moved here
and renamed to Installer + New constructor.
No behaviour change. Pure structural refactor that prepares the
codebase for the Codex hook installer added in the next commit.
CLAUDE.md gains a 'CodeGraph MCP guidance' section copied from
AGENTS.md to give Claude Code the same code-navigation defaults.
…ith 3 vaults
- New internal/initflow/hooks/codex.Installer registers a Codex
SessionStart hook in ~/.codex/hooks.json. The hook command is
'locksmith session ensure --quiet >/dev/null 2>&1 || true' with a
10s timeout. Best-effort: any failure is swallowed so Codex
startup is never blocked. Codex hooks cannot inject env vars, so
the agent still has to 'export LOCKSMITH_SESSION=\$(locksmith
session ensure --quiet)' in shell; the hook just removes the
vault-unlock latency spike on the first secret request.
- locksmith init detects Codex and (with --auto or user consent)
installs the hook via the new applyCodexHook step. Adds
Prompter.CodexHook to the interface.
- Per-agent templates rewritten to:
- mention only their own agent (no cross-agent text)
- show usage examples for all three shipped vault types: gopass,
keychain, and op (1Password)
- include the export LOCKSMITH_SESSION=\$(locksmith session
ensure --quiet) instruction for Codex / Gemini / OpenCode /
generic (no env-injection hook); claude templates omit it
(their hook handles env).
- CLAUDE.md gets an 'Adding a new vault plugin' checklist that
enumerates every doc + every template that must be updated when
shipping a new vault.
- Docs sweep: agent-integration.md gains Codex Hook Setup,
CHANGES.md adds the missing bullets (restart command, PATH
bootstrap, Codex hook, per-agent templates) and drops stale ones,
README and CONTRIBUTING list onepassword consistently, PLUGINS.md
links the onepassword plugin README, verification.md anchor
fixed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Large feature branch covering four user-facing additions plus a
structural refactor. Built up over many iterations and squashed to
seven logical commits before this PR.
plugins/onepassword/) backed by theopCLI. Auto-detected whenopis on PATH. Supportsop://-full references plus shorthandItem/fieldandItem(with the default field set to
password) via the newvaults.<name>.vaultconfig field. Test coverage >=90%, noreal-
opshellouts in tests.--hookfor Claude Code. TheUserPromptSubmit hook is now
locksmith session ensure --hook,emitting
{\"env\":{\"LOCKSMITH_SESSION\":\"...\"}}viaencoding/json. Eliminates theprintf/$(...)shell-quotingdrift that surfaced as
Bash(export LOCKSMITH...)lines inagent logs. Legacy
~/.config/locksmith/agent-hook.shinstallsare migrated transparently on the next
init.~/.codex/hooks.jsonso vault unlock (passphrase, biometric) fires before the agent's
first secret request. Codex hooks cannot inject env vars; the
pre-warm just removes the first-touch latency spike.
locksmith restartstandalone command + sharedinternal/daemonlifecycle helpers (IsRunning,Start,Stop,Restart).locksmith initnow restarts a running daemon at endof run; PATH-snippet appended to shell rc when the locksmith
binary directory is not already on
$PATH.internal/initflowdecomposition.flow.go(1002 lines)split into
types.go,prompter.go,apply.go,select.go,agents.go. Per-agent installer logic moved intointernal/initflow/agents/<name>/subpackages (claude, codex,gemini, opencode, generic). Hook installers moved into
internal/initflow/hooks/<name>/(claude + new codex) withshared
hooks.ReadSettings/WriteSettings/ErrMalformed.Gemini CLI is now actually wired up (
DetectAgents+ dispatcher);it was dead code before. Per-agent templates rewritten: each
mentions only its own agent and shows examples for all three
shipped vault types (gopass, keychain, op).
Plus:
Bash(locksmith:*)added topermissions.allowso the agentcan call
locksmithwithout per-call approval. Agent templateswrapped with
<!-- LOCKSMITH_START -->/<!-- LOCKSMITH_END -->markers so re-runs of
initupsert the locksmith-managed blockwithout disturbing user content.
CLAUDE.mdgets an 'Adding a newvault plugin' checklist + CodeGraph guidance copied from
AGENTS.md.Commit history
Seven squashed commits (50+ original commits collapsed; the
abandoned
_agent-hooksubcommand attempt is dropped entirely):feat(sdk): add InvalidArgumentError constructorfeat(plugin): add 1Password vault plugin via op CLIfeat(init): wrap agent templates with markers and pre-allow Bash(locksmith:*)feat(daemon): add lifecycle helpers, locksmith restart, and PATH bootstrapfeat(init): native session-ensure --hook with legacy migration and daemon auto-restartrefactor(initflow): decompose flow.go into agents/<name> and hooks/<name> subpackagesfeat(init): Codex SessionStart hook and per-agent template rewrites with 3 vaultsTest plan
make verifypasses (lint, race, coverage >=90%, GPG, docs,CHANGES.md).
locksmith initon a clean machine with Claude Code,Codex, Gemini CLI, and OpenCode detected; confirm:
-
~/.claude/settings.jsonregisterslocksmith session ensure --hookas the UserPromptSubmit hook, withBash(locksmith:*)allow rule.-
~/.codex/hooks.jsonregisters the SessionStart pre-warm.- Per-agent docs (
CLAUDE.md,AGENTS.md,GEMINI.md, etc.)each carry only their own agent's instructions and show all
three vault types.
- A running daemon is restarted at end of
init.~/.claude/settings.jsonwith the legacyagent-hook.shcommand; runinit; confirm the entry isrewritten to the native command, the
Bash(.../agent-hook.sh)permission rule is gone, and the on-disk script is removed.
locksmith vault healthreports the pluginavailable when
opis signed in;locksmith get --vault op --path \"op://Personal/<item>/<field>\"retrieves the secret.Known external issue
The CI gate flags one unsigned commit (
196ed29 Merge pull request #6 from lorem-dev/feat/json-rpc-lazy-auth) — that's theGitHub-generated merge commit on
developand not part of thisbranch. Same fix applies project-wide; out of scope for this PR.