campfire-cli 0.1.0__py3-none-any.whl
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/__init__.py +3 -0
- campfire_cli/alembic/__init__.py +1 -0
- campfire_cli/alembic/env.py +37 -0
- campfire_cli/alembic/script.py.mako +22 -0
- campfire_cli/alembic/versions/001_initial_schema.py +81 -0
- campfire_cli/alembic/versions/002_workspace_project_registry.py +47 -0
- campfire_cli/alembic/versions/003_unified_workspace_state.py +113 -0
- campfire_cli/alembic/versions/004_restructure_workspace_domain.py +23 -0
- campfire_cli/alembic/versions/005_decision_channel.py +71 -0
- campfire_cli/alembic/versions/006_workspace_topology_index.py +61 -0
- campfire_cli/alembic/versions/007_workspace_adoption.py +38 -0
- campfire_cli/alembic/versions/__init__.py +1 -0
- campfire_cli/app/SPEC.md +12 -0
- campfire_cli/app/__init__.py +1 -0
- campfire_cli/app/base/SPEC.md +3 -0
- campfire_cli/app/base/__init__.py +1 -0
- campfire_cli/app/base/cli/base_cli.py +37 -0
- campfire_cli/app/base/repository/base_repository.py +17 -0
- campfire_cli/app/base/schema/base_schema.py +18 -0
- campfire_cli/app/base/service/base_service.py +129 -0
- campfire_cli/app/decision/SPEC.md +16 -0
- campfire_cli/app/decision/__init__.py +1 -0
- campfire_cli/app/decision/cli/decision_cli.py +112 -0
- campfire_cli/app/decision/repository/decision_repository.py +199 -0
- campfire_cli/app/decision/schema/decision_schema.py +56 -0
- campfire_cli/app/decision/service/decision_projection_service.py +151 -0
- campfire_cli/app/decision/service/decision_service.py +124 -0
- campfire_cli/app/document/SPEC.md +51 -0
- campfire_cli/app/document/__init__.py +1 -0
- campfire_cli/app/document/cli/__init__.py +1 -0
- campfire_cli/app/document/cli/document_cli.py +63 -0
- campfire_cli/app/document/cli/profile_cli.py +65 -0
- campfire_cli/app/document/cli/type_cli.py +49 -0
- campfire_cli/app/document/repository/__init__.py +1 -0
- campfire_cli/app/document/repository/document_profile_repository.py +17 -0
- campfire_cli/app/document/repository/document_type_repository.py +17 -0
- campfire_cli/app/document/service/__init__.py +1 -0
- campfire_cli/app/document/service/document_profile_service.py +66 -0
- campfire_cli/app/document/service/document_rule_service.py +316 -0
- campfire_cli/app/document/service/document_scanner.py +38 -0
- campfire_cli/app/document/service/document_service.py +139 -0
- campfire_cli/app/document/service/document_type_service.py +28 -0
- campfire_cli/app/document/service/frontmatter_apply.py +98 -0
- campfire_cli/app/document/service/frontmatter_formatter.py +104 -0
- campfire_cli/app/document/service/frontmatter_plan.py +75 -0
- campfire_cli/app/document/service/profile_registry.py +166 -0
- campfire_cli/app/document/service/type_apply.py +185 -0
- campfire_cli/app/document/service/type_plan.py +80 -0
- campfire_cli/app/maintenance/SPEC.md +22 -0
- campfire_cli/app/maintenance/__init__.py +1 -0
- campfire_cli/app/maintenance/cli/maintenance_cli.py +103 -0
- campfire_cli/app/maintenance/repository/maintenance_repository.py +115 -0
- campfire_cli/app/maintenance/schema/maintenance_schema.py +109 -0
- campfire_cli/app/maintenance/service/archive_service.py +255 -0
- campfire_cli/app/maintenance/service/link_service.py +91 -0
- campfire_cli/app/maintenance/service/maintenance_protocol.py +25 -0
- campfire_cli/app/maintenance/service/maintenance_service.py +461 -0
- campfire_cli/app/maintenance/service/moc_service.py +310 -0
- campfire_cli/app/maintenance/service/plan_service.py +411 -0
- campfire_cli/app/skill/SPEC.md +3 -0
- campfire_cli/app/skill/__init__.py +1 -0
- campfire_cli/app/skill/cli/skill_cli.py +44 -0
- campfire_cli/app/skill/repository/skill_repository.py +33 -0
- campfire_cli/app/skill/schema/skill_schema.py +18 -0
- campfire_cli/app/skill/service/skill_service.py +146 -0
- campfire_cli/app/workspace/SPEC.md +25 -0
- campfire_cli/app/workspace/__init__.py +1 -0
- campfire_cli/app/workspace/cli/__init__.py +1 -0
- campfire_cli/app/workspace/cli/adoption_cli.py +83 -0
- campfire_cli/app/workspace/cli/config_cli.py +30 -0
- campfire_cli/app/workspace/cli/domain_cli.py +101 -0
- campfire_cli/app/workspace/cli/project_cli.py +135 -0
- campfire_cli/app/workspace/cli/restructure_cli.py +133 -0
- campfire_cli/app/workspace/cli/space_cli.py +65 -0
- campfire_cli/app/workspace/cli/workspace_cli.py +145 -0
- campfire_cli/app/workspace/repository/__init__.py +1 -0
- campfire_cli/app/workspace/repository/adoption_repository.py +55 -0
- campfire_cli/app/workspace/repository/manifest_repository.py +38 -0
- campfire_cli/app/workspace/repository/restructure_repository.py +93 -0
- campfire_cli/app/workspace/repository/workspace_repository.py +125 -0
- campfire_cli/app/workspace/schema/__init__.py +1 -0
- campfire_cli/app/workspace/schema/adoption_schema.py +47 -0
- campfire_cli/app/workspace/schema/restructure_schema.py +64 -0
- campfire_cli/app/workspace/schema/workspace_schema.py +225 -0
- campfire_cli/app/workspace/service/__init__.py +1 -0
- campfire_cli/app/workspace/service/adoption_protocol.py +11 -0
- campfire_cli/app/workspace/service/adoption_service.py +364 -0
- campfire_cli/app/workspace/service/config_service.py +189 -0
- campfire_cli/app/workspace/service/domain_restructure_service.py +316 -0
- campfire_cli/app/workspace/service/project_service.py +389 -0
- campfire_cli/app/workspace/service/restructure_protocol.py +19 -0
- campfire_cli/app/workspace/service/restructure_service.py +392 -0
- campfire_cli/app/workspace/service/restructure_verifier.py +83 -0
- campfire_cli/app/workspace/service/structure_service.py +494 -0
- campfire_cli/app/workspace/service/workspace_protocol.py +24 -0
- campfire_cli/app/workspace/service/workspace_service.py +304 -0
- campfire_cli/common/SPEC.md +12 -0
- campfire_cli/common/__init__.py +1 -0
- campfire_cli/common/database/__init__.py +4 -0
- campfire_cli/common/database/migrations.py +18 -0
- campfire_cli/common/database/models.py +215 -0
- campfire_cli/common/database/session.py +30 -0
- campfire_cli/common/documents/__init__.py +1 -0
- campfire_cli/common/documents/document_types.py +119 -0
- campfire_cli/common/documents/frontmatter_schema.py +42 -0
- campfire_cli/common/documents/markdown.py +66 -0
- campfire_cli/common/exceptions/__init__.py +7 -0
- campfire_cli/common/exceptions/base.py +19 -0
- campfire_cli/common/filesystem/__init__.py +6 -0
- campfire_cli/common/filesystem/atomic.py +30 -0
- campfire_cli/common/filesystem/locking.py +55 -0
- campfire_cli/common/governance/__init__.py +13 -0
- campfire_cli/common/governance/issues.py +38 -0
- campfire_cli/common/governance/locking.py +31 -0
- campfire_cli/common/governance/snapshots.py +26 -0
- campfire_cli/common/hashing.py +10 -0
- campfire_cli/common/reports/__init__.py +1 -0
- campfire_cli/common/reports/json_report.py +8 -0
- campfire_cli/common/reports/markdown_report.py +29 -0
- campfire_cli/config/SPEC.md +5 -0
- campfire_cli/config/defaults.py +64 -0
- campfire_cli/config/settings.py +50 -0
- campfire_cli/container.py +130 -0
- campfire_cli/main.py +133 -0
- campfire_cli/resources/__init__.py +1 -0
- campfire_cli/resources/bases/Agent/346/211/247/350/241/214/345/267/245/344/275/234/345/217/260.base +35 -0
- campfire_cli/resources/bases/SPEC.md +3 -0
- campfire_cli/resources/bases//344/273/273/345/212/241/345/267/245/344/275/234/345/217/260.base +49 -0
- campfire_cli/resources/bases//345/206/263/347/255/226/345/267/245/344/275/234/345/217/260.base +40 -0
- campfire_cli/resources/bases//345/276/205/345/275/222/346/241/243/346/226/207/346/241/243.base +28 -0
- campfire_cli/resources/bases//351/241/271/347/233/256/346/226/207/346/241/243.base +35 -0
- campfire_cli/resources/defaults/__init__.py +1 -0
- campfire_cli/resources/defaults/config.yml +458 -0
- campfire_cli/resources/skills/SPEC.md +3 -0
- campfire_cli/resources/skills/campfire-context-bootstrap/SKILL.md +62 -0
- campfire_cli/resources/skills/campfire-context-bootstrap/agents/openai.yaml +3 -0
- campfire_cli/resources/skills/campfire-conversation-router/SKILL.md +53 -0
- campfire_cli/resources/skills/campfire-conversation-router/agents/openai.yaml +3 -0
- campfire_cli/resources/skills/campfire-conversation-router/references/inbox-handoff.md +19 -0
- campfire_cli/resources/skills/campfire-conversation-router/references/learning-intake.md +31 -0
- campfire_cli/resources/skills/campfire-conversation-router/references/task-intake.md +28 -0
- campfire_cli/resources/skills/campfire-document-capture/SKILL.md +109 -0
- campfire_cli/resources/skills/campfire-inbox-triage/SKILL.md +23 -0
- campfire_cli/resources/skills/campfire-weekly-report-writing/SKILL.md +21 -0
- campfire_cli/resources/skills/campfire-weekly-report-writing/agents/openai.yaml +3 -0
- campfire_cli/resources/skills/campfire-workspace-adoption/SKILL.md +42 -0
- campfire_cli/resources/skills/campfire-workspace-maintenance/SKILL.md +74 -0
- 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 +23 -0
- campfire_cli/resources/skills/campfire-workspace-restructure/SKILL.md +52 -0
- campfire_cli-0.1.0.dist-info/METADATA +141 -0
- campfire_cli-0.1.0.dist-info/RECORD +154 -0
- campfire_cli-0.1.0.dist-info/WHEEL +4 -0
- campfire_cli-0.1.0.dist-info/entry_points.txt +2 -0
- campfire_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
campfire_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Packaged Alembic migration environment."""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import engine_from_config, pool
|
|
4
|
+
|
|
5
|
+
from alembic import context
|
|
6
|
+
from campfire_cli.common.database.models import Base
|
|
7
|
+
|
|
8
|
+
config = context.config
|
|
9
|
+
target_metadata = Base.metadata
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_migrations_offline() -> None:
|
|
13
|
+
context.configure(
|
|
14
|
+
url=config.get_main_option("sqlalchemy.url"),
|
|
15
|
+
target_metadata=target_metadata,
|
|
16
|
+
literal_binds=True,
|
|
17
|
+
)
|
|
18
|
+
with context.begin_transaction():
|
|
19
|
+
context.run_migrations()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run_migrations_online() -> None:
|
|
23
|
+
connectable = engine_from_config(
|
|
24
|
+
config.get_section(config.config_ini_section, {}),
|
|
25
|
+
prefix="sqlalchemy.",
|
|
26
|
+
poolclass=pool.NullPool,
|
|
27
|
+
)
|
|
28
|
+
with connectable.connect() as connection:
|
|
29
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
30
|
+
with context.begin_transaction():
|
|
31
|
+
context.run_migrations()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if context.is_offline_mode():
|
|
35
|
+
run_migrations_offline()
|
|
36
|
+
else:
|
|
37
|
+
run_migrations_online()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
"""
|
|
7
|
+
from alembic import op
|
|
8
|
+
import sqlalchemy as sa
|
|
9
|
+
${imports if imports else ""}
|
|
10
|
+
|
|
11
|
+
revision = ${repr(up_revision)}
|
|
12
|
+
down_revision = ${repr(down_revision)}
|
|
13
|
+
branch_labels = ${repr(branch_labels)}
|
|
14
|
+
depends_on = ${repr(depends_on)}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def upgrade() -> None:
|
|
18
|
+
${upgrades if upgrades else "pass"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def downgrade() -> None:
|
|
22
|
+
${downgrades if downgrades else "pass"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Create Campfire state tables."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "001"
|
|
8
|
+
down_revision = None
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def upgrade() -> None:
|
|
14
|
+
op.create_table(
|
|
15
|
+
"documents",
|
|
16
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
17
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
18
|
+
sa.Column("content_hash", sa.String(64), nullable=False),
|
|
19
|
+
sa.Column("document_type", sa.String(64)),
|
|
20
|
+
sa.Column("domain_id", sa.String(128)),
|
|
21
|
+
sa.Column("status", sa.String(32)),
|
|
22
|
+
sa.Column("exists", sa.Boolean(), nullable=False),
|
|
23
|
+
sa.Column("indexed_at", sa.DateTime(), nullable=False),
|
|
24
|
+
sa.UniqueConstraint("path"),
|
|
25
|
+
)
|
|
26
|
+
op.create_index("ix_documents_path", "documents", ["path"])
|
|
27
|
+
op.create_table(
|
|
28
|
+
"governance_issues",
|
|
29
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
30
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
31
|
+
sa.Column("code", sa.String(96), nullable=False),
|
|
32
|
+
sa.Column("detail", sa.Text(), nullable=False),
|
|
33
|
+
sa.Column("severity", sa.String(16), nullable=False),
|
|
34
|
+
sa.Column("status", sa.String(16), nullable=False),
|
|
35
|
+
sa.UniqueConstraint("path", "code", "detail"),
|
|
36
|
+
)
|
|
37
|
+
op.create_index("ix_governance_issues_code", "governance_issues", ["code"])
|
|
38
|
+
op.create_table(
|
|
39
|
+
"maintenance_runs",
|
|
40
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
41
|
+
sa.Column("run_id", sa.String(36), nullable=False, unique=True),
|
|
42
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
43
|
+
sa.Column("scanned_count", sa.Integer(), nullable=False),
|
|
44
|
+
sa.Column("issue_count", sa.Integer(), nullable=False),
|
|
45
|
+
sa.Column("started_at", sa.DateTime(), nullable=False),
|
|
46
|
+
sa.Column("finished_at", sa.DateTime()),
|
|
47
|
+
)
|
|
48
|
+
op.create_table(
|
|
49
|
+
"migration_batches",
|
|
50
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
51
|
+
sa.Column("batch_uuid", sa.String(36), nullable=False, unique=True),
|
|
52
|
+
sa.Column("batch_name", sa.String(128), nullable=False, unique=True),
|
|
53
|
+
sa.Column("scope", sa.Text(), nullable=False),
|
|
54
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
55
|
+
sa.Column("config_hash", sa.String(64), nullable=False),
|
|
56
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
57
|
+
)
|
|
58
|
+
op.create_table(
|
|
59
|
+
"migration_items",
|
|
60
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
61
|
+
sa.Column("batch_id", sa.Integer(), sa.ForeignKey("migration_batches.id"), nullable=False),
|
|
62
|
+
sa.Column("item_uuid", sa.String(36), nullable=False, unique=True),
|
|
63
|
+
sa.Column("source_path", sa.Text(), nullable=False),
|
|
64
|
+
sa.Column("target_path", sa.Text(), nullable=False),
|
|
65
|
+
sa.Column("source_hash", sa.String(64), nullable=False),
|
|
66
|
+
sa.Column("action", sa.String(32), nullable=False),
|
|
67
|
+
sa.Column("proposed_type", sa.String(64)),
|
|
68
|
+
sa.Column("reason", sa.Text(), nullable=False),
|
|
69
|
+
sa.Column("confidence", sa.String(16), nullable=False),
|
|
70
|
+
sa.Column("approved", sa.Boolean(), nullable=False),
|
|
71
|
+
sa.Column("execution_status", sa.String(24), nullable=False),
|
|
72
|
+
)
|
|
73
|
+
op.create_index("ix_migration_items_batch_id", "migration_items", ["batch_id"])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def downgrade() -> None:
|
|
77
|
+
op.drop_table("migration_items")
|
|
78
|
+
op.drop_table("migration_batches")
|
|
79
|
+
op.drop_table("maintenance_runs")
|
|
80
|
+
op.drop_table("governance_issues")
|
|
81
|
+
op.drop_table("documents")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Create the SQLite-backed Workspace and Project registry."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "002"
|
|
8
|
+
down_revision = "001"
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def upgrade() -> None:
|
|
14
|
+
op.create_table(
|
|
15
|
+
"workspaces",
|
|
16
|
+
sa.Column("id", sa.String(63), primary_key=True),
|
|
17
|
+
sa.Column("path", sa.Text(), nullable=False, unique=True),
|
|
18
|
+
sa.Column("is_default", sa.Boolean(), nullable=False),
|
|
19
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
20
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
21
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
22
|
+
)
|
|
23
|
+
op.create_index("ix_workspaces_is_default", "workspaces", ["is_default"])
|
|
24
|
+
op.create_table(
|
|
25
|
+
"projects",
|
|
26
|
+
sa.Column("id", sa.String(63), primary_key=True),
|
|
27
|
+
sa.Column(
|
|
28
|
+
"workspace_id",
|
|
29
|
+
sa.String(63),
|
|
30
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
31
|
+
nullable=False,
|
|
32
|
+
),
|
|
33
|
+
sa.Column("name", sa.String(160), nullable=False),
|
|
34
|
+
sa.Column("document_domain", sa.Text(), nullable=False),
|
|
35
|
+
sa.Column("git_remote_url", sa.Text()),
|
|
36
|
+
sa.Column("local_path", sa.Text()),
|
|
37
|
+
sa.Column("default_branch", sa.String(160)),
|
|
38
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
39
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
40
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
41
|
+
)
|
|
42
|
+
op.create_index("ix_projects_workspace_id", "projects", ["workspace_id"])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def downgrade() -> None:
|
|
46
|
+
op.drop_table("projects")
|
|
47
|
+
op.drop_table("workspaces")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Move all operational state into the workspace-scoped global database."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "003"
|
|
8
|
+
down_revision = "002"
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def workspace_column() -> sa.Column:
|
|
14
|
+
return sa.Column(
|
|
15
|
+
"workspace_id",
|
|
16
|
+
sa.String(63),
|
|
17
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
18
|
+
nullable=False,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# Versions before 0.9 never wrote operational state to the global database.
|
|
24
|
+
# Recreate these empty legacy tables with explicit Workspace ownership.
|
|
25
|
+
for table in (
|
|
26
|
+
"migration_items",
|
|
27
|
+
"migration_batches",
|
|
28
|
+
"maintenance_runs",
|
|
29
|
+
"governance_issues",
|
|
30
|
+
"documents",
|
|
31
|
+
):
|
|
32
|
+
op.drop_table(table)
|
|
33
|
+
|
|
34
|
+
op.create_table(
|
|
35
|
+
"documents",
|
|
36
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
37
|
+
workspace_column(),
|
|
38
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
39
|
+
sa.Column("content_hash", sa.String(64), nullable=False),
|
|
40
|
+
sa.Column("document_type", sa.String(64)),
|
|
41
|
+
sa.Column("domain_id", sa.String(128)),
|
|
42
|
+
sa.Column("status", sa.String(32)),
|
|
43
|
+
sa.Column("exists", sa.Boolean(), nullable=False),
|
|
44
|
+
sa.Column("indexed_at", sa.DateTime(), nullable=False),
|
|
45
|
+
sa.UniqueConstraint("workspace_id", "path"),
|
|
46
|
+
)
|
|
47
|
+
op.create_index("ix_documents_workspace_id", "documents", ["workspace_id"])
|
|
48
|
+
op.create_index("ix_documents_path", "documents", ["path"])
|
|
49
|
+
op.create_table(
|
|
50
|
+
"governance_issues",
|
|
51
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
52
|
+
workspace_column(),
|
|
53
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
54
|
+
sa.Column("code", sa.String(96), nullable=False),
|
|
55
|
+
sa.Column("detail", sa.Text(), nullable=False),
|
|
56
|
+
sa.Column("severity", sa.String(16), nullable=False),
|
|
57
|
+
sa.Column("status", sa.String(16), nullable=False),
|
|
58
|
+
sa.UniqueConstraint("workspace_id", "path", "code", "detail"),
|
|
59
|
+
)
|
|
60
|
+
op.create_index("ix_governance_issues_workspace_id", "governance_issues", ["workspace_id"])
|
|
61
|
+
op.create_index("ix_governance_issues_code", "governance_issues", ["code"])
|
|
62
|
+
op.create_table(
|
|
63
|
+
"maintenance_runs",
|
|
64
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
65
|
+
workspace_column(),
|
|
66
|
+
sa.Column("run_id", sa.String(36), nullable=False, unique=True),
|
|
67
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
68
|
+
sa.Column("scanned_count", sa.Integer(), nullable=False),
|
|
69
|
+
sa.Column("issue_count", sa.Integer(), nullable=False),
|
|
70
|
+
sa.Column("started_at", sa.DateTime(), nullable=False),
|
|
71
|
+
sa.Column("finished_at", sa.DateTime()),
|
|
72
|
+
)
|
|
73
|
+
op.create_index("ix_maintenance_runs_workspace_id", "maintenance_runs", ["workspace_id"])
|
|
74
|
+
op.create_table(
|
|
75
|
+
"migration_batches",
|
|
76
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
77
|
+
workspace_column(),
|
|
78
|
+
sa.Column("batch_uuid", sa.String(36), nullable=False, unique=True),
|
|
79
|
+
sa.Column("batch_name", sa.String(128), nullable=False),
|
|
80
|
+
sa.Column("scope", sa.Text(), nullable=False),
|
|
81
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
82
|
+
sa.Column("config_hash", sa.String(64), nullable=False),
|
|
83
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
84
|
+
sa.UniqueConstraint("workspace_id", "batch_name"),
|
|
85
|
+
)
|
|
86
|
+
op.create_index("ix_migration_batches_workspace_id", "migration_batches", ["workspace_id"])
|
|
87
|
+
op.create_table(
|
|
88
|
+
"migration_items",
|
|
89
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
90
|
+
workspace_column(),
|
|
91
|
+
sa.Column(
|
|
92
|
+
"batch_id",
|
|
93
|
+
sa.Integer(),
|
|
94
|
+
sa.ForeignKey("migration_batches.id", ondelete="CASCADE"),
|
|
95
|
+
nullable=False,
|
|
96
|
+
),
|
|
97
|
+
sa.Column("item_uuid", sa.String(36), nullable=False, unique=True),
|
|
98
|
+
sa.Column("source_path", sa.Text(), nullable=False),
|
|
99
|
+
sa.Column("target_path", sa.Text(), nullable=False),
|
|
100
|
+
sa.Column("source_hash", sa.String(64), nullable=False),
|
|
101
|
+
sa.Column("action", sa.String(32), nullable=False),
|
|
102
|
+
sa.Column("proposed_type", sa.String(64)),
|
|
103
|
+
sa.Column("reason", sa.Text(), nullable=False),
|
|
104
|
+
sa.Column("confidence", sa.String(16), nullable=False),
|
|
105
|
+
sa.Column("approved", sa.Boolean(), nullable=False),
|
|
106
|
+
sa.Column("execution_status", sa.String(24), nullable=False),
|
|
107
|
+
)
|
|
108
|
+
op.create_index("ix_migration_items_workspace_id", "migration_items", ["workspace_id"])
|
|
109
|
+
op.create_index("ix_migration_items_batch_id", "migration_items", ["batch_id"])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def downgrade() -> None:
|
|
113
|
+
raise RuntimeError("The unified database migration is intentionally irreversible")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Rename the former migration persistence model to Workspace restructure."""
|
|
2
|
+
|
|
3
|
+
from alembic import op
|
|
4
|
+
|
|
5
|
+
revision = "004"
|
|
6
|
+
down_revision = "003"
|
|
7
|
+
branch_labels = None
|
|
8
|
+
depends_on = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def upgrade() -> None:
|
|
12
|
+
op.rename_table("migration_batches", "restructure_batches")
|
|
13
|
+
op.rename_table("migration_items", "restructure_items")
|
|
14
|
+
op.drop_index("ix_migration_batches_workspace_id", table_name="restructure_batches")
|
|
15
|
+
op.drop_index("ix_migration_items_workspace_id", table_name="restructure_items")
|
|
16
|
+
op.drop_index("ix_migration_items_batch_id", table_name="restructure_items")
|
|
17
|
+
op.create_index("ix_restructure_batches_workspace_id", "restructure_batches", ["workspace_id"])
|
|
18
|
+
op.create_index("ix_restructure_items_workspace_id", "restructure_items", ["workspace_id"])
|
|
19
|
+
op.create_index("ix_restructure_items_batch_id", "restructure_items", ["batch_id"])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def downgrade() -> None:
|
|
23
|
+
raise RuntimeError("The Workspace restructure rename is intentionally irreversible")
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Add persistent Decision state and append-only events."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "005"
|
|
8
|
+
down_revision = "004"
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def upgrade() -> None:
|
|
14
|
+
op.create_table(
|
|
15
|
+
"decisions",
|
|
16
|
+
sa.Column("id", sa.String(36), primary_key=True),
|
|
17
|
+
sa.Column(
|
|
18
|
+
"workspace_id",
|
|
19
|
+
sa.String(63),
|
|
20
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
21
|
+
nullable=False,
|
|
22
|
+
),
|
|
23
|
+
sa.Column("dedupe_key", sa.String(160), nullable=False),
|
|
24
|
+
sa.Column("question", sa.Text(), nullable=False),
|
|
25
|
+
sa.Column("context", sa.Text(), nullable=False),
|
|
26
|
+
sa.Column("recommendation", sa.Text(), nullable=False),
|
|
27
|
+
sa.Column("options_json", sa.Text(), nullable=False),
|
|
28
|
+
sa.Column("related_documents_json", sa.Text(), nullable=False),
|
|
29
|
+
sa.Column("source_type", sa.String(64), nullable=False),
|
|
30
|
+
sa.Column("source_id", sa.String(160)),
|
|
31
|
+
sa.Column("session_provider", sa.String(64)),
|
|
32
|
+
sa.Column("session_id", sa.String(200)),
|
|
33
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
34
|
+
sa.Column("answer", sa.Text()),
|
|
35
|
+
sa.Column("answered_by", sa.String(160)),
|
|
36
|
+
sa.Column("cancellation_reason", sa.Text()),
|
|
37
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
38
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
39
|
+
sa.Column("answered_at", sa.DateTime()),
|
|
40
|
+
sa.Column("closed_at", sa.DateTime()),
|
|
41
|
+
sa.UniqueConstraint("workspace_id", "dedupe_key"),
|
|
42
|
+
)
|
|
43
|
+
op.create_index("ix_decisions_workspace_id", "decisions", ["workspace_id"])
|
|
44
|
+
op.create_index("ix_decisions_status", "decisions", ["status"])
|
|
45
|
+
op.create_table(
|
|
46
|
+
"decision_events",
|
|
47
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
48
|
+
sa.Column(
|
|
49
|
+
"workspace_id",
|
|
50
|
+
sa.String(63),
|
|
51
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
52
|
+
nullable=False,
|
|
53
|
+
),
|
|
54
|
+
sa.Column(
|
|
55
|
+
"decision_id",
|
|
56
|
+
sa.String(36),
|
|
57
|
+
sa.ForeignKey("decisions.id", ondelete="CASCADE"),
|
|
58
|
+
nullable=False,
|
|
59
|
+
),
|
|
60
|
+
sa.Column("event_type", sa.String(64), nullable=False),
|
|
61
|
+
sa.Column("actor", sa.String(160)),
|
|
62
|
+
sa.Column("payload_json", sa.Text(), nullable=False),
|
|
63
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
64
|
+
)
|
|
65
|
+
op.create_index("ix_decision_events_workspace_id", "decision_events", ["workspace_id"])
|
|
66
|
+
op.create_index("ix_decision_events_decision_id", "decision_events", ["decision_id"])
|
|
67
|
+
op.create_index("ix_decision_events_event_type", "decision_events", ["event_type"])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def downgrade() -> None:
|
|
71
|
+
raise RuntimeError("Decision history is intentionally irreversible")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Add rebuildable Space and Domain topology indexes."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "006"
|
|
8
|
+
down_revision = "005"
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def workspace_column() -> sa.Column:
|
|
14
|
+
return sa.Column(
|
|
15
|
+
"workspace_id",
|
|
16
|
+
sa.String(63),
|
|
17
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
18
|
+
nullable=False,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
op.create_table(
|
|
24
|
+
"spaces",
|
|
25
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
26
|
+
workspace_column(),
|
|
27
|
+
sa.Column("space_id", sa.String(128), nullable=False),
|
|
28
|
+
sa.Column("name", sa.String(160), nullable=False),
|
|
29
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
30
|
+
sa.Column("space_type", sa.String(64), nullable=False),
|
|
31
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
32
|
+
sa.Column("source_hash", sa.String(64), nullable=False),
|
|
33
|
+
sa.Column("indexed_at", sa.DateTime(), nullable=False),
|
|
34
|
+
sa.UniqueConstraint("workspace_id", "space_id"),
|
|
35
|
+
)
|
|
36
|
+
op.create_index("ix_spaces_workspace_id", "spaces", ["workspace_id"])
|
|
37
|
+
op.create_table(
|
|
38
|
+
"domains",
|
|
39
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
40
|
+
workspace_column(),
|
|
41
|
+
sa.Column("domain_id", sa.String(128), nullable=False),
|
|
42
|
+
sa.Column("space_id", sa.String(128), nullable=False),
|
|
43
|
+
sa.Column("parent_domain_id", sa.String(128)),
|
|
44
|
+
sa.Column("project_id", sa.String(63)),
|
|
45
|
+
sa.Column("name", sa.String(160), nullable=False),
|
|
46
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
47
|
+
sa.Column("domain_type", sa.String(64), nullable=False),
|
|
48
|
+
sa.Column("governance", sa.String(64), nullable=False),
|
|
49
|
+
sa.Column("moc", sa.Text(), nullable=False),
|
|
50
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
51
|
+
sa.Column("source_hash", sa.String(64), nullable=False),
|
|
52
|
+
sa.Column("indexed_at", sa.DateTime(), nullable=False),
|
|
53
|
+
sa.UniqueConstraint("workspace_id", "domain_id"),
|
|
54
|
+
)
|
|
55
|
+
op.create_index("ix_domains_workspace_id", "domains", ["workspace_id"])
|
|
56
|
+
op.create_index("ix_domains_space_id", "domains", ["space_id"])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def downgrade() -> None:
|
|
60
|
+
op.drop_table("domains")
|
|
61
|
+
op.drop_table("spaces")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Add Workspace folder adoption batches."""
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
from alembic import op
|
|
6
|
+
|
|
7
|
+
revision = "007"
|
|
8
|
+
down_revision = "006"
|
|
9
|
+
branch_labels = None
|
|
10
|
+
depends_on = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def upgrade() -> None:
|
|
14
|
+
op.create_table(
|
|
15
|
+
"adoption_batches",
|
|
16
|
+
sa.Column("id", sa.Integer(), primary_key=True),
|
|
17
|
+
sa.Column(
|
|
18
|
+
"workspace_id",
|
|
19
|
+
sa.String(63),
|
|
20
|
+
sa.ForeignKey("workspaces.id", ondelete="CASCADE"),
|
|
21
|
+
nullable=False,
|
|
22
|
+
),
|
|
23
|
+
sa.Column("batch_name", sa.String(128), nullable=False),
|
|
24
|
+
sa.Column("source_path", sa.Text(), nullable=False),
|
|
25
|
+
sa.Column("source_kind", sa.String(16), nullable=False),
|
|
26
|
+
sa.Column("staging_path", sa.Text()),
|
|
27
|
+
sa.Column("status", sa.String(24), nullable=False),
|
|
28
|
+
sa.Column("inventory_json", sa.Text(), nullable=False),
|
|
29
|
+
sa.Column("plan_json", sa.Text()),
|
|
30
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
31
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
32
|
+
sa.UniqueConstraint("workspace_id", "batch_name"),
|
|
33
|
+
)
|
|
34
|
+
op.create_index("ix_adoption_batches_workspace_id", "adoption_batches", ["workspace_id"])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def downgrade() -> None:
|
|
38
|
+
op.drop_table("adoption_batches")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Campfire database revisions."""
|
campfire_cli/app/SPEC.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# App SPEC
|
|
2
|
+
|
|
3
|
+
业务模块采用 `CLI → Service → Protocol ← Implementation` 单向依赖。CLI 只解析参数、选择输出格式并翻译异常;Service 实现用例;Protocol 由消费方定义;Repository 负责 SQLite 或文件持久化,不作语义判断。
|
|
4
|
+
|
|
5
|
+
当前模块:
|
|
6
|
+
|
|
7
|
+
- `document/`:文档自身、文档规则以及面向用户和 Agent 的文档命令。
|
|
8
|
+
- `decision/`:需要人类或高级 Agent 回答的持久判断、状态机、事件历史和只读 Vault 投影。
|
|
9
|
+
- `maintenance/`:新增与变更文档的持续维护,归档属于其用例。
|
|
10
|
+
- `workspace/`:注册、初始化、解析和选择多个 Workspace,并通过扁平的 Restructure Service 执行一次性、大规模、批次化结构重构。
|
|
11
|
+
- `skill/`:Campfire SOP Skill 的发现、加载、校验和同步。
|
|
12
|
+
- `base/`:Obsidian Base 标准治理视图。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Campfire business modules."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Obsidian Base management application module."""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
base_cli = typer.Typer(
|
|
8
|
+
help="管理 Obsidian Base 标准治理视图", context_settings={"help_option_names": ["-h", "--help"]}
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def emit(result: object) -> None:
|
|
13
|
+
typer.echo(json.dumps(result.model_dump(mode="json"), ensure_ascii=False, indent=2))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@base_cli.command("list")
|
|
17
|
+
def list_bases(ctx: typer.Context) -> None:
|
|
18
|
+
"""列出包内 SSOT Base 及 Vault 同步状态。"""
|
|
19
|
+
emit(ctx.obj.base.list())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@base_cli.command("show")
|
|
23
|
+
def show(ctx: typer.Context, name: str, source: str = typer.Option("ssot", "--source")) -> None:
|
|
24
|
+
"""读取一个 Base;source 可选 ssot 或 vault。"""
|
|
25
|
+
emit(ctx.obj.base.show(name, source))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@base_cli.command("check")
|
|
29
|
+
def check(ctx: typer.Context) -> None:
|
|
30
|
+
"""只读检查 Base 配置、YAML 和同步状态。"""
|
|
31
|
+
emit(ctx.obj.base.check())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@base_cli.command("sync")
|
|
35
|
+
def sync(ctx: typer.Context, dry_run: bool = typer.Option(False, "--dry-run")) -> None:
|
|
36
|
+
"""将托管 Base 从包内 SSOT 确定性同步到 Vault。"""
|
|
37
|
+
emit(ctx.obj.base.sync(dry_run))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from campfire_cli.common.filesystem import atomic_write
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseRepository:
|
|
10
|
+
def source_root(self) -> Path:
|
|
11
|
+
return Path(str(files("campfire_cli.resources") / "bases"))
|
|
12
|
+
|
|
13
|
+
def read(self, path: Path) -> str:
|
|
14
|
+
return path.read_text(encoding="utf-8")
|
|
15
|
+
|
|
16
|
+
def write(self, path: Path, content: str) -> None:
|
|
17
|
+
atomic_write(path, content)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseInfo(BaseModel):
|
|
7
|
+
name: str
|
|
8
|
+
path: str
|
|
9
|
+
status: str
|
|
10
|
+
views: list[str] = Field(default_factory=list)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseResult(BaseModel):
|
|
14
|
+
status: str
|
|
15
|
+
bases: list[BaseInfo] = Field(default_factory=list)
|
|
16
|
+
operations: list[dict[str, str]] = Field(default_factory=list)
|
|
17
|
+
issues: list[dict[str, str]] = Field(default_factory=list)
|
|
18
|
+
content: str | None = None
|