agent-notes 2.25.0__tar.gz → 2.26.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.25.0 → agent_notes-2.26.0}/PKG-INFO +1 -1
- agent_notes-2.26.0/agent_notes/VERSION +1 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/notes.py +4 -2
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/cost_reporting.md +1 -1
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/hard_limits.md +1 -1
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/pricing.yaml +3 -3
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/scripts/_claude_backend.py +10 -62
- agent_notes-2.26.0/agent_notes/scripts/_formatting.py +120 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/scripts/_opencode_backend.py +10 -68
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/PKG-INFO +1 -1
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/SOURCES.txt +2 -0
- agent_notes-2.26.0/tests/functional/memory/test_memory_add_local_backend.py +55 -0
- agent_notes-2.26.0/tests/unit/scripts/test_opencode_backend_pricing.py +100 -0
- agent_notes-2.25.0/agent_notes/VERSION +0 -1
- agent_notes-2.25.0/agent_notes/scripts/_formatting.py +0 -49
- {agent_notes-2.25.0 → agent_notes-2.26.0}/LICENSE +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/README.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/__main__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/cli.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/_install_helpers.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/build.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/config.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/doctor.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/hook.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/info.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/install.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/list.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/_common.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/migrate.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/reset.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/transfer.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/vault.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/memory/wiki.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/regenerate.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/set_role.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/uninstall.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/validate.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/wizard/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/wizard/_common.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/wizard/cost_report.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/wizard/execute.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/commands/wizard/orchestrator.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/config.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/constants.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/agents.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/analyst.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/api-reviewer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/architect.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/coder.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/database-specialist.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/debugger.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/devil.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/devops.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/explorer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/integrations.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/lead.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/performance-profiler.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/refactorer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/reviewer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/security-auditor.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/execution.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/guardrails.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/phase0.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/pipelines.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/review.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/verification.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/shared/wiki_compile.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/system-auditor.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/tech-writer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/test-runner.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/test-writer.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/agents/wiki-compiler.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/cli/claude.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/cli/copilot.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/cli/opencode.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/commands/brainstorm.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/commands/debug.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/commands/review.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/global-claude.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/global-copilot.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/global-opencode.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/hooks/session-context.md.tpl +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-haiku-4-5.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-opus-4-1.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-opus-4-5.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-opus-4-6.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-opus-4-7.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-opus-4-8.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-sonnet-4-5.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-sonnet-4-6.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/models/claude-sonnet-4.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/plugin/claude.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/plugin/opencode-index.js.template +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/plugin/opencode.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/roles/orchestrator.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/roles/reasoner.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/roles/scout.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/roles/worker.yaml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/rules/code-quality.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/rules/safety.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/brainstorming/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/caveman/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/code-review/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/debugging-protocol/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/docker/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/docker/compose.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/docker/dockerfile.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/git/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/grill-me/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/grill-with-docs/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/handoff/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/improve-codebase-architecture/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/ingest/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/migrate-memory/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/obsidian-memory/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/prototype/LOGIC.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/prototype/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/prototype/UI.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/controllers.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/frontend.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/infra.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/models.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/testing.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/rails/views.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/refactoring-protocol/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/setup-project-context/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/tdd/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/to-issues/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/to-prd/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/write-a-skill/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/skills/zoom-out/SKILL.md +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/claude.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/data/templates/frontmatter/opencode.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/doctor_checks.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/agent.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/cli_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/diagnostics.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/diff.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/model.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/role.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/rule.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/skill.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/domain/state.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/_base.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/agent_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/cli_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/model_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/role_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/rule_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/registries/skill_registry.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/scripts/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/scripts/_pricing.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/scripts/cost_report.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/_memory_utils.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/counts.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/credentials.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/diagnostics/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/diagnostics/_checks.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/diagnostics/_display.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/diagnostics/_fix.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/diff.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/fs.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/install_state_builder.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/installer.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/local_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/memory_router.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/migrations/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/obsidian_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/rendering.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/session_context.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/settings_writer.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/state_store.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/ui.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/user_config.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/validation.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/_wiki_utils.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/wiki_index.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/wiki_ingest.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/wiki_lint.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/wiki_query.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki/wiki_storage.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes/services/wiki_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/dependency_links.txt +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/entry_points.txt +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/requires.txt +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/agent_notes.egg-info/top_level.txt +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/pyproject.toml +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/setup.cfg +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/conftest.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_config_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_doctor_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_info_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_install_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_list_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_regenerate_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_uninstall_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/commands/test_validate_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/memory/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/memory/test_memory_command.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/scripts/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/functional/scripts/test_release_script.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/build_output/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/build_output/test_build_output.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/install/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/install/test_install_methods.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/plugin_builders/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/integration/plugin_builders/test_plugin_builders.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/plugins/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/plugins/claude/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/plugins/claude/test_agents.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/plugins/test_skills.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_cost_report_subcommand.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_count_agents.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_info.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_memory_add_description.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_memory_imports.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_memory_migrate.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_wizard_imports.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_wizard_orchestrator_skip.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_wizard_preflight.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/test_wizard_steps.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/commands/wizard/test_cost_report_step.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/registries/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/registries/test_registries.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/scripts/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/scripts/test_cost_report.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/scripts/test_cost_report_scoping.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/scripts/test_formatting_tty.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/scripts/test_time_aggregation.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/__init__.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_build_functions.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_credential_filter.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_credentials.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_fs.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_installer_hooks.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_installer_plan.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_local_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_memory_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_memory_backend_io.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_memory_router.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_rendering_includes.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_session_context.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_settings_writer.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_skill_filtering.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_state_store.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_user_config_cost_report.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_validation.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_wiki_backend.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/services/test_wiki_imports.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/test_import_health.py +0 -0
- {agent_notes-2.25.0 → agent_notes-2.26.0}/tests/unit/test_memory_dir_for_backend.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.26.0
|
|
@@ -45,8 +45,10 @@ def do_add(title: str, body: str, note_type: str = "context", agent: str = "", p
|
|
|
45
45
|
)
|
|
46
46
|
print(f"{Color.GREEN}Note saved: {note_path}{Color.NC}")
|
|
47
47
|
else:
|
|
48
|
-
|
|
49
|
-
print("
|
|
48
|
+
import sys
|
|
49
|
+
print("The `add` subcommand is for obsidian or wiki storage.", file=sys.stderr)
|
|
50
|
+
print("For local storage, write files directly to the agent subdirectory.", file=sys.stderr)
|
|
51
|
+
sys.exit(1)
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
def do_list() -> None:
|
|
@@ -4,7 +4,7 @@ At the END of every response, run `agent-notes cost-report` and include the outp
|
|
|
4
4
|
|
|
5
5
|
**Session cost** (cumulative for the entire conversation):
|
|
6
6
|
|
|
7
|
-
Render every column the `agent-notes cost-report` CLI emits — `agent(model)`, `in/out/cache`, `time`, `actual`, `vs Claude Opus 4.
|
|
7
|
+
Render every column the `agent-notes cost-report` CLI emits — `agent(model)`, `in/out/cache`, `time`, `actual`, `vs Claude Opus 4.8` — in that order. Do not split, drop, or rename columns. Preserve the data verbatim.
|
|
8
8
|
|
|
9
9
|
**On failure or skip — never fabricate.** If `agent-notes cost-report` returns non-zero, errors, or you skip running it, do NOT render a placeholder table or invent rows like `(cost report unavailable — agent-notes cost-report not run)`. Instead, print one plain line under the heading:
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ You MUST NOT directly:
|
|
|
13
13
|
- Run installs, builds, migrations, or destructive commands — dispatch `devops`
|
|
14
14
|
- Use `bash` for anything beyond the read-only verification list above
|
|
15
15
|
|
|
16
|
-
If you feel the urge to "just quickly check a file" — STOP. Dispatch `explorer`. Every file read by the lead is a budget leak (Opus tokens are
|
|
16
|
+
If you feel the urge to "just quickly check a file" — STOP. Dispatch `explorer`. Every file read by the lead is a budget leak (Opus tokens are 5× Haiku).
|
|
17
17
|
|
|
18
18
|
Exception: trivial requests (factual questions, conversational replies, single-line answers) may be handled inline with no tools.
|
|
19
19
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
baseline:
|
|
2
|
-
label: Claude Opus 4.
|
|
2
|
+
label: Claude Opus 4.8
|
|
3
3
|
price:
|
|
4
4
|
in: 5.00
|
|
5
5
|
out: 25.00
|
|
@@ -16,8 +16,8 @@ providers:
|
|
|
16
16
|
match: "*sonnet*"
|
|
17
17
|
price: {in: 3.00, out: 15.00, cache: 0.30}
|
|
18
18
|
updated_at: "2026-04"
|
|
19
|
-
- name: Claude Opus 4.7 / 4.6 / 4.5
|
|
20
|
-
match: ["*opus-4.7*", "*opus-4.6*", "*opus-4.5*"]
|
|
19
|
+
- name: Claude Opus 4.8 / 4.7 / 4.6 / 4.5
|
|
20
|
+
match: ["*opus-4.8*", "*opus-4.7*", "*opus-4.6*", "*opus-4.5*"]
|
|
21
21
|
price: {in: 5.00, out: 25.00, cache: 0.50}
|
|
22
22
|
updated_at: "2026-04"
|
|
23
23
|
- name: Claude Opus (legacy)
|
|
@@ -6,8 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from . import _pricing
|
|
8
8
|
from ._formatting import (
|
|
9
|
-
|
|
10
|
-
tier_color, fmt_tokens, fmt_cost, fmt_time,
|
|
9
|
+
fmt_time, render_cost_table,
|
|
11
10
|
)
|
|
12
11
|
from ..services.state_store import state_file as _state_file, load_state as _load_state
|
|
13
12
|
|
|
@@ -279,66 +278,15 @@ def run(since: float | None = None, session_id: str | None = None) -> int:
|
|
|
279
278
|
ms = agent_time_ms.get(label, 0)
|
|
280
279
|
return fmt_time(ms / 1000) if ms > 0 else "n/a"
|
|
281
280
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
len(fmt_tokens(
|
|
286
|
-
sum(i for _, _, i, *_ in costs),
|
|
287
|
-
sum(o for _, _, _, o, *_ in costs),
|
|
288
|
-
sum(c for _, _, _, _, c, *_ in costs),
|
|
289
|
-
))
|
|
290
|
-
) + 2
|
|
291
|
-
all_time_strs = [_time_str_for(a) for a, *_ in costs]
|
|
292
|
-
time_col_w = max(len(s) for s in all_time_strs) + 2
|
|
293
|
-
W = (agent_col_w, tok_col_w, time_col_w, 12, 12)
|
|
294
|
-
|
|
295
|
-
bl_label = _pricing.baseline_label()
|
|
296
|
-
header = (
|
|
297
|
-
f"{'agent(model)':<{W[0]}}"
|
|
298
|
-
f" {'in/out/cache':<{W[1]}}"
|
|
299
|
-
f" {'time':<{W[2]}}"
|
|
300
|
-
f" {'actual':<{W[3]}}"
|
|
301
|
-
f" {f'vs {bl_label}':<{W[4]}}"
|
|
281
|
+
total_time_ms = sum(
|
|
282
|
+
lead_time_ms if agent == "lead" else agent_time_ms.get(agent, 0)
|
|
283
|
+
for agent, *_ in costs
|
|
302
284
|
)
|
|
303
|
-
print(BOLD + header + NC)
|
|
304
|
-
print(DIM + "-" * len(header) + NC)
|
|
305
|
-
|
|
306
|
-
total_inp = total_outp = total_cache = 0
|
|
307
|
-
total_actual = total_vs = 0.0
|
|
308
|
-
total_time_ms = 0
|
|
309
|
-
|
|
310
|
-
for agent, model, inp, outp, cache, actual, vs in costs:
|
|
311
|
-
label = f"{agent}({model})"
|
|
312
|
-
col = tier_color(model)
|
|
313
|
-
t_str = _time_str_for(agent)
|
|
314
|
-
print(
|
|
315
|
-
col + f"{label:<{W[0]}}" + NC
|
|
316
|
-
+ f" {fmt_tokens(inp, outp, cache):<{W[1]}}"
|
|
317
|
-
+ f" {t_str:<{W[2]}}"
|
|
318
|
-
+ f" {fmt_cost(actual):<{W[3]}}"
|
|
319
|
-
+ f" {fmt_cost(vs):<{W[4]}}"
|
|
320
|
-
)
|
|
321
|
-
total_inp += inp
|
|
322
|
-
total_outp += outp
|
|
323
|
-
total_cache += cache
|
|
324
|
-
total_actual += actual
|
|
325
|
-
total_vs += vs
|
|
326
|
-
if agent == "lead":
|
|
327
|
-
total_time_ms += lead_time_ms
|
|
328
|
-
else:
|
|
329
|
-
total_time_ms += agent_time_ms.get(agent, 0)
|
|
330
|
-
|
|
331
|
-
saved_pct = round((1 - total_actual / total_vs) * 100) if total_vs else 0
|
|
332
|
-
total_label = f"TOTAL (saved {saved_pct}%)"
|
|
333
285
|
total_time_str = fmt_time(total_time_ms / 1000) if total_time_ms > 0 else "n/a"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
+ f" {fmt_cost(total_actual):<{W[3]}}"
|
|
341
|
-
+ f" {fmt_cost(total_vs):<{W[4]}}"
|
|
342
|
-
+ NC
|
|
343
|
-
)
|
|
286
|
+
|
|
287
|
+
rows = [
|
|
288
|
+
(f"{agent}({model})", model, inp, outp, cache, _time_str_for(agent), actual, vs)
|
|
289
|
+
for agent, model, inp, outp, cache, actual, vs in costs
|
|
290
|
+
]
|
|
291
|
+
render_cost_table(rows, total_time_str, _pricing.baseline_label())
|
|
344
292
|
return 0
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""ANSI color constants and shared formatting helpers."""
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
_USE_COLOR = sys.stdout.isatty() and not os.environ.get("NO_COLOR")
|
|
6
|
+
|
|
7
|
+
BOLD = "\033[1m" if _USE_COLOR else ""
|
|
8
|
+
DIM = "\033[2m" if _USE_COLOR else ""
|
|
9
|
+
YELLOW = "\033[0;33m" if _USE_COLOR else ""
|
|
10
|
+
GREEN = "\033[0;32m" if _USE_COLOR else ""
|
|
11
|
+
CYAN = "\033[0;36m" if _USE_COLOR else ""
|
|
12
|
+
NC = "\033[0m" if _USE_COLOR else ""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def tier_color(model_id: str) -> str:
|
|
16
|
+
if not _USE_COLOR:
|
|
17
|
+
return ""
|
|
18
|
+
if "opus" in model_id:
|
|
19
|
+
return YELLOW
|
|
20
|
+
if "sonnet" in model_id:
|
|
21
|
+
return CYAN
|
|
22
|
+
return DIM
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def fmt_num(n: int) -> str:
|
|
26
|
+
if n >= 1_000_000:
|
|
27
|
+
return f"{n / 1_000_000:.2f}m"
|
|
28
|
+
if n >= 1_000:
|
|
29
|
+
return f"{n / 1_000:.2f}k"
|
|
30
|
+
return str(n)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fmt_tokens(inp, outp, cache) -> str:
|
|
34
|
+
return f"{fmt_num(inp)}/{fmt_num(outp)}/{fmt_num(cache)}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def fmt_time(sec: float) -> str:
|
|
38
|
+
s = int(round(sec))
|
|
39
|
+
if s < 60:
|
|
40
|
+
return f"{s}s"
|
|
41
|
+
m, s = divmod(s, 60)
|
|
42
|
+
if m < 60:
|
|
43
|
+
return f"{m}m {s}s" if s else f"{m}m"
|
|
44
|
+
h, m = divmod(m, 60)
|
|
45
|
+
return f"{h}h {m}m" if m else f"{h}h"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def fmt_cost(c: float) -> str:
|
|
49
|
+
return f"${c:.4f}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def render_cost_table(
|
|
53
|
+
rows: list[tuple],
|
|
54
|
+
total_time_str: str,
|
|
55
|
+
baseline_label: str,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Print a cost table to stdout.
|
|
58
|
+
|
|
59
|
+
Each row must be a tuple of:
|
|
60
|
+
(label: str, model: str, inp: int, outp: int, cache: int,
|
|
61
|
+
time_str: str, actual: float, vs: float)
|
|
62
|
+
|
|
63
|
+
total_time_str — pre-formatted total time (backends compute it differently)
|
|
64
|
+
baseline_label — label for the rightmost "vs" column header
|
|
65
|
+
"""
|
|
66
|
+
agent_col_w = max(len(label) for label, *_ in rows) + 2
|
|
67
|
+
tok_col_w = max(
|
|
68
|
+
max(len(fmt_tokens(i, o, c)) for _, _, i, o, c, *_ in rows),
|
|
69
|
+
len(fmt_tokens(
|
|
70
|
+
sum(i for _, _, i, *_ in rows),
|
|
71
|
+
sum(o for _, _, _, o, *_ in rows),
|
|
72
|
+
sum(c for _, _, _, _, c, *_ in rows),
|
|
73
|
+
))
|
|
74
|
+
) + 2
|
|
75
|
+
time_col_w = max(
|
|
76
|
+
max(len(t) for _, _, _, _, _, t, *_ in rows),
|
|
77
|
+
len(total_time_str)
|
|
78
|
+
) + 2
|
|
79
|
+
W = (agent_col_w, tok_col_w, time_col_w, 12, 12)
|
|
80
|
+
|
|
81
|
+
header = (
|
|
82
|
+
f"{'agent(model)':<{W[0]}}"
|
|
83
|
+
f" {'in/out/cache':<{W[1]}}"
|
|
84
|
+
f" {'time':<{W[2]}}"
|
|
85
|
+
f" {'actual':<{W[3]}}"
|
|
86
|
+
f" {f'vs {baseline_label}':<{W[4]}}"
|
|
87
|
+
)
|
|
88
|
+
print(BOLD + header + NC)
|
|
89
|
+
print(DIM + "-" * len(header) + NC)
|
|
90
|
+
|
|
91
|
+
total_inp = total_outp = total_cache = 0
|
|
92
|
+
total_actual = total_vs = 0.0
|
|
93
|
+
|
|
94
|
+
for label, model, inp, outp, cache, time_str, actual, vs in rows:
|
|
95
|
+
col = tier_color(model)
|
|
96
|
+
print(
|
|
97
|
+
col + f"{label:<{W[0]}}" + NC
|
|
98
|
+
+ f" {fmt_tokens(inp, outp, cache):<{W[1]}}"
|
|
99
|
+
+ f" {time_str:<{W[2]}}"
|
|
100
|
+
+ f" {fmt_cost(actual):<{W[3]}}"
|
|
101
|
+
+ f" {fmt_cost(vs):<{W[4]}}"
|
|
102
|
+
)
|
|
103
|
+
total_inp += inp
|
|
104
|
+
total_outp += outp
|
|
105
|
+
total_cache += cache
|
|
106
|
+
total_actual += actual
|
|
107
|
+
total_vs += vs
|
|
108
|
+
|
|
109
|
+
saved_pct = round((1 - total_actual / total_vs) * 100) if total_vs else 0
|
|
110
|
+
total_label = f"TOTAL (saved {saved_pct}%)"
|
|
111
|
+
col = GREEN if total_actual <= 5 else YELLOW
|
|
112
|
+
print(
|
|
113
|
+
col + BOLD
|
|
114
|
+
+ f"{total_label:<{W[0]}}"
|
|
115
|
+
+ f" {fmt_tokens(total_inp, total_outp, total_cache):<{W[1]}}"
|
|
116
|
+
+ f" {total_time_str:<{W[2]}}"
|
|
117
|
+
+ f" {fmt_cost(total_actual):<{W[3]}}"
|
|
118
|
+
+ f" {fmt_cost(total_vs):<{W[4]}}"
|
|
119
|
+
+ NC
|
|
120
|
+
)
|
|
@@ -3,10 +3,7 @@ import sqlite3
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from . import _pricing
|
|
6
|
-
from ._formatting import
|
|
7
|
-
BOLD, DIM, GREEN, YELLOW, NC,
|
|
8
|
-
tier_color, fmt_tokens, fmt_time, fmt_cost,
|
|
9
|
-
)
|
|
6
|
+
from ._formatting import fmt_time, render_cost_table
|
|
10
7
|
|
|
11
8
|
DB = Path.home() / ".local/share/opencode/opencode.db"
|
|
12
9
|
|
|
@@ -63,7 +60,7 @@ def run() -> int:
|
|
|
63
60
|
return 0
|
|
64
61
|
|
|
65
62
|
records = [
|
|
66
|
-
(agent, model or "unknown", inp or 0, outp or 0, cache or 0, sec or 0)
|
|
63
|
+
(agent, _pricing.normalize_model(model or "unknown"), inp or 0, outp or 0, cache or 0, sec or 0)
|
|
67
64
|
for agent, model, inp, outp, cache, sec in rows
|
|
68
65
|
]
|
|
69
66
|
|
|
@@ -74,68 +71,13 @@ def run() -> int:
|
|
|
74
71
|
for agent, model, inp, outp, cache, sec in records
|
|
75
72
|
]
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
_max_sec = max(s for _, _, _, _, _, s, *_ in costs)
|
|
81
|
-
_total_sec = sum(s for _, _, _, _, _, s, *_ in costs)
|
|
82
|
-
_total_time = f"{fmt_time(_max_sec)} / {fmt_time(_total_sec)} seq"
|
|
83
|
-
|
|
84
|
-
agent_col_w = max(len(f"{a}({m})") for a, m, *_ in costs) + 2
|
|
85
|
-
tok_col_w = max(
|
|
86
|
-
max(len(fmt_tokens(i, o, c)) for _, _, i, o, c, *_ in costs),
|
|
87
|
-
len(fmt_tokens(_total_inp, _total_outp, _total_cache))
|
|
88
|
-
) + 2
|
|
89
|
-
time_col_w = max(
|
|
90
|
-
max(len(fmt_time(s)) for _, _, _, _, _, s, *_ in costs),
|
|
91
|
-
len(_total_time)
|
|
92
|
-
) + 2
|
|
93
|
-
W = (agent_col_w, tok_col_w, time_col_w, 12, 12)
|
|
94
|
-
|
|
95
|
-
bl_label = _pricing.baseline_label()
|
|
96
|
-
header = (
|
|
97
|
-
f"{'agent(model)':<{W[0]}}"
|
|
98
|
-
f" {'in/out/cache':<{W[1]}}"
|
|
99
|
-
f" {'time':<{W[2]}}"
|
|
100
|
-
f" {'actual':<{W[3]}}"
|
|
101
|
-
f" {f'vs {bl_label}':<{W[4]}}"
|
|
102
|
-
)
|
|
103
|
-
print(BOLD + header + NC)
|
|
104
|
-
print(DIM + "-" * len(header) + NC)
|
|
74
|
+
max_sec = max(s for _, _, _, _, _, s, *_ in costs)
|
|
75
|
+
total_sec = sum(s for _, _, _, _, _, s, *_ in costs)
|
|
76
|
+
total_time_str = f"{fmt_time(max_sec)} / {fmt_time(total_sec)} seq"
|
|
105
77
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
time_str = fmt_time(sec)
|
|
112
|
-
col = tier_color(model)
|
|
113
|
-
print(
|
|
114
|
-
col + f"{label:<{W[0]}}" + NC
|
|
115
|
-
+ f" {fmt_tokens(inp, outp, cache):<{W[1]}}"
|
|
116
|
-
+ f" {time_str:<{W[2]}}"
|
|
117
|
-
+ f" {fmt_cost(actual):<{W[3]}}"
|
|
118
|
-
+ f" {fmt_cost(vs):<{W[4]}}"
|
|
119
|
-
)
|
|
120
|
-
total_inp += inp
|
|
121
|
-
total_outp += outp
|
|
122
|
-
total_cache += cache
|
|
123
|
-
total_actual += actual
|
|
124
|
-
total_vs += vs
|
|
125
|
-
max_sec = max(max_sec, sec)
|
|
126
|
-
total_sec += sec
|
|
127
|
-
|
|
128
|
-
saved_pct = round((1 - total_actual / total_vs) * 100) if total_vs else 0
|
|
129
|
-
total_label = f"TOTAL (saved {saved_pct}%)"
|
|
130
|
-
total_time = _total_time
|
|
131
|
-
col = GREEN if total_actual <= 5 else YELLOW
|
|
132
|
-
print(
|
|
133
|
-
col + BOLD
|
|
134
|
-
+ f"{total_label:<{W[0]}}"
|
|
135
|
-
+ f" {fmt_tokens(total_inp, total_outp, total_cache):<{W[1]}}"
|
|
136
|
-
+ f" {total_time:<{W[2]}}"
|
|
137
|
-
+ f" {fmt_cost(total_actual):<{W[3]}}"
|
|
138
|
-
+ f" {fmt_cost(total_vs):<{W[4]}}"
|
|
139
|
-
+ NC
|
|
140
|
-
)
|
|
78
|
+
rows = [
|
|
79
|
+
(f"{agent}({model})", model, inp, outp, cache, fmt_time(sec), actual, vs)
|
|
80
|
+
for agent, model, inp, outp, cache, sec, actual, vs in costs
|
|
81
|
+
]
|
|
82
|
+
render_cost_table(rows, total_time_str, _pricing.baseline_label())
|
|
141
83
|
return 0
|
|
@@ -206,6 +206,7 @@ tests/functional/commands/test_regenerate_command.py
|
|
|
206
206
|
tests/functional/commands/test_uninstall_command.py
|
|
207
207
|
tests/functional/commands/test_validate_command.py
|
|
208
208
|
tests/functional/memory/__init__.py
|
|
209
|
+
tests/functional/memory/test_memory_add_local_backend.py
|
|
209
210
|
tests/functional/memory/test_memory_command.py
|
|
210
211
|
tests/functional/scripts/__init__.py
|
|
211
212
|
tests/functional/scripts/test_release_script.py
|
|
@@ -241,6 +242,7 @@ tests/unit/scripts/__init__.py
|
|
|
241
242
|
tests/unit/scripts/test_cost_report.py
|
|
242
243
|
tests/unit/scripts/test_cost_report_scoping.py
|
|
243
244
|
tests/unit/scripts/test_formatting_tty.py
|
|
245
|
+
tests/unit/scripts/test_opencode_backend_pricing.py
|
|
244
246
|
tests/unit/scripts/test_time_aggregation.py
|
|
245
247
|
tests/unit/services/__init__.py
|
|
246
248
|
tests/unit/services/test_build_functions.py
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Regression test: memory add on local backend exits non-zero and prints to stderr.
|
|
2
|
+
|
|
3
|
+
Previously the command printed guidance to stdout and returned silently (exit 0),
|
|
4
|
+
causing callers to believe the note was saved when nothing was written.
|
|
5
|
+
"""
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestMemoryAddLocalBackendExitsNonZero:
|
|
10
|
+
def test_local_backend_add_exits_nonzero(self, tmp_path, monkeypatch, capsys):
|
|
11
|
+
"""memory add on local backend must exit non-zero — not silently succeed."""
|
|
12
|
+
mem_dir = tmp_path / "memory"
|
|
13
|
+
|
|
14
|
+
import agent_notes.commands.memory as mem_mod
|
|
15
|
+
monkeypatch.setattr(mem_mod._common, "_load_memory_config", lambda: ("local", mem_dir))
|
|
16
|
+
|
|
17
|
+
from agent_notes.commands.memory import memory
|
|
18
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
19
|
+
memory(action="add", name="My Note", extra=["Body text", "context", "lead"])
|
|
20
|
+
|
|
21
|
+
assert exc_info.value.code != 0, (
|
|
22
|
+
"memory add on local backend must exit non-zero so callers detect the failure"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def test_local_backend_add_prints_guidance_to_stderr(self, tmp_path, monkeypatch, capsys):
|
|
26
|
+
"""Guidance message must go to stderr, not stdout, so pipelines can separate it."""
|
|
27
|
+
mem_dir = tmp_path / "memory"
|
|
28
|
+
|
|
29
|
+
import agent_notes.commands.memory as mem_mod
|
|
30
|
+
monkeypatch.setattr(mem_mod._common, "_load_memory_config", lambda: ("local", mem_dir))
|
|
31
|
+
|
|
32
|
+
from agent_notes.commands.memory import memory
|
|
33
|
+
with pytest.raises(SystemExit):
|
|
34
|
+
memory(action="add", name="My Note", extra=["Body text", "context", "lead"])
|
|
35
|
+
|
|
36
|
+
captured = capsys.readouterr()
|
|
37
|
+
assert captured.out == "", f"Expected no stdout output, got: {captured.out!r}"
|
|
38
|
+
assert "local storage" in captured.err.lower() or "directly" in captured.err.lower(), (
|
|
39
|
+
f"Expected guidance in stderr, got: {captured.err!r}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def test_local_backend_add_writes_nothing(self, tmp_path, monkeypatch):
|
|
43
|
+
"""No file should be created in MEMORY_DIR when local backend is used."""
|
|
44
|
+
mem_dir = tmp_path / "memory"
|
|
45
|
+
mem_dir.mkdir()
|
|
46
|
+
|
|
47
|
+
import agent_notes.commands.memory as mem_mod
|
|
48
|
+
monkeypatch.setattr(mem_mod._common, "_load_memory_config", lambda: ("local", mem_dir))
|
|
49
|
+
|
|
50
|
+
from agent_notes.commands.memory import memory
|
|
51
|
+
with pytest.raises(SystemExit):
|
|
52
|
+
memory(action="add", name="My Note", extra=["Body text", "context", "lead"])
|
|
53
|
+
|
|
54
|
+
written = list(mem_dir.rglob("*"))
|
|
55
|
+
assert written == [], f"Expected no files written, found: {written}"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Regression test: OpenCode backend normalizes dash-form model IDs before pricing lookup.
|
|
2
|
+
|
|
3
|
+
Without normalization, `claude-opus-4-8` fails the `*opus-4.8*` fnmatch glob and
|
|
4
|
+
falls through to the legacy `*opus*` catch-all ($15/M in instead of $5/M in).
|
|
5
|
+
"""
|
|
6
|
+
import sqlite3
|
|
7
|
+
import pytest
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from unittest.mock import patch
|
|
10
|
+
|
|
11
|
+
from agent_notes.scripts import _pricing
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ── helpers ───────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
def _make_opencode_db(tmp_path: Path, model: str, inp: int = 1_000_000, outp: int = 1_000_000) -> Path:
|
|
17
|
+
"""Build a minimal OpenCode SQLite DB with one session and one assistant message."""
|
|
18
|
+
db_path = tmp_path / "opencode.db"
|
|
19
|
+
conn = sqlite3.connect(db_path)
|
|
20
|
+
conn.executescript("""
|
|
21
|
+
CREATE TABLE session (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
parent_id TEXT,
|
|
24
|
+
time_created INTEGER
|
|
25
|
+
);
|
|
26
|
+
CREATE TABLE message (
|
|
27
|
+
id TEXT PRIMARY KEY,
|
|
28
|
+
session_id TEXT,
|
|
29
|
+
data TEXT
|
|
30
|
+
);
|
|
31
|
+
""")
|
|
32
|
+
|
|
33
|
+
import json, time
|
|
34
|
+
now_ms = int(time.time() * 1000)
|
|
35
|
+
session_id = "sess-001"
|
|
36
|
+
conn.execute(
|
|
37
|
+
"INSERT INTO session VALUES (?, NULL, ?)",
|
|
38
|
+
(session_id, now_ms),
|
|
39
|
+
)
|
|
40
|
+
# assistant message with the raw dash-form model ID
|
|
41
|
+
msg_data = json.dumps({
|
|
42
|
+
"role": "assistant",
|
|
43
|
+
"modelID": model,
|
|
44
|
+
"tokens": {"input": inp, "output": outp, "cache": {"read": 0}},
|
|
45
|
+
"time": {"created": now_ms - 1000, "completed": now_ms},
|
|
46
|
+
"agent": "lead",
|
|
47
|
+
})
|
|
48
|
+
conn.execute(
|
|
49
|
+
"INSERT INTO message VALUES (?, ?, ?)",
|
|
50
|
+
("msg-001", session_id, msg_data),
|
|
51
|
+
)
|
|
52
|
+
conn.commit()
|
|
53
|
+
conn.close()
|
|
54
|
+
return db_path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ── tests ─────────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
class TestOpencodeBackendNormalizesModel:
|
|
60
|
+
def test_opus_dash_form_uses_opus_rate_not_legacy(self, tmp_path):
|
|
61
|
+
"""claude-opus-4-8 (dash form) must resolve to the $5/M Opus rate, not $15/M legacy."""
|
|
62
|
+
opus_price = _pricing.get_price("claude-opus-4.8")
|
|
63
|
+
legacy_price = _pricing.get_price("claude-opus-4.1")
|
|
64
|
+
|
|
65
|
+
# Sanity: confirm we have distinct pricing tiers to differentiate
|
|
66
|
+
assert opus_price["in"] < legacy_price["in"], (
|
|
67
|
+
"Test precondition failed: expected Opus 4.8 cheaper than legacy Opus"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Normalize as the backend should
|
|
71
|
+
normalized = _pricing.normalize_model("claude-opus-4-8")
|
|
72
|
+
assert normalized == "claude-opus-4.8"
|
|
73
|
+
|
|
74
|
+
actual_price = _pricing.get_price(normalized)
|
|
75
|
+
assert actual_price["in"] == opus_price["in"], (
|
|
76
|
+
f"Normalized 'claude-opus-4-8' priced at {actual_price['in']} instead of {opus_price['in']}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def test_opencode_run_uses_normalized_model(self, tmp_path, capsys):
|
|
80
|
+
"""Integration: _opencode_backend.run() prices opus-4-8 at the Opus rate end-to-end."""
|
|
81
|
+
from agent_notes.scripts import _opencode_backend
|
|
82
|
+
|
|
83
|
+
db = _make_opencode_db(tmp_path, model="claude-opus-4-8", inp=1_000_000, outp=0)
|
|
84
|
+
|
|
85
|
+
with patch.object(_opencode_backend, "DB", db):
|
|
86
|
+
rc = _opencode_backend.run()
|
|
87
|
+
|
|
88
|
+
assert rc == 0
|
|
89
|
+
out = capsys.readouterr().out
|
|
90
|
+
|
|
91
|
+
# At 1M input tokens:
|
|
92
|
+
# Opus rate ($5/M) → $5.00
|
|
93
|
+
# Legacy rate ($15/M) → $15.00
|
|
94
|
+
# The output must contain the Opus cost, not the legacy cost.
|
|
95
|
+
assert "$5.00" in out or "5.00" in out, (
|
|
96
|
+
f"Expected ~$5.00 (Opus rate) in output, got:\n{out}"
|
|
97
|
+
)
|
|
98
|
+
assert "$15.00" not in out and "15.00" not in out.replace("$5.00", ""), (
|
|
99
|
+
f"Legacy rate ($15.00) appeared in output — normalization not applied:\n{out}"
|
|
100
|
+
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.25.0
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""ANSI color constants and shared formatting helpers."""
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
_USE_COLOR = sys.stdout.isatty() and not os.environ.get("NO_COLOR")
|
|
6
|
-
|
|
7
|
-
BOLD = "\033[1m" if _USE_COLOR else ""
|
|
8
|
-
DIM = "\033[2m" if _USE_COLOR else ""
|
|
9
|
-
YELLOW = "\033[0;33m" if _USE_COLOR else ""
|
|
10
|
-
GREEN = "\033[0;32m" if _USE_COLOR else ""
|
|
11
|
-
CYAN = "\033[0;36m" if _USE_COLOR else ""
|
|
12
|
-
NC = "\033[0m" if _USE_COLOR else ""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def tier_color(model_id: str) -> str:
|
|
16
|
-
if not _USE_COLOR:
|
|
17
|
-
return ""
|
|
18
|
-
if "opus" in model_id:
|
|
19
|
-
return YELLOW
|
|
20
|
-
if "sonnet" in model_id:
|
|
21
|
-
return CYAN
|
|
22
|
-
return DIM
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def fmt_num(n: int) -> str:
|
|
26
|
-
if n >= 1_000_000:
|
|
27
|
-
return f"{n / 1_000_000:.2f}m"
|
|
28
|
-
if n >= 1_000:
|
|
29
|
-
return f"{n / 1_000:.2f}k"
|
|
30
|
-
return str(n)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def fmt_tokens(inp, outp, cache) -> str:
|
|
34
|
-
return f"{fmt_num(inp)}/{fmt_num(outp)}/{fmt_num(cache)}"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def fmt_time(sec: float) -> str:
|
|
38
|
-
s = int(round(sec))
|
|
39
|
-
if s < 60:
|
|
40
|
-
return f"{s}s"
|
|
41
|
-
m, s = divmod(s, 60)
|
|
42
|
-
if m < 60:
|
|
43
|
-
return f"{m}m {s}s" if s else f"{m}m"
|
|
44
|
-
h, m = divmod(m, 60)
|
|
45
|
-
return f"{h}h {m}m" if m else f"{h}h"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def fmt_cost(c: float) -> str:
|
|
49
|
-
return f"${c:.4f}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|