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,324 @@
1
+ """Bootstrap bundled base Rai assets into a project.
2
+
3
+ Copies identity, patterns, and methodology from the raise_cli.rai_base
4
+ package to the project's .raise/rai/ directory during `raise init`.
5
+
6
+ Uses importlib.resources to read bundled files (Python 3.9+).
7
+ Per-file idempotency: existing files are never overwritten.
8
+
9
+ Example:
10
+ from raise_cli.onboarding.bootstrap import bootstrap_rai_base
11
+
12
+ result = bootstrap_rai_base(project_path)
13
+ if result.identity_copied:
14
+ print("Base identity installed")
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ from importlib.resources import files
21
+ from importlib.resources.abc import Traversable
22
+ from pathlib import Path
23
+
24
+ from pydantic import BaseModel, Field
25
+
26
+ from raise_cli.config.paths import (
27
+ get_framework_dir,
28
+ get_identity_dir,
29
+ get_memory_dir,
30
+ get_personal_dir,
31
+ )
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class BootstrapResult(BaseModel):
37
+ """Result of base Rai bootstrap operation."""
38
+
39
+ identity_copied: bool = False
40
+ patterns_copied: bool = False
41
+ methodology_copied: bool = False
42
+ base_version: str = ""
43
+ already_existed: bool = False
44
+ files_copied: list[str] = Field(default_factory=list)
45
+ files_skipped: list[str] = Field(default_factory=list)
46
+ patterns_added: int = 0
47
+ patterns_updated: int = 0
48
+
49
+
50
+ def bootstrap_rai_base(project_root: Path) -> BootstrapResult:
51
+ """Copy bundled base Rai assets to project .raise/rai/ directory.
52
+
53
+ Copies identity files, base patterns, and methodology definition
54
+ from the installed raise_cli.rai_base package. Uses per-file
55
+ idempotency — existing files are never overwritten.
56
+
57
+ Args:
58
+ project_root: Project root directory.
59
+
60
+ Returns:
61
+ BootstrapResult with details of what was copied or skipped.
62
+ """
63
+ from raise_cli.rai_base import __version__ as base_version
64
+
65
+ base = files("raise_cli.rai_base")
66
+ result = BootstrapResult(base_version=base_version)
67
+
68
+ # Copy identity files
69
+ _copy_identity(base, project_root, result)
70
+
71
+ # Copy patterns
72
+ _copy_patterns(base, project_root, result)
73
+
74
+ # Copy methodology
75
+ _copy_methodology(base, project_root, result)
76
+
77
+ # Ensure personal directory exists with .gitkeep
78
+ _ensure_personal_dir(project_root)
79
+
80
+ # Ensure .gitignore has entries for personal/ephemeral paths
81
+ ensure_gitignore(project_root)
82
+
83
+ # Determine if everything already existed
84
+ result.already_existed = len(result.files_copied) == 0
85
+
86
+ return result
87
+
88
+
89
+ def _copy_identity(
90
+ base: Traversable, project_root: Path, result: BootstrapResult
91
+ ) -> None:
92
+ """Copy base identity files to .raise/rai/identity/.
93
+
94
+ Args:
95
+ base: importlib.resources Traversable for rai_base package.
96
+ project_root: Project root directory.
97
+ result: BootstrapResult to update.
98
+ """
99
+ identity_dir = get_identity_dir(project_root)
100
+ identity_base = base / "identity"
101
+
102
+ identity_files = ["core.md", "perspective.md"]
103
+ copied_any = False
104
+
105
+ for filename in identity_files:
106
+ dest = identity_dir / filename
107
+ if dest.exists():
108
+ result.files_skipped.append(str(dest))
109
+ logger.debug("Skipped (exists): %s", dest)
110
+ continue
111
+
112
+ content = (identity_base / filename).read_text(encoding="utf-8")
113
+ dest.parent.mkdir(parents=True, exist_ok=True)
114
+ dest.write_text(content, encoding="utf-8")
115
+ result.files_copied.append(str(dest))
116
+ copied_any = True
117
+ logger.debug("Copied: %s", dest)
118
+
119
+ result.identity_copied = copied_any
120
+
121
+
122
+ def _copy_patterns(
123
+ base: Traversable, project_root: Path, result: BootstrapResult
124
+ ) -> None:
125
+ """Copy or merge base patterns to .raise/rai/memory/patterns.jsonl.
126
+
127
+ First init: copies all base patterns (fresh install).
128
+ Re-init: merges base patterns — adds new, updates versioned, preserves project patterns.
129
+
130
+ Args:
131
+ base: importlib.resources Traversable for rai_base package.
132
+ project_root: Project root directory.
133
+ result: BootstrapResult to update.
134
+ """
135
+ import json
136
+ import tempfile
137
+
138
+ memory_dir = get_memory_dir(project_root)
139
+ dest = memory_dir / "patterns.jsonl"
140
+ source = base / "memory" / "patterns-base.jsonl"
141
+
142
+ if not dest.exists():
143
+ # Fresh install: copy all base patterns
144
+ content = source.read_text(encoding="utf-8")
145
+ dest.parent.mkdir(parents=True, exist_ok=True)
146
+ dest.write_text(content, encoding="utf-8")
147
+ result.files_copied.append(str(dest))
148
+ result.patterns_copied = True
149
+ logger.debug("Copied: %s", dest)
150
+ return
151
+
152
+ # Re-init: merge base patterns into existing file
153
+ existing_lines = dest.read_text(encoding="utf-8").strip().splitlines()
154
+ existing_patterns: list[dict[str, object]] = []
155
+ existing_by_id: dict[str, int] = {} # id -> index in existing_patterns
156
+
157
+ for line in existing_lines:
158
+ line = line.strip()
159
+ if not line:
160
+ continue
161
+ pattern = json.loads(line)
162
+ idx = len(existing_patterns)
163
+ existing_patterns.append(pattern)
164
+ pid = str(pattern.get("id", ""))
165
+ if pid:
166
+ existing_by_id[pid] = idx
167
+
168
+ # Read base patterns from package
169
+ base_lines = source.read_text(encoding="utf-8").strip().splitlines()
170
+ added = 0
171
+ updated = 0
172
+
173
+ for line in base_lines:
174
+ line = line.strip()
175
+ if not line:
176
+ continue
177
+ base_pattern = json.loads(line)
178
+ pid = str(base_pattern.get("id", ""))
179
+ if not pid:
180
+ continue
181
+
182
+ if pid not in existing_by_id:
183
+ # New base pattern — append
184
+ existing_patterns.append(base_pattern)
185
+ existing_by_id[pid] = len(existing_patterns) - 1
186
+ added += 1
187
+ logger.debug("Added base pattern: %s", pid)
188
+ else:
189
+ # Existing — check version for upgrade
190
+ idx = existing_by_id[pid]
191
+ existing_version = int(str(existing_patterns[idx].get("version", 0)))
192
+ package_version = int(str(base_pattern.get("version", 0)))
193
+ if package_version > existing_version:
194
+ existing_patterns[idx] = base_pattern
195
+ updated += 1
196
+ logger.debug("Updated base pattern: %s v%d → v%d", pid, existing_version, package_version)
197
+
198
+ result.patterns_added = added
199
+ result.patterns_updated = updated
200
+
201
+ if added > 0 or updated > 0:
202
+ # Write atomically: temp file + rename
203
+ merged_content = "\n".join(json.dumps(p) for p in existing_patterns) + "\n"
204
+ dest.parent.mkdir(parents=True, exist_ok=True)
205
+ fd, tmp_path = tempfile.mkstemp(
206
+ dir=str(dest.parent), suffix=".tmp", prefix="patterns_"
207
+ )
208
+ try:
209
+ Path(tmp_path).write_text(merged_content, encoding="utf-8")
210
+ Path(tmp_path).replace(dest)
211
+ except Exception:
212
+ Path(tmp_path).unlink(missing_ok=True)
213
+ raise
214
+ finally:
215
+ import contextlib
216
+ import os
217
+ with contextlib.suppress(OSError):
218
+ os.close(fd)
219
+ result.files_copied.append(str(dest))
220
+ logger.debug("Merged base patterns into %s: %d added, %d updated", dest, added, updated)
221
+ else:
222
+ result.files_skipped.append(str(dest))
223
+ logger.debug("Base patterns already current: %s", dest)
224
+
225
+
226
+ def _copy_methodology(
227
+ base: Traversable, project_root: Path, result: BootstrapResult
228
+ ) -> None:
229
+ """Copy methodology.yaml to .raise/rai/framework/.
230
+
231
+ Args:
232
+ base: importlib.resources Traversable for rai_base package.
233
+ project_root: Project root directory.
234
+ result: BootstrapResult to update.
235
+ """
236
+ framework_dir = get_framework_dir(project_root)
237
+ dest = framework_dir / "methodology.yaml"
238
+
239
+ if dest.exists():
240
+ result.files_skipped.append(str(dest))
241
+ logger.debug("Skipped (exists): %s", dest)
242
+ return
243
+
244
+ source = base / "framework" / "methodology.yaml"
245
+ content = source.read_text(encoding="utf-8")
246
+
247
+ dest.parent.mkdir(parents=True, exist_ok=True)
248
+ dest.write_text(content, encoding="utf-8")
249
+ result.files_copied.append(str(dest))
250
+ result.methodology_copied = True
251
+ logger.debug("Copied: %s", dest)
252
+
253
+
254
+ def ensure_gitignore(project_root: Path) -> bool:
255
+ """Ensure .gitignore contains entries for RaiSE personal/ephemeral paths.
256
+
257
+ Appends a RaiSE-managed block to the project .gitignore if the entries
258
+ are not already present. Idempotent — running multiple times will not
259
+ create duplicate entries.
260
+
261
+ Args:
262
+ project_root: Project root directory.
263
+
264
+ Returns:
265
+ True if entries were added, False if already present.
266
+ """
267
+ gitignore_path = project_root / ".gitignore"
268
+
269
+ # Entries to ensure are present
270
+ entries = [
271
+ ".raise/rai/personal/",
272
+ ]
273
+
274
+ # Read existing content (if any)
275
+ existing_content = ""
276
+ if gitignore_path.exists():
277
+ existing_content = gitignore_path.read_text(encoding="utf-8")
278
+
279
+ # Check which entries are missing
280
+ existing_lines = {line.strip() for line in existing_content.splitlines()}
281
+ missing = [e for e in entries if e not in existing_lines]
282
+
283
+ if not missing:
284
+ logger.debug("All RaiSE gitignore entries already present")
285
+ return False
286
+
287
+ # Build block to append
288
+ block_lines = [
289
+ "",
290
+ "# RaiSE personal directory (per-developer, not shared)",
291
+ ]
292
+ for entry in missing:
293
+ block_lines.append(entry)
294
+ block_lines.append("")
295
+
296
+ block = "\n".join(block_lines)
297
+
298
+ # Ensure file ends with newline before appending
299
+ if existing_content and not existing_content.endswith("\n"):
300
+ block = "\n" + block
301
+
302
+ with gitignore_path.open("a", encoding="utf-8") as f:
303
+ f.write(block)
304
+
305
+ logger.debug("Added RaiSE entries to .gitignore: %s", missing)
306
+ return True
307
+
308
+
309
+ def _ensure_personal_dir(project_root: Path) -> None:
310
+ """Ensure .raise/rai/personal/ exists with a .gitkeep file.
311
+
312
+ The personal directory is gitignored and stores per-developer data
313
+ (sessions, telemetry, calibration). The .gitkeep ensures the directory
314
+ structure exists after `rai init` before any subsystem creates files.
315
+
316
+ Args:
317
+ project_root: Project root directory.
318
+ """
319
+ personal_dir = get_personal_dir(project_root)
320
+ personal_dir.mkdir(parents=True, exist_ok=True)
321
+ gitkeep = personal_dir / ".gitkeep"
322
+ if not gitkeep.exists():
323
+ gitkeep.touch()
324
+ logger.debug("Created: %s", gitkeep)
@@ -0,0 +1,17 @@
1
+ """Backward-compatibility shim for claudemd module.
2
+
3
+ All types have moved to raise_cli.onboarding.instructions.
4
+ """
5
+
6
+ from raise_cli.onboarding.instructions import (
7
+ ClaudeMdGenerator as ClaudeMdGenerator,
8
+ )
9
+ from raise_cli.onboarding.instructions import (
10
+ InstructionsGenerator as InstructionsGenerator,
11
+ )
12
+ from raise_cli.onboarding.instructions import (
13
+ generate_claude_md as generate_claude_md,
14
+ )
15
+ from raise_cli.onboarding.instructions import (
16
+ generate_instructions as generate_instructions,
17
+ )