agent-notes 2.29.0__tar.gz → 2.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_notes-2.29.0 → agent_notes-2.30.0}/PKG-INFO +56 -8
- {agent_notes-2.29.0 → agent_notes-2.30.0}/README.md +55 -7
- agent_notes-2.30.0/agent_notes/VERSION +1 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/cli.py +1 -1
- agent_notes-2.30.0/agent_notes/commands/hook.py +479 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/wizard/__init__.py +5 -11
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/constants.py +6 -0
- agent_notes-2.30.0/agent_notes/data/agents/shared/pipelines.md +18 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/verification.md +1 -1
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/pricing.yaml +8 -6
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/roles/reasoner.yaml +0 -1
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/chrome-test/SKILL.md +1 -1
- agent_notes-2.30.0/agent_notes/data/templates/frontmatter/__pycache__/base.cpython-314.pyc +0 -0
- agent_notes-2.30.0/agent_notes/data/templates/frontmatter/__pycache__/codex.cpython-314.pyc +0 -0
- agent_notes-2.30.0/agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- agent_notes-2.30.0/agent_notes/data/templates/frontmatter/base.py +47 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/codex.py +7 -24
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/opencode.py +3 -47
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/model.py +1 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/role.py +1 -2
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/model_registry.py +1 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/role_registry.py +0 -1
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/_claude_backend.py +24 -8
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/_pricing.py +39 -7
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/credentials.py +2 -2
- agent_notes-2.30.0/agent_notes/services/install_executor.py +142 -0
- agent_notes-2.30.0/agent_notes/services/install_plan.py +283 -0
- agent_notes-2.30.0/agent_notes/services/installer.py +306 -0
- agent_notes-2.30.0/agent_notes/services/memory_backend.py +63 -0
- agent_notes-2.30.0/agent_notes/services/memory_router.py +14 -0
- agent_notes-2.30.0/agent_notes/services/model_resolver.py +222 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/rendering.py +13 -98
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/settings_writer.py +2 -2
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/PKG-INFO +56 -8
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/SOURCES.txt +16 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/pyproject.toml +3 -0
- agent_notes-2.30.0/tests/functional/commands/test_install_roundtrip.py +162 -0
- agent_notes-2.30.0/tests/functional/commands/test_uninstall_full_hooks.py +297 -0
- agent_notes-2.30.0/tests/functional/commands/test_wizard_happy_path.py +163 -0
- agent_notes-2.30.0/tests/unit/commands/test_guard_credentials.py +652 -0
- agent_notes-2.30.0/tests/unit/commands/test_precompact_memory_bridge.py +182 -0
- agent_notes-2.30.0/tests/unit/commands/test_wizard_role_model_default.py +108 -0
- agent_notes-2.30.0/tests/unit/scripts/test_cache_write_pricing.py +348 -0
- agent_notes-2.30.0/tests/unit/services/test_installer_credential_guard.py +149 -0
- agent_notes-2.30.0/tests/unit/services/test_installer_precompact_hook.py +154 -0
- agent_notes-2.30.0/tests/unit/services/test_model_resolver_characterization.py +530 -0
- agent_notes-2.29.0/agent_notes/VERSION +0 -1
- agent_notes-2.29.0/agent_notes/commands/hook.py +0 -69
- agent_notes-2.29.0/agent_notes/data/agents/shared/pipelines.md +0 -18
- agent_notes-2.29.0/agent_notes/data/templates/frontmatter/__pycache__/codex.cpython-314.pyc +0 -0
- agent_notes-2.29.0/agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- agent_notes-2.29.0/agent_notes/services/installer.py +0 -586
- agent_notes-2.29.0/agent_notes/services/memory_router.py +0 -32
- {agent_notes-2.29.0 → agent_notes-2.30.0}/LICENSE +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/__main__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/_install_helpers.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/build.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/config.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/doctor.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/info.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/install.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/list.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/_common.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/migrate.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/notes.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/reset.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/transfer.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/vault.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/memory/wiki.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/regenerate.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/set_role.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/uninstall.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/validate.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/wizard/_common.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/wizard/cost_report.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/wizard/execute.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/commands/wizard/orchestrator.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/config.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/agents.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/analyst.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/api-reviewer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/architect.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/coder.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/database-specialist.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/debugger.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/devil.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/devops.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/explorer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/integrations.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/lead.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/performance-profiler.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/refactorer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/reviewer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/security-auditor.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/cost_reporting.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/execution.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/guardrails.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/hard_limits.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/phase0.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/review.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/shared/wiki_compile.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/system-auditor.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/tech-writer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/test-runner.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/test-writer.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/agents/wiki-compiler.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/cli/claude.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/cli/codex.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/cli/copilot.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/cli/opencode.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/commands/brainstorm.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/commands/debug.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/commands/review.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/global-claude.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/global-codex.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/global-copilot.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/global-opencode.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/hooks/session-context.md.tpl +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-haiku-4-5.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-opus-4-1.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-opus-4-5.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-opus-4-6.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-opus-4-7.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-opus-4-8.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-sonnet-4-5.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-sonnet-4-6.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/claude-sonnet-4.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/gpt-5-4-mini.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/gpt-5-4.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/models/gpt-5-5.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/plugin/claude.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/plugin/opencode-index.js.template +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/plugin/opencode.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/roles/orchestrator.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/roles/scout.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/roles/worker.yaml +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/rules/code-quality.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/rules/safety.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/brainstorming/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/caveman/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/code-review/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/debugging-protocol/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/docker/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/docker/compose.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/docker/dockerfile.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/git/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/grill-me/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/grill-with-docs/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/handoff/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/improve-codebase-architecture/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/ingest/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/migrate-memory/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/obsidian-memory/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/prototype/LOGIC.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/prototype/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/prototype/UI.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/controllers.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/frontend.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/infra.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/models.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/testing.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rails/views.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/refactoring-protocol/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/rsi/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/setup-project-context/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/tdd/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/to-issues/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/to-prd/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/write-a-skill/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/skills/zoom-out/SKILL.md +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-312.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-312.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/codex.cpython-312.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-312.pyc +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/data/templates/frontmatter/claude.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/doctor_checks.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/agent.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/cli_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/diagnostics.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/diff.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/rule.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/skill.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/domain/state.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/_base.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/agent_registry.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/cli_registry.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/rule_registry.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/registries/skill_registry.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/_formatting.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/_opencode_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/scripts/cost_report.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/_memory_utils.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/counts.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/diagnostics/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/diagnostics/_checks.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/diagnostics/_display.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/diagnostics/_fix.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/diff.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/fs.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/install_state_builder.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/local_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/migrations/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/obsidian_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/session_context.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/state_store.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/ui.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/user_config.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/validation.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/_wiki_utils.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/wiki_index.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/wiki_ingest.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/wiki_lint.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/wiki_query.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki/wiki_storage.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes/services/wiki_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/dependency_links.txt +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/entry_points.txt +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/requires.txt +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/agent_notes.egg-info/top_level.txt +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/setup.cfg +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/conftest.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_config_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_doctor_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_info_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_install_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_list_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_regenerate_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_uninstall_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_validate_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/commands/test_validate_path_filter.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/memory/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/memory/test_memory_add_local_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/memory/test_memory_command.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/scripts/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/functional/scripts/test_release_script.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/build_output/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/build_output/test_build_output.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/install/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/install/test_install_methods.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/plugin_builders/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/integration/plugin_builders/test_plugin_builders.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/claude/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/claude/test_agents.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/codex/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/codex/test_agents.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/plugins/test_skills.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_cost_report_subcommand.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_count_agents.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_info.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_memory_add_description.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_memory_imports.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_memory_migrate.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_wizard_imports.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_wizard_orchestrator_skip.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_wizard_preflight.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/test_wizard_steps.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/commands/wizard/test_cost_report_step.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/registries/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/registries/test_cli_registry.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/registries/test_registries.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_cost_report.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_cost_report_scoping.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_formatting_tty.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_opencode_backend_connection.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_opencode_backend_pricing.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/scripts/test_time_aggregation.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_build_functions.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_credential_filter.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_credentials.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_fs.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_installer_codex.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_installer_hook_quoting.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_installer_hooks.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_installer_plan.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_local_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_memory_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_memory_backend_io.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_memory_router.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_rendering_includes.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_session_context.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_settings_writer.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_skill_filtering.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_state_store.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_user_config_cost_report.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_validation.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_wiki_atomic_write.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_wiki_backend.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/services/test_wiki_imports.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/templates/__init__.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/templates/test_codex_frontmatter.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/test_import_health.py +0 -0
- {agent_notes-2.29.0 → agent_notes-2.30.0}/tests/unit/test_memory_dir_for_backend.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-notes
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.30.0
|
|
4
4
|
Summary: AI agent configuration manager for Claude Code, OpenCode, and Copilot
|
|
5
5
|
Author-email: Eugene Naumov <min.verkligheten@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -63,12 +63,6 @@ pipx install agent-notes
|
|
|
63
63
|
agent-notes install
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Update anytime:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
pipx upgrade agent-notes && agent-notes install
|
|
70
|
-
```
|
|
71
|
-
|
|
72
66
|
**venv + pip (if you prefer to manage your own environment):**
|
|
73
67
|
|
|
74
68
|
```bash
|
|
@@ -77,12 +71,34 @@ pip install agent-notes
|
|
|
77
71
|
agent-notes install
|
|
78
72
|
```
|
|
79
73
|
|
|
80
|
-
|
|
74
|
+
### Upgrade from a previous version
|
|
75
|
+
|
|
76
|
+
**Update in place (normal upgrade):**
|
|
77
|
+
|
|
78
|
+
With pipx:
|
|
79
|
+
```bash
|
|
80
|
+
pipx upgrade agent-notes && agent-notes install
|
|
81
|
+
```
|
|
81
82
|
|
|
83
|
+
With venv:
|
|
82
84
|
```bash
|
|
83
85
|
pip install --upgrade agent-notes && agent-notes install
|
|
84
86
|
```
|
|
85
87
|
|
|
88
|
+
**Clean reinstall (from an older version, fresh slate):**
|
|
89
|
+
|
|
90
|
+
If you are on a significantly older version or want a fresh configuration, uninstall completely and reinstall:
|
|
91
|
+
|
|
92
|
+
With pipx:
|
|
93
|
+
```bash
|
|
94
|
+
agent-notes uninstall && pipx uninstall agent-notes && pipx install agent-notes && agent-notes install
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
With venv:
|
|
98
|
+
```bash
|
|
99
|
+
agent-notes uninstall && pip uninstall agent-notes && pip install agent-notes && agent-notes install
|
|
100
|
+
```
|
|
101
|
+
|
|
86
102
|
### Local build (developers)
|
|
87
103
|
|
|
88
104
|
```bash
|
|
@@ -119,6 +135,36 @@ agent-notes config provider openrouter # prints "configured" or "no key"
|
|
|
119
135
|
|
|
120
136
|
</details>
|
|
121
137
|
|
|
138
|
+
<details>
|
|
139
|
+
<summary>Uninstall</summary>
|
|
140
|
+
|
|
141
|
+
To fully remove agent-notes:
|
|
142
|
+
|
|
143
|
+
**pipx:**
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
agent-notes uninstall
|
|
147
|
+
pipx uninstall agent-notes
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Run `agent-notes uninstall` first — it removes agent files, hooks, settings.json entries, context file, and state that agent-notes installed. Then `pipx uninstall` removes the package itself. The `agent-notes uninstall` command must run while the CLI is still installed.
|
|
151
|
+
|
|
152
|
+
**venv:**
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
agent-notes uninstall
|
|
156
|
+
deactivate
|
|
157
|
+
rm -rf .venv
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Run `agent-notes uninstall` first to clean up installed components, then deactivate the environment and delete the `.venv` directory.
|
|
161
|
+
|
|
162
|
+
**Note on memory and vault permissions:**
|
|
163
|
+
|
|
164
|
+
If you configured agent-notes to access your memory vault (Obsidian or local), those read/write permissions are intentionally left in place after uninstalling so Claude Code and other tools can still access your notes. To remove them, edit `~/.claude/settings.json` and delete the entries under `permissions.allow` that reference your vault path (e.g. `Read(/path/to/your/vault/**)`, `Write(/path/to/your/vault/**)`, `Edit(/path/to/your/vault/**)`).
|
|
165
|
+
|
|
166
|
+
</details>
|
|
167
|
+
|
|
122
168
|
---
|
|
123
169
|
|
|
124
170
|
<details>
|
|
@@ -409,6 +455,8 @@ By default, `agent-notes install` re-runs the interactive wizard and prompts for
|
|
|
409
455
|
|
|
410
456
|
The plugin build scripts (`scripts/build-claude-plugin.sh`, `scripts/build-opencode-plugin.sh`) automatically use `.venv/bin/python` when present, fall back to system `python3`, and honor a `PYTHON=` override.
|
|
411
457
|
|
|
458
|
+
**Note:** `uv` is used only in the local development and release workflow (see `scripts/release`). It is not required to install or use agent-notes — end users should use `pipx` or `pip` / `venv`.
|
|
459
|
+
|
|
412
460
|
### Test structure
|
|
413
461
|
|
|
414
462
|
- `tests/functional/` — Unit tests
|
|
@@ -31,12 +31,6 @@ pipx install agent-notes
|
|
|
31
31
|
agent-notes install
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Update anytime:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pipx upgrade agent-notes && agent-notes install
|
|
38
|
-
```
|
|
39
|
-
|
|
40
34
|
**venv + pip (if you prefer to manage your own environment):**
|
|
41
35
|
|
|
42
36
|
```bash
|
|
@@ -45,12 +39,34 @@ pip install agent-notes
|
|
|
45
39
|
agent-notes install
|
|
46
40
|
```
|
|
47
41
|
|
|
48
|
-
|
|
42
|
+
### Upgrade from a previous version
|
|
43
|
+
|
|
44
|
+
**Update in place (normal upgrade):**
|
|
45
|
+
|
|
46
|
+
With pipx:
|
|
47
|
+
```bash
|
|
48
|
+
pipx upgrade agent-notes && agent-notes install
|
|
49
|
+
```
|
|
49
50
|
|
|
51
|
+
With venv:
|
|
50
52
|
```bash
|
|
51
53
|
pip install --upgrade agent-notes && agent-notes install
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
**Clean reinstall (from an older version, fresh slate):**
|
|
57
|
+
|
|
58
|
+
If you are on a significantly older version or want a fresh configuration, uninstall completely and reinstall:
|
|
59
|
+
|
|
60
|
+
With pipx:
|
|
61
|
+
```bash
|
|
62
|
+
agent-notes uninstall && pipx uninstall agent-notes && pipx install agent-notes && agent-notes install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
With venv:
|
|
66
|
+
```bash
|
|
67
|
+
agent-notes uninstall && pip uninstall agent-notes && pip install agent-notes && agent-notes install
|
|
68
|
+
```
|
|
69
|
+
|
|
54
70
|
### Local build (developers)
|
|
55
71
|
|
|
56
72
|
```bash
|
|
@@ -87,6 +103,36 @@ agent-notes config provider openrouter # prints "configured" or "no key"
|
|
|
87
103
|
|
|
88
104
|
</details>
|
|
89
105
|
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Uninstall</summary>
|
|
108
|
+
|
|
109
|
+
To fully remove agent-notes:
|
|
110
|
+
|
|
111
|
+
**pipx:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
agent-notes uninstall
|
|
115
|
+
pipx uninstall agent-notes
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Run `agent-notes uninstall` first — it removes agent files, hooks, settings.json entries, context file, and state that agent-notes installed. Then `pipx uninstall` removes the package itself. The `agent-notes uninstall` command must run while the CLI is still installed.
|
|
119
|
+
|
|
120
|
+
**venv:**
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
agent-notes uninstall
|
|
124
|
+
deactivate
|
|
125
|
+
rm -rf .venv
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Run `agent-notes uninstall` first to clean up installed components, then deactivate the environment and delete the `.venv` directory.
|
|
129
|
+
|
|
130
|
+
**Note on memory and vault permissions:**
|
|
131
|
+
|
|
132
|
+
If you configured agent-notes to access your memory vault (Obsidian or local), those read/write permissions are intentionally left in place after uninstalling so Claude Code and other tools can still access your notes. To remove them, edit `~/.claude/settings.json` and delete the entries under `permissions.allow` that reference your vault path (e.g. `Read(/path/to/your/vault/**)`, `Write(/path/to/your/vault/**)`, `Edit(/path/to/your/vault/**)`).
|
|
133
|
+
|
|
134
|
+
</details>
|
|
135
|
+
|
|
90
136
|
---
|
|
91
137
|
|
|
92
138
|
<details>
|
|
@@ -377,6 +423,8 @@ By default, `agent-notes install` re-runs the interactive wizard and prompts for
|
|
|
377
423
|
|
|
378
424
|
The plugin build scripts (`scripts/build-claude-plugin.sh`, `scripts/build-opencode-plugin.sh`) automatically use `.venv/bin/python` when present, fall back to system `python3`, and honor a `PYTHON=` override.
|
|
379
425
|
|
|
426
|
+
**Note:** `uv` is used only in the local development and release workflow (see `scripts/release`). It is not required to install or use agent-notes — end users should use `pipx` or `pip` / `venv`.
|
|
427
|
+
|
|
380
428
|
### Test structure
|
|
381
429
|
|
|
382
430
|
- `tests/functional/` — Unit tests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.30.0
|
|
@@ -291,7 +291,7 @@ def main():
|
|
|
291
291
|
|
|
292
292
|
# hook
|
|
293
293
|
p_hook = subparsers.add_parser("hook", help="Claude Code hook integrations")
|
|
294
|
-
p_hook.add_argument("subaction", choices=["memory-bridge", "session-discover"],
|
|
294
|
+
p_hook.add_argument("subaction", choices=["memory-bridge", "precompact-memory-bridge", "session-discover", "guard-credentials"],
|
|
295
295
|
help="Hook to run")
|
|
296
296
|
|
|
297
297
|
# cost-report
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""Hook command - Claude Code hook integrations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def hook(subaction: str) -> None:
|
|
13
|
+
"""Handle hook subactions."""
|
|
14
|
+
if subaction == "memory-bridge":
|
|
15
|
+
_memory_bridge()
|
|
16
|
+
elif subaction == "precompact-memory-bridge":
|
|
17
|
+
_precompact_memory_bridge()
|
|
18
|
+
elif subaction == "session-discover":
|
|
19
|
+
_session_discover()
|
|
20
|
+
elif subaction == "guard-credentials":
|
|
21
|
+
_guard_credentials()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _session_discover() -> None:
|
|
25
|
+
"""Discover all agent-notes profiles for the current project and emit combined context."""
|
|
26
|
+
try:
|
|
27
|
+
from ..services.state_store import load_state, get_profiles_for_project
|
|
28
|
+
from ..registries.cli_registry import load_registry
|
|
29
|
+
|
|
30
|
+
state = load_state()
|
|
31
|
+
if state is None:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
registry = load_registry()
|
|
35
|
+
default_local_dirs = {b.name: b.local_dir for b in registry.all()}
|
|
36
|
+
|
|
37
|
+
for key, scope_state in get_profiles_for_project(state, Path.cwd()):
|
|
38
|
+
for cli_name, backend_state in scope_state.clis.items():
|
|
39
|
+
local_dir = backend_state.local_dir_override or default_local_dirs.get(cli_name, ".claude")
|
|
40
|
+
context_file = Path(local_dir) / "agent-notes-context.md"
|
|
41
|
+
if context_file.exists():
|
|
42
|
+
label = scope_state.profile_label or "default"
|
|
43
|
+
print(f"<!-- agent-notes profile: {label} -->")
|
|
44
|
+
print(context_file.read_text(encoding="utf-8"))
|
|
45
|
+
except Exception:
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _guard_credentials() -> None:
|
|
50
|
+
"""PreToolUse hook that denies attempts to read credential files.
|
|
51
|
+
|
|
52
|
+
Reads the PreToolUse JSON payload from stdin, calls evaluate_credential_access,
|
|
53
|
+
and prints a deny or allow response. Always exits 0 so Claude Code parses stdout.
|
|
54
|
+
|
|
55
|
+
Fail-closed for recognized tool types: if stdin parsed and tool_name is a
|
|
56
|
+
guarded tool (Read/Bash/Grep), any downstream error emits a static deny.
|
|
57
|
+
Only a total stdin/JSON parse failure fails open (writes to stderr).
|
|
58
|
+
"""
|
|
59
|
+
raw = ""
|
|
60
|
+
try:
|
|
61
|
+
raw = sys.stdin.read()
|
|
62
|
+
payload = json.loads(raw)
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
# Total infrastructure failure — cannot parse stdin at all; fail open
|
|
65
|
+
# with a stderr note so operators can diagnose.
|
|
66
|
+
print(f"credential-guard: stdin parse error ({exc})", file=sys.stderr)
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
tool_name = payload.get("tool_name", "")
|
|
70
|
+
tool_input = payload.get("tool_input", {})
|
|
71
|
+
|
|
72
|
+
_GUARDED_TOOLS = {"Read", "Bash", "Grep"}
|
|
73
|
+
|
|
74
|
+
if tool_name in _GUARDED_TOOLS:
|
|
75
|
+
try:
|
|
76
|
+
decision = evaluate_credential_access(tool_name, tool_input)
|
|
77
|
+
if decision is not None:
|
|
78
|
+
print(json.dumps(decision))
|
|
79
|
+
except Exception:
|
|
80
|
+
# Evaluation error on a guarded tool — fail closed
|
|
81
|
+
print(json.dumps(_deny_payload(
|
|
82
|
+
"Credential guard: evaluation error — denying as a precaution; "
|
|
83
|
+
"remove the guard hook from settings.json to override."
|
|
84
|
+
)))
|
|
85
|
+
else:
|
|
86
|
+
# Non-guarded tool: pass through (hook matcher already scopes to Read|Bash|Grep,
|
|
87
|
+
# but defensive check here in case Claude Code invokes us for other tools)
|
|
88
|
+
try:
|
|
89
|
+
decision = evaluate_credential_access(tool_name, tool_input)
|
|
90
|
+
if decision is not None:
|
|
91
|
+
print(json.dumps(decision))
|
|
92
|
+
except Exception:
|
|
93
|
+
pass # Non-guarded tool errors fail open
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Credential patterns (from safety.md "Credentials — ABSOLUTE PROHIBITION")
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
# Non-secret template suffixes — .env.example / .env.sample / .env.template /
|
|
101
|
+
# .env.dist are not real secret files. Allow them explicitly.
|
|
102
|
+
_ENV_TEMPLATE_SUFFIXES = frozenset({
|
|
103
|
+
".example", ".sample", ".template", ".dist",
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
# Filename-level patterns: matched against the basename of the path.
|
|
107
|
+
# Order: most specific first; the .env allowlist check runs BEFORE these.
|
|
108
|
+
_CREDENTIAL_BASENAME_PATTERNS: list[re.Pattern[str]] = [
|
|
109
|
+
re.compile(r"^service-account.*\.json$", re.IGNORECASE),
|
|
110
|
+
re.compile(r"^credentials\.", re.IGNORECASE),
|
|
111
|
+
re.compile(r"^secrets\.", re.IGNORECASE),
|
|
112
|
+
re.compile(r"^.*-secrets\.", re.IGNORECASE),
|
|
113
|
+
re.compile(r"^.*\.env$", re.IGNORECASE), # bare *.env
|
|
114
|
+
re.compile(r"^\.env(\.|$)", re.IGNORECASE), # .env, .env.production, etc.
|
|
115
|
+
re.compile(r"^.*\.key$", re.IGNORECASE),
|
|
116
|
+
re.compile(r"^.*\.pem$", re.IGNORECASE),
|
|
117
|
+
re.compile(r"^.*\.p12$", re.IGNORECASE),
|
|
118
|
+
re.compile(r"^.*\.pfx$", re.IGNORECASE),
|
|
119
|
+
re.compile(r"^.*\.jks$", re.IGNORECASE),
|
|
120
|
+
re.compile(r"^.*\.keystore$", re.IGNORECASE),
|
|
121
|
+
re.compile(r"^.*\.truststore$", re.IGNORECASE),
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
# Word-boundary separators for keyword matching within a path segment.
|
|
125
|
+
# A keyword only matches when surrounded by start/end of segment or one of these.
|
|
126
|
+
_SEG_SEPS = re.compile(r"[-_.\s]")
|
|
127
|
+
|
|
128
|
+
# Path-segment keywords: match only as DELIMITED words within a segment.
|
|
129
|
+
# E.g. "token" matches "auth-token.json" and "my_token" but NOT "tokenizer.py"
|
|
130
|
+
# or "PasswordResetToken.tsx". Boundaries are start/end of segment or [-_.\s].
|
|
131
|
+
_CREDENTIAL_SEGMENT_KEYWORDS = (
|
|
132
|
+
"secret",
|
|
133
|
+
"credential",
|
|
134
|
+
"token",
|
|
135
|
+
"apikey",
|
|
136
|
+
"auth-key",
|
|
137
|
+
"private-key",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Existence/metadata commands that are ALWAYS allowed even if they reference a
|
|
141
|
+
# credential path — these never surface file contents.
|
|
142
|
+
_SAFE_EXISTENCE_COMMANDS = frozenset({"test", "[", "ls", "stat", "find", "file"})
|
|
143
|
+
|
|
144
|
+
# Prefix tokens that do not change the effective command — stripped before
|
|
145
|
+
# identifying the first real command.
|
|
146
|
+
_COMMAND_PREFIXES = frozenset({
|
|
147
|
+
"sudo", "time", "nohup", "env", "xargs", "exec", "command",
|
|
148
|
+
"nice", "doas",
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _is_env_template(basename: str) -> bool:
|
|
153
|
+
"""Return True if the basename is a non-secret .env template file.
|
|
154
|
+
|
|
155
|
+
.env.example / .env.sample / .env.template / .env.dist are scaffolding
|
|
156
|
+
files checked into repos — they contain placeholder values, not real secrets.
|
|
157
|
+
"""
|
|
158
|
+
lower = basename.lower()
|
|
159
|
+
if not lower.startswith(".env"):
|
|
160
|
+
return False
|
|
161
|
+
suffix = lower[4:] # everything after ".env"
|
|
162
|
+
return suffix in _ENV_TEMPLATE_SUFFIXES
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _keyword_in_segment(keyword: str, segment: str) -> bool:
|
|
166
|
+
"""Return True if keyword appears as a delimited word within segment.
|
|
167
|
+
|
|
168
|
+
Boundaries are start/end of segment or a separator character (-, _, ., space).
|
|
169
|
+
Case-insensitive. Multi-word keywords like "auth-key" are matched as a unit.
|
|
170
|
+
"""
|
|
171
|
+
seg_lower = segment.lower()
|
|
172
|
+
kw_lower = keyword.lower()
|
|
173
|
+
kw_len = len(kw_lower)
|
|
174
|
+
seg_len = len(seg_lower)
|
|
175
|
+
|
|
176
|
+
start = 0
|
|
177
|
+
while True:
|
|
178
|
+
idx = seg_lower.find(kw_lower, start)
|
|
179
|
+
if idx == -1:
|
|
180
|
+
break
|
|
181
|
+
# Check left boundary
|
|
182
|
+
left_ok = (idx == 0) or bool(_SEG_SEPS.match(seg_lower[idx - 1]))
|
|
183
|
+
# Check right boundary
|
|
184
|
+
right_idx = idx + kw_len
|
|
185
|
+
right_ok = (right_idx == seg_len) or bool(_SEG_SEPS.match(seg_lower[right_idx]))
|
|
186
|
+
if left_ok and right_ok:
|
|
187
|
+
return True
|
|
188
|
+
start = idx + 1
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _is_credential_path(path_str: str) -> bool:
|
|
193
|
+
"""Return True if path_str refers to a credential file.
|
|
194
|
+
|
|
195
|
+
Checks (in order):
|
|
196
|
+
1. Explicit allow: non-secret .env template files (.env.example etc.)
|
|
197
|
+
2. Basename against known credential file patterns.
|
|
198
|
+
3. Every path segment for credential-related DELIMITED keywords.
|
|
199
|
+
|
|
200
|
+
This function ONLY inspects the path string — it never reads file contents.
|
|
201
|
+
"""
|
|
202
|
+
if not path_str:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
# Normalise separators for segment splitting
|
|
206
|
+
normalised = path_str.replace("\\", "/")
|
|
207
|
+
segments = [s for s in normalised.split("/") if s]
|
|
208
|
+
if not segments:
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
basename = segments[-1]
|
|
212
|
+
|
|
213
|
+
# 1. Allow non-secret .env templates before any deny pattern fires
|
|
214
|
+
if _is_env_template(basename):
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
# 2. Basename pattern match
|
|
218
|
+
for pattern in _CREDENTIAL_BASENAME_PATTERNS:
|
|
219
|
+
if pattern.match(basename):
|
|
220
|
+
return True
|
|
221
|
+
|
|
222
|
+
# 3. Delimited keyword match in any segment
|
|
223
|
+
for seg in segments:
|
|
224
|
+
for keyword in _CREDENTIAL_SEGMENT_KEYWORDS:
|
|
225
|
+
if _keyword_in_segment(keyword, seg):
|
|
226
|
+
return True
|
|
227
|
+
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _first_effective_command(tokens: list[str]) -> str:
|
|
232
|
+
"""Return the basename of the first non-prefix, non-env-var token."""
|
|
233
|
+
i = 0
|
|
234
|
+
while i < len(tokens):
|
|
235
|
+
t = tokens[i]
|
|
236
|
+
# Skip VAR=value env-var assignments
|
|
237
|
+
if "=" in t and not t.startswith("-"):
|
|
238
|
+
i += 1
|
|
239
|
+
continue
|
|
240
|
+
# Strip absolute path prefix (/usr/bin/cat → cat)
|
|
241
|
+
cmd = t.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
|
|
242
|
+
if cmd in _COMMAND_PREFIXES:
|
|
243
|
+
i += 1
|
|
244
|
+
continue
|
|
245
|
+
# Handle `su -c` (next token is the command string, not a prefix)
|
|
246
|
+
if cmd == "su":
|
|
247
|
+
break
|
|
248
|
+
return cmd
|
|
249
|
+
return ""
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _strip_token_punctuation(token: str) -> str:
|
|
253
|
+
"""Strip surrounding shell metacharacters that shlex may leave on a token.
|
|
254
|
+
|
|
255
|
+
Handles: leading < > ' " ( and trailing ) ' " so that tokens like
|
|
256
|
+
`'.env)'`, `<.env`, `"< .env`, `$(cat .env)` pass through to path check.
|
|
257
|
+
"""
|
|
258
|
+
return token.strip("<>()'\"`")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _command_contains_credential_path(command: str) -> bool:
|
|
262
|
+
"""Return True if any path-like token or substring in the command is a credential.
|
|
263
|
+
|
|
264
|
+
Two-pass scan:
|
|
265
|
+
1. shlex tokens (handles quoted paths, explicit < redirections)
|
|
266
|
+
2. Raw regex scan of the whole command string (catches subshells, backticks,
|
|
267
|
+
no-space redirections like `<.env`, compound commands)
|
|
268
|
+
"""
|
|
269
|
+
# Pass 1: shlex tokens
|
|
270
|
+
try:
|
|
271
|
+
import shlex
|
|
272
|
+
tokens = shlex.split(command)
|
|
273
|
+
except ValueError:
|
|
274
|
+
tokens = command.split()
|
|
275
|
+
|
|
276
|
+
for token in tokens:
|
|
277
|
+
cleaned = _strip_token_punctuation(token)
|
|
278
|
+
if cleaned and _is_credential_path(cleaned):
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
# Pass 2: raw scan — extract all path-candidate substrings via regex.
|
|
282
|
+
# Matches sequences that look like a path (optional leading ./ or / or ~/,
|
|
283
|
+
# then word chars, dots, hyphens, underscores, slashes) and also bare
|
|
284
|
+
# no-space redirections like `<.env`, `if=.env`, and quoted paths like `'.env'`.
|
|
285
|
+
#
|
|
286
|
+
# Known non-goals (static string analysis cannot catch):
|
|
287
|
+
# - Variable-built paths: f=.env; cat $f (path not present as literal)
|
|
288
|
+
# - Glob obfuscation: cat .e* (glob expands at runtime)
|
|
289
|
+
# These are accepted limitations, not bugs.
|
|
290
|
+
for match in re.finditer(
|
|
291
|
+
r"(?:^|(?<=\s)|(?<=[<>|;&()`=']))([~./]?[\w./\-]+)",
|
|
292
|
+
command,
|
|
293
|
+
):
|
|
294
|
+
candidate = _strip_token_punctuation(match.group(1))
|
|
295
|
+
if candidate and _is_credential_path(candidate):
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _bash_reads_credential(command: str) -> bool:
|
|
302
|
+
"""Return True if the Bash command may expose credential file contents.
|
|
303
|
+
|
|
304
|
+
New deny boundary (defense-in-depth, correctness over leniency):
|
|
305
|
+
|
|
306
|
+
ALLOW only when the first effective command (after stripping prefixes) is a
|
|
307
|
+
pure existence/metadata op: test / [ / ls / stat / find / file.
|
|
308
|
+
These never surface file contents even when given a credential path.
|
|
309
|
+
|
|
310
|
+
DENY when a credential path appears ANYWHERE in the command AND the first
|
|
311
|
+
effective command is NOT a safe existence check.
|
|
312
|
+
|
|
313
|
+
Over-blocking writes/deletes (rm .env, git add .env, echo x >> .env) is
|
|
314
|
+
intentional — deny those too. Defense-in-depth beats precision here.
|
|
315
|
+
|
|
316
|
+
This function ONLY inspects the command string — it never reads file contents.
|
|
317
|
+
"""
|
|
318
|
+
if not command:
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
import shlex
|
|
323
|
+
tokens = shlex.split(command)
|
|
324
|
+
except ValueError:
|
|
325
|
+
tokens = command.split()
|
|
326
|
+
|
|
327
|
+
if not tokens:
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
first_cmd = _first_effective_command(tokens)
|
|
331
|
+
|
|
332
|
+
# Pure existence/metadata commands: always allow regardless of path
|
|
333
|
+
if first_cmd in _SAFE_EXISTENCE_COMMANDS:
|
|
334
|
+
return False
|
|
335
|
+
|
|
336
|
+
# For everything else: deny if a credential path appears anywhere
|
|
337
|
+
return _command_contains_credential_path(command)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _deny_payload(reason: str) -> dict:
|
|
341
|
+
"""Return the Claude Code PreToolUse deny payload."""
|
|
342
|
+
return {
|
|
343
|
+
"hookSpecificOutput": {
|
|
344
|
+
"hookEventName": "PreToolUse",
|
|
345
|
+
"permissionDecision": "deny",
|
|
346
|
+
"permissionDecisionReason": reason,
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def evaluate_credential_access(
|
|
352
|
+
tool_name: str, tool_input: dict
|
|
353
|
+
) -> Optional[dict]:
|
|
354
|
+
"""Evaluate whether the tool call attempts to read a credential file.
|
|
355
|
+
|
|
356
|
+
Returns a deny payload dict if the call should be blocked, or None to allow.
|
|
357
|
+
|
|
358
|
+
This is a pure function with no side effects — it only inspects the
|
|
359
|
+
tool_name and tool_input strings and never reads file contents or logs values.
|
|
360
|
+
|
|
361
|
+
Deny cases:
|
|
362
|
+
- Read tool: file_path matches a credential pattern
|
|
363
|
+
- Bash tool: credential path appears anywhere in command AND first effective
|
|
364
|
+
command is not a safe existence check (test/ls/stat/find/file/[)
|
|
365
|
+
- Grep tool: path or glob targets a credential path
|
|
366
|
+
|
|
367
|
+
Allow cases:
|
|
368
|
+
- Read of non-credential paths (and .env.example/.env.sample templates)
|
|
369
|
+
- Bash with first effective command = test/ls/stat/find/file/[
|
|
370
|
+
- Bash with no credential path in the command
|
|
371
|
+
- Grep on non-credential paths
|
|
372
|
+
- Edit/Write/WebFetch — out of scope; not in matcher
|
|
373
|
+
- Any other unrecognized tool
|
|
374
|
+
|
|
375
|
+
Note: Edit and Write are intentionally excluded from the guard scope —
|
|
376
|
+
writing to a credential path is not the same threat as reading it into
|
|
377
|
+
model context. WebFetch is not path-based. Both are left to the prompt rule.
|
|
378
|
+
"""
|
|
379
|
+
if tool_name == "Read":
|
|
380
|
+
path_str = tool_input.get("file_path", "")
|
|
381
|
+
if _is_credential_path(path_str):
|
|
382
|
+
return _deny_payload(
|
|
383
|
+
f"Credential guard: reading '{path_str}' is prohibited. "
|
|
384
|
+
"Use 'test -f' or 'ls' to verify file existence instead."
|
|
385
|
+
)
|
|
386
|
+
return None
|
|
387
|
+
|
|
388
|
+
if tool_name == "Bash":
|
|
389
|
+
command = tool_input.get("command", "")
|
|
390
|
+
if _bash_reads_credential(command):
|
|
391
|
+
return _deny_payload(
|
|
392
|
+
"Credential guard: the command may expose credential file contents. "
|
|
393
|
+
"Use 'test -f <path>' to check existence without exposing contents."
|
|
394
|
+
)
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
if tool_name == "Grep":
|
|
398
|
+
# Grep can surface file contents; deny when path/glob targets a credential.
|
|
399
|
+
# tool_input fields used by Claude Code's Grep: "path" and/or "glob".
|
|
400
|
+
for field in ("path", "glob"):
|
|
401
|
+
val = tool_input.get(field, "")
|
|
402
|
+
if val and _is_credential_path(val):
|
|
403
|
+
return _deny_payload(
|
|
404
|
+
f"Credential guard: grep on '{val}' is prohibited. "
|
|
405
|
+
"Credential file contents must not be surfaced."
|
|
406
|
+
)
|
|
407
|
+
return None
|
|
408
|
+
|
|
409
|
+
# All other tools: allow (hook matcher scopes to Read|Bash|Grep)
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _load_memory_index() -> Optional[str]:
|
|
414
|
+
"""Load the agent-notes memory index content, or return None if unavailable.
|
|
415
|
+
|
|
416
|
+
Shared renderer used by both the SessionStart memory-bridge hook and the
|
|
417
|
+
PreCompact memory-bridge hook so both emit from one source of truth.
|
|
418
|
+
"""
|
|
419
|
+
try:
|
|
420
|
+
from .memory._common import _load_memory_config
|
|
421
|
+
from ..constants import Obsidian, Wiki
|
|
422
|
+
|
|
423
|
+
backend, path = _load_memory_config()
|
|
424
|
+
|
|
425
|
+
if backend == "none" or backend is None:
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
if backend == "obsidian":
|
|
429
|
+
index_file = Path(path) / Obsidian.INDEX
|
|
430
|
+
elif backend == "wiki":
|
|
431
|
+
index_file = Path(path) / Wiki.DIR / Wiki.INDEX
|
|
432
|
+
else:
|
|
433
|
+
# local and any unknown backends: use Index.md at root
|
|
434
|
+
index_file = Path(path) / "Index.md"
|
|
435
|
+
|
|
436
|
+
if not index_file.exists():
|
|
437
|
+
return None
|
|
438
|
+
|
|
439
|
+
return index_file.read_text(encoding="utf-8")
|
|
440
|
+
except Exception:
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _memory_bridge() -> None:
|
|
445
|
+
"""SessionStart hook that prints the agent-notes memory index.
|
|
446
|
+
|
|
447
|
+
Unconditionally loads and prints the memory index so it is visible in
|
|
448
|
+
context at the start of every Claude Code session.
|
|
449
|
+
"""
|
|
450
|
+
content = _load_memory_index()
|
|
451
|
+
if content is None:
|
|
452
|
+
return
|
|
453
|
+
print("<!-- agent-notes memory index (auto-loaded) -->")
|
|
454
|
+
print(content)
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def _precompact_memory_bridge() -> None:
|
|
458
|
+
"""PreCompact hook that re-emits the memory index before context compaction.
|
|
459
|
+
|
|
460
|
+
When Claude Code compacts a long conversation the SessionStart context
|
|
461
|
+
injected by memory-bridge can be summarised away, losing the pointer to
|
|
462
|
+
durable memory. This hook fires immediately before compaction and returns
|
|
463
|
+
the index via the `additionalContext` field in the hook JSON output schema
|
|
464
|
+
(see docs/CLI_CAPABILITIES.md §Hooks → "JSON output schema"). Claude Code
|
|
465
|
+
merges `additionalContext` into the compacted context, keeping the memory
|
|
466
|
+
pointer alive.
|
|
467
|
+
|
|
468
|
+
Contract: exit 0, emit a single JSON object to stdout:
|
|
469
|
+
{"additionalContext": "<header>\\n<index content>"}
|
|
470
|
+
If the memory index is unavailable (no backend configured, no index file)
|
|
471
|
+
the hook exits silently with no output so compaction proceeds normally.
|
|
472
|
+
"""
|
|
473
|
+
content = _load_memory_index()
|
|
474
|
+
if content is None:
|
|
475
|
+
return
|
|
476
|
+
payload = {
|
|
477
|
+
"additionalContext": "<!-- agent-notes memory index (auto-loaded) -->\n" + content,
|
|
478
|
+
}
|
|
479
|
+
print(json.dumps(payload))
|