raise-cli 2.2.1__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.
- raise_cli/__init__.py +38 -0
- raise_cli/__main__.py +30 -0
- raise_cli/adapters/__init__.py +91 -0
- raise_cli/adapters/declarative/__init__.py +26 -0
- raise_cli/adapters/declarative/adapter.py +267 -0
- raise_cli/adapters/declarative/discovery.py +94 -0
- raise_cli/adapters/declarative/expressions.py +150 -0
- raise_cli/adapters/declarative/reference/__init__.py +1 -0
- raise_cli/adapters/declarative/reference/github.yaml +143 -0
- raise_cli/adapters/declarative/schema.py +98 -0
- raise_cli/adapters/filesystem.py +299 -0
- raise_cli/adapters/mcp_bridge.py +10 -0
- raise_cli/adapters/mcp_confluence.py +246 -0
- raise_cli/adapters/mcp_jira.py +405 -0
- raise_cli/adapters/models.py +205 -0
- raise_cli/adapters/protocols.py +180 -0
- raise_cli/adapters/registry.py +90 -0
- raise_cli/adapters/sync.py +149 -0
- raise_cli/agents/__init__.py +14 -0
- raise_cli/agents/antigravity.yaml +8 -0
- raise_cli/agents/claude.yaml +8 -0
- raise_cli/agents/copilot.yaml +8 -0
- raise_cli/agents/copilot_plugin.py +124 -0
- raise_cli/agents/cursor.yaml +7 -0
- raise_cli/agents/roo.yaml +8 -0
- raise_cli/agents/windsurf.yaml +8 -0
- raise_cli/artifacts/__init__.py +30 -0
- raise_cli/artifacts/models.py +43 -0
- raise_cli/artifacts/reader.py +55 -0
- raise_cli/artifacts/renderer.py +104 -0
- raise_cli/artifacts/story_design.py +69 -0
- raise_cli/artifacts/writer.py +45 -0
- raise_cli/backlog/__init__.py +1 -0
- raise_cli/backlog/sync.py +115 -0
- raise_cli/cli/__init__.py +3 -0
- raise_cli/cli/commands/__init__.py +3 -0
- raise_cli/cli/commands/_resolve.py +153 -0
- raise_cli/cli/commands/adapters.py +362 -0
- raise_cli/cli/commands/artifact.py +137 -0
- raise_cli/cli/commands/backlog.py +333 -0
- raise_cli/cli/commands/base.py +31 -0
- raise_cli/cli/commands/discover.py +551 -0
- raise_cli/cli/commands/docs.py +130 -0
- raise_cli/cli/commands/doctor.py +177 -0
- raise_cli/cli/commands/gate.py +223 -0
- raise_cli/cli/commands/graph.py +1086 -0
- raise_cli/cli/commands/info.py +81 -0
- raise_cli/cli/commands/init.py +746 -0
- raise_cli/cli/commands/journal.py +167 -0
- raise_cli/cli/commands/mcp.py +524 -0
- raise_cli/cli/commands/memory.py +467 -0
- raise_cli/cli/commands/pattern.py +348 -0
- raise_cli/cli/commands/profile.py +59 -0
- raise_cli/cli/commands/publish.py +80 -0
- raise_cli/cli/commands/release.py +338 -0
- raise_cli/cli/commands/session.py +528 -0
- raise_cli/cli/commands/signal.py +410 -0
- raise_cli/cli/commands/skill.py +350 -0
- raise_cli/cli/commands/skill_set.py +145 -0
- raise_cli/cli/error_handler.py +158 -0
- raise_cli/cli/main.py +163 -0
- raise_cli/compat.py +66 -0
- raise_cli/config/__init__.py +41 -0
- raise_cli/config/agent_plugin.py +105 -0
- raise_cli/config/agent_registry.py +233 -0
- raise_cli/config/agents.py +120 -0
- raise_cli/config/ide.py +32 -0
- raise_cli/config/paths.py +379 -0
- raise_cli/config/settings.py +180 -0
- raise_cli/context/__init__.py +42 -0
- raise_cli/context/analyzers/__init__.py +16 -0
- raise_cli/context/analyzers/models.py +36 -0
- raise_cli/context/analyzers/protocol.py +43 -0
- raise_cli/context/analyzers/python.py +292 -0
- raise_cli/context/builder.py +1569 -0
- raise_cli/context/diff.py +213 -0
- raise_cli/context/extractors/__init__.py +13 -0
- raise_cli/context/extractors/skills.py +121 -0
- raise_cli/core/__init__.py +37 -0
- raise_cli/core/files.py +66 -0
- raise_cli/core/text.py +174 -0
- raise_cli/core/tools.py +441 -0
- raise_cli/discovery/__init__.py +50 -0
- raise_cli/discovery/analyzer.py +691 -0
- raise_cli/discovery/drift.py +355 -0
- raise_cli/discovery/scanner.py +1687 -0
- raise_cli/doctor/__init__.py +4 -0
- raise_cli/doctor/checks/__init__.py +1 -0
- raise_cli/doctor/checks/environment.py +110 -0
- raise_cli/doctor/checks/project.py +238 -0
- raise_cli/doctor/fix.py +80 -0
- raise_cli/doctor/models.py +56 -0
- raise_cli/doctor/protocol.py +43 -0
- raise_cli/doctor/registry.py +100 -0
- raise_cli/doctor/report.py +141 -0
- raise_cli/doctor/runner.py +95 -0
- raise_cli/engines/__init__.py +3 -0
- raise_cli/exceptions.py +215 -0
- raise_cli/gates/__init__.py +19 -0
- raise_cli/gates/builtin/__init__.py +1 -0
- raise_cli/gates/builtin/coverage.py +52 -0
- raise_cli/gates/builtin/lint.py +48 -0
- raise_cli/gates/builtin/tests.py +48 -0
- raise_cli/gates/builtin/types.py +48 -0
- raise_cli/gates/models.py +40 -0
- raise_cli/gates/protocol.py +41 -0
- raise_cli/gates/registry.py +141 -0
- raise_cli/governance/__init__.py +11 -0
- raise_cli/governance/extractor.py +412 -0
- raise_cli/governance/models.py +134 -0
- raise_cli/governance/parsers/__init__.py +35 -0
- raise_cli/governance/parsers/_convert.py +38 -0
- raise_cli/governance/parsers/adr.py +274 -0
- raise_cli/governance/parsers/backlog.py +356 -0
- raise_cli/governance/parsers/constitution.py +119 -0
- raise_cli/governance/parsers/epic.py +323 -0
- raise_cli/governance/parsers/glossary.py +316 -0
- raise_cli/governance/parsers/guardrails.py +345 -0
- raise_cli/governance/parsers/prd.py +112 -0
- raise_cli/governance/parsers/roadmap.py +118 -0
- raise_cli/governance/parsers/vision.py +116 -0
- raise_cli/graph/__init__.py +1 -0
- raise_cli/graph/backends/__init__.py +57 -0
- raise_cli/graph/backends/api.py +137 -0
- raise_cli/graph/backends/dual.py +139 -0
- raise_cli/graph/backends/pending.py +84 -0
- raise_cli/handlers/__init__.py +3 -0
- raise_cli/hooks/__init__.py +54 -0
- raise_cli/hooks/builtin/__init__.py +1 -0
- raise_cli/hooks/builtin/backlog.py +216 -0
- raise_cli/hooks/builtin/gate_bridge.py +83 -0
- raise_cli/hooks/builtin/jira_sync.py +127 -0
- raise_cli/hooks/builtin/memory.py +117 -0
- raise_cli/hooks/builtin/telemetry.py +72 -0
- raise_cli/hooks/emitter.py +184 -0
- raise_cli/hooks/events.py +262 -0
- raise_cli/hooks/protocol.py +38 -0
- raise_cli/hooks/registry.py +117 -0
- raise_cli/mcp/__init__.py +33 -0
- raise_cli/mcp/bridge.py +218 -0
- raise_cli/mcp/models.py +43 -0
- raise_cli/mcp/registry.py +77 -0
- raise_cli/mcp/schema.py +41 -0
- raise_cli/memory/__init__.py +58 -0
- raise_cli/memory/loader.py +247 -0
- raise_cli/memory/migration.py +241 -0
- raise_cli/memory/models.py +169 -0
- raise_cli/memory/writer.py +598 -0
- raise_cli/onboarding/__init__.py +103 -0
- raise_cli/onboarding/bootstrap.py +324 -0
- raise_cli/onboarding/claudemd.py +17 -0
- raise_cli/onboarding/conventions.py +742 -0
- raise_cli/onboarding/detection.py +374 -0
- raise_cli/onboarding/governance.py +443 -0
- raise_cli/onboarding/instructions.py +672 -0
- raise_cli/onboarding/manifest.py +201 -0
- raise_cli/onboarding/memory_md.py +399 -0
- raise_cli/onboarding/migration.py +207 -0
- raise_cli/onboarding/profile.py +624 -0
- raise_cli/onboarding/skill_conflict.py +100 -0
- raise_cli/onboarding/skill_manifest.py +176 -0
- raise_cli/onboarding/skills.py +437 -0
- raise_cli/onboarding/workflows.py +101 -0
- raise_cli/output/__init__.py +28 -0
- raise_cli/output/console.py +394 -0
- raise_cli/output/formatters/__init__.py +9 -0
- raise_cli/output/formatters/adapters.py +135 -0
- raise_cli/output/formatters/discover.py +439 -0
- raise_cli/output/formatters/skill.py +298 -0
- raise_cli/publish/__init__.py +3 -0
- raise_cli/publish/changelog.py +80 -0
- raise_cli/publish/check.py +179 -0
- raise_cli/publish/version.py +172 -0
- raise_cli/rai_base/__init__.py +22 -0
- raise_cli/rai_base/framework/__init__.py +7 -0
- raise_cli/rai_base/framework/methodology.yaml +233 -0
- raise_cli/rai_base/governance/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/domain-model.md +20 -0
- raise_cli/rai_base/governance/architecture/system-context.md +34 -0
- raise_cli/rai_base/governance/architecture/system-design.md +24 -0
- raise_cli/rai_base/governance/backlog.md +8 -0
- raise_cli/rai_base/governance/guardrails.md +17 -0
- raise_cli/rai_base/governance/prd.md +25 -0
- raise_cli/rai_base/governance/vision.md +16 -0
- raise_cli/rai_base/identity/__init__.py +8 -0
- raise_cli/rai_base/identity/core.md +119 -0
- raise_cli/rai_base/identity/perspective.md +119 -0
- raise_cli/rai_base/memory/__init__.py +7 -0
- raise_cli/rai_base/memory/patterns-base.jsonl +55 -0
- raise_cli/schemas/__init__.py +3 -0
- raise_cli/schemas/journal.py +49 -0
- raise_cli/schemas/session_state.py +117 -0
- raise_cli/session/__init__.py +5 -0
- raise_cli/session/bundle.py +820 -0
- raise_cli/session/close.py +268 -0
- raise_cli/session/journal.py +119 -0
- raise_cli/session/resolver.py +126 -0
- raise_cli/session/state.py +187 -0
- raise_cli/skills/__init__.py +44 -0
- raise_cli/skills/locator.py +141 -0
- raise_cli/skills/name_checker.py +199 -0
- raise_cli/skills/parser.py +145 -0
- raise_cli/skills/scaffold.py +212 -0
- raise_cli/skills/schema.py +132 -0
- raise_cli/skills/skillsets.py +195 -0
- raise_cli/skills/validator.py +197 -0
- raise_cli/skills_base/__init__.py +80 -0
- raise_cli/skills_base/contract-template.md +60 -0
- raise_cli/skills_base/preamble.md +37 -0
- raise_cli/skills_base/rai-architecture-review/SKILL.md +137 -0
- raise_cli/skills_base/rai-debug/SKILL.md +171 -0
- raise_cli/skills_base/rai-discover/SKILL.md +167 -0
- raise_cli/skills_base/rai-discover-document/SKILL.md +128 -0
- raise_cli/skills_base/rai-discover-scan/SKILL.md +147 -0
- raise_cli/skills_base/rai-discover-start/SKILL.md +145 -0
- raise_cli/skills_base/rai-discover-validate/SKILL.md +142 -0
- raise_cli/skills_base/rai-docs-update/SKILL.md +142 -0
- raise_cli/skills_base/rai-doctor/SKILL.md +120 -0
- raise_cli/skills_base/rai-epic-close/SKILL.md +165 -0
- raise_cli/skills_base/rai-epic-close/templates/retrospective.md +68 -0
- raise_cli/skills_base/rai-epic-design/SKILL.md +146 -0
- raise_cli/skills_base/rai-epic-design/templates/design.md +24 -0
- raise_cli/skills_base/rai-epic-design/templates/scope.md +76 -0
- raise_cli/skills_base/rai-epic-plan/SKILL.md +153 -0
- raise_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
- raise_cli/skills_base/rai-epic-plan/templates/plan-section.md +49 -0
- raise_cli/skills_base/rai-epic-run/SKILL.md +208 -0
- raise_cli/skills_base/rai-epic-start/SKILL.md +136 -0
- raise_cli/skills_base/rai-epic-start/templates/brief.md +34 -0
- raise_cli/skills_base/rai-mcp-add/SKILL.md +176 -0
- raise_cli/skills_base/rai-mcp-remove/SKILL.md +120 -0
- raise_cli/skills_base/rai-mcp-status/SKILL.md +147 -0
- raise_cli/skills_base/rai-problem-shape/SKILL.md +138 -0
- raise_cli/skills_base/rai-project-create/SKILL.md +144 -0
- raise_cli/skills_base/rai-project-onboard/SKILL.md +162 -0
- raise_cli/skills_base/rai-quality-review/SKILL.md +189 -0
- raise_cli/skills_base/rai-research/SKILL.md +143 -0
- raise_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
- raise_cli/skills_base/rai-session-close/SKILL.md +176 -0
- raise_cli/skills_base/rai-session-start/SKILL.md +110 -0
- raise_cli/skills_base/rai-story-close/SKILL.md +198 -0
- raise_cli/skills_base/rai-story-design/SKILL.md +203 -0
- raise_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
- raise_cli/skills_base/rai-story-implement/SKILL.md +115 -0
- raise_cli/skills_base/rai-story-plan/SKILL.md +135 -0
- raise_cli/skills_base/rai-story-review/SKILL.md +178 -0
- raise_cli/skills_base/rai-story-run/SKILL.md +282 -0
- raise_cli/skills_base/rai-story-start/SKILL.md +166 -0
- raise_cli/skills_base/rai-story-start/templates/story.md +38 -0
- raise_cli/skills_base/rai-welcome/SKILL.md +134 -0
- raise_cli/telemetry/__init__.py +42 -0
- raise_cli/telemetry/schemas.py +285 -0
- raise_cli/telemetry/writer.py +217 -0
- raise_cli/tier/__init__.py +0 -0
- raise_cli/tier/context.py +134 -0
- raise_cli/viz/__init__.py +7 -0
- raise_cli/viz/generator.py +406 -0
- raise_cli-2.2.1.dist-info/METADATA +433 -0
- raise_cli-2.2.1.dist-info/RECORD +264 -0
- raise_cli-2.2.1.dist-info/WHEEL +4 -0
- raise_cli-2.2.1.dist-info/entry_points.txt +40 -0
- raise_cli-2.2.1.dist-info/licenses/LICENSE +190 -0
- raise_cli-2.2.1.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Migration module for multi-developer memory architecture.
|
|
2
|
+
|
|
3
|
+
Migrates session, telemetry, and calibration data from project directory
|
|
4
|
+
to personal directory for proper data separation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import shutil
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class MigrationResult:
|
|
17
|
+
"""Result of a migration operation.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
success: Whether migration completed without errors.
|
|
21
|
+
sessions_migrated: Number of session entries migrated.
|
|
22
|
+
telemetry_migrated: Number of telemetry entries migrated.
|
|
23
|
+
calibration_migrated: Number of calibration entries migrated.
|
|
24
|
+
message: Status message describing the migration.
|
|
25
|
+
dry_run: Whether this was a dry run (no actual changes).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
success: bool
|
|
29
|
+
sessions_migrated: int = 0
|
|
30
|
+
telemetry_migrated: int = 0
|
|
31
|
+
calibration_migrated: int = 0
|
|
32
|
+
message: str = ""
|
|
33
|
+
dry_run: bool = False
|
|
34
|
+
skipped_items: list[str] = field(default_factory=lambda: [])
|
|
35
|
+
|
|
36
|
+
def summary(self) -> str:
|
|
37
|
+
"""Generate human-readable summary of migration."""
|
|
38
|
+
prefix = "Would migrate" if self.dry_run else "Migrated"
|
|
39
|
+
|
|
40
|
+
parts: list[str] = []
|
|
41
|
+
if self.sessions_migrated > 0:
|
|
42
|
+
parts.append(f"{self.sessions_migrated} sessions")
|
|
43
|
+
if self.telemetry_migrated > 0:
|
|
44
|
+
parts.append(f"{self.telemetry_migrated} telemetry")
|
|
45
|
+
if self.calibration_migrated > 0:
|
|
46
|
+
parts.append(f"{self.calibration_migrated} calibration")
|
|
47
|
+
|
|
48
|
+
if not parts:
|
|
49
|
+
return "Nothing to migrate"
|
|
50
|
+
|
|
51
|
+
summary = f"{prefix}: {', '.join(parts)}"
|
|
52
|
+
if self.dry_run:
|
|
53
|
+
summary += " (dry run)"
|
|
54
|
+
return summary
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _count_jsonl_entries(file_path: Path) -> int:
|
|
58
|
+
"""Count the number of valid JSON entries in a JSONL file."""
|
|
59
|
+
if not file_path.exists():
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
count = 0
|
|
63
|
+
with file_path.open("r", encoding="utf-8") as f:
|
|
64
|
+
for line in f:
|
|
65
|
+
line = line.strip()
|
|
66
|
+
if line:
|
|
67
|
+
try:
|
|
68
|
+
json.loads(line)
|
|
69
|
+
count += 1
|
|
70
|
+
except json.JSONDecodeError:
|
|
71
|
+
pass
|
|
72
|
+
return count
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _has_content(file_path: Path) -> bool:
|
|
76
|
+
"""Check if a JSONL file has any valid entries."""
|
|
77
|
+
return _count_jsonl_entries(file_path) > 0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def needs_migration(
|
|
81
|
+
memory_dir: Path,
|
|
82
|
+
personal_dir: Path | None = None,
|
|
83
|
+
rai_dir: Path | None = None,
|
|
84
|
+
) -> bool:
|
|
85
|
+
"""Check if migration from project to personal is needed.
|
|
86
|
+
|
|
87
|
+
Migration is needed when:
|
|
88
|
+
- Project has sessions in memory/sessions/ AND personal doesn't have sessions
|
|
89
|
+
- Project has telemetry in rai/telemetry/ AND personal doesn't have telemetry
|
|
90
|
+
- Project has calibration in memory/calibration.jsonl AND personal doesn't
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
memory_dir: Path to .raise/rai/memory/ directory.
|
|
94
|
+
personal_dir: Path to .raise/rai/personal/ directory.
|
|
95
|
+
If None, derived from memory_dir.
|
|
96
|
+
rai_dir: Path to .raise/rai/ directory (for telemetry).
|
|
97
|
+
If None, derived from memory_dir.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
True if migration is needed, False otherwise.
|
|
101
|
+
"""
|
|
102
|
+
if personal_dir is None:
|
|
103
|
+
personal_dir = memory_dir.parent / "personal"
|
|
104
|
+
|
|
105
|
+
if rai_dir is None:
|
|
106
|
+
rai_dir = memory_dir.parent
|
|
107
|
+
|
|
108
|
+
# Check for sessions
|
|
109
|
+
project_sessions = memory_dir / "sessions" / "index.jsonl"
|
|
110
|
+
personal_sessions = personal_dir / "sessions" / "index.jsonl"
|
|
111
|
+
if _has_content(project_sessions) and not _has_content(personal_sessions):
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
# Check for telemetry
|
|
115
|
+
project_telemetry = rai_dir / "telemetry" / "signals.jsonl"
|
|
116
|
+
personal_telemetry = personal_dir / "telemetry" / "signals.jsonl"
|
|
117
|
+
if _has_content(project_telemetry) and not _has_content(personal_telemetry):
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
# Check for calibration
|
|
121
|
+
project_calibration = memory_dir / "calibration.jsonl"
|
|
122
|
+
personal_calibration = personal_dir / "calibration.jsonl"
|
|
123
|
+
return _has_content(project_calibration) and not _has_content(personal_calibration)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _migrate_file(
|
|
127
|
+
source: Path,
|
|
128
|
+
dest: Path,
|
|
129
|
+
dry_run: bool = False,
|
|
130
|
+
) -> int:
|
|
131
|
+
"""Migrate a single file from source to destination.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
source: Source file path.
|
|
135
|
+
dest: Destination file path.
|
|
136
|
+
dry_run: If True, don't actually move files.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Number of entries migrated.
|
|
140
|
+
"""
|
|
141
|
+
if not source.exists():
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
count = _count_jsonl_entries(source)
|
|
145
|
+
if count == 0:
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
if dry_run:
|
|
149
|
+
return count
|
|
150
|
+
|
|
151
|
+
# Create destination directory
|
|
152
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
|
|
154
|
+
# Copy file to destination
|
|
155
|
+
shutil.copy2(source, dest)
|
|
156
|
+
|
|
157
|
+
# Rename source to backup
|
|
158
|
+
backup_path = source.with_suffix(source.suffix + ".backup")
|
|
159
|
+
source.rename(backup_path)
|
|
160
|
+
|
|
161
|
+
return count
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def migrate_to_personal(
|
|
165
|
+
memory_dir: Path,
|
|
166
|
+
personal_dir: Path,
|
|
167
|
+
rai_dir: Path | None = None,
|
|
168
|
+
dry_run: bool = False,
|
|
169
|
+
) -> MigrationResult:
|
|
170
|
+
"""Migrate session, telemetry, and calibration data to personal directory.
|
|
171
|
+
|
|
172
|
+
Copies data from project locations to personal directory, then renames
|
|
173
|
+
originals to .backup files. Does not overwrite existing personal data.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
memory_dir: Path to .raise/rai/memory/ directory.
|
|
177
|
+
personal_dir: Path to .raise/rai/personal/ directory.
|
|
178
|
+
rai_dir: Path to .raise/rai/ directory (for telemetry).
|
|
179
|
+
If None, derived from memory_dir.
|
|
180
|
+
dry_run: If True, report what would be migrated without making changes.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
MigrationResult with counts and status.
|
|
184
|
+
"""
|
|
185
|
+
if rai_dir is None:
|
|
186
|
+
rai_dir = memory_dir.parent
|
|
187
|
+
|
|
188
|
+
result = MigrationResult(success=True, dry_run=dry_run)
|
|
189
|
+
skipped: list[str] = []
|
|
190
|
+
|
|
191
|
+
# Migrate sessions
|
|
192
|
+
project_sessions = memory_dir / "sessions" / "index.jsonl"
|
|
193
|
+
personal_sessions = personal_dir / "sessions" / "index.jsonl"
|
|
194
|
+
if _has_content(personal_sessions):
|
|
195
|
+
if _has_content(project_sessions):
|
|
196
|
+
skipped.append("sessions (personal already exists)")
|
|
197
|
+
else:
|
|
198
|
+
result.sessions_migrated = _migrate_file(
|
|
199
|
+
project_sessions, personal_sessions, dry_run
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Migrate telemetry
|
|
203
|
+
project_telemetry = rai_dir / "telemetry" / "signals.jsonl"
|
|
204
|
+
personal_telemetry = personal_dir / "telemetry" / "signals.jsonl"
|
|
205
|
+
if _has_content(personal_telemetry):
|
|
206
|
+
if _has_content(project_telemetry):
|
|
207
|
+
skipped.append("telemetry (personal already exists)")
|
|
208
|
+
else:
|
|
209
|
+
result.telemetry_migrated = _migrate_file(
|
|
210
|
+
project_telemetry, personal_telemetry, dry_run
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Migrate calibration
|
|
214
|
+
project_calibration = memory_dir / "calibration.jsonl"
|
|
215
|
+
personal_calibration = personal_dir / "calibration.jsonl"
|
|
216
|
+
if _has_content(personal_calibration):
|
|
217
|
+
if _has_content(project_calibration):
|
|
218
|
+
skipped.append("calibration (personal already exists)")
|
|
219
|
+
else:
|
|
220
|
+
result.calibration_migrated = _migrate_file(
|
|
221
|
+
project_calibration, personal_calibration, dry_run
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Build message
|
|
225
|
+
total = (
|
|
226
|
+
result.sessions_migrated
|
|
227
|
+
+ result.telemetry_migrated
|
|
228
|
+
+ result.calibration_migrated
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if total == 0 and not skipped:
|
|
232
|
+
result.message = "Nothing to migrate"
|
|
233
|
+
elif total == 0 and skipped:
|
|
234
|
+
result.message = f"Skipped: {', '.join(skipped)}"
|
|
235
|
+
else:
|
|
236
|
+
result.message = result.summary()
|
|
237
|
+
if skipped:
|
|
238
|
+
result.message += f" (skipped: {', '.join(skipped)})"
|
|
239
|
+
|
|
240
|
+
result.skipped_items = skipped
|
|
241
|
+
return result
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Pydantic models for memory concepts.
|
|
2
|
+
|
|
3
|
+
This module defines the core data structures for representing Rai's
|
|
4
|
+
memories loaded from JSONL files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from datetime import date
|
|
10
|
+
from enum import StrEnum
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MemoryScope(StrEnum):
|
|
17
|
+
"""Scope/tier for memory data.
|
|
18
|
+
|
|
19
|
+
Determines where memory data is stored and its visibility:
|
|
20
|
+
- GLOBAL: ~/.rai/ - Cross-repo, universal patterns
|
|
21
|
+
- PROJECT: .raise/rai/memory/ - Shared, committed to repo
|
|
22
|
+
- PERSONAL: .raise/rai/personal/ - Developer-specific, gitignored
|
|
23
|
+
|
|
24
|
+
Precedence for same-ID concepts: PERSONAL > PROJECT > GLOBAL
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
GLOBAL = "global"
|
|
28
|
+
PROJECT = "project"
|
|
29
|
+
PERSONAL = "personal"
|
|
30
|
+
|
|
31
|
+
def __str__(self) -> str:
|
|
32
|
+
"""Return value for string interpolation."""
|
|
33
|
+
return self.value
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MemoryConceptType(StrEnum):
|
|
37
|
+
"""Types of memory concepts.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
PATTERN: Learned patterns (from patterns.jsonl).
|
|
41
|
+
CALIBRATION: Velocity/estimation data (from calibration.jsonl).
|
|
42
|
+
SESSION: Session records (from sessions/index.jsonl).
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
PATTERN = "pattern"
|
|
46
|
+
CALIBRATION = "calibration"
|
|
47
|
+
SESSION = "session"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PatternSubType(StrEnum):
|
|
51
|
+
"""Sub-types for pattern concepts.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
CODEBASE: Code-level patterns.
|
|
55
|
+
PROCESS: Process/workflow patterns.
|
|
56
|
+
ARCHITECTURE: Architectural patterns.
|
|
57
|
+
TECHNICAL: Technical discoveries.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
CODEBASE = "codebase"
|
|
61
|
+
PROCESS = "process"
|
|
62
|
+
ARCHITECTURE = "architecture"
|
|
63
|
+
TECHNICAL = "technical"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MemoryRelationshipType(StrEnum):
|
|
67
|
+
"""Types of relationships between memory concepts.
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
LEARNED_FROM: Pattern/calibration was learned from a session.
|
|
71
|
+
RELATED_TO: Concepts share context keywords.
|
|
72
|
+
VALIDATES: Calibration data validates a pattern.
|
|
73
|
+
APPLIES_TO: Pattern applies to a context domain.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
LEARNED_FROM = "learned_from"
|
|
77
|
+
RELATED_TO = "related_to"
|
|
78
|
+
VALIDATES = "validates"
|
|
79
|
+
APPLIES_TO = "applies_to"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MemoryConcept(BaseModel):
|
|
83
|
+
"""A memory concept extracted from JSONL files.
|
|
84
|
+
|
|
85
|
+
Represents a single unit of Rai's memory (pattern, calibration record,
|
|
86
|
+
or session record) with its metadata.
|
|
87
|
+
|
|
88
|
+
Attributes:
|
|
89
|
+
id: Unique identifier (e.g., 'PAT-001', 'CAL-001', 'SES-001').
|
|
90
|
+
type: Type of memory concept.
|
|
91
|
+
content: Main content or summary.
|
|
92
|
+
context: Context keywords for retrieval.
|
|
93
|
+
created: Date when memory was created.
|
|
94
|
+
metadata: Type-specific metadata.
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
>>> concept = MemoryConcept(
|
|
98
|
+
... id="PAT-001",
|
|
99
|
+
... type=MemoryConceptType.PATTERN,
|
|
100
|
+
... content="Singleton with get/set/configure",
|
|
101
|
+
... context=["testing", "module-design"],
|
|
102
|
+
... created=date(2026, 1, 31),
|
|
103
|
+
... metadata={"sub_type": "codebase", "learned_from": "F1.4"}
|
|
104
|
+
... )
|
|
105
|
+
>>> concept.id
|
|
106
|
+
'PAT-001'
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
id: str = Field(..., description="Unique identifier (e.g., 'PAT-001')")
|
|
110
|
+
type: MemoryConceptType = Field(..., description="Memory concept type")
|
|
111
|
+
content: str = Field(..., description="Main content or summary")
|
|
112
|
+
context: list[str] = Field(default_factory=list, description="Context keywords")
|
|
113
|
+
created: date = Field(..., description="Date when memory was created")
|
|
114
|
+
metadata: dict[str, Any] = Field(
|
|
115
|
+
default_factory=dict, description="Type-specific metadata"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def token_estimate(self) -> int:
|
|
120
|
+
"""Estimate tokens for this concept.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Estimated token count (content length // 4).
|
|
124
|
+
"""
|
|
125
|
+
return len(self.content) // 4
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class MemoryRelationship(BaseModel):
|
|
129
|
+
"""A relationship between two memory concepts.
|
|
130
|
+
|
|
131
|
+
Attributes:
|
|
132
|
+
source: Source concept ID.
|
|
133
|
+
target: Target concept ID.
|
|
134
|
+
type: Relationship type.
|
|
135
|
+
metadata: Additional relationship metadata.
|
|
136
|
+
|
|
137
|
+
Examples:
|
|
138
|
+
>>> rel = MemoryRelationship(
|
|
139
|
+
... source="PAT-001",
|
|
140
|
+
... target="SES-001",
|
|
141
|
+
... type=MemoryRelationshipType.LEARNED_FROM,
|
|
142
|
+
... metadata={"confidence": 1.0}
|
|
143
|
+
... )
|
|
144
|
+
>>> rel.type
|
|
145
|
+
<MemoryRelationshipType.LEARNED_FROM: 'learned_from'>
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
source: str = Field(..., description="Source concept ID")
|
|
149
|
+
target: str = Field(..., description="Target concept ID")
|
|
150
|
+
type: MemoryRelationshipType = Field(..., description="Relationship type")
|
|
151
|
+
metadata: dict[str, Any] = Field(
|
|
152
|
+
default_factory=dict, description="Additional metadata"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class MemoryLoadResult(BaseModel):
|
|
157
|
+
"""Result of loading memory from JSONL files.
|
|
158
|
+
|
|
159
|
+
Attributes:
|
|
160
|
+
concepts: List of loaded memory concepts.
|
|
161
|
+
total: Total number of concepts loaded.
|
|
162
|
+
files_processed: Number of files successfully processed.
|
|
163
|
+
errors: List of error messages.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
concepts: list[MemoryConcept] = Field(default_factory=lambda: list[MemoryConcept]())
|
|
167
|
+
total: int = Field(default=0, description="Total concepts loaded")
|
|
168
|
+
files_processed: int = Field(default=0, description="Files processed")
|
|
169
|
+
errors: list[str] = Field(default_factory=lambda: list[str]())
|