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,120 @@
1
+ """Agent configuration model and factory.
2
+
3
+ Defines agent-specific conventions (paths, file names) as data.
4
+ Each supported agent has a pre-built AgentConfig in the registry.
5
+ Factory function returns the config for a given agent type.
6
+
7
+ Replaces the IDE-specific model from ADR-031 with a generic agent abstraction.
8
+ Architecture decision: ADR-032 (Multi-agent skill distribution).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import StrEnum
14
+ from typing import Literal
15
+
16
+ from pydantic import BaseModel, ConfigDict, Field
17
+
18
+ BuiltinAgentType = Literal[
19
+ "claude", "cursor", "windsurf", "copilot", "antigravity", "roo"
20
+ ]
21
+
22
+
23
+ class AgentChoice(StrEnum):
24
+ """Typer-compatible enum for --agent CLI option."""
25
+
26
+ claude = "claude"
27
+ cursor = "cursor"
28
+ windsurf = "windsurf"
29
+ copilot = "copilot"
30
+ antigravity = "antigravity"
31
+ roo = "roo"
32
+
33
+
34
+ class AgentConfig(BaseModel):
35
+ """Configuration for a target agent/IDE/CLI.
36
+
37
+ Attributes:
38
+ name: Display name for this agent.
39
+ agent_type: Registry key (e.g. "claude", "codex-cli", "azure-devops").
40
+ instructions_file: Relative path to instructions/rules file from project root.
41
+ skills_dir: Relative path to skills directory (None if agent has no skills support).
42
+ workflows_dir: Relative path to workflows directory (None if agent has no equivalent).
43
+ detection_markers: Paths to check for presence during auto-detection.
44
+ plugin: Python module path for custom logic (None for default pass-through).
45
+ """
46
+
47
+ model_config = ConfigDict(frozen=True)
48
+
49
+ name: str
50
+ agent_type: str
51
+ instructions_file: str
52
+ skills_dir: str | None = None
53
+ workflows_dir: str | None = None
54
+ detection_markers: list[str] = Field(default_factory=list)
55
+ plugin: str | None = None
56
+
57
+
58
+ BUILTIN_AGENTS: dict[BuiltinAgentType, AgentConfig] = {
59
+ "claude": AgentConfig(
60
+ name="Claude Code",
61
+ agent_type="claude",
62
+ skills_dir=".claude/skills",
63
+ instructions_file="CLAUDE.md",
64
+ detection_markers=["CLAUDE.md", ".claude"],
65
+ ),
66
+ "cursor": AgentConfig(
67
+ name="Cursor",
68
+ agent_type="cursor",
69
+ skills_dir=".cursor/skills",
70
+ instructions_file=".cursor/rules/raise.mdc",
71
+ detection_markers=[".cursor/rules", ".cursor"],
72
+ ),
73
+ "windsurf": AgentConfig(
74
+ name="Windsurf",
75
+ agent_type="windsurf",
76
+ skills_dir=".windsurf/skills",
77
+ instructions_file=".windsurf/rules/raise.md",
78
+ workflows_dir=".windsurf/workflows",
79
+ detection_markers=[".windsurf/rules", ".windsurf"],
80
+ ),
81
+ "copilot": AgentConfig(
82
+ name="GitHub Copilot",
83
+ agent_type="copilot",
84
+ skills_dir=".github/agents",
85
+ instructions_file=".github/copilot-instructions.md",
86
+ workflows_dir=".github/prompts",
87
+ detection_markers=[".github/copilot-instructions.md"],
88
+ plugin="raise_cli.agents.copilot_plugin",
89
+ ),
90
+ "antigravity": AgentConfig(
91
+ name="Antigravity",
92
+ agent_type="antigravity",
93
+ skills_dir=".agent/skills",
94
+ instructions_file=".agent/rules/raise.md",
95
+ workflows_dir=".agent/workflows",
96
+ detection_markers=[".agent/rules", ".agent"],
97
+ ),
98
+ "roo": AgentConfig(
99
+ name="Roo Code",
100
+ agent_type="roo",
101
+ skills_dir=".roo/skills",
102
+ instructions_file=".roo/rules/raise.md",
103
+ detection_markers=[".roo/rules", ".roo", ".rooignore"],
104
+ ),
105
+ }
106
+
107
+
108
+ def get_agent_config(agent_type: BuiltinAgentType = "claude") -> AgentConfig:
109
+ """Get the agent configuration for a given agent type.
110
+
111
+ Args:
112
+ agent_type: The agent to get config for. Defaults to "claude".
113
+
114
+ Returns:
115
+ AgentConfig with paths and conventions for the requested agent.
116
+
117
+ Raises:
118
+ KeyError: If agent_type is not in the built-in registry.
119
+ """
120
+ return BUILTIN_AGENTS[agent_type]
@@ -0,0 +1,32 @@
1
+ """Backward-compatibility shim for IDE configuration.
2
+
3
+ All types have been moved to raise_cli.config.agents.
4
+ This module re-exports old names for external consumers.
5
+
6
+ Migration: IdeConfig → AgentConfig, IdeType → BuiltinAgentType,
7
+ IdeChoice → AgentChoice, IDE_CONFIGS → BUILTIN_AGENTS,
8
+ get_ide_config → get_agent_config.
9
+ """
10
+
11
+ from raise_cli.config.agents import (
12
+ BUILTIN_AGENTS as BUILTIN_AGENTS,
13
+ )
14
+ from raise_cli.config.agents import (
15
+ AgentChoice as AgentChoice,
16
+ )
17
+ from raise_cli.config.agents import (
18
+ AgentConfig as AgentConfig,
19
+ )
20
+ from raise_cli.config.agents import (
21
+ BuiltinAgentType as BuiltinAgentType,
22
+ )
23
+ from raise_cli.config.agents import (
24
+ get_agent_config as get_agent_config,
25
+ )
26
+
27
+ # Backward-compat aliases
28
+ IdeConfig = AgentConfig
29
+ IdeType = BuiltinAgentType
30
+ IdeChoice = AgentChoice
31
+ IDE_CONFIGS = BUILTIN_AGENTS
32
+ get_ide_config = get_agent_config
@@ -0,0 +1,379 @@
1
+ """Path helpers for raise-cli.
2
+
3
+ Includes:
4
+ - XDG Base Directory paths (global user config/cache/data)
5
+ - Per-project directory structure constants
6
+
7
+ Directory Structure (per-project):
8
+ .raise/ # RaiSE framework presence
9
+ ├── manifest.yaml # Project metadata
10
+ ├── config.yaml # Project configuration
11
+ ├── graph/ # Context graph cache
12
+ └── rai/ # AI partner state
13
+ ├── identity/ # Rai's identity
14
+ ├── memory/ # Patterns, calibration, graph (shared, committed)
15
+ └── personal/ # Sessions, telemetry (per-developer, gitignored)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ from pathlib import Path
22
+
23
+ # =============================================================================
24
+ # Per-Project Directory Constants
25
+ # =============================================================================
26
+
27
+ # Root directory for RaiSE in a project
28
+ RAISE_PROJECT_DIR = ".raise"
29
+
30
+ # Subdirectory for Rai (AI partner) state within .raise/
31
+ RAI_SUBDIR = "rai"
32
+
33
+ # Common subdirectories
34
+ MEMORY_SUBDIR = "memory"
35
+ TELEMETRY_SUBDIR = "telemetry"
36
+ IDENTITY_SUBDIR = "identity"
37
+ GRAPH_SUBDIR = "graph"
38
+ FRAMEWORK_SUBDIR = "framework"
39
+ MANIFESTS_SUBDIR = "manifests"
40
+
41
+ # File names
42
+ SKILLS_MANIFEST_FILE = "skills.json"
43
+ MANIFEST_FILE = "manifest.yaml"
44
+ CONFIG_FILE = "config.yaml"
45
+ PATTERNS_FILE = "patterns.jsonl"
46
+ CALIBRATION_FILE = "calibration.jsonl"
47
+ SESSIONS_DIR = "sessions"
48
+ SIGNALS_FILE = "signals.jsonl"
49
+
50
+
51
+ def get_raise_dir(project_root: Path | None = None) -> Path:
52
+ """Get the .raise/ directory for a project.
53
+
54
+ Args:
55
+ project_root: Project root path. Defaults to current directory.
56
+
57
+ Returns:
58
+ Path to .raise/ directory.
59
+ """
60
+ root = project_root or Path.cwd()
61
+ return root / RAISE_PROJECT_DIR
62
+
63
+
64
+ def get_rai_dir(project_root: Path | None = None) -> Path:
65
+ """Get the .raise/rai/ directory for AI partner state.
66
+
67
+ Args:
68
+ project_root: Project root path. Defaults to current directory.
69
+
70
+ Returns:
71
+ Path to .raise/rai/ directory.
72
+ """
73
+ return get_raise_dir(project_root) / RAI_SUBDIR
74
+
75
+
76
+ def get_memory_dir(project_root: Path | None = None) -> Path:
77
+ """Get the .raise/rai/memory/ directory.
78
+
79
+ Args:
80
+ project_root: Project root path. Defaults to current directory.
81
+
82
+ Returns:
83
+ Path to memory directory.
84
+ """
85
+ return get_rai_dir(project_root) / MEMORY_SUBDIR
86
+
87
+
88
+ def get_telemetry_dir(project_root: Path | None = None) -> Path:
89
+ """Get the .raise/rai/personal/telemetry/ directory.
90
+
91
+ Telemetry is personal data (developer-specific, gitignored).
92
+
93
+ Args:
94
+ project_root: Project root path. Defaults to current directory.
95
+
96
+ Returns:
97
+ Path to telemetry directory.
98
+ """
99
+ return get_personal_dir(project_root) / TELEMETRY_SUBDIR
100
+
101
+
102
+ def get_identity_dir(project_root: Path | None = None) -> Path:
103
+ """Get the .raise/rai/identity/ directory.
104
+
105
+ Args:
106
+ project_root: Project root path. Defaults to current directory.
107
+
108
+ Returns:
109
+ Path to identity directory.
110
+ """
111
+ return get_rai_dir(project_root) / IDENTITY_SUBDIR
112
+
113
+
114
+ def get_framework_dir(project_root: Path | None = None) -> Path:
115
+ """Get the .raise/rai/framework/ directory.
116
+
117
+ Args:
118
+ project_root: Project root path. Defaults to current directory.
119
+
120
+ Returns:
121
+ Path to framework directory.
122
+ """
123
+ return get_rai_dir(project_root) / FRAMEWORK_SUBDIR
124
+
125
+
126
+ def get_graph_dir(project_root: Path | None = None) -> Path:
127
+ """Get the memory index directory (.raise/rai/memory/).
128
+
129
+ The "graph" is an implementation detail — it's really memory indexing.
130
+ This function returns the memory directory where index.json lives.
131
+
132
+ Args:
133
+ project_root: Project root path. Defaults to current directory.
134
+
135
+ Returns:
136
+ Path to memory directory (contains index.json).
137
+ """
138
+ return get_memory_dir(project_root)
139
+
140
+
141
+ # =============================================================================
142
+ # Global User Directory (XDG)
143
+ # =============================================================================
144
+
145
+ # Global Rai directory in user home (for developer profile)
146
+ GLOBAL_RAI_DIR = ".rai"
147
+
148
+ # Personal subdirectory (gitignored, per-developer within project)
149
+ PERSONAL_SUBDIR = "personal"
150
+
151
+
152
+ def _sanitize_env_path(raw: str, var_name: str) -> Path:
153
+ """Sanitize a path read from an environment variable (CWE-23).
154
+
155
+ Validates that the raw value does not contain ``..`` path components
156
+ (before resolution) and that the resolved result is absolute.
157
+
158
+ Args:
159
+ raw: Raw environment variable value.
160
+ var_name: Variable name (for error messages).
161
+
162
+ Returns:
163
+ Resolved, absolute ``Path``.
164
+
165
+ Raises:
166
+ ValueError: If the path contains traversal components or is not absolute.
167
+ """
168
+ if ".." in raw.split(os.sep):
169
+ msg = f"${var_name} must not contain '..' path components: {raw}"
170
+ raise ValueError(msg)
171
+ resolved = Path(raw).resolve()
172
+ if not resolved.is_absolute():
173
+ msg = f"${var_name} must resolve to an absolute path: {raw}"
174
+ raise ValueError(msg)
175
+ return resolved
176
+
177
+
178
+ def get_global_rai_dir() -> Path:
179
+ """Get the global ~/.rai directory for cross-repo Rai state.
180
+
181
+ This directory stores:
182
+ - developer.yaml (identity, already exists)
183
+ - patterns.jsonl (universal patterns, NEW)
184
+ - calibration.jsonl (global calibration, NEW)
185
+
186
+ Can be overridden with RAI_HOME environment variable.
187
+
188
+ Returns:
189
+ Path to global Rai directory (e.g., ~/.rai or $RAI_HOME)
190
+
191
+ Raises:
192
+ ValueError: If RAI_HOME contains path-traversal components.
193
+
194
+ Example:
195
+ >>> global_dir = get_global_rai_dir()
196
+ >>> patterns_file = global_dir / "patterns.jsonl"
197
+ """
198
+ rai_home = os.environ.get("RAI_HOME")
199
+ if rai_home:
200
+ return _sanitize_env_path(rai_home, "RAI_HOME")
201
+ return Path.home() / GLOBAL_RAI_DIR
202
+
203
+
204
+ def ensure_global_rai_dir() -> Path:
205
+ """Ensure the global ~/.rai directory exists with required files.
206
+
207
+ Creates:
208
+ - ~/.rai/ directory (if not exists)
209
+ - ~/.rai/patterns.jsonl (empty, if not exists)
210
+ - ~/.rai/calibration.jsonl (empty, if not exists)
211
+
212
+ Does NOT overwrite existing files.
213
+
214
+ Returns:
215
+ Path to global Rai directory.
216
+
217
+ Example:
218
+ >>> global_dir = ensure_global_rai_dir()
219
+ >>> # Now safe to write patterns to global_dir / "patterns.jsonl"
220
+ """
221
+ global_dir = get_global_rai_dir()
222
+ global_dir.mkdir(parents=True, exist_ok=True)
223
+
224
+ # Create empty JSONL files if they don't exist
225
+ patterns_file = global_dir / PATTERNS_FILE
226
+ if not patterns_file.exists():
227
+ patterns_file.touch()
228
+
229
+ calibration_file = global_dir / CALIBRATION_FILE
230
+ if not calibration_file.exists():
231
+ calibration_file.touch()
232
+
233
+ return global_dir
234
+
235
+
236
+ def get_credentials_path() -> Path:
237
+ """Get path to encrypted credentials file for external providers.
238
+
239
+ Returns path to ~/.rai/credentials.json which stores OAuth tokens
240
+ for external providers (JIRA, GitLab, etc.) with Fernet encryption.
241
+
242
+ The file is created with user-only permissions (0600) when first written.
243
+
244
+ Returns:
245
+ Path to credentials file (e.g., ~/.rai/credentials.json).
246
+
247
+ Example:
248
+ >>> creds_path = get_credentials_path()
249
+ >>> # Use with rai_pro.providers.auth.credentials.store_token()
250
+ """
251
+ return get_global_rai_dir() / "credentials.json"
252
+
253
+
254
+ def get_session_dir(session_id: str, project_root: Path | None = None) -> Path:
255
+ """Get the per-session directory for isolated session state.
256
+
257
+ Each session instance gets its own directory containing:
258
+ - state.yaml (session working state)
259
+ - signals.jsonl (session telemetry)
260
+
261
+ Args:
262
+ session_id: Session identifier (e.g., "SES-177").
263
+ project_root: Project root path. Defaults to current directory.
264
+
265
+ Returns:
266
+ Path to per-session directory (e.g., .raise/rai/personal/sessions/SES-177/)
267
+ """
268
+ sessions_base = (get_personal_dir(project_root) / SESSIONS_DIR).resolve()
269
+ session_path = (sessions_base / session_id).resolve()
270
+ if not session_path.is_relative_to(sessions_base):
271
+ raise ValueError(f"Invalid session_id — path traversal detected: {session_id!r}")
272
+ return session_path
273
+
274
+
275
+ def get_personal_dir(project_root: Path | None = None) -> Path:
276
+ """Get the personal directory for developer-specific project data.
277
+
278
+ This directory is gitignored and stores:
279
+ - sessions/index.jsonl (my sessions)
280
+ - telemetry/signals.jsonl (my telemetry)
281
+ - calibration.jsonl (project-specific calibration)
282
+ - patterns.jsonl (project-specific learnings)
283
+
284
+ Args:
285
+ project_root: Project root path. Defaults to current directory.
286
+
287
+ Returns:
288
+ Path to personal directory (e.g., .raise/rai/personal/)
289
+
290
+ Example:
291
+ >>> personal_dir = get_personal_dir()
292
+ >>> my_sessions = personal_dir / "sessions" / "index.jsonl"
293
+ """
294
+ return get_rai_dir(project_root) / PERSONAL_SUBDIR
295
+
296
+
297
+ def get_claude_memory_path(project_root: Path) -> Path:
298
+ """Get the Claude Code MEMORY.md path for a project.
299
+
300
+ Claude Code stores per-project memory at:
301
+ ~/.claude/projects/{encoded_path}/memory/MEMORY.md
302
+
303
+ Where {encoded_path} replaces '/' with '-' and prepends '-'.
304
+ This is the first IDE-specific path helper — future IDEs
305
+ (Cursor, Windsurf, etc.) will have sibling functions.
306
+
307
+ Args:
308
+ project_root: Absolute path to the project root.
309
+
310
+ Returns:
311
+ Path to the Claude Code MEMORY.md file.
312
+
313
+ Example:
314
+ >>> get_claude_memory_path(Path("/home/user/Code/my-project"))
315
+ PosixPath('/home/user/.claude/projects/-home-user-Code-my-project/memory/MEMORY.md')
316
+ """
317
+ # Claude Code convention: replace path separators with -, prepend -
318
+ # Must handle both Unix (/) and Windows (\) separators
319
+ path_str = str(project_root)
320
+ # Normalize Windows backslashes to forward slashes
321
+ path_str = path_str.replace("\\", "/")
322
+ # Remove drive letter colon on Windows (C:/Users -> C/Users)
323
+ path_str = path_str.replace(":", "")
324
+ encoded = path_str.replace("/", "-")
325
+ return Path.home() / ".claude" / "projects" / encoded / "memory" / "MEMORY.md"
326
+
327
+
328
+ def _get_xdg_dir(env_var: str, fallback: str) -> Path:
329
+ """Get an XDG directory for raise-cli.
330
+
331
+ Args:
332
+ env_var: XDG environment variable name (e.g., "XDG_CONFIG_HOME").
333
+ fallback: Fallback path relative to home (e.g., ".config").
334
+
335
+ Returns:
336
+ Path to the rai subdirectory within the XDG directory.
337
+ """
338
+ xdg_value = os.environ.get(env_var)
339
+ base = _sanitize_env_path(xdg_value, env_var) if xdg_value else Path.home() / fallback
340
+ return base / "rai"
341
+
342
+
343
+ def get_config_dir() -> Path:
344
+ """Get the XDG config directory for raise-cli.
345
+
346
+ Returns:
347
+ Path to config directory (e.g., ~/.config/rai/ or $XDG_CONFIG_HOME/rai/)
348
+
349
+ Example:
350
+ >>> config_dir = get_config_dir()
351
+ >>> config_file = config_dir / "config.toml"
352
+ """
353
+ return _get_xdg_dir("XDG_CONFIG_HOME", ".config")
354
+
355
+
356
+ def get_cache_dir() -> Path:
357
+ """Get the XDG cache directory for raise-cli.
358
+
359
+ Returns:
360
+ Path to cache directory (e.g., ~/.cache/rai/ or $XDG_CACHE_HOME/rai/)
361
+
362
+ Example:
363
+ >>> cache_dir = get_cache_dir()
364
+ >>> cache_file = cache_dir / "downloaded_katas.json"
365
+ """
366
+ return _get_xdg_dir("XDG_CACHE_HOME", ".cache")
367
+
368
+
369
+ def get_data_dir() -> Path:
370
+ """Get the XDG data directory for raise-cli.
371
+
372
+ Returns:
373
+ Path to data directory (e.g., ~/.local/share/rai/ or $XDG_DATA_HOME/rai/)
374
+
375
+ Example:
376
+ >>> data_dir = get_data_dir()
377
+ >>> state_file = data_dir / "session_state.json"
378
+ """
379
+ return _get_xdg_dir("XDG_DATA_HOME", ".local/share")