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,746 @@
1
+ """Init CLI command for RaiSE project initialization.
2
+
3
+ This module provides the `raise init` command that:
4
+ - Detects if the project is greenfield or brownfield
5
+ - Creates .raise/manifest.yaml with project metadata
6
+ - Loads or creates ~/.rai/developer.yaml for personal profile
7
+ - Scaffolds skills, workflows, and governance for each target agent
8
+ - Supports multiple agents via --agent (repeatable) or --detect
9
+
10
+ Example:
11
+ $ raise init # defaults to claude
12
+ $ raise init --agent cursor # single agent
13
+ $ raise init --agent claude --agent cursor # multi-agent
14
+ $ raise init --detect # auto-detect installed agents
15
+ $ raise init --ide antigravity # (deprecated) alias for --agent
16
+ """
17
+
18
+ import sys
19
+ from datetime import date
20
+ from pathlib import Path
21
+ from typing import Annotated
22
+
23
+ import typer
24
+ from rich.console import Console
25
+ from rich.panel import Panel
26
+
27
+ from raise_cli.config.agent_registry import AgentRegistry, load_registry
28
+ from raise_cli.config.agents import AgentChoice, AgentConfig
29
+ from raise_cli.hooks.emitter import create_emitter
30
+ from raise_cli.hooks.events import InitCompleteEvent
31
+ from raise_cli.onboarding.bootstrap import BootstrapResult
32
+ from raise_cli.onboarding.conventions import detect_conventions
33
+ from raise_cli.onboarding.detection import ProjectType, detect_project_type
34
+ from raise_cli.onboarding.governance import (
35
+ GovernanceScaffoldResult,
36
+ generate_guardrails,
37
+ )
38
+ from raise_cli.onboarding.instructions import generate_instructions
39
+ from raise_cli.onboarding.manifest import (
40
+ AgentsManifest,
41
+ BranchConfig,
42
+ ProjectInfo,
43
+ ProjectManifest,
44
+ load_manifest,
45
+ save_manifest,
46
+ )
47
+ from raise_cli.onboarding.profile import (
48
+ DeveloperProfile,
49
+ ExperienceLevel,
50
+ load_developer_profile,
51
+ save_developer_profile,
52
+ )
53
+ from raise_cli.onboarding.skills import SkillScaffoldResult
54
+
55
+ console = Console()
56
+
57
+
58
+ def _print_skill_sync_summary(result: SkillScaffoldResult) -> None:
59
+ """Print a summary table of skill sync actions."""
60
+ from raise_cli.skills_base import __version__ as cli_version
61
+
62
+ console.print(f"\n[bold]Skill sync: raise-cli {cli_version}[/bold]\n")
63
+
64
+ rows: list[tuple[str, str, str]] = []
65
+ for name in result.skills_installed:
66
+ rows.append((name, "[green]new[/green]", "install"))
67
+ for name in result.skills_updated:
68
+ rows.append((name, "[cyan]updated[/cyan]", "auto-update"))
69
+ for name in result.skills_conflicted:
70
+ rows.append((name, "[yellow]conflict[/yellow]", "prompt"))
71
+ for name in result.skills_kept:
72
+ rows.append((name, "[yellow]kept[/yellow]", "user chose keep"))
73
+ for name in result.skills_overwritten:
74
+ rows.append((name, "[cyan]overwritten[/cyan]", "user chose overwrite"))
75
+ for name in result.skills_current:
76
+ rows.append((name, "current", "skip"))
77
+
78
+ rows.sort(key=lambda r: r[0])
79
+
80
+ from rich.table import Table
81
+
82
+ table = Table(show_header=True)
83
+ table.add_column("Skill", style="bold")
84
+ table.add_column("Status")
85
+ table.add_column("Action")
86
+ for name, status, action in rows:
87
+ table.add_row(name, status, action)
88
+
89
+ console.print(table)
90
+
91
+ n_install = len(result.skills_installed)
92
+ n_update = len(result.skills_updated)
93
+ n_conflict = len(result.skills_conflicted) + len(result.skills_kept)
94
+ n_current = len(result.skills_current) + len(result.skills_overwritten)
95
+ parts: list[str] = []
96
+ if n_install:
97
+ parts.append(f"{n_install} new")
98
+ if n_update:
99
+ parts.append(f"{n_update} auto-update")
100
+ if n_conflict:
101
+ parts.append(f"{n_conflict} conflict")
102
+ if n_current:
103
+ parts.append(f"{n_current} current")
104
+ console.print(f"\n Summary: {', '.join(parts)}\n")
105
+
106
+
107
+ # Message templates for different experience levels
108
+ WELCOME_SHU = """[bold cyan]Welcome to RaiSE![/bold cyan]
109
+
110
+ I'm [bold]Rai[/bold] — your AI partner for reliable software engineering.
111
+
112
+ Together, we'll build software that's both fast AND reliable.
113
+ The RaiSE methodology guides our collaboration:
114
+ • [dim]You[/dim] bring intuition and judgment
115
+ • [dim]I[/dim] bring execution and memory
116
+ • [dim]Together[/dim]: reliable software at AI speed
117
+ """
118
+
119
+ WELCOME_BACK_RI = "[dim]Welcome back, {name}.[/dim]"
120
+
121
+ PROJECT_DETECTED_SHU = """
122
+ [bold]Project detected:[/bold] {project_type} ({file_count} code files)
123
+ {files_section}
124
+
125
+ [bold cyan]What's next?[/bold cyan]
126
+
127
+ [bold]1. Fill governance[/bold] (in Claude Code / AI editor):
128
+ Type [bold cyan]{skill_recommendation}[/bold cyan]
129
+ [dim]→ {skill_description}[/dim]
130
+
131
+ [bold]2. Start a session[/bold] (after governance is set up):
132
+ Type [bold cyan]/rai-session-start[/bold cyan]
133
+ [dim]→ Loads your context, remembers patterns, proposes focused work[/dim]
134
+
135
+ [bold]3. Explore the CLI[/bold] (in terminal):
136
+ [dim]raise --help[/dim] — see all commands
137
+ [dim]raise context[/dim] — query project context
138
+ [dim]raise memory[/dim] — query Rai's memory
139
+
140
+ [dim]Don't have Claude Code? https://claude.ai/download[/dim]
141
+ """
142
+
143
+ PROJECT_DETECTED_RI = """{project_type} project ({file_count} files). Created .raise/manifest.yaml
144
+
145
+ [dim]Next:[/dim] {skill_recommendation} [dim]Then:[/dim] /rai-session-start [dim]CLI:[/dim] raise --help [dim](claude.ai/download)[/dim]
146
+ """
147
+
148
+
149
+ def _get_welcome_message(profile: DeveloperProfile | None) -> str:
150
+ """Get welcome message based on profile existence and level."""
151
+ if profile is None:
152
+ return WELCOME_SHU
153
+
154
+ if profile.experience_level == ExperienceLevel.RI:
155
+ return WELCOME_BACK_RI.format(name=profile.name)
156
+ elif profile.experience_level == ExperienceLevel.HA:
157
+ return f"[cyan]Welcome back, {profile.name}.[/cyan]\n"
158
+ else:
159
+ return WELCOME_SHU
160
+
161
+
162
+ def _get_skill_recommendation(project_type: str) -> tuple[str, str]:
163
+ """Get recommended skill based on project type."""
164
+ if project_type == "brownfield":
165
+ return (
166
+ "/rai-project-onboard",
167
+ "Analyze codebase and fill governance from conversation",
168
+ )
169
+ return (
170
+ "/rai-project-create",
171
+ "Fill governance from conversation (new project)",
172
+ )
173
+
174
+
175
+ def _get_project_message(
176
+ project_type: str,
177
+ file_count: int,
178
+ profile: DeveloperProfile | None,
179
+ created_profile: bool,
180
+ bootstrap_result: BootstrapResult | None = None,
181
+ skills_result: SkillScaffoldResult | None = None,
182
+ governance_result: GovernanceScaffoldResult | None = None,
183
+ agent_config: AgentConfig | None = None,
184
+ ) -> str:
185
+ """Get project detection message based on experience level."""
186
+ skills_dir = agent_config.skills_dir if agent_config else ".claude/skills"
187
+ skill_cmd, skill_desc = _get_skill_recommendation(project_type)
188
+
189
+ if profile is None or profile.experience_level == ExperienceLevel.SHU:
190
+ lines = [
191
+ "[bold]Created:[/bold] .raise/manifest.yaml [dim]— project metadata[/dim]"
192
+ ]
193
+ if created_profile:
194
+ lines.append(
195
+ "[bold]Created:[/bold] ~/.rai/developer.yaml "
196
+ "[dim]— your preferences (first time)[/dim]"
197
+ )
198
+ else:
199
+ lines.append(
200
+ "[bold]Loaded:[/bold] ~/.rai/developer.yaml [dim]— your preferences[/dim]"
201
+ )
202
+
203
+ if bootstrap_result is not None:
204
+ if bootstrap_result.already_existed:
205
+ # Check if base patterns were synced even though everything else existed
206
+ if bootstrap_result.patterns_added > 0 or bootstrap_result.patterns_updated > 0:
207
+ parts: list[str] = []
208
+ if bootstrap_result.patterns_added > 0:
209
+ parts.append(f"{bootstrap_result.patterns_added} new")
210
+ if bootstrap_result.patterns_updated > 0:
211
+ parts.append(f"{bootstrap_result.patterns_updated} updated")
212
+ lines.append(
213
+ "[bold]Synced:[/bold] .raise/rai/memory/ "
214
+ f"[dim]— {', '.join(parts)} base patterns[/dim]"
215
+ )
216
+ else:
217
+ lines.append(
218
+ "[bold]Loaded:[/bold] .raise/rai/ "
219
+ "[dim]— Rai base already present[/dim]"
220
+ )
221
+ else:
222
+ if bootstrap_result.identity_copied:
223
+ lines.append(
224
+ "[bold]Created:[/bold] .raise/rai/identity/ "
225
+ "[dim]— Rai's base identity[/dim]"
226
+ )
227
+ if bootstrap_result.patterns_copied:
228
+ from importlib.resources import files as _res_files
229
+
230
+ _base = _res_files("raise_cli.rai_base")
231
+ _src = _base / "memory" / "patterns-base.jsonl"
232
+ _count = len([
233
+ ln for ln in _src.read_text(encoding="utf-8").strip().splitlines()
234
+ if ln.strip()
235
+ ])
236
+ lines.append(
237
+ "[bold]Created:[/bold] .raise/rai/memory/ "
238
+ f"[dim]— {_count} base patterns[/dim]"
239
+ )
240
+ if bootstrap_result.methodology_copied:
241
+ lines.append(
242
+ "[bold]Created:[/bold] .raise/rai/framework/ "
243
+ "[dim]— methodology definition[/dim]"
244
+ )
245
+
246
+ if skills_result is not None:
247
+ if skills_result.already_existed:
248
+ lines.append(
249
+ f"[bold]Loaded:[/bold] {skills_dir}/ "
250
+ "[dim]— skills already present[/dim]"
251
+ )
252
+ elif skills_result.skills_copied > 0:
253
+ lines.append(
254
+ f"[bold]Created:[/bold] {skills_dir}/ "
255
+ f"[dim]— {skills_result.skills_copied} onboarding skills[/dim]"
256
+ )
257
+
258
+ if governance_result is not None:
259
+ if governance_result.already_existed:
260
+ lines.append(
261
+ "[bold]Loaded:[/bold] governance/ "
262
+ "[dim]— governance templates already present[/dim]"
263
+ )
264
+ elif governance_result.files_created > 0:
265
+ lines.append(
266
+ f"[bold]Created:[/bold] governance/ "
267
+ f"[dim]— {governance_result.files_created} governance templates[/dim]"
268
+ )
269
+
270
+ files_section = "\n".join(lines)
271
+
272
+ return PROJECT_DETECTED_SHU.format(
273
+ project_type=project_type.capitalize(),
274
+ file_count=file_count,
275
+ files_section=files_section,
276
+ skill_recommendation=skill_cmd,
277
+ skill_description=skill_desc,
278
+ )
279
+ else:
280
+ bootstrap_msg = ""
281
+ if bootstrap_result is not None:
282
+ if not bootstrap_result.already_existed:
283
+ bootstrap_msg = (
284
+ f" Bootstrapped Rai base v{bootstrap_result.base_version}\n"
285
+ )
286
+ elif bootstrap_result.patterns_added > 0 or bootstrap_result.patterns_updated > 0:
287
+ parts_ri: list[str] = []
288
+ if bootstrap_result.patterns_added > 0:
289
+ parts_ri.append(f"{bootstrap_result.patterns_added} new")
290
+ if bootstrap_result.patterns_updated > 0:
291
+ parts_ri.append(f"{bootstrap_result.patterns_updated} updated")
292
+ bootstrap_msg = (
293
+ f" Synced base patterns: {', '.join(parts_ri)}\n"
294
+ )
295
+ skills_msg = ""
296
+ if skills_result is not None and not skills_result.already_existed:
297
+ skills_msg = (
298
+ f" Installed {skills_result.skills_copied} skills to {skills_dir}/\n"
299
+ )
300
+ governance_msg = ""
301
+ if governance_result is not None and not governance_result.already_existed:
302
+ governance_msg = f" Scaffolded governance/ ({governance_result.files_created} templates)\n"
303
+ return (
304
+ PROJECT_DETECTED_RI.format(
305
+ project_type=project_type.capitalize(),
306
+ file_count=file_count,
307
+ skill_recommendation=skill_cmd,
308
+ )
309
+ + bootstrap_msg
310
+ + skills_msg
311
+ + governance_msg
312
+ )
313
+
314
+
315
+ def _create_new_profile(project_path: Path) -> DeveloperProfile:
316
+ """Create a new developer profile with defaults."""
317
+ today = date.today()
318
+ return DeveloperProfile(
319
+ name="Developer",
320
+ experience_level=ExperienceLevel.SHU,
321
+ first_session=today,
322
+ last_session=today,
323
+ projects=[str(project_path.resolve())],
324
+ )
325
+
326
+
327
+ def _update_profile_with_project(
328
+ profile: DeveloperProfile, project_path: Path
329
+ ) -> DeveloperProfile:
330
+ """Update profile to include current project."""
331
+ project_str = str(project_path.resolve())
332
+ if project_str not in profile.projects:
333
+ profile.projects.append(project_str)
334
+ profile.last_session = date.today()
335
+ return profile
336
+
337
+
338
+ def _resolve_agent_types(
339
+ agent: list[str] | None,
340
+ ide: AgentChoice | None,
341
+ detect: bool,
342
+ project_path: Path,
343
+ registry: AgentRegistry,
344
+ ) -> list[str]:
345
+ """Resolve the list of agent types to initialize for.
346
+
347
+ Priority: --agent > --ide (deprecated) > --detect > default ["claude"]
348
+ """
349
+ if agent:
350
+ return list(agent)
351
+ if ide is not None:
352
+ return [ide.value]
353
+ if detect:
354
+ detected = registry.detect_agents(project_path)
355
+ return detected if detected else ["claude"]
356
+ return ["claude"]
357
+
358
+
359
+ def _prompt_agent_selection(
360
+ detected: list[str],
361
+ registry: AgentRegistry,
362
+ ) -> list[str]:
363
+ """Show detected agents and prompt user to confirm or extend selection.
364
+
365
+ In non-interactive contexts (no TTY), prints the detected agents and
366
+ returns them without prompting.
367
+
368
+ Args:
369
+ detected: Agent types found by auto-detection.
370
+ registry: Registry used to list all available agents.
371
+
372
+ Returns:
373
+ Final list of agent types selected by the user.
374
+ """
375
+ all_agents = registry.list_agents()
376
+ detected_label = ", ".join(detected) if detected else "none"
377
+ console.print(f"\n[bold]Detected agents:[/bold] {detected_label}")
378
+
379
+ if not sys.stdin.isatty():
380
+ return detected if detected else ["claude"]
381
+
382
+ console.print(f"[dim]Available:[/dim] {', '.join(all_agents)}")
383
+ default = ",".join(detected) if detected else "claude"
384
+ raw = typer.prompt(
385
+ "Configure agents (comma-separated)",
386
+ default=default,
387
+ )
388
+ return [a.strip() for a in raw.split(",") if a.strip()]
389
+
390
+
391
+ def _generate_agents_md(
392
+ project_path: Path, agent_types: list[str], project_name: str
393
+ ) -> None:
394
+ """Generate AGENTS.md at project root — cross-tool instructions file.
395
+
396
+ AGENTS.md is supported by Cursor, Windsurf, Copilot, Codex CLI, Kilo Code,
397
+ OpenCode (60K+ repos use it as the universal agent instructions file).
398
+
399
+ Uses IDE-specific session start instructions when only one agent is
400
+ configured; uses generic instructions for multi-agent setups.
401
+ """
402
+ agents_md_path = project_path / "AGENTS.md"
403
+ if agents_md_path.exists():
404
+ return
405
+
406
+ # Claude Code uses /skill-name syntax; other IDEs do not.
407
+ # Use the slash-command form only for single-claude setups.
408
+ if agent_types == ["claude"]:
409
+ session_instruction = "Run `/rai-session-start` to load full context."
410
+ else:
411
+ session_instruction = (
412
+ "Invoke the `rai-session-start` skill from your IDE to load full context."
413
+ )
414
+
415
+ content = (
416
+ f"# {project_name}\n\n"
417
+ f"> RaiSE-governed project. {session_instruction}\n\n"
418
+ f"## Active Agents\n\n" + "\n".join(f"- {a}" for a in agent_types) + "\n\n"
419
+ "## Process\n\n"
420
+ "This project follows the RaiSE methodology. "
421
+ "See `.raise/` for governance artifacts and `rai --help` for CLI.\n"
422
+ )
423
+ agents_md_path.write_text(content, encoding="utf-8")
424
+
425
+
426
+ def init_command(
427
+ name: Annotated[
428
+ str | None,
429
+ typer.Option(
430
+ "--name",
431
+ "-n",
432
+ help="Project name (defaults to directory name)",
433
+ ),
434
+ ] = None,
435
+ path: Annotated[
436
+ Path | None,
437
+ typer.Option(
438
+ "--path",
439
+ "-p",
440
+ help="Project path (defaults to current directory)",
441
+ ),
442
+ ] = None,
443
+ detect: Annotated[
444
+ bool,
445
+ typer.Option(
446
+ "--detect",
447
+ "-d",
448
+ help="Auto-detect installed agents from project markers. Also generates AGENTS.md.",
449
+ ),
450
+ ] = False,
451
+ agent: Annotated[
452
+ list[str] | None,
453
+ typer.Option(
454
+ "--agent",
455
+ help="Target agent(s): claude, cursor, windsurf, copilot, antigravity. Repeatable.",
456
+ ),
457
+ ] = None,
458
+ ide: Annotated[
459
+ AgentChoice | None,
460
+ typer.Option(
461
+ "--ide",
462
+ help="[deprecated] Use --agent instead.",
463
+ hidden=False,
464
+ ),
465
+ ] = None,
466
+ dry_run: Annotated[
467
+ bool,
468
+ typer.Option(
469
+ "--dry-run",
470
+ help="Preview skill updates without writing files.",
471
+ ),
472
+ ] = False,
473
+ force: Annotated[
474
+ bool,
475
+ typer.Option(
476
+ "--force",
477
+ help="Overwrite all skill files without prompting.",
478
+ ),
479
+ ] = False,
480
+ skip_updates: Annotated[
481
+ bool,
482
+ typer.Option(
483
+ "--skip-updates",
484
+ help="Keep all existing skills, only install new ones.",
485
+ ),
486
+ ] = False,
487
+ skill_set: Annotated[
488
+ str | None,
489
+ typer.Option(
490
+ "--skill-set",
491
+ help="Overlay a skill set from .raise/skills/{name}/ on top of builtins.",
492
+ ),
493
+ ] = None,
494
+ ) -> None:
495
+ """Initialize a RaiSE project in the current directory.
496
+
497
+ Detects project type (greenfield/brownfield), creates .raise/manifest.yaml,
498
+ and scaffolds skills/workflows for each target agent.
499
+
500
+ Examples:
501
+ $ raise init # defaults to claude
502
+ $ raise init --agent cursor # single agent
503
+ $ raise init --agent claude --agent cursor # multi-agent
504
+ $ raise init --detect # auto-detect agents
505
+ $ raise init --ide antigravity # (deprecated) alias
506
+ """
507
+ # Determine project path
508
+ project_path = path if path is not None else Path.cwd()
509
+ project_path = project_path.resolve()
510
+
511
+ # Determine project name
512
+ project_name = name if name is not None else project_path.name
513
+
514
+ # Load or create developer profile
515
+ profile = load_developer_profile()
516
+ created_profile = False
517
+
518
+ if profile is None:
519
+ profile = _create_new_profile(project_path)
520
+ save_developer_profile(profile)
521
+ created_profile = True
522
+ else:
523
+ profile = _update_profile_with_project(profile, project_path)
524
+ save_developer_profile(profile)
525
+
526
+ # Detect project type
527
+ detection = detect_project_type(project_path)
528
+
529
+ # Load agent registry (3-tier: built-in → .raise/agents/ → ~/.rai/agents/)
530
+ registry = load_registry(project_root=project_path)
531
+
532
+ # Resolve agent types from flags
533
+ agent_types = _resolve_agent_types(agent, ide, detect, project_path, registry)
534
+
535
+ # When --detect is used, confirm selection interactively
536
+ if detect and agent is None and ide is None:
537
+ agent_types = _prompt_agent_selection(agent_types, registry)
538
+
539
+ # Validate agent types are in registry; skip unknown with warning
540
+ valid_agent_types: list[str] = []
541
+ for at in agent_types:
542
+ try:
543
+ registry.get_config(at)
544
+ valid_agent_types.append(at)
545
+ except KeyError:
546
+ console.print(f"[yellow]Warning:[/yellow] Unknown agent '{at}' — skipped.")
547
+ if not valid_agent_types:
548
+ valid_agent_types = ["claude"]
549
+
550
+ # Create and save manifest with agent types, preserving existing config
551
+ existing_manifest = load_manifest(project_path)
552
+
553
+ project_info = ProjectInfo(
554
+ name=project_name,
555
+ project_type=detection.project_type,
556
+ code_file_count=detection.code_file_count,
557
+ language=detection.language,
558
+ test_command=detection.toolchain.test_command if detection.toolchain else None,
559
+ lint_command=detection.toolchain.lint_command if detection.toolchain else None,
560
+ type_check_command=(
561
+ detection.toolchain.type_check_command if detection.toolchain else None
562
+ ),
563
+ )
564
+ manifest = ProjectManifest(
565
+ project=project_info,
566
+ agents=AgentsManifest(types=valid_agent_types),
567
+ branches=existing_manifest.branches if existing_manifest else BranchConfig(),
568
+ tier=existing_manifest.tier if existing_manifest else None,
569
+ )
570
+ save_manifest(manifest, project_path)
571
+
572
+ # Bootstrap Rai base assets (once, agent-agnostic)
573
+ from raise_cli.onboarding.bootstrap import bootstrap_rai_base
574
+
575
+ bootstrap_result = bootstrap_rai_base(project_path)
576
+
577
+ # Scaffold governance templates (once)
578
+ from raise_cli.onboarding.governance import scaffold_governance
579
+
580
+ governance_result = scaffold_governance(project_path, project_name)
581
+
582
+ # Generate MEMORY.md canonical copy
583
+ from raise_cli.config.paths import (
584
+ get_claude_memory_path,
585
+ get_framework_dir,
586
+ get_memory_dir,
587
+ )
588
+ from raise_cli.onboarding.memory_md import generate_memory_md
589
+
590
+ methodology_path = get_framework_dir(project_path) / "methodology.yaml"
591
+ patterns_path = get_memory_dir(project_path) / "patterns.jsonl"
592
+ memory_content = generate_memory_md(
593
+ methodology_path=methodology_path,
594
+ patterns_path=patterns_path,
595
+ project_name=project_name,
596
+ development_branch=manifest.branches.development,
597
+ )
598
+ canonical_memory = get_memory_dir(project_path) / "MEMORY.md"
599
+ canonical_memory.parent.mkdir(parents=True, exist_ok=True)
600
+ canonical_memory.write_text(memory_content, encoding="utf-8")
601
+
602
+ # Per-agent scaffolding
603
+ first_config = registry.get_config(valid_agent_types[0])
604
+ first_skills_result = None
605
+
606
+ from raise_cli.onboarding.skills import scaffold_skills
607
+ from raise_cli.onboarding.workflows import scaffold_workflows
608
+
609
+ for agent_type in valid_agent_types:
610
+ config = registry.get_config(agent_type)
611
+ plugin = registry.get_plugin(agent_type)
612
+
613
+ # Skills
614
+ skills_result = scaffold_skills(
615
+ project_path,
616
+ agent_config=config,
617
+ plugin=plugin,
618
+ force=force,
619
+ skip_updates=skip_updates,
620
+ dry_run=dry_run,
621
+ skill_set=skill_set,
622
+ )
623
+ if agent_type == valid_agent_types[0]:
624
+ first_skills_result = skills_result
625
+
626
+ # Workflows
627
+ scaffold_workflows(project_path, agent_config=config)
628
+
629
+ # Claude-specific: copy MEMORY.md to .claude/projects/
630
+ if config.agent_type == "claude":
631
+ claude_memory = get_claude_memory_path(project_path)
632
+ claude_memory.parent.mkdir(parents=True, exist_ok=True)
633
+ claude_memory.write_text(memory_content, encoding="utf-8")
634
+
635
+ # Plugin post_init hook
636
+ plugin.post_init(project_path, config)
637
+
638
+ # Dry-run: show skill sync summary and exit
639
+ if dry_run and first_skills_result is not None:
640
+ _print_skill_sync_summary(first_skills_result)
641
+ has_updates = bool(
642
+ first_skills_result.skills_updated
643
+ or first_skills_result.skills_installed
644
+ or first_skills_result.skills_conflicted
645
+ )
646
+ raise typer.Exit(code=0 if not has_updates else 1)
647
+
648
+ # Generate CLAUDE.md (or agent-specific instructions) for RaiSE projects
649
+ # This runs always (not just on --detect) so CLAUDE.md stays in sync with .raise/
650
+ if (project_path / ".raise").is_dir():
651
+ instructions_content = generate_instructions(
652
+ project_name=project_name,
653
+ detection=detection,
654
+ project_path=project_path,
655
+ )
656
+ instructions_path = project_path / first_config.instructions_file
657
+ instructions_path.parent.mkdir(parents=True, exist_ok=True)
658
+ instructions_path.write_text(instructions_content, encoding="utf-8")
659
+
660
+ # Emit init:complete event
661
+ emitter = create_emitter()
662
+ emitter.emit(
663
+ InitCompleteEvent(
664
+ project_path=project_path,
665
+ project_name=project_name,
666
+ )
667
+ )
668
+
669
+ # AGENTS.md on --detect
670
+ if detect:
671
+ _generate_agents_md(project_path, valid_agent_types, project_name)
672
+
673
+ # Output messages
674
+ welcome = _get_welcome_message(profile if not created_profile else None)
675
+ project_msg = _get_project_message(
676
+ project_type=detection.project_type.value,
677
+ file_count=detection.code_file_count,
678
+ profile=profile,
679
+ created_profile=created_profile,
680
+ bootstrap_result=bootstrap_result,
681
+ skills_result=first_skills_result,
682
+ governance_result=governance_result,
683
+ agent_config=first_config,
684
+ )
685
+
686
+ if profile.experience_level == ExperienceLevel.RI and not created_profile:
687
+ console.print(welcome)
688
+ console.print(project_msg)
689
+ else:
690
+ console.print(Panel(welcome.strip(), border_style="cyan"))
691
+ console.print(project_msg)
692
+
693
+ # Warn when brownfield governance was just scaffolded (docs are empty templates)
694
+ if (
695
+ detection.project_type == ProjectType.BROWNFIELD
696
+ and not governance_result.already_existed
697
+ and governance_result.files_created > 0
698
+ ):
699
+ skill_cmd, _ = _get_skill_recommendation("brownfield")
700
+ if profile.experience_level == ExperienceLevel.RI:
701
+ console.print(
702
+ f"\n[yellow]⚠ Governance docs are empty templates.[/yellow] "
703
+ f"Run [bold cyan]{skill_cmd}[/bold cyan] to fill them."
704
+ )
705
+ else:
706
+ console.print(
707
+ Panel(
708
+ f"[bold yellow]Governance docs need your input[/bold yellow]\n\n"
709
+ f"[dim]vision.md, prd.md, backlog.md[/dim] were created as empty templates.\n"
710
+ f"Any agent that reads them now will get [bold]no context[/bold].\n\n"
711
+ f"Fill them before starting work:\n"
712
+ f" [bold cyan]{skill_cmd}[/bold cyan]",
713
+ border_style="yellow",
714
+ title="[yellow]⚠ Next step required[/yellow]",
715
+ )
716
+ )
717
+
718
+ # Convention detection and guardrails generation (--detect only)
719
+ instructions_path = project_path / first_config.instructions_file
720
+ if detect and detection.project_type == ProjectType.BROWNFIELD:
721
+ conventions = detect_conventions(project_path)
722
+
723
+ if conventions.files_analyzed > 0:
724
+ guardrails_content = generate_guardrails(
725
+ conventions, project_name=project_name
726
+ )
727
+ guardrails_dir = project_path / "governance"
728
+ guardrails_dir.mkdir(parents=True, exist_ok=True)
729
+ guardrails_path = guardrails_dir / "guardrails.md"
730
+ guardrails_path.write_text(guardrails_content, encoding="utf-8")
731
+
732
+ conf = conventions.overall_confidence.value.upper()
733
+ if profile.experience_level == ExperienceLevel.RI:
734
+ console.print(
735
+ f"\n[dim]Conventions detected ({conventions.files_analyzed} files, "
736
+ f"{conf} confidence). Generated guardrails.md and {first_config.instructions_file}[/dim]"
737
+ )
738
+ else:
739
+ console.print(
740
+ f"\n[bold cyan]Convention Detection[/bold cyan]\n"
741
+ f"Analyzed {conventions.files_analyzed} files with {conf} confidence.\n"
742
+ f"Generated:\n"
743
+ f" - [bold]{guardrails_path}[/bold] (code standards)\n"
744
+ f" - [bold]{instructions_path}[/bold] (project context)\n\n"
745
+ f"[dim]Review and adjust as needed.[/dim]"
746
+ )