rai-cli 2.0.0a1__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 (137) hide show
  1. rai_cli/__init__.py +38 -0
  2. rai_cli/__main__.py +30 -0
  3. rai_cli/cli/__init__.py +3 -0
  4. rai_cli/cli/commands/__init__.py +3 -0
  5. rai_cli/cli/commands/base.py +101 -0
  6. rai_cli/cli/commands/discover.py +547 -0
  7. rai_cli/cli/commands/init.py +460 -0
  8. rai_cli/cli/commands/memory.py +1626 -0
  9. rai_cli/cli/commands/profile.py +51 -0
  10. rai_cli/cli/commands/session.py +264 -0
  11. rai_cli/cli/commands/skill.py +226 -0
  12. rai_cli/cli/error_handler.py +158 -0
  13. rai_cli/cli/main.py +137 -0
  14. rai_cli/config/__init__.py +11 -0
  15. rai_cli/config/paths.py +309 -0
  16. rai_cli/config/settings.py +180 -0
  17. rai_cli/context/__init__.py +42 -0
  18. rai_cli/context/analyzers/__init__.py +16 -0
  19. rai_cli/context/analyzers/models.py +36 -0
  20. rai_cli/context/analyzers/protocol.py +43 -0
  21. rai_cli/context/analyzers/python.py +291 -0
  22. rai_cli/context/builder.py +1566 -0
  23. rai_cli/context/diff.py +213 -0
  24. rai_cli/context/extractors/__init__.py +13 -0
  25. rai_cli/context/extractors/skills.py +121 -0
  26. rai_cli/context/graph.py +300 -0
  27. rai_cli/context/models.py +134 -0
  28. rai_cli/context/query.py +507 -0
  29. rai_cli/core/__init__.py +37 -0
  30. rai_cli/core/files.py +66 -0
  31. rai_cli/core/text.py +174 -0
  32. rai_cli/core/tools.py +441 -0
  33. rai_cli/discovery/__init__.py +50 -0
  34. rai_cli/discovery/analyzer.py +601 -0
  35. rai_cli/discovery/drift.py +355 -0
  36. rai_cli/discovery/scanner.py +1200 -0
  37. rai_cli/engines/__init__.py +3 -0
  38. rai_cli/exceptions.py +200 -0
  39. rai_cli/governance/__init__.py +11 -0
  40. rai_cli/governance/extractor.py +311 -0
  41. rai_cli/governance/models.py +132 -0
  42. rai_cli/governance/parsers/__init__.py +35 -0
  43. rai_cli/governance/parsers/adr.py +255 -0
  44. rai_cli/governance/parsers/backlog.py +302 -0
  45. rai_cli/governance/parsers/constitution.py +100 -0
  46. rai_cli/governance/parsers/epic.py +299 -0
  47. rai_cli/governance/parsers/glossary.py +297 -0
  48. rai_cli/governance/parsers/guardrails.py +326 -0
  49. rai_cli/governance/parsers/prd.py +93 -0
  50. rai_cli/governance/parsers/vision.py +97 -0
  51. rai_cli/handlers/__init__.py +3 -0
  52. rai_cli/memory/__init__.py +58 -0
  53. rai_cli/memory/loader.py +247 -0
  54. rai_cli/memory/migration.py +247 -0
  55. rai_cli/memory/models.py +169 -0
  56. rai_cli/memory/writer.py +485 -0
  57. rai_cli/onboarding/__init__.py +96 -0
  58. rai_cli/onboarding/bootstrap.py +164 -0
  59. rai_cli/onboarding/claudemd.py +209 -0
  60. rai_cli/onboarding/conventions.py +742 -0
  61. rai_cli/onboarding/detection.py +155 -0
  62. rai_cli/onboarding/governance.py +443 -0
  63. rai_cli/onboarding/manifest.py +101 -0
  64. rai_cli/onboarding/memory_md.py +387 -0
  65. rai_cli/onboarding/migration.py +207 -0
  66. rai_cli/onboarding/profile.py +457 -0
  67. rai_cli/onboarding/skills.py +114 -0
  68. rai_cli/output/__init__.py +28 -0
  69. rai_cli/output/console.py +394 -0
  70. rai_cli/output/formatters/__init__.py +9 -0
  71. rai_cli/output/formatters/discover.py +442 -0
  72. rai_cli/output/formatters/skill.py +293 -0
  73. rai_cli/rai_base/__init__.py +22 -0
  74. rai_cli/rai_base/framework/__init__.py +7 -0
  75. rai_cli/rai_base/framework/methodology.yaml +235 -0
  76. rai_cli/rai_base/governance/__init__.py +1 -0
  77. rai_cli/rai_base/governance/architecture/__init__.py +1 -0
  78. rai_cli/rai_base/governance/architecture/domain-model.md +20 -0
  79. rai_cli/rai_base/governance/architecture/system-context.md +34 -0
  80. rai_cli/rai_base/governance/architecture/system-design.md +24 -0
  81. rai_cli/rai_base/governance/backlog.md +8 -0
  82. rai_cli/rai_base/governance/guardrails.md +18 -0
  83. rai_cli/rai_base/governance/prd.md +25 -0
  84. rai_cli/rai_base/governance/vision.md +16 -0
  85. rai_cli/rai_base/identity/__init__.py +8 -0
  86. rai_cli/rai_base/identity/core.md +119 -0
  87. rai_cli/rai_base/identity/perspective.md +119 -0
  88. rai_cli/rai_base/memory/__init__.py +7 -0
  89. rai_cli/rai_base/memory/patterns-base.jsonl +20 -0
  90. rai_cli/schemas/__init__.py +3 -0
  91. rai_cli/schemas/session_state.py +106 -0
  92. rai_cli/session/__init__.py +5 -0
  93. rai_cli/session/bundle.py +389 -0
  94. rai_cli/session/close.py +255 -0
  95. rai_cli/session/state.py +108 -0
  96. rai_cli/skills/__init__.py +44 -0
  97. rai_cli/skills/locator.py +129 -0
  98. rai_cli/skills/name_checker.py +203 -0
  99. rai_cli/skills/parser.py +145 -0
  100. rai_cli/skills/scaffold.py +185 -0
  101. rai_cli/skills/schema.py +130 -0
  102. rai_cli/skills/validator.py +172 -0
  103. rai_cli/skills_base/__init__.py +59 -0
  104. rai_cli/skills_base/rai-debug/SKILL.md +296 -0
  105. rai_cli/skills_base/rai-discover-document/SKILL.md +292 -0
  106. rai_cli/skills_base/rai-discover-scan/SKILL.md +325 -0
  107. rai_cli/skills_base/rai-discover-start/SKILL.md +213 -0
  108. rai_cli/skills_base/rai-discover-validate/SKILL.md +310 -0
  109. rai_cli/skills_base/rai-epic-close/SKILL.md +369 -0
  110. rai_cli/skills_base/rai-epic-design/SKILL.md +622 -0
  111. rai_cli/skills_base/rai-epic-plan/SKILL.md +672 -0
  112. rai_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
  113. rai_cli/skills_base/rai-epic-start/SKILL.md +217 -0
  114. rai_cli/skills_base/rai-project-create/SKILL.md +455 -0
  115. rai_cli/skills_base/rai-project-onboard/SKILL.md +503 -0
  116. rai_cli/skills_base/rai-research/SKILL.md +264 -0
  117. rai_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
  118. rai_cli/skills_base/rai-session-close/SKILL.md +151 -0
  119. rai_cli/skills_base/rai-session-start/SKILL.md +110 -0
  120. rai_cli/skills_base/rai-story-close/SKILL.md +367 -0
  121. rai_cli/skills_base/rai-story-design/SKILL.md +339 -0
  122. rai_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
  123. rai_cli/skills_base/rai-story-implement/SKILL.md +256 -0
  124. rai_cli/skills_base/rai-story-plan/SKILL.md +307 -0
  125. rai_cli/skills_base/rai-story-review/SKILL.md +276 -0
  126. rai_cli/skills_base/rai-story-start/SKILL.md +288 -0
  127. rai_cli/telemetry/__init__.py +42 -0
  128. rai_cli/telemetry/schemas.py +285 -0
  129. rai_cli/telemetry/writer.py +210 -0
  130. rai_cli/viz/__init__.py +7 -0
  131. rai_cli/viz/generator.py +404 -0
  132. rai_cli-2.0.0a1.dist-info/METADATA +289 -0
  133. rai_cli-2.0.0a1.dist-info/RECORD +137 -0
  134. rai_cli-2.0.0a1.dist-info/WHEEL +4 -0
  135. rai_cli-2.0.0a1.dist-info/entry_points.txt +2 -0
  136. rai_cli-2.0.0a1.dist-info/licenses/LICENSE +190 -0
  137. rai_cli-2.0.0a1.dist-info/licenses/NOTICE +4 -0
@@ -0,0 +1,460 @@
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
+ - Outputs adaptive messages based on experience level
8
+ - Optionally detects conventions and generates guardrails (--detect)
9
+
10
+ Example:
11
+ $ raise init
12
+ $ raise init --name my-custom-name
13
+ $ raise init --detect # Detect conventions and generate guardrails
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from datetime import date
19
+ from pathlib import Path
20
+ from typing import TYPE_CHECKING, Annotated
21
+
22
+ if TYPE_CHECKING:
23
+ from rai_cli.onboarding.bootstrap import BootstrapResult
24
+ from rai_cli.onboarding.governance import GovernanceScaffoldResult
25
+ from rai_cli.onboarding.skills import SkillScaffoldResult
26
+
27
+ import typer
28
+ from rich.console import Console
29
+ from rich.panel import Panel
30
+
31
+ from rai_cli.onboarding.claudemd import generate_claude_md
32
+ from rai_cli.onboarding.conventions import detect_conventions
33
+ from rai_cli.onboarding.detection import ProjectType, detect_project_type
34
+ from rai_cli.onboarding.governance import generate_guardrails
35
+ from rai_cli.onboarding.manifest import ProjectInfo, ProjectManifest, save_manifest
36
+ from rai_cli.onboarding.profile import (
37
+ DeveloperProfile,
38
+ ExperienceLevel,
39
+ load_developer_profile,
40
+ save_developer_profile,
41
+ )
42
+
43
+ console = Console()
44
+
45
+
46
+ # Message templates for different experience levels
47
+ WELCOME_SHU = """[bold cyan]Welcome to RaiSE![/bold cyan]
48
+
49
+ I'm [bold]Rai[/bold] — your AI partner for reliable software engineering.
50
+
51
+ Together, we'll build software that's both fast AND reliable.
52
+ The RaiSE methodology guides our collaboration:
53
+ • [dim]You[/dim] bring intuition and judgment
54
+ • [dim]I[/dim] bring execution and memory
55
+ • [dim]Together[/dim]: reliable software at AI speed
56
+ """
57
+
58
+ WELCOME_BACK_RI = "[dim]Welcome back, {name}.[/dim]"
59
+
60
+ PROJECT_DETECTED_SHU = """
61
+ [bold]Project detected:[/bold] {project_type} ({file_count} code files)
62
+ {files_section}
63
+
64
+ [bold cyan]What's next?[/bold cyan]
65
+
66
+ [bold]1. Fill governance[/bold] (in Claude Code / AI editor):
67
+ Type [bold cyan]{skill_recommendation}[/bold cyan]
68
+ [dim]→ {skill_description}[/dim]
69
+
70
+ [bold]2. Start a session[/bold] (after governance is set up):
71
+ Type [bold cyan]/rai-session-start[/bold cyan]
72
+ [dim]→ Loads your context, remembers patterns, proposes focused work[/dim]
73
+
74
+ [bold]3. Explore the CLI[/bold] (in terminal):
75
+ [dim]raise --help[/dim] — see all commands
76
+ [dim]raise context[/dim] — query project context
77
+ [dim]raise memory[/dim] — query Rai's memory
78
+
79
+ [dim]Don't have Claude Code? https://claude.ai/download[/dim]
80
+ """
81
+
82
+ PROJECT_DETECTED_RI = """{project_type} project ({file_count} files). Created .raise/manifest.yaml
83
+
84
+ [dim]Next:[/dim] {skill_recommendation} [dim]Then:[/dim] /rai-session-start [dim]CLI:[/dim] raise --help [dim](claude.ai/download)[/dim]
85
+ """
86
+
87
+
88
+ def _get_welcome_message(profile: DeveloperProfile | None) -> str:
89
+ """Get welcome message based on profile existence and level."""
90
+ if profile is None:
91
+ return WELCOME_SHU
92
+
93
+ if profile.experience_level == ExperienceLevel.RI:
94
+ return WELCOME_BACK_RI.format(name=profile.name)
95
+ elif profile.experience_level == ExperienceLevel.HA:
96
+ return f"[cyan]Welcome back, {profile.name}.[/cyan]\n"
97
+ else:
98
+ return WELCOME_SHU
99
+
100
+
101
+ def _get_skill_recommendation(project_type: str) -> tuple[str, str]:
102
+ """Get recommended skill based on project type.
103
+
104
+ Args:
105
+ project_type: Detected project type (greenfield/brownfield).
106
+
107
+ Returns:
108
+ Tuple of (skill_command, description).
109
+ """
110
+ if project_type == "brownfield":
111
+ return (
112
+ "/rai-project-onboard",
113
+ "Analyze codebase and fill governance from conversation",
114
+ )
115
+ return (
116
+ "/rai-project-create",
117
+ "Fill governance from conversation (new project)",
118
+ )
119
+
120
+
121
+ def _get_project_message(
122
+ project_type: str,
123
+ file_count: int,
124
+ profile: DeveloperProfile | None,
125
+ created_profile: bool,
126
+ bootstrap_result: BootstrapResult | None = None,
127
+ skills_result: SkillScaffoldResult | None = None,
128
+ governance_result: GovernanceScaffoldResult | None = None,
129
+ ) -> str:
130
+ """Get project detection message based on experience level.
131
+
132
+ Args:
133
+ project_type: Detected project type (greenfield/brownfield).
134
+ file_count: Number of code files detected.
135
+ profile: Developer profile (None for new users).
136
+ created_profile: Whether profile was just created.
137
+ bootstrap_result: Result of base Rai bootstrap (None if not run).
138
+ skills_result: Result of skill scaffolding (None if not run).
139
+ governance_result: Result of governance scaffolding (None if not run).
140
+
141
+ Returns:
142
+ Formatted message string for console output.
143
+ """
144
+ skill_cmd, skill_desc = _get_skill_recommendation(project_type)
145
+
146
+ if profile is None or profile.experience_level == ExperienceLevel.SHU:
147
+ # Build files section with descriptions
148
+ lines = [
149
+ "[bold]Created:[/bold] .raise/manifest.yaml [dim]— project metadata[/dim]"
150
+ ]
151
+ if created_profile:
152
+ lines.append(
153
+ "[bold]Created:[/bold] ~/.rai/developer.yaml "
154
+ "[dim]— your preferences (first time)[/dim]"
155
+ )
156
+ else:
157
+ lines.append(
158
+ "[bold]Loaded:[/bold] ~/.rai/developer.yaml [dim]— your preferences[/dim]"
159
+ )
160
+
161
+ # Bootstrap info
162
+ if bootstrap_result is not None:
163
+ if bootstrap_result.already_existed:
164
+ lines.append(
165
+ "[bold]Loaded:[/bold] .raise/rai/ "
166
+ "[dim]— Rai base already present[/dim]"
167
+ )
168
+ else:
169
+ if bootstrap_result.identity_copied:
170
+ lines.append(
171
+ "[bold]Created:[/bold] .raise/rai/identity/ "
172
+ "[dim]— Rai's base identity[/dim]"
173
+ )
174
+ if bootstrap_result.patterns_copied:
175
+ lines.append(
176
+ "[bold]Created:[/bold] .raise/rai/memory/ "
177
+ "[dim]— 20 universal patterns[/dim]"
178
+ )
179
+ if bootstrap_result.methodology_copied:
180
+ lines.append(
181
+ "[bold]Created:[/bold] .raise/rai/framework/ "
182
+ "[dim]— methodology definition[/dim]"
183
+ )
184
+
185
+ # Skills info
186
+ if skills_result is not None:
187
+ if skills_result.already_existed:
188
+ lines.append(
189
+ "[bold]Loaded:[/bold] .claude/skills/ "
190
+ "[dim]— skills already present[/dim]"
191
+ )
192
+ elif skills_result.skills_copied > 0:
193
+ lines.append(
194
+ f"[bold]Created:[/bold] .claude/skills/ "
195
+ f"[dim]— {skills_result.skills_copied} onboarding skills[/dim]"
196
+ )
197
+
198
+ # Governance info
199
+ if governance_result is not None:
200
+ if governance_result.already_existed:
201
+ lines.append(
202
+ "[bold]Loaded:[/bold] governance/ "
203
+ "[dim]— governance templates already present[/dim]"
204
+ )
205
+ elif governance_result.files_created > 0:
206
+ lines.append(
207
+ f"[bold]Created:[/bold] governance/ "
208
+ f"[dim]— {governance_result.files_created} governance templates[/dim]"
209
+ )
210
+
211
+ files_section = "\n".join(lines)
212
+
213
+ return PROJECT_DETECTED_SHU.format(
214
+ project_type=project_type.capitalize(),
215
+ file_count=file_count,
216
+ files_section=files_section,
217
+ skill_recommendation=skill_cmd,
218
+ skill_description=skill_desc,
219
+ )
220
+ else:
221
+ bootstrap_msg = ""
222
+ if bootstrap_result is not None and not bootstrap_result.already_existed:
223
+ bootstrap_msg = (
224
+ f" Bootstrapped Rai base v{bootstrap_result.base_version}\n"
225
+ )
226
+ skills_msg = ""
227
+ if skills_result is not None and not skills_result.already_existed:
228
+ skills_msg = (
229
+ f" Installed {skills_result.skills_copied} skills"
230
+ f" to .claude/skills/\n"
231
+ )
232
+ governance_msg = ""
233
+ if governance_result is not None and not governance_result.already_existed:
234
+ governance_msg = (
235
+ f" Scaffolded governance/ ({governance_result.files_created} templates)\n"
236
+ )
237
+ return (
238
+ PROJECT_DETECTED_RI.format(
239
+ project_type=project_type.capitalize(),
240
+ file_count=file_count,
241
+ skill_recommendation=skill_cmd,
242
+ )
243
+ + bootstrap_msg
244
+ + skills_msg
245
+ + governance_msg
246
+ )
247
+
248
+
249
+ def _create_new_profile(project_path: Path) -> DeveloperProfile:
250
+ """Create a new developer profile with defaults."""
251
+ today = date.today()
252
+ return DeveloperProfile(
253
+ name="Developer", # Will be personalized later
254
+ experience_level=ExperienceLevel.SHU,
255
+ first_session=today,
256
+ last_session=today,
257
+ projects=[str(project_path.resolve())],
258
+ )
259
+
260
+
261
+ def _update_profile_with_project(
262
+ profile: DeveloperProfile, project_path: Path
263
+ ) -> DeveloperProfile:
264
+ """Update profile to include current project."""
265
+ project_str = str(project_path.resolve())
266
+ if project_str not in profile.projects:
267
+ profile.projects.append(project_str)
268
+ profile.last_session = date.today()
269
+ return profile
270
+
271
+
272
+ def init_command(
273
+ name: Annotated[
274
+ str | None,
275
+ typer.Option(
276
+ "--name",
277
+ "-n",
278
+ help="Project name (defaults to directory name)",
279
+ ),
280
+ ] = None,
281
+ path: Annotated[
282
+ Path | None,
283
+ typer.Option(
284
+ "--path",
285
+ "-p",
286
+ help="Project path (defaults to current directory)",
287
+ ),
288
+ ] = None,
289
+ detect: Annotated[
290
+ bool,
291
+ typer.Option(
292
+ "--detect",
293
+ "-d",
294
+ help="Detect conventions and generate guardrails.md",
295
+ ),
296
+ ] = False,
297
+ ) -> None:
298
+ """Initialize a RaiSE project in the current directory.
299
+
300
+ Detects project type (greenfield/brownfield), creates .raise/manifest.yaml,
301
+ and sets up developer profile for personalized interaction.
302
+
303
+ With --detect, also analyzes code conventions and generates guardrails.
304
+
305
+ Examples:
306
+ $ raise init
307
+ $ raise init --name my-api
308
+ $ raise init --path /path/to/project
309
+ $ raise init --detect # Detect conventions and generate guardrails
310
+ """
311
+ # Determine project path
312
+ project_path = path if path is not None else Path.cwd()
313
+ project_path = project_path.resolve()
314
+
315
+ # Determine project name
316
+ project_name = name if name is not None else project_path.name
317
+
318
+ # Load or create developer profile
319
+ profile = load_developer_profile()
320
+ created_profile = False
321
+
322
+ if profile is None:
323
+ profile = _create_new_profile(project_path)
324
+ save_developer_profile(profile)
325
+ created_profile = True
326
+ else:
327
+ profile = _update_profile_with_project(profile, project_path)
328
+ save_developer_profile(profile)
329
+
330
+ # Detect project type
331
+ detection = detect_project_type(project_path)
332
+
333
+ # Create and save manifest
334
+ project_info = ProjectInfo(
335
+ name=project_name,
336
+ project_type=detection.project_type,
337
+ code_file_count=detection.code_file_count,
338
+ )
339
+ manifest = ProjectManifest(project=project_info)
340
+ save_manifest(manifest, project_path)
341
+
342
+ # Bootstrap Rai base assets
343
+ from rai_cli.onboarding.bootstrap import bootstrap_rai_base
344
+
345
+ bootstrap_result = bootstrap_rai_base(project_path)
346
+
347
+ # Scaffold onboarding skills
348
+ from rai_cli.onboarding.skills import scaffold_skills
349
+
350
+ skills_result = scaffold_skills(project_path)
351
+
352
+ # Scaffold governance templates
353
+ from rai_cli.onboarding.governance import scaffold_governance
354
+
355
+ governance_result = scaffold_governance(project_path, project_name)
356
+
357
+ # Generate MEMORY.md (canonical + Claude Code)
358
+ from rai_cli.config.paths import (
359
+ get_claude_memory_path,
360
+ get_framework_dir,
361
+ get_memory_dir,
362
+ )
363
+ from rai_cli.onboarding.memory_md import generate_memory_md
364
+
365
+ methodology_path = get_framework_dir(project_path) / "methodology.yaml"
366
+ patterns_path = get_memory_dir(project_path) / "patterns.jsonl"
367
+
368
+ memory_content = generate_memory_md(
369
+ methodology_path=methodology_path,
370
+ patterns_path=patterns_path,
371
+ project_name=project_name,
372
+ )
373
+
374
+ # Write canonical copy
375
+ canonical_memory = get_memory_dir(project_path) / "MEMORY.md"
376
+ canonical_memory.parent.mkdir(parents=True, exist_ok=True)
377
+ canonical_memory.write_text(memory_content)
378
+
379
+ # Write Claude Code copy
380
+ claude_memory = get_claude_memory_path(project_path)
381
+ claude_memory.parent.mkdir(parents=True, exist_ok=True)
382
+ claude_memory.write_text(memory_content)
383
+
384
+ # Output messages based on experience level
385
+ welcome = _get_welcome_message(profile if not created_profile else None)
386
+ project_msg = _get_project_message(
387
+ project_type=detection.project_type.value,
388
+ file_count=detection.code_file_count,
389
+ profile=profile,
390
+ created_profile=created_profile,
391
+ bootstrap_result=bootstrap_result,
392
+ skills_result=skills_result,
393
+ governance_result=governance_result,
394
+ )
395
+
396
+ if profile.experience_level == ExperienceLevel.RI and not created_profile:
397
+ # Concise output for experienced users
398
+ console.print(welcome)
399
+ console.print(project_msg)
400
+ else:
401
+ # Rich output for new/learning users
402
+ console.print(Panel(welcome.strip(), border_style="cyan"))
403
+ console.print(project_msg)
404
+
405
+ # Convention detection, guardrails, and CLAUDE.md generation
406
+ if detect and detection.project_type == ProjectType.BROWNFIELD:
407
+ conventions = detect_conventions(project_path)
408
+
409
+ if conventions.files_analyzed > 0:
410
+ # Generate guardrails markdown
411
+ guardrails_content = generate_guardrails(
412
+ conventions, project_name=project_name
413
+ )
414
+
415
+ # Write to governance/guardrails.md
416
+ guardrails_dir = project_path / "governance"
417
+ guardrails_dir.mkdir(parents=True, exist_ok=True)
418
+ guardrails_path = guardrails_dir / "guardrails.md"
419
+ guardrails_path.write_text(guardrails_content)
420
+
421
+ # Generate CLAUDE.md
422
+ claude_md_content = generate_claude_md(
423
+ project_name=project_name,
424
+ detection=detection,
425
+ conventions=conventions,
426
+ )
427
+ claude_md_path = project_path / "CLAUDE.md"
428
+ claude_md_path.write_text(claude_md_content)
429
+
430
+ # Output summary
431
+ conf = conventions.overall_confidence.value.upper()
432
+ if profile.experience_level == ExperienceLevel.RI:
433
+ console.print(
434
+ f"\n[dim]Conventions detected ({conventions.files_analyzed} files, "
435
+ f"{conf} confidence). Generated guardrails.md and CLAUDE.md[/dim]"
436
+ )
437
+ else:
438
+ console.print(
439
+ f"\n[bold cyan]Convention Detection[/bold cyan]\n"
440
+ f"Analyzed {conventions.files_analyzed} files with {conf} confidence.\n"
441
+ f"Generated:\n"
442
+ f" - [bold]{guardrails_path}[/bold] (code standards)\n"
443
+ f" - [bold]{claude_md_path}[/bold] (project context)\n\n"
444
+ f"[dim]Review and adjust as needed.[/dim]"
445
+ )
446
+ elif detect and detection.project_type == ProjectType.GREENFIELD:
447
+ # Generate minimal CLAUDE.md for greenfield
448
+ claude_md_content = generate_claude_md(
449
+ project_name=project_name,
450
+ detection=detection,
451
+ conventions=None,
452
+ )
453
+ claude_md_path = project_path / "CLAUDE.md"
454
+ claude_md_path.write_text(claude_md_content)
455
+
456
+ if profile.experience_level != ExperienceLevel.RI:
457
+ console.print(
458
+ f"\n[dim]Created {claude_md_path}. No code to analyze yet — "
459
+ "guardrails will be generated when conventions are established.[/dim]"
460
+ )