Skip to content

Replace finance-only hook with a generic config-driven artifact hook - #7

Merged
agb1986 merged 2 commits into
mainfrom
generic-artifact-hook
Aug 8, 2026
Merged

agb1986 merged 2 commits into
mainfrom
generic-artifact-hook

Conversation

@agb1986

@agb1986 agb1986 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Stacked on #6 — merge that first. Until then this PR's diff also shows #6's commit; once #6 lands, the diff collapses to just the hook refactor.

Summary

  • hooks/log_artifact_entry.py — a generic PostToolUse hook replacing the finance-agent-specific log_finance_entry.py. The engine keeps only the universal parts: the "script prints the path of the JSON artifact it wrote" convention, config discovery, and posting to /entries.
  • Opt-in per project: the hook acts only in projects with a .ai-data-store.json config at their root (found by walking up from the session cwd) and is a silent no-op everywhere else — so it's safe registered globally, and any future agent project joins by dropping in a config file, no new hook script needed.
  • Config format: regex rules over the artifact path + str.format templates for description/keywords. Named regex groups ({ticker}) and dotted paths into the artifact JSON ({content.verdict.winner}, list indices supported) both resolve. description accepts a template list and uses the first that fully resolves — this replaces the old hook's hand-written conditionals (e.g. price present vs. absent) with graceful degradation. Keywords that fail to resolve are dropped; duplicates removed.
  • hooks/configs/finance-agent.json ports all nine finance rules (verdict, debate, panel, check_stock, check_crypto, history, portfolio, news_results, analysis) and produces the same descriptions/keywords as the old hook.
  • Hardening over the old hook: artifacts outside the opted-in project root are refused, and relative stdout paths resolve against the session cwd.
  • scripts/uninstall_hook.sh removes an installed hook's file and settings entry; install_hook.sh now defaults to the new hook. README documents the config format and the migration:
    ./scripts/uninstall_hook.sh log_finance_entry.py
    ./scripts/install_hook.sh
    cp hooks/configs/finance-agent.json <finance-agent-repo>/.ai-data-store.json

Test plan

  • ruff check . — clean
  • pytest — 57 passed; tests/test_hook.py rewritten with 18 tests: template engine units, all finance artifact types end-to-end through main() asserting byte-identical descriptions/keywords to the old hook, and the no-op paths (no config, outside-project artifact, non-Bash, unmatched filename, missing/invalid artifact)
  • bash -n on both scripts; shipped config validated as JSON
  • After merge: run the migration steps above on the laptop and confirm a finance-agent run still ingests entries

🤖 Generated with Claude Code

agb1986 and others added 2 commits August 8, 2026 12:58
The hook and REST endpoint have each needed a bug-fix PR already, with
no safety net to catch regressions. This adds one and fixes the small
correctness issues found during a repo review:

- 50 pytest tests covering the MCP tools, REST endpoint, both auth
  middlewares, the dashboard, and the finance hook's parsing logic,
  backed by an in-memory MongoDB (mongomock-motor) so no containers
  are needed
- GitHub Actions workflow running ruff + pytest on every push and PR
- Constant-time API key comparison in the MCP/REST bearer auth,
  matching what the dashboard's basic auth already did
- Validate REST body field types with a Pydantic model instead of
  inserting whatever arrives into Mongo
- Clamp list_entries limit/skip (negative values raised from Mongo,
  and limit was unbounded)
- Create indexes on created_at/source/keywords at startup, so list
  and dashboard queries stop collection-scanning as the store grows
- Replace mutable default arguments and a dead `except (InvalidId,
  Exception)` clause

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
log_finance_entry.py hardcoded everything about finance-agent — the cwd
check, the artifact path shape, and every description/keyword rule — so
each new agent project would have needed its own hook script copied to
~/.claude/hooks. This splits the hook into a generic engine and
per-project config:

- hooks/log_artifact_entry.py keeps only the universal parts: the
  "script prints the path of the JSON artifact it wrote" convention,
  config discovery, and posting to /entries. A project opts in by
  having .ai-data-store.json at its root (found by walking up from the
  session cwd); everywhere else the hook stays a silent no-op, so the
  global registration adds no noise. Artifacts outside the opted-in
  project are refused, and relative stdout paths resolve against the
  session cwd.
- The config maps artifacts to entries with regex rules over the path
  and str.format templates for description/keywords. Named regex
  groups ({ticker}) and dotted paths into the artifact JSON
  ({content.verdict.winner}) both resolve; description takes a
  template list and uses the first that fully resolves, which replaces
  the old hook's hand-written conditionals (price present vs not).
- hooks/configs/finance-agent.json ports all nine finance rules and
  produces the same descriptions and keywords as before, verified by
  the rewritten test suite (18 hook tests, 57 total).
- scripts/uninstall_hook.sh removes an installed hook's file and
  settings entry, for migrating off log_finance_entry.py; the README
  documents the config format and migration steps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@agb1986
agb1986 merged commit bf3f6b2 into main Aug 8, 2026
1 check passed
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