agent-notes 2.22.0__tar.gz → 2.23.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.22.0 → agent_notes-2.23.0}/PKG-INFO +1 -1
- agent_notes-2.23.0/agent_notes/VERSION +1 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/cli.py +1 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/build.py +4 -4
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/config.py +24 -10
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/doctor.py +4 -6
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/info.py +2 -2
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/install.py +9 -8
- agent_notes-2.23.0/agent_notes/commands/memory/__init__.py +107 -0
- agent_notes-2.23.0/agent_notes/commands/memory/_common.py +63 -0
- agent_notes-2.23.0/agent_notes/commands/memory/migrate.py +152 -0
- agent_notes-2.23.0/agent_notes/commands/memory/notes.py +207 -0
- agent_notes-2.23.0/agent_notes/commands/memory/reset.py +87 -0
- agent_notes-2.23.0/agent_notes/commands/memory/transfer.py +73 -0
- agent_notes-2.23.0/agent_notes/commands/memory/vault.py +66 -0
- agent_notes-2.23.0/agent_notes/commands/memory/wiki.py +153 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/regenerate.py +6 -7
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/set_role.py +5 -7
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/uninstall.py +2 -2
- agent_notes-2.23.0/agent_notes/commands/wizard/__init__.py +421 -0
- agent_notes-2.23.0/agent_notes/commands/wizard/_common.py +63 -0
- agent_notes-2.23.0/agent_notes/commands/wizard/execute.py +195 -0
- agent_notes-2.23.0/agent_notes/commands/wizard/orchestrator.py +87 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/config.py +8 -4
- agent_notes-2.23.0/agent_notes/constants.py +24 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/analyst.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/api-reviewer.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/architect.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/coder.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/database-specialist.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/debugger.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/devil.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/devops.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/explorer.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/integrations.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/performance-profiler.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/refactorer.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/reviewer.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/security-auditor.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/system-auditor.md +1 -11
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/tech-writer.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/test-runner.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/test-writer.md +8 -18
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/global-claude.md +8 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/roles/reasoner.yaml +1 -0
- agent_notes-2.23.0/agent_notes/data/rules/safety.md +41 -0
- agent_notes-2.23.0/agent_notes/data/skills/ingest/SKILL.md +239 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/obsidian-memory/SKILL.md +4 -85
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/doctor_checks.py +2 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/role.py +2 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/skill.py +2 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/role_registry.py +1 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/skill_registry.py +24 -20
- agent_notes-2.23.0/agent_notes/services/_memory_utils.py +47 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/diagnostics/_checks.py +4 -6
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/diagnostics/_fix.py +2 -2
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/installer.py +65 -9
- agent_notes-2.23.0/agent_notes/services/local_backend.py +32 -0
- agent_notes-2.23.0/agent_notes/services/memory_router.py +32 -0
- agent_notes-2.22.0/agent_notes/services/memory_backend.py → agent_notes-2.23.0/agent_notes/services/obsidian_backend.py +36 -82
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/rendering.py +64 -6
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/settings_writer.py +12 -0
- agent_notes-2.23.0/agent_notes/services/wiki/__init__.py +25 -0
- agent_notes-2.23.0/agent_notes/services/wiki/_wiki_utils.py +182 -0
- agent_notes-2.23.0/agent_notes/services/wiki/wiki_index.py +171 -0
- agent_notes-2.23.0/agent_notes/services/wiki/wiki_ingest.py +375 -0
- agent_notes-2.23.0/agent_notes/services/wiki/wiki_lint.py +101 -0
- agent_notes-2.23.0/agent_notes/services/wiki/wiki_query.py +103 -0
- agent_notes-2.23.0/agent_notes/services/wiki/wiki_storage.py +122 -0
- agent_notes-2.23.0/agent_notes/services/wiki_backend.py +3 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/PKG-INFO +1 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/SOURCES.txt +33 -6
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_config_command.py +6 -6
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_regenerate_command.py +2 -2
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_uninstall_command.py +1 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/memory/test_memory_command.py +1 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/plugins/test_skills.py +17 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_info.py +2 -2
- agent_notes-2.23.0/tests/unit/commands/test_memory_imports.py +118 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_memory_migrate.py +4 -4
- agent_notes-2.23.0/tests/unit/commands/test_wizard_imports.py +106 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/registries/test_registries.py +9 -0
- agent_notes-2.23.0/tests/unit/services/test_credential_filter.py +132 -0
- agent_notes-2.23.0/tests/unit/services/test_local_backend.py +242 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_memory_backend.py +3 -1
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_memory_backend_io.py +76 -13
- agent_notes-2.23.0/tests/unit/services/test_memory_router.py +100 -0
- agent_notes-2.23.0/tests/unit/services/test_skill_filtering.py +65 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_wiki_backend.py +51 -0
- agent_notes-2.23.0/tests/unit/services/test_wiki_imports.py +222 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/test_memory_dir_for_backend.py +14 -12
- agent_notes-2.22.0/agent_notes/VERSION +0 -1
- agent_notes-2.22.0/agent_notes/commands/memory.py +0 -843
- agent_notes-2.22.0/agent_notes/commands/wizard.py +0 -749
- agent_notes-2.22.0/agent_notes/data/rules/safety.md +0 -10
- agent_notes-2.22.0/agent_notes/install_state.py +0 -11
- agent_notes-2.22.0/agent_notes/services/wiki_backend.py +0 -1022
- agent_notes-2.22.0/agent_notes/state.py +0 -21
- {agent_notes-2.22.0 → agent_notes-2.23.0}/LICENSE +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/README.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/__main__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/_install_helpers.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/list.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/commands/validate.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/agents.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/lead.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/cost_reporting.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/execution.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/guardrails.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/hard_limits.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/phase0.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/pipelines.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/review.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/verification.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/shared/wiki_compile.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/agents/wiki-compiler.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/cli/claude.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/cli/copilot.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/cli/opencode.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/commands/brainstorm.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/commands/debug.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/commands/review.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/global-copilot.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/global-opencode.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/hooks/session-context.md.tpl +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-haiku-4-5.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-opus-4-1.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-opus-4-5.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-opus-4-6.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-opus-4-7.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-sonnet-4-5.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-sonnet-4-6.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/models/claude-sonnet-4.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/plugin/claude.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/plugin/opencode-index.js.template +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/plugin/opencode.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/pricing.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/roles/orchestrator.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/roles/scout.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/roles/worker.yaml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/rules/code-quality.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/brainstorming/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/caveman/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/code-review/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/debugging-protocol/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/docker/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/docker/compose.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/docker/dockerfile.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/git/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/grill-me/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/grill-with-docs/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/improve-codebase-architecture/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/controllers.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/frontend.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/infra.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/models.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/testing.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/rails/views.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/refactoring-protocol/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/setup-project-context/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/tdd/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/skills/zoom-out/SKILL.md +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/claude.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/data/templates/frontmatter/opencode.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/agent.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/cli_backend.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/diagnostics.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/diff.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/model.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/rule.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/domain/state.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/_base.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/agent_registry.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/cli_registry.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/model_registry.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/registries/rule_registry.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/_claude_backend.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/_formatting.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/_opencode_backend.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/_pricing.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/scripts/cost_report.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/counts.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/credentials.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/diagnostics/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/diagnostics/_display.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/diff.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/fs.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/install_state_builder.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/session_context.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/state_store.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/ui.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/user_config.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes/services/validation.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/dependency_links.txt +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/entry_points.txt +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/requires.txt +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/agent_notes.egg-info/top_level.txt +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/pyproject.toml +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/setup.cfg +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/conftest.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_doctor_command.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_info_command.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_install_command.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_list_command.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/commands/test_validate_command.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/memory/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/scripts/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/functional/scripts/test_release_script.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/build_output/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/build_output/test_build_output.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/install/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/install/test_install_methods.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/plugin_builders/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/integration/plugin_builders/test_plugin_builders.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/plugins/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/plugins/claude/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/plugins/claude/test_agents.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_cost_report_subcommand.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_count_agents.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_wizard_orchestrator_skip.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_wizard_preflight.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/commands/test_wizard_steps.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/registries/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/scripts/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/scripts/test_cost_report.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/scripts/test_cost_report_scoping.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/scripts/test_formatting_tty.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/scripts/test_time_aggregation.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/__init__.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_build_functions.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_credentials.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_fs.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_installer_plan.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_rendering_includes.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_session_context.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_settings_writer.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_state_store.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/services/test_validation.py +0 -0
- {agent_notes-2.22.0 → agent_notes-2.23.0}/tests/unit/test_import_health.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.23.0
|
|
@@ -284,7 +284,7 @@ def main():
|
|
|
284
284
|
# config
|
|
285
285
|
p_config = subparsers.add_parser("config", help="Reconfigure role/agent/model/memory/skill assignments after install")
|
|
286
286
|
p_config.add_argument("action", nargs="?", default="wizard",
|
|
287
|
-
choices=["wizard", "show", "role-model", "role-agent", "provider", "providers"],
|
|
287
|
+
choices=["wizard", "show", "role-model", "role-agent", "provider", "providers", "memory"],
|
|
288
288
|
help="Config action (default: wizard)")
|
|
289
289
|
p_config.add_argument("extra", nargs="*", help="Additional positional args (role, model, agent)")
|
|
290
290
|
p_config.add_argument("--cli", help="Target CLI (claude / opencode / both)")
|
|
@@ -91,18 +91,18 @@ def count_lines(file_path: Path) -> int:
|
|
|
91
91
|
|
|
92
92
|
def build() -> None:
|
|
93
93
|
"""Build agent configuration files from source."""
|
|
94
|
-
from .. import
|
|
94
|
+
from ..services.state_store import load_state
|
|
95
95
|
from ..config import ROOT
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
# Read configuration
|
|
98
98
|
try:
|
|
99
99
|
agents_config, tiers = load_agents_config()
|
|
100
100
|
except FileNotFoundError as e:
|
|
101
101
|
print(f"Error: {e}")
|
|
102
102
|
return
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
# Load state if present (no error if missing)
|
|
105
|
-
state =
|
|
105
|
+
state = load_state()
|
|
106
106
|
|
|
107
107
|
# Generate agent files (state=None is backward compatible)
|
|
108
108
|
print("Generating agent files...")
|
|
@@ -7,11 +7,13 @@ import sys
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
|
+
from ..constants import DEFAULT_VAULT_DIR, DEFAULT_VAULT_NAME, Wiki, Obsidian
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
def _load_state():
|
|
12
14
|
"""Load state or exit with a clear message."""
|
|
13
|
-
from .. import
|
|
14
|
-
st =
|
|
15
|
+
from ..services.state_store import load_state
|
|
16
|
+
st = load_state()
|
|
15
17
|
if st is None:
|
|
16
18
|
print("No installation found. Run `agent-notes install` first.")
|
|
17
19
|
sys.exit(1)
|
|
@@ -95,7 +97,7 @@ def _print_diff(before: str, after: str) -> None:
|
|
|
95
97
|
|
|
96
98
|
def _apply_and_regenerate(state, before: str) -> None:
|
|
97
99
|
"""Show diff, prompt, then write + regenerate on Y."""
|
|
98
|
-
from .. import
|
|
100
|
+
from ..services.state_store import record_install_state
|
|
99
101
|
from ..config import Color
|
|
100
102
|
from ..services.ui import _safe_input
|
|
101
103
|
|
|
@@ -114,7 +116,7 @@ def _apply_and_regenerate(state, before: str) -> None:
|
|
|
114
116
|
print("Discarded. No changes written.")
|
|
115
117
|
return
|
|
116
118
|
|
|
117
|
-
|
|
119
|
+
record_install_state(state)
|
|
118
120
|
print("State written.")
|
|
119
121
|
|
|
120
122
|
# Regenerate
|
|
@@ -311,9 +313,9 @@ def _wizard_memory(state, before: str) -> bool:
|
|
|
311
313
|
from ..services.ui import _safe_input, _path_input
|
|
312
314
|
|
|
313
315
|
storage_options = {
|
|
314
|
-
"1": ("local", "Local
|
|
316
|
+
"1": ("local", "Local files"),
|
|
315
317
|
"2": ("obsidian", "Obsidian vault"),
|
|
316
|
-
"3": ("none", "
|
|
318
|
+
"3": ("none", "Disabled"),
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
print("\nMemory storage options:")
|
|
@@ -329,7 +331,10 @@ def _wizard_memory(state, before: str) -> bool:
|
|
|
329
331
|
path = ""
|
|
330
332
|
|
|
331
333
|
if backend == "obsidian":
|
|
332
|
-
mode_options = {
|
|
334
|
+
mode_options = {
|
|
335
|
+
"1": ("obsidian", "Session notes — project-scoped decisions, patterns, mistakes"),
|
|
336
|
+
"2": ("wiki", "Knowledge wiki — Karpathy compile-once pattern with concepts, entities, sources"),
|
|
337
|
+
}
|
|
333
338
|
print("\n Obsidian mode:")
|
|
334
339
|
for key, (_, mlabel) in mode_options.items():
|
|
335
340
|
print(f" {key}) {mlabel}")
|
|
@@ -337,8 +342,8 @@ def _wizard_memory(state, before: str) -> bool:
|
|
|
337
342
|
if mode_choice in mode_options:
|
|
338
343
|
backend, label = mode_options[mode_choice]
|
|
339
344
|
|
|
340
|
-
subfolder =
|
|
341
|
-
default_vault = str(Path.home() /
|
|
345
|
+
subfolder = Obsidian.SUBFOLDER if backend == "obsidian" else Wiki.SUBFOLDER
|
|
346
|
+
default_vault = str(Path.home() / DEFAULT_VAULT_DIR / DEFAULT_VAULT_NAME)
|
|
342
347
|
print(f" Folder name: {subfolder}")
|
|
343
348
|
print(" Press Tab to autocomplete paths")
|
|
344
349
|
raw = _path_input(f" Vault path [{default_vault}]: ", default_vault).strip()
|
|
@@ -453,6 +458,13 @@ def interactive_config() -> None:
|
|
|
453
458
|
print(f"Unknown choice '{choice}'. Quit.")
|
|
454
459
|
|
|
455
460
|
|
|
461
|
+
def interactive_config_memory() -> None:
|
|
462
|
+
"""Run the interactive memory config wizard."""
|
|
463
|
+
state = _load_state()
|
|
464
|
+
before = _state_snapshot(state)
|
|
465
|
+
_wizard_memory(state, before)
|
|
466
|
+
|
|
467
|
+
|
|
456
468
|
# ── Entry point ──────────────────────────────────────────────────────────────
|
|
457
469
|
|
|
458
470
|
def config(action: str = "wizard", args: Optional[list] = None, cli_filter: Optional[str] = None) -> None:
|
|
@@ -483,7 +495,9 @@ def config(action: str = "wizard", args: Optional[list] = None, cli_filter: Opti
|
|
|
483
495
|
print("Usage: agent-notes config provider <name>")
|
|
484
496
|
sys.exit(1)
|
|
485
497
|
_wizard_provider_status(args[0])
|
|
498
|
+
elif action == "memory":
|
|
499
|
+
interactive_config_memory()
|
|
486
500
|
else:
|
|
487
501
|
print(f"Unknown config action: {action}")
|
|
488
|
-
print("Actions: wizard, show, role-model, role-agent, providers, provider")
|
|
502
|
+
print("Actions: wizard, show, role-model, role-agent, providers, provider, memory")
|
|
489
503
|
sys.exit(1)
|
|
@@ -64,13 +64,12 @@ def _check_session_hook(scope: str, issues: list) -> None:
|
|
|
64
64
|
|
|
65
65
|
def check_version_drift(scope: str, issues: list, fix_actions: list) -> None:
|
|
66
66
|
"""Check if the installed package version matches the current running version."""
|
|
67
|
-
from .. import
|
|
67
|
+
from ..services.state_store import load_current_state, get_scope
|
|
68
68
|
from ..config import get_version
|
|
69
69
|
from ..domain.diagnostics import Issue, FixAction
|
|
70
|
-
from ..services.state_store import get_scope
|
|
71
70
|
from pathlib import Path
|
|
72
71
|
|
|
73
|
-
state =
|
|
72
|
+
state = load_current_state()
|
|
74
73
|
if state is None:
|
|
75
74
|
return
|
|
76
75
|
|
|
@@ -96,8 +95,6 @@ def check_version_drift(scope: str, issues: list, fix_actions: list) -> None:
|
|
|
96
95
|
|
|
97
96
|
def diagnose(scope: str, fix: bool = False) -> bool:
|
|
98
97
|
"""Run all diagnostic checks and optionally apply fixes."""
|
|
99
|
-
from .. import install_state
|
|
100
|
-
|
|
101
98
|
print_summary(scope)
|
|
102
99
|
|
|
103
100
|
issues = []
|
|
@@ -120,7 +117,8 @@ def diagnose(scope: str, fix: bool = False) -> bool:
|
|
|
120
117
|
_check_session_hook(scope, issues)
|
|
121
118
|
|
|
122
119
|
# Print role→model assignments
|
|
123
|
-
|
|
120
|
+
from ..services.state_store import load_current_state
|
|
121
|
+
state = load_current_state()
|
|
124
122
|
if state is not None:
|
|
125
123
|
_check_role_models(state)
|
|
126
124
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from .. import
|
|
5
|
+
from ..services.state_store import load_current_state as _load_current_state
|
|
6
6
|
from ..config import get_version, CLAUDE_HOME, Color
|
|
7
7
|
from ._install_helpers import count_skills, count_agents, count_global
|
|
8
8
|
|
|
@@ -49,7 +49,7 @@ def show_info() -> None:
|
|
|
49
49
|
print(f" Local: {Color.CYAN}not detected{Color.NC}")
|
|
50
50
|
|
|
51
51
|
# State info
|
|
52
|
-
st =
|
|
52
|
+
st = _load_current_state()
|
|
53
53
|
if st is not None:
|
|
54
54
|
print("")
|
|
55
55
|
print("Last install:")
|
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from ..config import Color, PKG_DIR
|
|
6
|
-
from .. import
|
|
6
|
+
from ..services.install_state_builder import build_install_state
|
|
7
|
+
from ..services.state_store import load_current_state, record_install_state, remove_install_state
|
|
7
8
|
from ._install_helpers import _verify_install
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def install(local: bool = False, copy: bool = False, reconfigure: bool = False) -> None:
|
|
11
12
|
"""Build from source and install to targets."""
|
|
12
|
-
from ..
|
|
13
|
+
from ..services.state_store import get_scope, state_file
|
|
13
14
|
from pathlib import Path
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
scope = "local" if local else "global"
|
|
16
17
|
project_path = Path.cwd().resolve() if local else None
|
|
17
18
|
|
|
18
|
-
state =
|
|
19
|
+
state = load_current_state()
|
|
19
20
|
existing = get_scope(state, scope, project_path) if state else None
|
|
20
21
|
|
|
21
22
|
if existing and not reconfigure:
|
|
@@ -55,7 +56,7 @@ def install(local: bool = False, copy: bool = False, reconfigure: bool = False)
|
|
|
55
56
|
|
|
56
57
|
if existing and reconfigure:
|
|
57
58
|
print(f"Clearing existing {scope} state (--reconfigure) ...")
|
|
58
|
-
|
|
59
|
+
remove_install_state(scope, project_path)
|
|
59
60
|
# Fall through to normal install flow
|
|
60
61
|
|
|
61
62
|
# Validate args
|
|
@@ -88,13 +89,13 @@ def install(local: bool = False, copy: bool = False, reconfigure: bool = False)
|
|
|
88
89
|
# Record state
|
|
89
90
|
try:
|
|
90
91
|
project_path = Path.cwd() if local else None
|
|
91
|
-
st =
|
|
92
|
+
st = build_install_state(
|
|
92
93
|
mode="copy" if copy else "symlink",
|
|
93
94
|
scope="local" if local else "global",
|
|
94
95
|
repo_root=PKG_DIR.parent, # repo root (parent of agent_notes pkg)
|
|
95
96
|
project_path=project_path,
|
|
96
97
|
)
|
|
97
|
-
|
|
98
|
+
record_install_state(st)
|
|
98
99
|
except Exception as e:
|
|
99
100
|
print(f"{Color.YELLOW}Warning: failed to write state.json: {e}{Color.NC}")
|
|
100
101
|
|
|
@@ -117,7 +118,7 @@ def uninstall(local: bool = False, global_: bool = False) -> None:
|
|
|
117
118
|
|
|
118
119
|
# Remove state entry for this scope
|
|
119
120
|
try:
|
|
120
|
-
|
|
121
|
+
remove_install_state(scope, project_path)
|
|
121
122
|
except Exception as e:
|
|
122
123
|
print(f"{Color.YELLOW}Warning: failed to clear state.json: {e}{Color.NC}")
|
|
123
124
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Manage agent memory stored in ~/.claude/agent-memory/."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from . import _common
|
|
6
|
+
from ._common import _load_memory_config, get_directory_size, format_size, _WIKI_TYPE_MAP
|
|
7
|
+
from .vault import do_vault, do_init, do_index
|
|
8
|
+
from .notes import do_add, do_list, do_show, do_size
|
|
9
|
+
from .transfer import do_export, do_import
|
|
10
|
+
from .wiki import do_ingest, do_query, do_lint, do_scan_raw
|
|
11
|
+
from .migrate import do_migrate
|
|
12
|
+
from .reset import do_reset
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def show_help() -> None:
|
|
16
|
+
"""Show memory command help."""
|
|
17
|
+
help_text = """Usage: agent-notes memory [command] [args]
|
|
18
|
+
|
|
19
|
+
Manage agent memory.
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
init Create folder structure and Index.md
|
|
23
|
+
list List all agent memories with sizes (default)
|
|
24
|
+
vault Show current backend and memory path
|
|
25
|
+
index Regenerate Index.md for the current backend
|
|
26
|
+
add <title> <body> Add a note (obsidian and wiki backends)
|
|
27
|
+
migrate Migrate old per-project layout to new shared flat layout
|
|
28
|
+
size Total disk usage
|
|
29
|
+
show <name> Show memory contents for one agent/category
|
|
30
|
+
reset Clear ALL memories (requires confirmation)
|
|
31
|
+
reset <name> Clear one agent's memory
|
|
32
|
+
export Back up memories to agent-notes/memory-backup/
|
|
33
|
+
import Restore from agent-notes/memory-backup/
|
|
34
|
+
ingest <title> <body> Ingest source material and fan-out to concepts/entities (wiki backend)
|
|
35
|
+
query <keyword> Search wiki pages by keyword (wiki backend)
|
|
36
|
+
lint Check wiki health: orphans, broken links, stale index (wiki backend)
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
agent-notes memory List all memories
|
|
40
|
+
agent-notes memory vault Show backend configuration
|
|
41
|
+
agent-notes memory index Regenerate Index.md
|
|
42
|
+
agent-notes memory migrate Migrate to new flat layout
|
|
43
|
+
agent-notes memory show coder View coder agent's memory
|
|
44
|
+
agent-notes memory reset reviewer Clear reviewer's memory
|
|
45
|
+
agent-notes memory export Back up before cleanup"""
|
|
46
|
+
|
|
47
|
+
print(help_text)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def memory(action: str = "list", name: Optional[str] = None, extra: Optional[list] = None) -> None:
|
|
51
|
+
"""Manage agent memory."""
|
|
52
|
+
if action == "list":
|
|
53
|
+
do_list()
|
|
54
|
+
elif action == "init":
|
|
55
|
+
do_init()
|
|
56
|
+
elif action == "vault":
|
|
57
|
+
do_vault()
|
|
58
|
+
elif action == "index":
|
|
59
|
+
do_index()
|
|
60
|
+
elif action == "add":
|
|
61
|
+
# name is title, extra[0] is body
|
|
62
|
+
if not name:
|
|
63
|
+
print("Error: add requires a title.")
|
|
64
|
+
exit(1)
|
|
65
|
+
body = extra[0] if extra else ""
|
|
66
|
+
note_type = extra[1] if extra and len(extra) > 1 else "context"
|
|
67
|
+
agent = extra[2] if extra and len(extra) > 2 else ""
|
|
68
|
+
project = extra[3] if extra and len(extra) > 3 else ""
|
|
69
|
+
do_add(name, body, note_type=note_type, agent=agent, project=project)
|
|
70
|
+
elif action == "size":
|
|
71
|
+
do_size()
|
|
72
|
+
elif action == "show":
|
|
73
|
+
if not name:
|
|
74
|
+
print("Error: show requires an agent name.")
|
|
75
|
+
exit(1)
|
|
76
|
+
do_show(name)
|
|
77
|
+
elif action == "reset":
|
|
78
|
+
do_reset(name)
|
|
79
|
+
elif action == "export":
|
|
80
|
+
do_export()
|
|
81
|
+
elif action == "import":
|
|
82
|
+
do_import()
|
|
83
|
+
elif action == "migrate":
|
|
84
|
+
do_migrate()
|
|
85
|
+
elif action == "ingest":
|
|
86
|
+
if not name:
|
|
87
|
+
do_scan_raw()
|
|
88
|
+
exit(0)
|
|
89
|
+
body = extra[0] if extra else ""
|
|
90
|
+
concepts_csv = extra[1] if extra and len(extra) > 1 else ""
|
|
91
|
+
entities_csv = extra[2] if extra and len(extra) > 2 else ""
|
|
92
|
+
tags_csv = extra[3] if extra and len(extra) > 3 else ""
|
|
93
|
+
concepts = [c.strip() for c in concepts_csv.split(",") if c.strip()] if concepts_csv else None
|
|
94
|
+
entities = [e.strip() for e in entities_csv.split(",") if e.strip()] if entities_csv else None
|
|
95
|
+
tags = [t.strip() for t in tags_csv.split(",") if t.strip()] if tags_csv else None
|
|
96
|
+
do_ingest(name, body, concepts=concepts, entities=entities, tags=tags)
|
|
97
|
+
elif action == "query":
|
|
98
|
+
if not name:
|
|
99
|
+
print("Error: query requires a keyword.")
|
|
100
|
+
exit(1)
|
|
101
|
+
do_query(name)
|
|
102
|
+
elif action == "lint":
|
|
103
|
+
do_lint()
|
|
104
|
+
else:
|
|
105
|
+
print(f"Unknown command: {action}")
|
|
106
|
+
show_help()
|
|
107
|
+
exit(1)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Shared helpers for memory subcommands."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from ...config import MEMORY_DIR
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_WIKI_TYPE_MAP = {
|
|
10
|
+
"pattern": "concepts",
|
|
11
|
+
"decision": "concepts",
|
|
12
|
+
"mistake": "concepts",
|
|
13
|
+
"context": "concepts",
|
|
14
|
+
"concept": "concepts",
|
|
15
|
+
"concepts": "concepts",
|
|
16
|
+
"entity": "entities",
|
|
17
|
+
"entities": "entities",
|
|
18
|
+
"synthesis": "synthesis",
|
|
19
|
+
"session": "sessions",
|
|
20
|
+
"sessions": "sessions",
|
|
21
|
+
"source": "sources",
|
|
22
|
+
"sources": "sources",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _load_memory_config():
|
|
27
|
+
from ...services.state_store import load_state
|
|
28
|
+
from ...config import memory_dir_for_backend
|
|
29
|
+
state = load_state()
|
|
30
|
+
if state is None:
|
|
31
|
+
return "local", MEMORY_DIR
|
|
32
|
+
backend = state.memory.backend
|
|
33
|
+
path = memory_dir_for_backend(backend, state.memory.path)
|
|
34
|
+
return backend, path
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_directory_size(path: Path) -> int:
|
|
38
|
+
"""Calculate total size of directory in bytes."""
|
|
39
|
+
total = 0
|
|
40
|
+
try:
|
|
41
|
+
for item in path.rglob('*'):
|
|
42
|
+
if item.is_file():
|
|
43
|
+
total += item.stat().st_size
|
|
44
|
+
except (OSError, PermissionError):
|
|
45
|
+
pass
|
|
46
|
+
return total
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def format_size(size_bytes: int) -> str:
|
|
50
|
+
"""Format size in human-readable format."""
|
|
51
|
+
if size_bytes == 0:
|
|
52
|
+
return "0B"
|
|
53
|
+
|
|
54
|
+
original_size = size_bytes
|
|
55
|
+
for unit in ['B', 'K', 'M', 'G', 'T']:
|
|
56
|
+
if original_size < 1024:
|
|
57
|
+
if unit == 'B':
|
|
58
|
+
return f"{original_size}B"
|
|
59
|
+
else:
|
|
60
|
+
return f"{original_size:.1f}{unit}"
|
|
61
|
+
original_size /= 1024
|
|
62
|
+
|
|
63
|
+
return f"{original_size:.1f}P"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Migration subcommand: migrate."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from . import _common
|
|
10
|
+
from ...config import Color
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def do_migrate() -> None:
|
|
14
|
+
"""Migrate vault from per-project layout to flat shared layout with new filenames."""
|
|
15
|
+
backend, vault = _common._load_memory_config()
|
|
16
|
+
if backend != "obsidian":
|
|
17
|
+
print("migrate is only available for obsidian storage.")
|
|
18
|
+
return
|
|
19
|
+
if vault is None:
|
|
20
|
+
print("Memory path not configured.")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
from ...services.obsidian_backend import OBSIDIAN_CATEGORIES
|
|
24
|
+
from ...services.memory_router import memory_regenerate_index
|
|
25
|
+
from ...services._memory_utils import _parse_frontmatter
|
|
26
|
+
|
|
27
|
+
_NEW_FILE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}_")
|
|
28
|
+
_LEGACY_TS_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(.+)$")
|
|
29
|
+
_BARE_UUID_RE = re.compile(
|
|
30
|
+
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.IGNORECASE
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
moved = 0
|
|
34
|
+
renamed = 0
|
|
35
|
+
skipped = 0
|
|
36
|
+
errors: list[str] = []
|
|
37
|
+
|
|
38
|
+
def _date_from_frontmatter(path: Path) -> Optional[str]:
|
|
39
|
+
try:
|
|
40
|
+
text = path.read_text()
|
|
41
|
+
fm, _ = _parse_frontmatter(text)
|
|
42
|
+
ca = fm.get("created_at", "")
|
|
43
|
+
if re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", ca):
|
|
44
|
+
return ca[:10]
|
|
45
|
+
except OSError:
|
|
46
|
+
pass
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def _date_from_mtime(path: Path) -> str:
|
|
50
|
+
ts = path.stat().st_mtime
|
|
51
|
+
return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d")
|
|
52
|
+
|
|
53
|
+
def _safe_rename(src: Path, dst: Path) -> bool:
|
|
54
|
+
if dst.exists():
|
|
55
|
+
return False
|
|
56
|
+
src.rename(dst)
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
def _new_stem(old_stem: str, folder: Path, path: Path) -> Optional[str]:
|
|
60
|
+
"""Return new stem under new naming scheme, or None if already correct."""
|
|
61
|
+
if _NEW_FILE_RE.match(old_stem):
|
|
62
|
+
return None # already in new format
|
|
63
|
+
|
|
64
|
+
# Legacy timestamp: YYYY-MM-DD-HH-MM-SS-<slug>
|
|
65
|
+
m = _LEGACY_TS_RE.match(old_stem)
|
|
66
|
+
if m:
|
|
67
|
+
date_part = f"{m.group(1)}-{m.group(2)}-{m.group(3)}"
|
|
68
|
+
slug_part = m.group(7)
|
|
69
|
+
base = f"{date_part}_{slug_part}"
|
|
70
|
+
candidate = f"{base}.md"
|
|
71
|
+
if not (folder / candidate).exists():
|
|
72
|
+
return base
|
|
73
|
+
# collision: append HHMMSS from the original timestamp
|
|
74
|
+
hhmmss = f"{m.group(4)}{m.group(5)}{m.group(6)}"
|
|
75
|
+
return f"{base}_{hhmmss}"
|
|
76
|
+
|
|
77
|
+
# Bare session UUID: <uuid>.md → <date>_<uuid>.md
|
|
78
|
+
if _BARE_UUID_RE.match(old_stem):
|
|
79
|
+
date_part = _date_from_frontmatter(path) or _date_from_mtime(path)
|
|
80
|
+
base = f"{date_part}_{old_stem}"
|
|
81
|
+
candidate = f"{base}.md"
|
|
82
|
+
if not (folder / candidate).exists():
|
|
83
|
+
return base
|
|
84
|
+
hhmmss = datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc).strftime("%H%M%S")
|
|
85
|
+
return f"{base}_{hhmmss}"
|
|
86
|
+
|
|
87
|
+
# Unrecognized pattern — skip
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
# Step 1: move files from per-project subfolders into the shared root
|
|
91
|
+
for item in list(vault.iterdir()):
|
|
92
|
+
if not item.is_dir():
|
|
93
|
+
continue
|
|
94
|
+
if item.name in OBSIDIAN_CATEGORIES or item.name == "Index.md":
|
|
95
|
+
continue
|
|
96
|
+
# item is a per-project subfolder
|
|
97
|
+
for cat in OBSIDIAN_CATEGORIES:
|
|
98
|
+
src_cat = item / cat
|
|
99
|
+
if not src_cat.exists():
|
|
100
|
+
continue
|
|
101
|
+
dst_cat = vault / cat
|
|
102
|
+
dst_cat.mkdir(exist_ok=True)
|
|
103
|
+
for note in src_cat.glob("*.md"):
|
|
104
|
+
dst = dst_cat / note.name
|
|
105
|
+
if dst.exists():
|
|
106
|
+
errors.append(f"collision: {note} -> {dst}")
|
|
107
|
+
continue
|
|
108
|
+
try:
|
|
109
|
+
shutil.move(str(note), str(dst))
|
|
110
|
+
moved += 1
|
|
111
|
+
except OSError as exc:
|
|
112
|
+
errors.append(f"move failed: {note}: {exc}")
|
|
113
|
+
# Remove now-empty category subdir so parent rmdir can succeed
|
|
114
|
+
try:
|
|
115
|
+
src_cat.rmdir()
|
|
116
|
+
except OSError:
|
|
117
|
+
pass
|
|
118
|
+
# Remove subfolder only if empty (preserves any uncategorized files the user may have there)
|
|
119
|
+
try:
|
|
120
|
+
item.rmdir()
|
|
121
|
+
except OSError:
|
|
122
|
+
errors.append(f"per-project subfolder not removed (non-empty): {item}")
|
|
123
|
+
|
|
124
|
+
# Step 2: rename files in each category to the new naming scheme
|
|
125
|
+
for cat in OBSIDIAN_CATEGORIES:
|
|
126
|
+
cat_dir = vault / cat
|
|
127
|
+
if not cat_dir.exists():
|
|
128
|
+
continue
|
|
129
|
+
for note in list(cat_dir.glob("*.md")):
|
|
130
|
+
new_stem = _new_stem(note.stem, cat_dir, note)
|
|
131
|
+
if new_stem is None:
|
|
132
|
+
skipped += 1
|
|
133
|
+
continue
|
|
134
|
+
dst = cat_dir / f"{new_stem}.md"
|
|
135
|
+
try:
|
|
136
|
+
if not _safe_rename(note, dst):
|
|
137
|
+
errors.append(f"rename collision: {note.name} -> {dst.name}")
|
|
138
|
+
skipped += 1
|
|
139
|
+
else:
|
|
140
|
+
renamed += 1
|
|
141
|
+
except OSError as exc:
|
|
142
|
+
errors.append(f"rename failed: {note}: {exc}")
|
|
143
|
+
skipped += 1
|
|
144
|
+
|
|
145
|
+
# Step 3: regenerate index
|
|
146
|
+
memory_regenerate_index(backend, vault)
|
|
147
|
+
|
|
148
|
+
print(f"{moved} moved, {renamed} renamed, {skipped} skipped", end="")
|
|
149
|
+
if errors:
|
|
150
|
+
print(f", errors: {'; '.join(errors)}")
|
|
151
|
+
else:
|
|
152
|
+
print()
|