Skip to content

feat: 1Password vault plugin, Codex hook, native session-ensure --hook, initflow decomposition - #7

Merged
lorem-dev merged 7 commits into
developfrom
feat/1password-support
May 22, 2026
Merged

feat: 1Password vault plugin, Codex hook, native session-ensure --hook, initflow decomposition#7
lorem-dev merged 7 commits into
developfrom
feat/1password-support

Conversation

@lorem-dev

Copy link
Copy Markdown
Owner

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.

  1. 1Password vault plugin (plugins/onepassword/) backed by the
    op CLI. Auto-detected when op is on PATH. Supports
    op://-full references plus shorthand Item/field and Item
    (with the default field set to password) via the new
    vaults.<name>.vault config field. Test coverage >=90%, no
    real-op shellouts in tests.
  2. Native session-ensure --hook for Claude Code. The
    UserPromptSubmit hook is now locksmith session ensure --hook,
    emitting {\"env\":{\"LOCKSMITH_SESSION\":\"...\"}} via
    encoding/json. Eliminates the printf/$(...) shell-quoting
    drift that surfaced as Bash(export LOCKSMITH...) lines in
    agent logs. Legacy ~/.config/locksmith/agent-hook.sh installs
    are migrated transparently on the next init.
  3. Codex SessionStart hook installed into ~/.codex/hooks.json
    so 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.
  4. locksmith restart standalone command + shared
    internal/daemon lifecycle helpers (IsRunning, Start, Stop,
    Restart). locksmith init now restarts a running daemon at end
    of run; PATH-snippet appended to shell rc when the locksmith
    binary directory is not already on $PATH.
  5. internal/initflow decomposition. flow.go (1002 lines)
    split into types.go, prompter.go, apply.go, select.go,
    agents.go. Per-agent installer logic moved into
    internal/initflow/agents/<name>/ subpackages (claude, codex,
    gemini, opencode, generic). Hook installers moved into
    internal/initflow/hooks/<name>/ (claude + new codex) with
    shared 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 to permissions.allow so the agent
can call locksmith without per-call approval. Agent templates
wrapped with <!-- LOCKSMITH_START -->/<!-- LOCKSMITH_END -->
markers so re-runs of init upsert the locksmith-managed block
without disturbing user content. CLAUDE.md gets an 'Adding a new
vault plugin' checklist + CodeGraph guidance copied from AGENTS.md.

Commit history

Seven squashed commits (50+ original commits collapsed; the
abandoned _agent-hook subcommand attempt is dropped entirely):

  1. feat(sdk): add InvalidArgumentError constructor
  2. feat(plugin): add 1Password vault plugin via op CLI
  3. feat(init): wrap agent templates with markers and pre-allow Bash(locksmith:*)
  4. feat(daemon): add lifecycle helpers, locksmith restart, and PATH bootstrap
  5. feat(init): native session-ensure --hook with legacy migration and daemon auto-restart
  6. refactor(initflow): decompose flow.go into agents/<name> and hooks/<name> subpackages
  7. feat(init): Codex SessionStart hook and per-agent template rewrites with 3 vaults

Test plan

  • make verify passes (lint, race, coverage >=90%, GPG, docs,
    CHANGES.md).
  • Run locksmith init on a clean machine with Claude Code,
    Codex, Gemini CLI, and OpenCode detected; confirm:
    - ~/.claude/settings.json registers locksmith session ensure --hook as the UserPromptSubmit hook, with
    Bash(locksmith:*) allow rule.
    - ~/.codex/hooks.json registers 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.
  • Migration: pre-seed ~/.claude/settings.json with the legacy
    agent-hook.sh command; run init; confirm the entry is
    rewritten to the native command, the Bash(.../agent-hook.sh)
    permission rule is gone, and the on-disk script is removed.
  • 1Password plugin: locksmith vault health reports the plugin
    available when op is 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 the
GitHub-generated merge commit on develop and not part of this
branch. Same fix applies project-wide; out of scope for this PR.

lorem-dev added 7 commits May 23, 2026 00:37
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.
@lorem-dev
lorem-dev merged commit c7bcb59 into develop May 22, 2026
3 checks passed
@lorem-dev
lorem-dev deleted the feat/1password-support branch May 23, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant