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.
Files changed (264) hide show
  1. raise_cli/__init__.py +38 -0
  2. raise_cli/__main__.py +30 -0
  3. raise_cli/adapters/__init__.py +91 -0
  4. raise_cli/adapters/declarative/__init__.py +26 -0
  5. raise_cli/adapters/declarative/adapter.py +267 -0
  6. raise_cli/adapters/declarative/discovery.py +94 -0
  7. raise_cli/adapters/declarative/expressions.py +150 -0
  8. raise_cli/adapters/declarative/reference/__init__.py +1 -0
  9. raise_cli/adapters/declarative/reference/github.yaml +143 -0
  10. raise_cli/adapters/declarative/schema.py +98 -0
  11. raise_cli/adapters/filesystem.py +299 -0
  12. raise_cli/adapters/mcp_bridge.py +10 -0
  13. raise_cli/adapters/mcp_confluence.py +246 -0
  14. raise_cli/adapters/mcp_jira.py +405 -0
  15. raise_cli/adapters/models.py +205 -0
  16. raise_cli/adapters/protocols.py +180 -0
  17. raise_cli/adapters/registry.py +90 -0
  18. raise_cli/adapters/sync.py +149 -0
  19. raise_cli/agents/__init__.py +14 -0
  20. raise_cli/agents/antigravity.yaml +8 -0
  21. raise_cli/agents/claude.yaml +8 -0
  22. raise_cli/agents/copilot.yaml +8 -0
  23. raise_cli/agents/copilot_plugin.py +124 -0
  24. raise_cli/agents/cursor.yaml +7 -0
  25. raise_cli/agents/roo.yaml +8 -0
  26. raise_cli/agents/windsurf.yaml +8 -0
  27. raise_cli/artifacts/__init__.py +30 -0
  28. raise_cli/artifacts/models.py +43 -0
  29. raise_cli/artifacts/reader.py +55 -0
  30. raise_cli/artifacts/renderer.py +104 -0
  31. raise_cli/artifacts/story_design.py +69 -0
  32. raise_cli/artifacts/writer.py +45 -0
  33. raise_cli/backlog/__init__.py +1 -0
  34. raise_cli/backlog/sync.py +115 -0
  35. raise_cli/cli/__init__.py +3 -0
  36. raise_cli/cli/commands/__init__.py +3 -0
  37. raise_cli/cli/commands/_resolve.py +153 -0
  38. raise_cli/cli/commands/adapters.py +362 -0
  39. raise_cli/cli/commands/artifact.py +137 -0
  40. raise_cli/cli/commands/backlog.py +333 -0
  41. raise_cli/cli/commands/base.py +31 -0
  42. raise_cli/cli/commands/discover.py +551 -0
  43. raise_cli/cli/commands/docs.py +130 -0
  44. raise_cli/cli/commands/doctor.py +177 -0
  45. raise_cli/cli/commands/gate.py +223 -0
  46. raise_cli/cli/commands/graph.py +1086 -0
  47. raise_cli/cli/commands/info.py +81 -0
  48. raise_cli/cli/commands/init.py +746 -0
  49. raise_cli/cli/commands/journal.py +167 -0
  50. raise_cli/cli/commands/mcp.py +524 -0
  51. raise_cli/cli/commands/memory.py +467 -0
  52. raise_cli/cli/commands/pattern.py +348 -0
  53. raise_cli/cli/commands/profile.py +59 -0
  54. raise_cli/cli/commands/publish.py +80 -0
  55. raise_cli/cli/commands/release.py +338 -0
  56. raise_cli/cli/commands/session.py +528 -0
  57. raise_cli/cli/commands/signal.py +410 -0
  58. raise_cli/cli/commands/skill.py +350 -0
  59. raise_cli/cli/commands/skill_set.py +145 -0
  60. raise_cli/cli/error_handler.py +158 -0
  61. raise_cli/cli/main.py +163 -0
  62. raise_cli/compat.py +66 -0
  63. raise_cli/config/__init__.py +41 -0
  64. raise_cli/config/agent_plugin.py +105 -0
  65. raise_cli/config/agent_registry.py +233 -0
  66. raise_cli/config/agents.py +120 -0
  67. raise_cli/config/ide.py +32 -0
  68. raise_cli/config/paths.py +379 -0
  69. raise_cli/config/settings.py +180 -0
  70. raise_cli/context/__init__.py +42 -0
  71. raise_cli/context/analyzers/__init__.py +16 -0
  72. raise_cli/context/analyzers/models.py +36 -0
  73. raise_cli/context/analyzers/protocol.py +43 -0
  74. raise_cli/context/analyzers/python.py +292 -0
  75. raise_cli/context/builder.py +1569 -0
  76. raise_cli/context/diff.py +213 -0
  77. raise_cli/context/extractors/__init__.py +13 -0
  78. raise_cli/context/extractors/skills.py +121 -0
  79. raise_cli/core/__init__.py +37 -0
  80. raise_cli/core/files.py +66 -0
  81. raise_cli/core/text.py +174 -0
  82. raise_cli/core/tools.py +441 -0
  83. raise_cli/discovery/__init__.py +50 -0
  84. raise_cli/discovery/analyzer.py +691 -0
  85. raise_cli/discovery/drift.py +355 -0
  86. raise_cli/discovery/scanner.py +1687 -0
  87. raise_cli/doctor/__init__.py +4 -0
  88. raise_cli/doctor/checks/__init__.py +1 -0
  89. raise_cli/doctor/checks/environment.py +110 -0
  90. raise_cli/doctor/checks/project.py +238 -0
  91. raise_cli/doctor/fix.py +80 -0
  92. raise_cli/doctor/models.py +56 -0
  93. raise_cli/doctor/protocol.py +43 -0
  94. raise_cli/doctor/registry.py +100 -0
  95. raise_cli/doctor/report.py +141 -0
  96. raise_cli/doctor/runner.py +95 -0
  97. raise_cli/engines/__init__.py +3 -0
  98. raise_cli/exceptions.py +215 -0
  99. raise_cli/gates/__init__.py +19 -0
  100. raise_cli/gates/builtin/__init__.py +1 -0
  101. raise_cli/gates/builtin/coverage.py +52 -0
  102. raise_cli/gates/builtin/lint.py +48 -0
  103. raise_cli/gates/builtin/tests.py +48 -0
  104. raise_cli/gates/builtin/types.py +48 -0
  105. raise_cli/gates/models.py +40 -0
  106. raise_cli/gates/protocol.py +41 -0
  107. raise_cli/gates/registry.py +141 -0
  108. raise_cli/governance/__init__.py +11 -0
  109. raise_cli/governance/extractor.py +412 -0
  110. raise_cli/governance/models.py +134 -0
  111. raise_cli/governance/parsers/__init__.py +35 -0
  112. raise_cli/governance/parsers/_convert.py +38 -0
  113. raise_cli/governance/parsers/adr.py +274 -0
  114. raise_cli/governance/parsers/backlog.py +356 -0
  115. raise_cli/governance/parsers/constitution.py +119 -0
  116. raise_cli/governance/parsers/epic.py +323 -0
  117. raise_cli/governance/parsers/glossary.py +316 -0
  118. raise_cli/governance/parsers/guardrails.py +345 -0
  119. raise_cli/governance/parsers/prd.py +112 -0
  120. raise_cli/governance/parsers/roadmap.py +118 -0
  121. raise_cli/governance/parsers/vision.py +116 -0
  122. raise_cli/graph/__init__.py +1 -0
  123. raise_cli/graph/backends/__init__.py +57 -0
  124. raise_cli/graph/backends/api.py +137 -0
  125. raise_cli/graph/backends/dual.py +139 -0
  126. raise_cli/graph/backends/pending.py +84 -0
  127. raise_cli/handlers/__init__.py +3 -0
  128. raise_cli/hooks/__init__.py +54 -0
  129. raise_cli/hooks/builtin/__init__.py +1 -0
  130. raise_cli/hooks/builtin/backlog.py +216 -0
  131. raise_cli/hooks/builtin/gate_bridge.py +83 -0
  132. raise_cli/hooks/builtin/jira_sync.py +127 -0
  133. raise_cli/hooks/builtin/memory.py +117 -0
  134. raise_cli/hooks/builtin/telemetry.py +72 -0
  135. raise_cli/hooks/emitter.py +184 -0
  136. raise_cli/hooks/events.py +262 -0
  137. raise_cli/hooks/protocol.py +38 -0
  138. raise_cli/hooks/registry.py +117 -0
  139. raise_cli/mcp/__init__.py +33 -0
  140. raise_cli/mcp/bridge.py +218 -0
  141. raise_cli/mcp/models.py +43 -0
  142. raise_cli/mcp/registry.py +77 -0
  143. raise_cli/mcp/schema.py +41 -0
  144. raise_cli/memory/__init__.py +58 -0
  145. raise_cli/memory/loader.py +247 -0
  146. raise_cli/memory/migration.py +241 -0
  147. raise_cli/memory/models.py +169 -0
  148. raise_cli/memory/writer.py +598 -0
  149. raise_cli/onboarding/__init__.py +103 -0
  150. raise_cli/onboarding/bootstrap.py +324 -0
  151. raise_cli/onboarding/claudemd.py +17 -0
  152. raise_cli/onboarding/conventions.py +742 -0
  153. raise_cli/onboarding/detection.py +374 -0
  154. raise_cli/onboarding/governance.py +443 -0
  155. raise_cli/onboarding/instructions.py +672 -0
  156. raise_cli/onboarding/manifest.py +201 -0
  157. raise_cli/onboarding/memory_md.py +399 -0
  158. raise_cli/onboarding/migration.py +207 -0
  159. raise_cli/onboarding/profile.py +624 -0
  160. raise_cli/onboarding/skill_conflict.py +100 -0
  161. raise_cli/onboarding/skill_manifest.py +176 -0
  162. raise_cli/onboarding/skills.py +437 -0
  163. raise_cli/onboarding/workflows.py +101 -0
  164. raise_cli/output/__init__.py +28 -0
  165. raise_cli/output/console.py +394 -0
  166. raise_cli/output/formatters/__init__.py +9 -0
  167. raise_cli/output/formatters/adapters.py +135 -0
  168. raise_cli/output/formatters/discover.py +439 -0
  169. raise_cli/output/formatters/skill.py +298 -0
  170. raise_cli/publish/__init__.py +3 -0
  171. raise_cli/publish/changelog.py +80 -0
  172. raise_cli/publish/check.py +179 -0
  173. raise_cli/publish/version.py +172 -0
  174. raise_cli/rai_base/__init__.py +22 -0
  175. raise_cli/rai_base/framework/__init__.py +7 -0
  176. raise_cli/rai_base/framework/methodology.yaml +233 -0
  177. raise_cli/rai_base/governance/__init__.py +1 -0
  178. raise_cli/rai_base/governance/architecture/__init__.py +1 -0
  179. raise_cli/rai_base/governance/architecture/domain-model.md +20 -0
  180. raise_cli/rai_base/governance/architecture/system-context.md +34 -0
  181. raise_cli/rai_base/governance/architecture/system-design.md +24 -0
  182. raise_cli/rai_base/governance/backlog.md +8 -0
  183. raise_cli/rai_base/governance/guardrails.md +17 -0
  184. raise_cli/rai_base/governance/prd.md +25 -0
  185. raise_cli/rai_base/governance/vision.md +16 -0
  186. raise_cli/rai_base/identity/__init__.py +8 -0
  187. raise_cli/rai_base/identity/core.md +119 -0
  188. raise_cli/rai_base/identity/perspective.md +119 -0
  189. raise_cli/rai_base/memory/__init__.py +7 -0
  190. raise_cli/rai_base/memory/patterns-base.jsonl +55 -0
  191. raise_cli/schemas/__init__.py +3 -0
  192. raise_cli/schemas/journal.py +49 -0
  193. raise_cli/schemas/session_state.py +117 -0
  194. raise_cli/session/__init__.py +5 -0
  195. raise_cli/session/bundle.py +820 -0
  196. raise_cli/session/close.py +268 -0
  197. raise_cli/session/journal.py +119 -0
  198. raise_cli/session/resolver.py +126 -0
  199. raise_cli/session/state.py +187 -0
  200. raise_cli/skills/__init__.py +44 -0
  201. raise_cli/skills/locator.py +141 -0
  202. raise_cli/skills/name_checker.py +199 -0
  203. raise_cli/skills/parser.py +145 -0
  204. raise_cli/skills/scaffold.py +212 -0
  205. raise_cli/skills/schema.py +132 -0
  206. raise_cli/skills/skillsets.py +195 -0
  207. raise_cli/skills/validator.py +197 -0
  208. raise_cli/skills_base/__init__.py +80 -0
  209. raise_cli/skills_base/contract-template.md +60 -0
  210. raise_cli/skills_base/preamble.md +37 -0
  211. raise_cli/skills_base/rai-architecture-review/SKILL.md +137 -0
  212. raise_cli/skills_base/rai-debug/SKILL.md +171 -0
  213. raise_cli/skills_base/rai-discover/SKILL.md +167 -0
  214. raise_cli/skills_base/rai-discover-document/SKILL.md +128 -0
  215. raise_cli/skills_base/rai-discover-scan/SKILL.md +147 -0
  216. raise_cli/skills_base/rai-discover-start/SKILL.md +145 -0
  217. raise_cli/skills_base/rai-discover-validate/SKILL.md +142 -0
  218. raise_cli/skills_base/rai-docs-update/SKILL.md +142 -0
  219. raise_cli/skills_base/rai-doctor/SKILL.md +120 -0
  220. raise_cli/skills_base/rai-epic-close/SKILL.md +165 -0
  221. raise_cli/skills_base/rai-epic-close/templates/retrospective.md +68 -0
  222. raise_cli/skills_base/rai-epic-design/SKILL.md +146 -0
  223. raise_cli/skills_base/rai-epic-design/templates/design.md +24 -0
  224. raise_cli/skills_base/rai-epic-design/templates/scope.md +76 -0
  225. raise_cli/skills_base/rai-epic-plan/SKILL.md +153 -0
  226. raise_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
  227. raise_cli/skills_base/rai-epic-plan/templates/plan-section.md +49 -0
  228. raise_cli/skills_base/rai-epic-run/SKILL.md +208 -0
  229. raise_cli/skills_base/rai-epic-start/SKILL.md +136 -0
  230. raise_cli/skills_base/rai-epic-start/templates/brief.md +34 -0
  231. raise_cli/skills_base/rai-mcp-add/SKILL.md +176 -0
  232. raise_cli/skills_base/rai-mcp-remove/SKILL.md +120 -0
  233. raise_cli/skills_base/rai-mcp-status/SKILL.md +147 -0
  234. raise_cli/skills_base/rai-problem-shape/SKILL.md +138 -0
  235. raise_cli/skills_base/rai-project-create/SKILL.md +144 -0
  236. raise_cli/skills_base/rai-project-onboard/SKILL.md +162 -0
  237. raise_cli/skills_base/rai-quality-review/SKILL.md +189 -0
  238. raise_cli/skills_base/rai-research/SKILL.md +143 -0
  239. raise_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
  240. raise_cli/skills_base/rai-session-close/SKILL.md +176 -0
  241. raise_cli/skills_base/rai-session-start/SKILL.md +110 -0
  242. raise_cli/skills_base/rai-story-close/SKILL.md +198 -0
  243. raise_cli/skills_base/rai-story-design/SKILL.md +203 -0
  244. raise_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
  245. raise_cli/skills_base/rai-story-implement/SKILL.md +115 -0
  246. raise_cli/skills_base/rai-story-plan/SKILL.md +135 -0
  247. raise_cli/skills_base/rai-story-review/SKILL.md +178 -0
  248. raise_cli/skills_base/rai-story-run/SKILL.md +282 -0
  249. raise_cli/skills_base/rai-story-start/SKILL.md +166 -0
  250. raise_cli/skills_base/rai-story-start/templates/story.md +38 -0
  251. raise_cli/skills_base/rai-welcome/SKILL.md +134 -0
  252. raise_cli/telemetry/__init__.py +42 -0
  253. raise_cli/telemetry/schemas.py +285 -0
  254. raise_cli/telemetry/writer.py +217 -0
  255. raise_cli/tier/__init__.py +0 -0
  256. raise_cli/tier/context.py +134 -0
  257. raise_cli/viz/__init__.py +7 -0
  258. raise_cli/viz/generator.py +406 -0
  259. raise_cli-2.2.1.dist-info/METADATA +433 -0
  260. raise_cli-2.2.1.dist-info/RECORD +264 -0
  261. raise_cli-2.2.1.dist-info/WHEEL +4 -0
  262. raise_cli-2.2.1.dist-info/entry_points.txt +40 -0
  263. raise_cli-2.2.1.dist-info/licenses/LICENSE +190 -0
  264. 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]())