campfire-cli 0.1.9__tar.gz → 0.1.10__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.
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/ARCHITECTURE.md +2 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/COMMAND.md +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/PKG-INFO +4 -4
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/README.md +3 -3
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/SPEC.md +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/pyproject.toml +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/__init__.py +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/cli/document_cli.py +5 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/schema/document_schema.py +3 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_apply_service.py +86 -48
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_move_service.py +5 -50
- campfire_cli-0.1.10/src/campfire_cli/app/document/service/document_relocation.py +59 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/service/maintenance_service.py +17 -3
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/SPEC.md +2 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/restructure_cli.py +7 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/restructure_service.py +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/agent_hints.py +9 -7
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/defaults/config.yml +3 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/SPEC.md +6 -3
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-context-bootstrap/SKILL.md +4 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-document-capture/SKILL.md +4 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-task-management/SKILL.md +4 -9
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-workspace-maintenance/SKILL.md +4 -3
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-workspace-restructure/SKILL.md +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/integration/test_cli.py +153 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_document_apply.py +215 -2
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/uv.lock +1 -1
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/.github/workflows/ci.yml +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/.github/workflows/release.yml +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/.gitignore +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/AGENTS.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/DECISIONS.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/LICENSE +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/alembic/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/alembic/env.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/alembic/script.py.mako +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/alembic/versions/001_initial_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/alembic.ini +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/scripts/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/env.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/script.py.mako +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/001_initial_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/002_workspace_project_registry.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/003_unified_workspace_state.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/004_restructure_workspace_domain.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/005_decision_channel.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/006_workspace_topology_index.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/007_workspace_adoption.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/008_remove_adoption_batches.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/alembic/versions/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/cli/base_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/repository/base_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/schema/base_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/schema/operation_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/base/service/base_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/cli/decision_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/repository/decision_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/schema/decision_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/service/decision_projection_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/decision/service/decision_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/cli/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/cli/profile_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/cli/type_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/repository/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/repository/document_profile_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/repository/document_type_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/schema/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_profile_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_rule_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_scanner.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/document_type_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/frontmatter_apply.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/frontmatter_formatter.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/frontmatter_plan.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/kanban_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/profile_registry.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/type_apply.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/service/type_plan.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/cli/maintenance_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/repository/maintenance_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/schema/maintenance_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/service/archive_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/service/link_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/service/maintenance_protocol.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/maintenance/service/moc_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/cli/skill_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/repository/skill_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/schema/skill_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/skill/service/skill_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/config_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/domain_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/project_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/space_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/workspace_cli.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/repository/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/repository/manifest_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/repository/restructure_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/repository/workspace_repository.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/schema/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/schema/adoption_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/schema/restructure_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/schema/workspace_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/adoption_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/config_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/domain_restructure_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/project_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/restructure_protocol.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/restructure_verifier.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/structure_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/workspace_protocol.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/service/workspace_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/database/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/database/migrations.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/database/models.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/database/session.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/documents/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/documents/document_types.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/documents/domain_context.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/documents/frontmatter_schema.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/documents/markdown.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/exceptions/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/exceptions/base.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/filesystem/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/filesystem/atomic.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/filesystem/change_set.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/filesystem/cwd.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/filesystem/locking.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/governance/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/governance/issues.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/governance/locking.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/governance/snapshots.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/hashing.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/package_version.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/reports/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/reports/json_report.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/common/reports/markdown_report.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/config/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/config/defaults.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/config/settings.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/container.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/main.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases/Agent/346/211/247/350/241/214/345/267/245/344/275/234/345/217/260.base" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases//344/273/273/345/212/241/345/267/245/344/275/234/345/217/260.base" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases//345/206/263/347/255/226/345/267/245/344/275/234/345/217/260.base" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases//345/276/205/345/275/222/346/241/243/346/226/207/346/241/243.base" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/bases//351/241/271/347/233/256/346/226/207/346/241/243.base" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/defaults/__init__.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-context-bootstrap/agents/openai.yaml +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-conversation-router/SKILL.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-conversation-router/agents/openai.yaml +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-conversation-router/references/inbox-handoff.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-conversation-router/references/learning-intake.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-conversation-router/references/task-intake.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-inbox-triage/SKILL.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-kanban-board/SKILL.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-weekly-report-writing/SKILL.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-weekly-report-writing/agents/openai.yaml +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-workspace-adoption/SKILL.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-workspace-maintenance/references//344/273/273/345/212/241/346/255/243/346/226/207/347/273/223/346/236/204.md" +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/resources/skills/campfire-workspace-restructure/references/restructure-spec.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/SPEC.md +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/conftest.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/perf/test_read_counts.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/perf/test_resolve_subprocess_count.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_agent_hints.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_atomic_write.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_common_governance.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_config.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_domain_context.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_file_change_executor.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_frontmatter.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_kanban_service.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_migrations.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_package_version.py +0 -0
- {campfire_cli-0.1.9 → campfire_cli-0.1.10}/tests/unit/test_repository_boundaries.py +0 -0
|
@@ -118,7 +118,7 @@ Campfire 不是“Markdown 版 kubectl”,而是面向人机协作场景组合
|
|
|
118
118
|
命令设计遵循以下约束:
|
|
119
119
|
|
|
120
120
|
1. **对象域优先**:公共入口采用 `campfire <object-domain> <verb>`,例如 `document apply`、`maintenance check`;动词在所属业务对象内保持单义,不为同一行为保留多个别名。
|
|
121
|
-
2. **意图原子、派生显式**:`document apply`
|
|
121
|
+
2. **意图原子、派生显式**:`document apply` 创建或更新文档,并在显式类型变化时将类型、文件名前缀和确定性引用作为同一变更提交;`document move` 原子完成单文档跨 Domain 移动、目标 Profile 对齐和确定性引用修复。命令只在 MOC、关系页或索引可能变化时返回零或一个可直接执行的最小 scope `maintenance sync`。
|
|
122
122
|
3. **契约声明式,变更显式**:Profile 是字段、类型、枚举、顺序和条件必填的 SSOT。Agent 提交业务值,CLI 解析有效 Profile 并拒绝猜测;已有文档只修改明确给出的字段或正文操作。
|
|
123
123
|
4. **写入先证明安全**:写命令默认预览,显式确认后才提交;提交时在锁内复核快照或期望哈希,多文件变更作为一个 ChangeSet 执行,失败回滚,避免静默覆盖和部分写入。
|
|
124
124
|
5. **人类与 Agent 共用一个契约**:命令和结果只有一套语义。JSON 状态、issues、missing fields 与 follow-up 供 Agent 稳定消费,`tree` 和分层 `-h` 供人类与 Agent 渐进发现,不维护第二套参数目录。
|
|
@@ -128,7 +128,7 @@ Campfire 不是“Markdown 版 kubectl”,而是面向人机协作场景组合
|
|
|
128
128
|
|
|
129
129
|
横切关注点与业务 SOP 不使用同一种复用手段。哈希复核、写锁、原子替换和失败恢复由显式 ChangeSet Executor 复用;写命令返回的 `follow_up` 由 Skill 消费。不为了复用 Maintenance 而引入 AOP 切面、命令总线、全局钩子或隐式中间件。
|
|
130
130
|
|
|
131
|
-
因此,`document apply` 的准确含义是“对一个 Markdown 文档应用经 Profile
|
|
131
|
+
因此,`document apply` 的准确含义是“对一个 Markdown 文档应用经 Profile 校验的显式意图”,不是“把完整声明持续调谐到某个服务端状态”。创建时文件名可省略类型前缀;显式变更类型时,CLI 负责同步类型、前缀和可确定引用。用户或 Agent 可以直接 edit 唯一路径下的已有正文;只有新建文档、修改 Frontmatter/类型/归属或需要派生治理时才进入 CLI 工作流。
|
|
132
132
|
|
|
133
133
|
## 5. SSOT 与派生数据
|
|
134
134
|
|
|
@@ -46,4 +46,4 @@ campfire maintenance archive apply [--scope <path>] --confirm
|
|
|
46
46
|
|
|
47
47
|
Space、Domain、Project 的 `create`/`adopt`,以及文档移动、重构、归档写操作默认先预览;审查通过后才追加 `--confirm`。已注册 Workspace 只通过根级 `campfire --workspace <id> ...` 选择;已声明 Space、Domain 和 Project 使用稳定 ID,CLI 自行推导路径和父子关系。路径参数只用于新位置、具体文件、外部输入和显式扫描范围。
|
|
48
48
|
|
|
49
|
-
`document apply`
|
|
49
|
+
`document apply` 可新建、更新、为已有正文补齐 Frontmatter,或通过显式 `--type` 原子转换文档类型。创建和类型转换时,CLI 根据文档类型推导文件名前缀,并在结果的 `target` 中返回最终路径;调用方只提交标题或现有路径,不手工同步类型与前缀。`document move` 可在任意已声明 Domain 之间移动单篇文档并对齐可确定的 `domain`/`project`。目标 Profile 缺少语义字段时返回 `missing_fields`,调用方补齐明确业务值。只有实际写入成功的结果才可返回零或一个、且可直接执行的 scoped `maintenance sync` follow-up;Domain 内部路径会归一化为对应 Domain,预览、阻塞或无派生影响时返回空列表。具体参数以对应命令的 `-h` 为准。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: campfire-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: Local-first shared context and governance CLI for humans and agents
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -94,7 +94,7 @@ campfire maintenance sync [--dry-run] [--scope] # 刷新 MOC 与相关文档页
|
|
|
94
94
|
campfire maintenance archive check / apply # 归档候选检查与执行
|
|
95
95
|
|
|
96
96
|
campfire document inspect / check / format # 单篇文档查看、校验、格式化
|
|
97
|
-
campfire document apply / move #
|
|
97
|
+
campfire document apply / move # 创建更新、类型转换、跨 Domain 移动
|
|
98
98
|
campfire document profile list / show / resolve # Frontmatter Profile 规则
|
|
99
99
|
campfire document type list # 文档类型与前缀
|
|
100
100
|
|
|
@@ -139,12 +139,12 @@ campfire workspace restructure verify --batch move-001
|
|
|
139
139
|
- **校验分工**:`maintenance check` 统一负责正式文档的 Schema 与枚举校验,并刷新可重建索引;结构声明由 `workspace space/domain check` 校验;`maintenance sync` 只因结构、MOC、路径或并发安全问题阻塞,单篇文档问题不阻止其他领域刷新。
|
|
140
140
|
- **Frontmatter Profile**:声明式一层继承(`base` 或 `base → knowledge/project-doc/task`),`document profile show` 展示编译后的完整规则;Formatter 只按有效 Profile 排序并保留值,不允许字段由 Validator 报告、不自动删除。
|
|
141
141
|
- **并发与提交安全**:写入前在治理锁内复核内容哈希,外部变化返回 `concurrent-change` / `source-hash-changed`,拒绝覆盖;多文件写入和路径移动经同一 ChangeSet 提交,失败恢复到执行前。
|
|
142
|
-
-
|
|
142
|
+
- **按需后续治理**:写入命令只在实际写入成功且派生内容可能变化时返回零或一个、且可直接执行的最小 scope `maintenance sync`;预览和阻塞结果的 `follow_up` 为空。位于 Domain 内部的 scope 由 CLI 归一化为有效 Domain。Skill 消费该结果,没有 follow-up 就结束,不固定追加 dry-run 或全量 check。
|
|
143
143
|
- **配置两层模型**:产品默认契约在包内 `resources/defaults/config.yml`(SSOT),用户只在 `~/.campfire/config.yml` 写覆盖项;Mapping 递归合并,`campfire workspace config check` 验证有效配置。
|
|
144
144
|
|
|
145
145
|
## Agent 协作
|
|
146
146
|
|
|
147
|
-
全局 Skill(`campfire skill list` 查看托管清单,`campfire skill sync` 手动同步)定义了 Agent
|
|
147
|
+
全局 Skill(`campfire skill list` 查看托管清单,`campfire skill sync` 手动同步)定义了 Agent 的标准工作流:已给出唯一文件路径的正文读取或小改直接使用文件工具;新建文档、修改 Frontmatter/类型/归属或执行结构治理时才加载 bootstrap,并使用 `document apply/move` 等原子命令。批量结构调整用 `workspace restructure`,归档用 `maintenance archive`。有歧义的分类和重构进入 Decision,不由 Agent 擅自决定。
|
|
148
148
|
|
|
149
149
|
## 许可
|
|
150
150
|
|
|
@@ -78,7 +78,7 @@ campfire maintenance sync [--dry-run] [--scope] # 刷新 MOC 与相关文档页
|
|
|
78
78
|
campfire maintenance archive check / apply # 归档候选检查与执行
|
|
79
79
|
|
|
80
80
|
campfire document inspect / check / format # 单篇文档查看、校验、格式化
|
|
81
|
-
campfire document apply / move #
|
|
81
|
+
campfire document apply / move # 创建更新、类型转换、跨 Domain 移动
|
|
82
82
|
campfire document profile list / show / resolve # Frontmatter Profile 规则
|
|
83
83
|
campfire document type list # 文档类型与前缀
|
|
84
84
|
|
|
@@ -123,12 +123,12 @@ campfire workspace restructure verify --batch move-001
|
|
|
123
123
|
- **校验分工**:`maintenance check` 统一负责正式文档的 Schema 与枚举校验,并刷新可重建索引;结构声明由 `workspace space/domain check` 校验;`maintenance sync` 只因结构、MOC、路径或并发安全问题阻塞,单篇文档问题不阻止其他领域刷新。
|
|
124
124
|
- **Frontmatter Profile**:声明式一层继承(`base` 或 `base → knowledge/project-doc/task`),`document profile show` 展示编译后的完整规则;Formatter 只按有效 Profile 排序并保留值,不允许字段由 Validator 报告、不自动删除。
|
|
125
125
|
- **并发与提交安全**:写入前在治理锁内复核内容哈希,外部变化返回 `concurrent-change` / `source-hash-changed`,拒绝覆盖;多文件写入和路径移动经同一 ChangeSet 提交,失败恢复到执行前。
|
|
126
|
-
-
|
|
126
|
+
- **按需后续治理**:写入命令只在实际写入成功且派生内容可能变化时返回零或一个、且可直接执行的最小 scope `maintenance sync`;预览和阻塞结果的 `follow_up` 为空。位于 Domain 内部的 scope 由 CLI 归一化为有效 Domain。Skill 消费该结果,没有 follow-up 就结束,不固定追加 dry-run 或全量 check。
|
|
127
127
|
- **配置两层模型**:产品默认契约在包内 `resources/defaults/config.yml`(SSOT),用户只在 `~/.campfire/config.yml` 写覆盖项;Mapping 递归合并,`campfire workspace config check` 验证有效配置。
|
|
128
128
|
|
|
129
129
|
## Agent 协作
|
|
130
130
|
|
|
131
|
-
全局 Skill(`campfire skill list` 查看托管清单,`campfire skill sync` 手动同步)定义了 Agent
|
|
131
|
+
全局 Skill(`campfire skill list` 查看托管清单,`campfire skill sync` 手动同步)定义了 Agent 的标准工作流:已给出唯一文件路径的正文读取或小改直接使用文件工具;新建文档、修改 Frontmatter/类型/归属或执行结构治理时才加载 bootstrap,并使用 `document apply/move` 等原子命令。批量结构调整用 `workspace restructure`,归档用 `maintenance archive`。有歧义的分类和重构进入 Decision,不由 Agent 擅自决定。
|
|
132
132
|
|
|
133
133
|
## 许可
|
|
134
134
|
|
|
@@ -107,7 +107,7 @@ Campfire 使用语义化版本:
|
|
|
107
107
|
|
|
108
108
|
- SQLite `spaces`、`domains`、`documents` 只允许作为可重建本机投影,不得反向覆盖 Markdown SSOT。
|
|
109
109
|
- `setup` 与 `maintenance check` 必须自动刷新完整拓扑和文档索引。
|
|
110
|
-
-
|
|
110
|
+
- 写命令只在实际写入成功且派生状态可能变化时返回零或一个可直接执行的 scoped `maintenance sync` follow-up;Domain 内部路径必须归一化为对应 Domain,预览和阻塞结果必须返回空列表;
|
|
111
111
|
调用方只执行实际返回的 follow-up,不固定追加 check 或全量扫描。
|
|
112
112
|
- `workspace rebuild` 默认只预览;`--confirm` 后从 Manifest 和 Markdown 完整替换派生索引。
|
|
113
113
|
|
{campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/cli/document_cli.py
RENAMED
|
@@ -102,7 +102,11 @@ def apply_document(
|
|
|
102
102
|
expected_hash: str | None = typer.Option(None, "--expected-hash"),
|
|
103
103
|
confirm: bool = typer.Option(False, "--confirm"),
|
|
104
104
|
) -> None:
|
|
105
|
-
"""预览或应用一篇 Profile
|
|
105
|
+
"""预览或应用一篇 Profile 合法文档的创建、补丁与显式类型变更。
|
|
106
|
+
|
|
107
|
+
创建时文件名可省略类型前缀;CLI 根据 --type 返回并写入最终 target。
|
|
108
|
+
已有文档的 --type 发生变化时,同一原子操作同步文件名和引用。
|
|
109
|
+
"""
|
|
106
110
|
body = body_file.read_text(encoding="utf-8") if body_file else None
|
|
107
111
|
invoke(
|
|
108
112
|
lambda: service(ctx).apply(
|
{campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/document/schema/document_schema.py
RENAMED
|
@@ -21,11 +21,13 @@ class DocumentApplyRequest(BaseModel):
|
|
|
21
21
|
class DocumentApplyResult(BaseModel):
|
|
22
22
|
status: Literal["planned", "needs-input", "blocked", "applied"]
|
|
23
23
|
workspace_id: str
|
|
24
|
-
action: Literal["create", "update"]
|
|
24
|
+
action: Literal["create", "update", "retype"]
|
|
25
25
|
path: str
|
|
26
|
+
target: str
|
|
26
27
|
profile: str
|
|
27
28
|
expected_hash: str
|
|
28
29
|
write_performed: bool = False
|
|
30
|
+
updated_references: list[str] = Field(default_factory=list)
|
|
29
31
|
issues: list[dict[str, Any]] = Field(default_factory=list)
|
|
30
32
|
missing_fields: list[str] = Field(default_factory=list)
|
|
31
33
|
follow_up: list[CommandFollowUp] = Field(default_factory=list)
|
|
@@ -12,11 +12,13 @@ from campfire_cli.app.document.schema import (
|
|
|
12
12
|
DocumentApplyRequest,
|
|
13
13
|
DocumentApplyResult,
|
|
14
14
|
)
|
|
15
|
+
from campfire_cli.app.document.service.document_relocation import prepare_document_relocation
|
|
15
16
|
from campfire_cli.app.document.service.document_rule_service import DocumentRuleService
|
|
16
17
|
from campfire_cli.app.document.service.frontmatter_formatter import render_patch
|
|
17
18
|
from campfire_cli.app.document.service.profile_registry import ProfileRegistry
|
|
18
19
|
from campfire_cli.common.documents.document_types import prefixed_name
|
|
19
20
|
from campfire_cli.common.documents.domain_context import (
|
|
21
|
+
DomainContext,
|
|
20
22
|
DomainContextError,
|
|
21
23
|
resolve_domain_context,
|
|
22
24
|
)
|
|
@@ -42,14 +44,16 @@ class DocumentApplyService:
|
|
|
42
44
|
self._executor = FileChangeExecutor(settings.vault_root, settings.state_root)
|
|
43
45
|
|
|
44
46
|
def apply(self, request: DocumentApplyRequest) -> DocumentApplyResult:
|
|
45
|
-
|
|
46
|
-
if
|
|
47
|
+
source = safe_path(self._settings.vault_root, request.path)
|
|
48
|
+
if source.suffix.lower() != ".md":
|
|
47
49
|
raise ConfigurationError("document apply 目标必须是 Markdown 文件")
|
|
48
|
-
exists =
|
|
49
|
-
original =
|
|
50
|
+
exists = source.is_file()
|
|
51
|
+
original = source.read_text(encoding="utf-8") if exists else ""
|
|
50
52
|
parsed = parse_document(original)
|
|
51
53
|
|
|
52
54
|
current_type = parsed.frontmatter.get("type")
|
|
55
|
+
if "type" in request.values:
|
|
56
|
+
raise GovernanceBlockedError("文档类型只能通过 --type 表达")
|
|
53
57
|
document_type = request.document_type or (
|
|
54
58
|
current_type if isinstance(current_type, str) else None
|
|
55
59
|
)
|
|
@@ -57,24 +61,25 @@ class DocumentApplyService:
|
|
|
57
61
|
raise ConfigurationError("创建文档时必须提供 --type")
|
|
58
62
|
if document_type not in self._settings.document_types.get("types", {}):
|
|
59
63
|
raise ConfigurationError(f"未知文档类型:{document_type}")
|
|
60
|
-
if (
|
|
61
|
-
exists
|
|
62
|
-
and parsed.has_frontmatter
|
|
63
|
-
and request.document_type
|
|
64
|
-
and current_type != request.document_type
|
|
65
|
-
):
|
|
66
|
-
raise GovernanceBlockedError("不允许通过 document apply 改变已有文档的 type")
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
-
|
|
65
|
+
changes_type = (
|
|
66
|
+
exists and request.document_type is not None and current_type != document_type
|
|
67
|
+
)
|
|
68
|
+
action = "create" if not exists else "retype" if changes_type else "update"
|
|
69
|
+
target = (
|
|
70
|
+
source.with_name(
|
|
71
|
+
prefixed_name(source.name, document_type, self._settings.document_types)
|
|
72
|
+
)
|
|
73
|
+
if not exists or changes_type
|
|
74
|
+
else source
|
|
75
|
+
)
|
|
76
|
+
target_name = target.relative_to(self._settings.vault_root).as_posix()
|
|
77
|
+
context = self._domain_context(target)
|
|
73
78
|
|
|
74
79
|
today = date.today().isoformat()
|
|
75
80
|
frontmatter = dict(parsed.frontmatter)
|
|
76
81
|
if not exists or not parsed.has_frontmatter:
|
|
77
|
-
frontmatter.update(self._creation_defaults(
|
|
82
|
+
frontmatter.update(self._creation_defaults(target, context, document_type, today))
|
|
78
83
|
for key in ("project", "domain"):
|
|
79
84
|
if key in request.values and request.values[key] != frontmatter.get(key):
|
|
80
85
|
raise GovernanceBlockedError(
|
|
@@ -83,15 +88,15 @@ class DocumentApplyService:
|
|
|
83
88
|
frontmatter.update(request.values)
|
|
84
89
|
frontmatter["type"] = document_type
|
|
85
90
|
frontmatter["updated"] = today
|
|
86
|
-
profile = self._profiles.resolve(document_type, frontmatter,
|
|
87
|
-
actual_hash = file_sha256(
|
|
91
|
+
profile = self._profiles.resolve(document_type, frontmatter, target)
|
|
92
|
+
actual_hash = file_sha256(source) if exists else "missing"
|
|
88
93
|
|
|
89
94
|
unknown = sorted(set(request.values) - set(profile.allowed))
|
|
90
95
|
if unknown:
|
|
91
96
|
return self._result(
|
|
92
97
|
request,
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
target_name,
|
|
99
|
+
action,
|
|
95
100
|
profile.name,
|
|
96
101
|
actual_hash,
|
|
97
102
|
"blocked",
|
|
@@ -107,17 +112,20 @@ class DocumentApplyService:
|
|
|
107
112
|
|
|
108
113
|
next_body = self._next_body(request, parsed.body, exists)
|
|
109
114
|
if exists and parsed.has_frontmatter:
|
|
115
|
+
patch = {**request.values, "updated": today}
|
|
116
|
+
if changes_type:
|
|
117
|
+
patch["type"] = document_type
|
|
110
118
|
rendered, render_errors = render_patch(
|
|
111
119
|
original,
|
|
112
|
-
|
|
120
|
+
patch,
|
|
113
121
|
next_body,
|
|
114
122
|
list(profile.field_order),
|
|
115
123
|
)
|
|
116
124
|
if render_errors:
|
|
117
125
|
return self._result(
|
|
118
126
|
request,
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
target_name,
|
|
128
|
+
action,
|
|
121
129
|
profile.name,
|
|
122
130
|
actual_hash,
|
|
123
131
|
"blocked",
|
|
@@ -125,7 +133,9 @@ class DocumentApplyService:
|
|
|
125
133
|
)
|
|
126
134
|
else:
|
|
127
135
|
rendered = render_document(frontmatter, next_body, list(profile.field_order))
|
|
128
|
-
issues = self._rules.check_content(self._settings.vault_root,
|
|
136
|
+
issues = self._rules.check_content(self._settings.vault_root, target, rendered)
|
|
137
|
+
if target != source and target.exists():
|
|
138
|
+
issues.insert(0, {"code": "target-exists", "path": target_name})
|
|
129
139
|
missing_codes = {"frontmatter-field-missing", "frontmatter-field-empty"}
|
|
130
140
|
for issue in issues:
|
|
131
141
|
field = issue.get("field")
|
|
@@ -143,8 +153,8 @@ class DocumentApplyService:
|
|
|
143
153
|
status = "needs-input" if missing else "blocked" if issues else "planned"
|
|
144
154
|
result = self._result(
|
|
145
155
|
request,
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
target_name,
|
|
157
|
+
action,
|
|
148
158
|
profile.name,
|
|
149
159
|
actual_hash,
|
|
150
160
|
status,
|
|
@@ -161,22 +171,44 @@ class DocumentApplyService:
|
|
|
161
171
|
}
|
|
162
172
|
)
|
|
163
173
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
updated_references: list[str] = []
|
|
175
|
+
if exists and target != source:
|
|
176
|
+
writes, updated_references, expected = prepare_document_relocation(
|
|
177
|
+
self._settings.vault_root, source, target, rendered
|
|
178
|
+
)
|
|
179
|
+
expected[source] = actual_hash
|
|
180
|
+
expected[target] = None
|
|
181
|
+
change_set = FileChangeSet(
|
|
182
|
+
writes=tuple(writes),
|
|
183
|
+
deletes=(source,),
|
|
167
184
|
label="document apply",
|
|
168
|
-
expected=
|
|
185
|
+
expected=expected,
|
|
169
186
|
)
|
|
170
|
-
|
|
187
|
+
else:
|
|
188
|
+
change_set = FileChangeSet(
|
|
189
|
+
writes=(FileWrite(target, rendered),),
|
|
190
|
+
label="document apply",
|
|
191
|
+
expected={target: None if not exists else actual_hash},
|
|
192
|
+
)
|
|
193
|
+
self._executor.execute(change_set)
|
|
171
194
|
return result.model_copy(
|
|
172
195
|
update={
|
|
173
196
|
"status": "applied",
|
|
174
197
|
"write_performed": True,
|
|
175
|
-
"
|
|
198
|
+
"updated_references": updated_references,
|
|
199
|
+
"follow_up": self._follow_up(
|
|
200
|
+
request, exists, parsed.has_frontmatter, context, changes_type
|
|
201
|
+
),
|
|
176
202
|
}
|
|
177
203
|
)
|
|
178
204
|
|
|
179
|
-
def _creation_defaults(
|
|
205
|
+
def _creation_defaults(
|
|
206
|
+
self,
|
|
207
|
+
path: Path,
|
|
208
|
+
context: DomainContext,
|
|
209
|
+
document_type: str,
|
|
210
|
+
today: str,
|
|
211
|
+
) -> dict[str, Any]:
|
|
180
212
|
prefix = self._settings.document_types["types"][document_type]["prefix"]
|
|
181
213
|
values: dict[str, Any] = {
|
|
182
214
|
"name": path.stem[len(prefix) :] if path.stem.startswith(prefix) else path.stem,
|
|
@@ -186,16 +218,6 @@ class DocumentApplyService:
|
|
|
186
218
|
"updated": today,
|
|
187
219
|
"tags": [],
|
|
188
220
|
}
|
|
189
|
-
try:
|
|
190
|
-
context = resolve_domain_context(
|
|
191
|
-
self._settings.vault_root,
|
|
192
|
-
path,
|
|
193
|
-
self._settings.governance.get("domain_marker", "_领域.md"),
|
|
194
|
-
)
|
|
195
|
-
except DomainContextError as exc:
|
|
196
|
-
raise ConfigurationError(
|
|
197
|
-
f"无法解析目标 Domain 上下文:{exc.code}: {exc.detail}"
|
|
198
|
-
) from exc
|
|
199
221
|
values["domain"] = context.domain_id
|
|
200
222
|
if context.project_id:
|
|
201
223
|
values["project"] = context.project_id
|
|
@@ -224,8 +246,8 @@ class DocumentApplyService:
|
|
|
224
246
|
def _result(
|
|
225
247
|
self,
|
|
226
248
|
request: DocumentApplyRequest,
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
target: str,
|
|
250
|
+
action: str,
|
|
229
251
|
profile: str,
|
|
230
252
|
expected_hash: str,
|
|
231
253
|
status: str,
|
|
@@ -235,8 +257,9 @@ class DocumentApplyService:
|
|
|
235
257
|
return DocumentApplyResult(
|
|
236
258
|
status=status,
|
|
237
259
|
workspace_id=self._settings.workspace_id,
|
|
238
|
-
action=
|
|
260
|
+
action=action,
|
|
239
261
|
path=request.path,
|
|
262
|
+
target=target,
|
|
240
263
|
profile=profile,
|
|
241
264
|
expected_hash=expected_hash,
|
|
242
265
|
issues=issues,
|
|
@@ -248,16 +271,31 @@ class DocumentApplyService:
|
|
|
248
271
|
request: DocumentApplyRequest,
|
|
249
272
|
exists: bool,
|
|
250
273
|
had_frontmatter: bool,
|
|
274
|
+
context: DomainContext,
|
|
275
|
+
changes_type: bool,
|
|
251
276
|
) -> list[CommandFollowUp]:
|
|
252
277
|
derived_fields = {"name", "type", "status", "lifecycle", "domain", "project", "related"}
|
|
253
278
|
needs_sync = not exists or not had_frontmatter
|
|
254
279
|
needs_sync = needs_sync or bool(set(request.values) & derived_fields)
|
|
255
280
|
needs_sync = needs_sync or request.body is not None
|
|
281
|
+
needs_sync = needs_sync or changes_type
|
|
256
282
|
return (
|
|
257
283
|
maintenance_sync_follow_up(
|
|
258
284
|
self._settings.workspace_id,
|
|
259
|
-
[
|
|
285
|
+
[context.root.relative_to(self._settings.vault_root).as_posix()],
|
|
260
286
|
)
|
|
261
287
|
if needs_sync
|
|
262
288
|
else []
|
|
263
289
|
)
|
|
290
|
+
|
|
291
|
+
def _domain_context(self, path: Path) -> DomainContext:
|
|
292
|
+
try:
|
|
293
|
+
return resolve_domain_context(
|
|
294
|
+
self._settings.vault_root,
|
|
295
|
+
path,
|
|
296
|
+
self._settings.governance.get("domain_marker", "_领域.md"),
|
|
297
|
+
)
|
|
298
|
+
except DomainContextError as exc:
|
|
299
|
+
raise ConfigurationError(
|
|
300
|
+
f"无法解析目标 Domain 上下文:{exc.code}: {exc.detail}"
|
|
301
|
+
) from exc
|
|
@@ -3,18 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
from datetime import date
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any
|
|
6
|
-
from urllib.parse import quote
|
|
7
6
|
|
|
8
7
|
from campfire_cli.app.base.schema.operation_schema import maintenance_sync_follow_up
|
|
9
8
|
from campfire_cli.app.document.schema import DocumentMoveResult
|
|
9
|
+
from campfire_cli.app.document.service.document_relocation import prepare_document_relocation
|
|
10
10
|
from campfire_cli.app.document.service.document_rule_service import DocumentRuleService
|
|
11
11
|
from campfire_cli.app.document.service.frontmatter_formatter import render_patch
|
|
12
12
|
from campfire_cli.app.document.service.profile_registry import ProfileRegistry
|
|
13
|
-
from campfire_cli.app.document.service.type_apply import (
|
|
14
|
-
rebase_markdown_links,
|
|
15
|
-
rewrite_markdown_links,
|
|
16
|
-
rewrite_wikilinks,
|
|
17
|
-
)
|
|
18
13
|
from campfire_cli.common.documents.document_types import prefixed_name
|
|
19
14
|
from campfire_cli.common.documents.domain_context import (
|
|
20
15
|
DomainContext,
|
|
@@ -23,7 +18,7 @@ from campfire_cli.common.documents.domain_context import (
|
|
|
23
18
|
)
|
|
24
19
|
from campfire_cli.common.documents.markdown import parse_document
|
|
25
20
|
from campfire_cli.common.exceptions import ConfigurationError
|
|
26
|
-
from campfire_cli.common.filesystem import FileChangeExecutor, FileChangeSet,
|
|
21
|
+
from campfire_cli.common.filesystem import FileChangeExecutor, FileChangeSet, safe_path
|
|
27
22
|
from campfire_cli.common.hashing import file_sha256
|
|
28
23
|
from campfire_cli.config.settings import WorkspaceSettings
|
|
29
24
|
|
|
@@ -224,7 +219,9 @@ class DocumentMoveService:
|
|
|
224
219
|
if issues or not confirm:
|
|
225
220
|
return result
|
|
226
221
|
|
|
227
|
-
writes, updated_references, expected =
|
|
222
|
+
writes, updated_references, expected = prepare_document_relocation(
|
|
223
|
+
self._settings.vault_root, source, target, rendered
|
|
224
|
+
)
|
|
228
225
|
expected[source] = actual_hash
|
|
229
226
|
expected[target] = None
|
|
230
227
|
self._executor.execute(
|
|
@@ -244,36 +241,6 @@ class DocumentMoveService:
|
|
|
244
241
|
}
|
|
245
242
|
)
|
|
246
243
|
|
|
247
|
-
def _prepare_writes(
|
|
248
|
-
self, source: Path, target: Path, moved_text: str
|
|
249
|
-
) -> tuple[list[FileWrite], list[str], dict[Path, str | None]]:
|
|
250
|
-
references = self._reference_files()
|
|
251
|
-
unique_stem = (
|
|
252
|
-
sum(path.stem == source.stem for path in references if path.suffix == ".md") == 1
|
|
253
|
-
)
|
|
254
|
-
source_relative = source.relative_to(self._settings.vault_root).as_posix()
|
|
255
|
-
target_relative = target.relative_to(self._settings.vault_root).as_posix()
|
|
256
|
-
writes: dict[Path, str] = {}
|
|
257
|
-
changed: list[str] = []
|
|
258
|
-
expected = {path: file_sha256(path) for path in references}
|
|
259
|
-
for reference in references:
|
|
260
|
-
text = moved_text if reference == source else reference.read_text(encoding="utf-8")
|
|
261
|
-
updated = text.replace(source_relative, target_relative).replace(
|
|
262
|
-
quote(source_relative), quote(target_relative)
|
|
263
|
-
)
|
|
264
|
-
if unique_stem and source.stem != target.stem:
|
|
265
|
-
updated = rewrite_wikilinks(updated, source.stem, target.stem)
|
|
266
|
-
if reference.suffix.lower() == ".md":
|
|
267
|
-
updated = rewrite_markdown_links(updated, reference, source, target)
|
|
268
|
-
if reference == source and source.parent != target.parent:
|
|
269
|
-
updated = rebase_markdown_links(updated, source, target)
|
|
270
|
-
output = target if reference == source else reference
|
|
271
|
-
if reference == source or updated != text:
|
|
272
|
-
writes[output] = updated
|
|
273
|
-
if reference != source and updated != text:
|
|
274
|
-
changed.append(reference.relative_to(self._settings.vault_root).as_posix())
|
|
275
|
-
return [FileWrite(path, text) for path, text in writes.items()], changed, expected
|
|
276
|
-
|
|
277
244
|
def _domain_context(self, path: Path) -> tuple[DomainContext | None, str | None]:
|
|
278
245
|
try:
|
|
279
246
|
return (
|
|
@@ -294,15 +261,3 @@ class DocumentMoveService:
|
|
|
294
261
|
*self._settings.governance.get("project_reserved_directories", []),
|
|
295
262
|
}
|
|
296
263
|
return any(part in reserved or part.startswith(".") for part in relative.parts)
|
|
297
|
-
|
|
298
|
-
def _reference_files(self) -> list[Path]:
|
|
299
|
-
ignored = {".git", ".campfire"}
|
|
300
|
-
return sorted(
|
|
301
|
-
path
|
|
302
|
-
for path in self._settings.vault_root.rglob("*")
|
|
303
|
-
if path.is_file()
|
|
304
|
-
and path.suffix.lower() in {".md", ".canvas"}
|
|
305
|
-
and not any(
|
|
306
|
-
part in ignored for part in path.relative_to(self._settings.vault_root).parts
|
|
307
|
-
)
|
|
308
|
-
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
from campfire_cli.app.document.service.type_apply import (
|
|
7
|
+
rebase_markdown_links,
|
|
8
|
+
rewrite_markdown_links,
|
|
9
|
+
rewrite_wikilinks,
|
|
10
|
+
)
|
|
11
|
+
from campfire_cli.common.filesystem import FileWrite
|
|
12
|
+
from campfire_cli.common.hashing import file_sha256
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def prepare_document_relocation(
|
|
16
|
+
vault_root: Path,
|
|
17
|
+
source: Path,
|
|
18
|
+
target: Path,
|
|
19
|
+
moved_text: str,
|
|
20
|
+
) -> tuple[list[FileWrite], list[str], dict[Path, str | None]]:
|
|
21
|
+
"""Prepare one document relocation and every deterministic reference rewrite."""
|
|
22
|
+
|
|
23
|
+
references = _reference_files(vault_root)
|
|
24
|
+
unique_stem = sum(
|
|
25
|
+
path.stem == source.stem for path in references if path.suffix == ".md"
|
|
26
|
+
) == 1
|
|
27
|
+
source_relative = source.relative_to(vault_root).as_posix()
|
|
28
|
+
target_relative = target.relative_to(vault_root).as_posix()
|
|
29
|
+
writes: dict[Path, str] = {}
|
|
30
|
+
changed: list[str] = []
|
|
31
|
+
expected = {path: file_sha256(path) for path in references}
|
|
32
|
+
for reference in references:
|
|
33
|
+
text = moved_text if reference == source else reference.read_text(encoding="utf-8")
|
|
34
|
+
updated = text.replace(source_relative, target_relative).replace(
|
|
35
|
+
quote(source_relative), quote(target_relative)
|
|
36
|
+
)
|
|
37
|
+
if unique_stem and source.stem != target.stem:
|
|
38
|
+
updated = rewrite_wikilinks(updated, source.stem, target.stem)
|
|
39
|
+
if reference.suffix.lower() == ".md":
|
|
40
|
+
updated = rewrite_markdown_links(updated, reference, source, target)
|
|
41
|
+
if reference == source and source.parent != target.parent:
|
|
42
|
+
updated = rebase_markdown_links(updated, source, target)
|
|
43
|
+
output = target if reference == source else reference
|
|
44
|
+
if reference == source or updated != text:
|
|
45
|
+
writes[output] = updated
|
|
46
|
+
if reference != source and updated != text:
|
|
47
|
+
changed.append(reference.relative_to(vault_root).as_posix())
|
|
48
|
+
return [FileWrite(path, text) for path, text in writes.items()], changed, expected
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _reference_files(vault_root: Path) -> list[Path]:
|
|
52
|
+
ignored = {".git", ".campfire"}
|
|
53
|
+
return sorted(
|
|
54
|
+
path
|
|
55
|
+
for path in vault_root.rglob("*")
|
|
56
|
+
if path.is_file()
|
|
57
|
+
and path.suffix.lower() in {".md", ".canvas"}
|
|
58
|
+
and not any(part in ignored for part in path.relative_to(vault_root).parts)
|
|
59
|
+
)
|
|
@@ -153,14 +153,28 @@ class MaintenanceService:
|
|
|
153
153
|
scope_path = (self._settings.vault_root / scope).resolve()
|
|
154
154
|
if not self._is_within_workspace(scope_path):
|
|
155
155
|
return self._sync_blocked("scope-outside-workspace", scope, scope)
|
|
156
|
-
|
|
156
|
+
exact = [domain for domain in domains if domain.path == scope_path]
|
|
157
|
+
if not exact and not scope_path.exists():
|
|
158
|
+
return self._sync_blocked("scope-missing", scope, scope)
|
|
159
|
+
containing = [domain for domain in domains if domain.path in scope_path.parents]
|
|
160
|
+
if exact:
|
|
161
|
+
scope_path = exact[0].path
|
|
162
|
+
elif containing:
|
|
163
|
+
scope_path = max(containing, key=lambda domain: len(domain.path.parts)).path
|
|
164
|
+
scope = scope_path.relative_to(self._settings.vault_root).as_posix()
|
|
165
|
+
descendants = [
|
|
157
166
|
domain
|
|
158
167
|
for domain in domains
|
|
159
168
|
if domain.path == scope_path or scope_path in domain.path.parents
|
|
160
169
|
]
|
|
170
|
+
space_marker = self._settings.governance.get("space_marker", "_空间.md")
|
|
171
|
+
empty_governance_root = scope_path == self._settings.vault_root or (
|
|
172
|
+
scope_path / space_marker
|
|
173
|
+
).is_file()
|
|
174
|
+
if not descendants and not empty_governance_root:
|
|
175
|
+
return self._sync_blocked("scope-unmanaged", scope, scope)
|
|
176
|
+
domains = descendants
|
|
161
177
|
issues = [issue for issue in issues if self._path_matches_scope(issue["path"], scope)]
|
|
162
|
-
if not domains and not scope_path.exists():
|
|
163
|
-
return self._sync_blocked("scope-missing", scope, scope)
|
|
164
178
|
if issues:
|
|
165
179
|
return MaintenanceResult(
|
|
166
180
|
status="blocked",
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
`workspace config check` 统一校验包内产品默认值和当前 Workspace 的有效治理契约,包括目录与 Space 冲突、文档类型前缀、Profile 引用和继承、归档策略以及托管资源列表。
|
|
12
12
|
|
|
13
|
-
`workspace restructure` 负责原子命令无法表达的批量文档映射、领域拆分和 Frontmatter Patch:冻结范围、生成带内容哈希的审批计划、更新引用并验证迁移事实。Domain 的 rename/move/merge/delete/rekey 直接位于 `workspace domain`;move 使用目标 Space/Domain id 推导路径,merge 一次完成内容迁移和源领域移除,delete 只删除逻辑空领域。单篇文档移动属于 `document move`,以目标 Domain id
|
|
13
|
+
`workspace restructure` 负责原子命令无法表达的批量文档映射、领域拆分和 Frontmatter Patch:冻结范围、生成带内容哈希的审批计划、更新引用并验证迁移事实。Domain 的 rename/move/merge/delete/rekey 直接位于 `workspace domain`;move 使用目标 Space/Domain id 推导路径,merge 一次完成内容迁移和源领域移除,delete 只删除逻辑空领域。单篇文档移动属于 `document move`,以目标 Domain id 代替目标路径。无 `--spec` 时只推断可确定的文档类型与文件名前缀规范化;没有变化返回 `up-to-date`,任意路径映射或 Frontmatter Patch 必须使用已文档化的 spec。纯移动不得重写文档内容;`verify` 只验证本批次 source/target 结果。该能力属于 Workspace Service,不设独立 App,也不用于日常增量维护。
|
|
14
14
|
|
|
15
|
-
Adoption、文档批量 Restructure 与 Domain Restructure 均使用共享 ChangeSet Executor 提交文件写入和路径移动,并在协调的 Repository 写入失败时补偿恢复。Workspace Service 不调用 Maintenance Service;实际写入成功的结果只在必要时以轻量 `follow_up`
|
|
15
|
+
Adoption、文档批量 Restructure 与 Domain Restructure 均使用共享 ChangeSet Executor 提交文件写入和路径移动,并在协调的 Repository 写入失败时补偿恢复。Workspace Service 不调用 Maintenance Service;实际写入成功的结果只在必要时以轻量 `follow_up` 返回零或一个可直接执行的 scoped `maintenance sync`,预览和阻塞结果不返回可执行后续。
|
|
16
16
|
|
|
17
17
|
Space 是 Workspace 根下以 `_空间.md` 声明的顶级内容容器;Domain 位于 Space 内,以 `_领域.md` 声明并可任意嵌套。子 Domain 必须处于父 Domain 路径下、属于同一 Space 并继承 governance。`_收件箱` 与 `治理视图` 是系统区域,不是 Space。Maintenance 只消费本模块发现的结构,不维护第二套领域规则。
|
|
18
18
|
|
{campfire_cli-0.1.9 → campfire_cli-0.1.10}/src/campfire_cli/app/workspace/cli/restructure_cli.py
RENAMED
|
@@ -42,10 +42,15 @@ def plan(
|
|
|
42
42
|
spec: Path | None = typer.Option(
|
|
43
43
|
None,
|
|
44
44
|
"--spec",
|
|
45
|
-
help="YAML/JSON
|
|
45
|
+
help="YAML/JSON 意图规格文件",
|
|
46
46
|
),
|
|
47
47
|
) -> None:
|
|
48
|
-
"""生成带源哈希且默认未审批的逐文件计划。
|
|
48
|
+
"""生成带源哈希且默认未审批的逐文件计划。
|
|
49
|
+
|
|
50
|
+
无 --spec:只规范化类型与文件名。
|
|
51
|
+
移动或 Frontmatter Patch:使用 operations spec。
|
|
52
|
+
完整格式见 Restructure Skill reference。
|
|
53
|
+
"""
|
|
49
54
|
emit(invoke(lambda: service(ctx).plan(batch, spec)))
|
|
50
55
|
|
|
51
56
|
|
|
@@ -318,7 +318,7 @@ class RestructureService:
|
|
|
318
318
|
) -> RestructureResult:
|
|
319
319
|
approved_count = sum(item.approved for item in items)
|
|
320
320
|
return RestructureResult(
|
|
321
|
-
status="planned",
|
|
321
|
+
status="planned" if items else "up-to-date",
|
|
322
322
|
batch=batch,
|
|
323
323
|
item_count=len(items),
|
|
324
324
|
inventory_count=inventory_count,
|