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.
- raise_cli/__init__.py +38 -0
- raise_cli/__main__.py +30 -0
- raise_cli/adapters/__init__.py +91 -0
- raise_cli/adapters/declarative/__init__.py +26 -0
- raise_cli/adapters/declarative/adapter.py +267 -0
- raise_cli/adapters/declarative/discovery.py +94 -0
- raise_cli/adapters/declarative/expressions.py +150 -0
- raise_cli/adapters/declarative/reference/__init__.py +1 -0
- raise_cli/adapters/declarative/reference/github.yaml +143 -0
- raise_cli/adapters/declarative/schema.py +98 -0
- raise_cli/adapters/filesystem.py +299 -0
- raise_cli/adapters/mcp_bridge.py +10 -0
- raise_cli/adapters/mcp_confluence.py +246 -0
- raise_cli/adapters/mcp_jira.py +405 -0
- raise_cli/adapters/models.py +205 -0
- raise_cli/adapters/protocols.py +180 -0
- raise_cli/adapters/registry.py +90 -0
- raise_cli/adapters/sync.py +149 -0
- raise_cli/agents/__init__.py +14 -0
- raise_cli/agents/antigravity.yaml +8 -0
- raise_cli/agents/claude.yaml +8 -0
- raise_cli/agents/copilot.yaml +8 -0
- raise_cli/agents/copilot_plugin.py +124 -0
- raise_cli/agents/cursor.yaml +7 -0
- raise_cli/agents/roo.yaml +8 -0
- raise_cli/agents/windsurf.yaml +8 -0
- raise_cli/artifacts/__init__.py +30 -0
- raise_cli/artifacts/models.py +43 -0
- raise_cli/artifacts/reader.py +55 -0
- raise_cli/artifacts/renderer.py +104 -0
- raise_cli/artifacts/story_design.py +69 -0
- raise_cli/artifacts/writer.py +45 -0
- raise_cli/backlog/__init__.py +1 -0
- raise_cli/backlog/sync.py +115 -0
- raise_cli/cli/__init__.py +3 -0
- raise_cli/cli/commands/__init__.py +3 -0
- raise_cli/cli/commands/_resolve.py +153 -0
- raise_cli/cli/commands/adapters.py +362 -0
- raise_cli/cli/commands/artifact.py +137 -0
- raise_cli/cli/commands/backlog.py +333 -0
- raise_cli/cli/commands/base.py +31 -0
- raise_cli/cli/commands/discover.py +551 -0
- raise_cli/cli/commands/docs.py +130 -0
- raise_cli/cli/commands/doctor.py +177 -0
- raise_cli/cli/commands/gate.py +223 -0
- raise_cli/cli/commands/graph.py +1086 -0
- raise_cli/cli/commands/info.py +81 -0
- raise_cli/cli/commands/init.py +746 -0
- raise_cli/cli/commands/journal.py +167 -0
- raise_cli/cli/commands/mcp.py +524 -0
- raise_cli/cli/commands/memory.py +467 -0
- raise_cli/cli/commands/pattern.py +348 -0
- raise_cli/cli/commands/profile.py +59 -0
- raise_cli/cli/commands/publish.py +80 -0
- raise_cli/cli/commands/release.py +338 -0
- raise_cli/cli/commands/session.py +528 -0
- raise_cli/cli/commands/signal.py +410 -0
- raise_cli/cli/commands/skill.py +350 -0
- raise_cli/cli/commands/skill_set.py +145 -0
- raise_cli/cli/error_handler.py +158 -0
- raise_cli/cli/main.py +163 -0
- raise_cli/compat.py +66 -0
- raise_cli/config/__init__.py +41 -0
- raise_cli/config/agent_plugin.py +105 -0
- raise_cli/config/agent_registry.py +233 -0
- raise_cli/config/agents.py +120 -0
- raise_cli/config/ide.py +32 -0
- raise_cli/config/paths.py +379 -0
- raise_cli/config/settings.py +180 -0
- raise_cli/context/__init__.py +42 -0
- raise_cli/context/analyzers/__init__.py +16 -0
- raise_cli/context/analyzers/models.py +36 -0
- raise_cli/context/analyzers/protocol.py +43 -0
- raise_cli/context/analyzers/python.py +292 -0
- raise_cli/context/builder.py +1569 -0
- raise_cli/context/diff.py +213 -0
- raise_cli/context/extractors/__init__.py +13 -0
- raise_cli/context/extractors/skills.py +121 -0
- raise_cli/core/__init__.py +37 -0
- raise_cli/core/files.py +66 -0
- raise_cli/core/text.py +174 -0
- raise_cli/core/tools.py +441 -0
- raise_cli/discovery/__init__.py +50 -0
- raise_cli/discovery/analyzer.py +691 -0
- raise_cli/discovery/drift.py +355 -0
- raise_cli/discovery/scanner.py +1687 -0
- raise_cli/doctor/__init__.py +4 -0
- raise_cli/doctor/checks/__init__.py +1 -0
- raise_cli/doctor/checks/environment.py +110 -0
- raise_cli/doctor/checks/project.py +238 -0
- raise_cli/doctor/fix.py +80 -0
- raise_cli/doctor/models.py +56 -0
- raise_cli/doctor/protocol.py +43 -0
- raise_cli/doctor/registry.py +100 -0
- raise_cli/doctor/report.py +141 -0
- raise_cli/doctor/runner.py +95 -0
- raise_cli/engines/__init__.py +3 -0
- raise_cli/exceptions.py +215 -0
- raise_cli/gates/__init__.py +19 -0
- raise_cli/gates/builtin/__init__.py +1 -0
- raise_cli/gates/builtin/coverage.py +52 -0
- raise_cli/gates/builtin/lint.py +48 -0
- raise_cli/gates/builtin/tests.py +48 -0
- raise_cli/gates/builtin/types.py +48 -0
- raise_cli/gates/models.py +40 -0
- raise_cli/gates/protocol.py +41 -0
- raise_cli/gates/registry.py +141 -0
- raise_cli/governance/__init__.py +11 -0
- raise_cli/governance/extractor.py +412 -0
- raise_cli/governance/models.py +134 -0
- raise_cli/governance/parsers/__init__.py +35 -0
- raise_cli/governance/parsers/_convert.py +38 -0
- raise_cli/governance/parsers/adr.py +274 -0
- raise_cli/governance/parsers/backlog.py +356 -0
- raise_cli/governance/parsers/constitution.py +119 -0
- raise_cli/governance/parsers/epic.py +323 -0
- raise_cli/governance/parsers/glossary.py +316 -0
- raise_cli/governance/parsers/guardrails.py +345 -0
- raise_cli/governance/parsers/prd.py +112 -0
- raise_cli/governance/parsers/roadmap.py +118 -0
- raise_cli/governance/parsers/vision.py +116 -0
- raise_cli/graph/__init__.py +1 -0
- raise_cli/graph/backends/__init__.py +57 -0
- raise_cli/graph/backends/api.py +137 -0
- raise_cli/graph/backends/dual.py +139 -0
- raise_cli/graph/backends/pending.py +84 -0
- raise_cli/handlers/__init__.py +3 -0
- raise_cli/hooks/__init__.py +54 -0
- raise_cli/hooks/builtin/__init__.py +1 -0
- raise_cli/hooks/builtin/backlog.py +216 -0
- raise_cli/hooks/builtin/gate_bridge.py +83 -0
- raise_cli/hooks/builtin/jira_sync.py +127 -0
- raise_cli/hooks/builtin/memory.py +117 -0
- raise_cli/hooks/builtin/telemetry.py +72 -0
- raise_cli/hooks/emitter.py +184 -0
- raise_cli/hooks/events.py +262 -0
- raise_cli/hooks/protocol.py +38 -0
- raise_cli/hooks/registry.py +117 -0
- raise_cli/mcp/__init__.py +33 -0
- raise_cli/mcp/bridge.py +218 -0
- raise_cli/mcp/models.py +43 -0
- raise_cli/mcp/registry.py +77 -0
- raise_cli/mcp/schema.py +41 -0
- raise_cli/memory/__init__.py +58 -0
- raise_cli/memory/loader.py +247 -0
- raise_cli/memory/migration.py +241 -0
- raise_cli/memory/models.py +169 -0
- raise_cli/memory/writer.py +598 -0
- raise_cli/onboarding/__init__.py +103 -0
- raise_cli/onboarding/bootstrap.py +324 -0
- raise_cli/onboarding/claudemd.py +17 -0
- raise_cli/onboarding/conventions.py +742 -0
- raise_cli/onboarding/detection.py +374 -0
- raise_cli/onboarding/governance.py +443 -0
- raise_cli/onboarding/instructions.py +672 -0
- raise_cli/onboarding/manifest.py +201 -0
- raise_cli/onboarding/memory_md.py +399 -0
- raise_cli/onboarding/migration.py +207 -0
- raise_cli/onboarding/profile.py +624 -0
- raise_cli/onboarding/skill_conflict.py +100 -0
- raise_cli/onboarding/skill_manifest.py +176 -0
- raise_cli/onboarding/skills.py +437 -0
- raise_cli/onboarding/workflows.py +101 -0
- raise_cli/output/__init__.py +28 -0
- raise_cli/output/console.py +394 -0
- raise_cli/output/formatters/__init__.py +9 -0
- raise_cli/output/formatters/adapters.py +135 -0
- raise_cli/output/formatters/discover.py +439 -0
- raise_cli/output/formatters/skill.py +298 -0
- raise_cli/publish/__init__.py +3 -0
- raise_cli/publish/changelog.py +80 -0
- raise_cli/publish/check.py +179 -0
- raise_cli/publish/version.py +172 -0
- raise_cli/rai_base/__init__.py +22 -0
- raise_cli/rai_base/framework/__init__.py +7 -0
- raise_cli/rai_base/framework/methodology.yaml +233 -0
- raise_cli/rai_base/governance/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/domain-model.md +20 -0
- raise_cli/rai_base/governance/architecture/system-context.md +34 -0
- raise_cli/rai_base/governance/architecture/system-design.md +24 -0
- raise_cli/rai_base/governance/backlog.md +8 -0
- raise_cli/rai_base/governance/guardrails.md +17 -0
- raise_cli/rai_base/governance/prd.md +25 -0
- raise_cli/rai_base/governance/vision.md +16 -0
- raise_cli/rai_base/identity/__init__.py +8 -0
- raise_cli/rai_base/identity/core.md +119 -0
- raise_cli/rai_base/identity/perspective.md +119 -0
- raise_cli/rai_base/memory/__init__.py +7 -0
- raise_cli/rai_base/memory/patterns-base.jsonl +55 -0
- raise_cli/schemas/__init__.py +3 -0
- raise_cli/schemas/journal.py +49 -0
- raise_cli/schemas/session_state.py +117 -0
- raise_cli/session/__init__.py +5 -0
- raise_cli/session/bundle.py +820 -0
- raise_cli/session/close.py +268 -0
- raise_cli/session/journal.py +119 -0
- raise_cli/session/resolver.py +126 -0
- raise_cli/session/state.py +187 -0
- raise_cli/skills/__init__.py +44 -0
- raise_cli/skills/locator.py +141 -0
- raise_cli/skills/name_checker.py +199 -0
- raise_cli/skills/parser.py +145 -0
- raise_cli/skills/scaffold.py +212 -0
- raise_cli/skills/schema.py +132 -0
- raise_cli/skills/skillsets.py +195 -0
- raise_cli/skills/validator.py +197 -0
- raise_cli/skills_base/__init__.py +80 -0
- raise_cli/skills_base/contract-template.md +60 -0
- raise_cli/skills_base/preamble.md +37 -0
- raise_cli/skills_base/rai-architecture-review/SKILL.md +137 -0
- raise_cli/skills_base/rai-debug/SKILL.md +171 -0
- raise_cli/skills_base/rai-discover/SKILL.md +167 -0
- raise_cli/skills_base/rai-discover-document/SKILL.md +128 -0
- raise_cli/skills_base/rai-discover-scan/SKILL.md +147 -0
- raise_cli/skills_base/rai-discover-start/SKILL.md +145 -0
- raise_cli/skills_base/rai-discover-validate/SKILL.md +142 -0
- raise_cli/skills_base/rai-docs-update/SKILL.md +142 -0
- raise_cli/skills_base/rai-doctor/SKILL.md +120 -0
- raise_cli/skills_base/rai-epic-close/SKILL.md +165 -0
- raise_cli/skills_base/rai-epic-close/templates/retrospective.md +68 -0
- raise_cli/skills_base/rai-epic-design/SKILL.md +146 -0
- raise_cli/skills_base/rai-epic-design/templates/design.md +24 -0
- raise_cli/skills_base/rai-epic-design/templates/scope.md +76 -0
- raise_cli/skills_base/rai-epic-plan/SKILL.md +153 -0
- raise_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
- raise_cli/skills_base/rai-epic-plan/templates/plan-section.md +49 -0
- raise_cli/skills_base/rai-epic-run/SKILL.md +208 -0
- raise_cli/skills_base/rai-epic-start/SKILL.md +136 -0
- raise_cli/skills_base/rai-epic-start/templates/brief.md +34 -0
- raise_cli/skills_base/rai-mcp-add/SKILL.md +176 -0
- raise_cli/skills_base/rai-mcp-remove/SKILL.md +120 -0
- raise_cli/skills_base/rai-mcp-status/SKILL.md +147 -0
- raise_cli/skills_base/rai-problem-shape/SKILL.md +138 -0
- raise_cli/skills_base/rai-project-create/SKILL.md +144 -0
- raise_cli/skills_base/rai-project-onboard/SKILL.md +162 -0
- raise_cli/skills_base/rai-quality-review/SKILL.md +189 -0
- raise_cli/skills_base/rai-research/SKILL.md +143 -0
- raise_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
- raise_cli/skills_base/rai-session-close/SKILL.md +176 -0
- raise_cli/skills_base/rai-session-start/SKILL.md +110 -0
- raise_cli/skills_base/rai-story-close/SKILL.md +198 -0
- raise_cli/skills_base/rai-story-design/SKILL.md +203 -0
- raise_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
- raise_cli/skills_base/rai-story-implement/SKILL.md +115 -0
- raise_cli/skills_base/rai-story-plan/SKILL.md +135 -0
- raise_cli/skills_base/rai-story-review/SKILL.md +178 -0
- raise_cli/skills_base/rai-story-run/SKILL.md +282 -0
- raise_cli/skills_base/rai-story-start/SKILL.md +166 -0
- raise_cli/skills_base/rai-story-start/templates/story.md +38 -0
- raise_cli/skills_base/rai-welcome/SKILL.md +134 -0
- raise_cli/telemetry/__init__.py +42 -0
- raise_cli/telemetry/schemas.py +285 -0
- raise_cli/telemetry/writer.py +217 -0
- raise_cli/tier/__init__.py +0 -0
- raise_cli/tier/context.py +134 -0
- raise_cli/viz/__init__.py +7 -0
- raise_cli/viz/generator.py +406 -0
- raise_cli-2.2.1.dist-info/METADATA +433 -0
- raise_cli-2.2.1.dist-info/RECORD +264 -0
- raise_cli-2.2.1.dist-info/WHEEL +4 -0
- raise_cli-2.2.1.dist-info/entry_points.txt +40 -0
- raise_cli-2.2.1.dist-info/licenses/LICENSE +190 -0
- raise_cli-2.2.1.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""Governance generation and scaffolding.
|
|
2
|
+
|
|
3
|
+
Two capabilities:
|
|
4
|
+
1. Generate guardrails.md from detected conventions (brownfield, --detect).
|
|
5
|
+
2. Scaffold governance/ from bundled rai_base templates (all projects).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from datetime import date
|
|
13
|
+
from enum import StrEnum
|
|
14
|
+
from importlib.resources import files
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from pydantic import BaseModel
|
|
18
|
+
|
|
19
|
+
from raise_cli.onboarding.conventions import (
|
|
20
|
+
Confidence,
|
|
21
|
+
ConventionResult,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GuardrailLevel(StrEnum):
|
|
28
|
+
"""Guardrail enforcement level.
|
|
29
|
+
|
|
30
|
+
Maps to MoSCoW prioritization:
|
|
31
|
+
- MUST: Required, enforced by tooling
|
|
32
|
+
- SHOULD: Recommended, reviewed manually
|
|
33
|
+
- COULD: Optional, noted for awareness
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
MUST = "MUST"
|
|
37
|
+
SHOULD = "SHOULD"
|
|
38
|
+
COULD = "COULD"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class GeneratedGuardrail(BaseModel):
|
|
42
|
+
"""A guardrail generated from detected conventions.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
id: Unique identifier (e.g., "MUST-STYLE-001").
|
|
46
|
+
level: Enforcement level (MUST/SHOULD/COULD).
|
|
47
|
+
category: Category for grouping (e.g., "Code Style").
|
|
48
|
+
description: Human-readable description of the rule.
|
|
49
|
+
verification: Optional command or method to verify compliance.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
id: str
|
|
53
|
+
level: GuardrailLevel
|
|
54
|
+
category: str
|
|
55
|
+
description: str
|
|
56
|
+
verification: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class GuardrailGenerator:
|
|
60
|
+
"""Generates guardrails from detected conventions.
|
|
61
|
+
|
|
62
|
+
Maps confidence levels to guardrail enforcement levels:
|
|
63
|
+
- HIGH confidence → MUST (enforced)
|
|
64
|
+
- MEDIUM confidence → SHOULD (recommended)
|
|
65
|
+
- LOW confidence → COULD (optional)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def confidence_to_level(self, confidence: Confidence) -> GuardrailLevel:
|
|
69
|
+
"""Map confidence to guardrail level.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
confidence: Detection confidence level.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Corresponding guardrail enforcement level.
|
|
76
|
+
"""
|
|
77
|
+
mapping = {
|
|
78
|
+
Confidence.HIGH: GuardrailLevel.MUST,
|
|
79
|
+
Confidence.MEDIUM: GuardrailLevel.SHOULD,
|
|
80
|
+
Confidence.LOW: GuardrailLevel.COULD,
|
|
81
|
+
}
|
|
82
|
+
return mapping[confidence]
|
|
83
|
+
|
|
84
|
+
def _generate_style_guardrails(
|
|
85
|
+
self,
|
|
86
|
+
conventions: ConventionResult,
|
|
87
|
+
next_id: Callable[[GuardrailLevel, str], str],
|
|
88
|
+
) -> list[GeneratedGuardrail]:
|
|
89
|
+
"""Generate style-related guardrails (indentation, quotes, line length)."""
|
|
90
|
+
guardrails: list[GeneratedGuardrail] = []
|
|
91
|
+
|
|
92
|
+
# Indentation
|
|
93
|
+
indent = conventions.style.indentation
|
|
94
|
+
indent_level = self.confidence_to_level(indent.confidence)
|
|
95
|
+
if indent.style == "spaces" and indent.width:
|
|
96
|
+
indent_desc = f"Use {indent.width}-space indentation"
|
|
97
|
+
elif indent.style == "tabs":
|
|
98
|
+
indent_desc = "Use tab indentation"
|
|
99
|
+
else:
|
|
100
|
+
indent_desc = "Use consistent indentation (mixed detected)"
|
|
101
|
+
|
|
102
|
+
guardrails.append(
|
|
103
|
+
GeneratedGuardrail(
|
|
104
|
+
id=next_id(indent_level, "STYLE"),
|
|
105
|
+
level=indent_level,
|
|
106
|
+
category="Code Style",
|
|
107
|
+
description=indent_desc,
|
|
108
|
+
verification="ruff check ."
|
|
109
|
+
if indent_level == GuardrailLevel.MUST
|
|
110
|
+
else None,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Quote style
|
|
115
|
+
quotes = conventions.style.quote_style
|
|
116
|
+
quote_level = self.confidence_to_level(quotes.confidence)
|
|
117
|
+
guardrails.append(
|
|
118
|
+
GeneratedGuardrail(
|
|
119
|
+
id=next_id(quote_level, "STYLE"),
|
|
120
|
+
level=quote_level,
|
|
121
|
+
category="Code Style",
|
|
122
|
+
description=f"Use {quotes.style} quotes for strings",
|
|
123
|
+
verification="ruff check ."
|
|
124
|
+
if quote_level == GuardrailLevel.MUST
|
|
125
|
+
else None,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Line length
|
|
130
|
+
line_length = conventions.style.line_length
|
|
131
|
+
line_level = self.confidence_to_level(line_length.confidence)
|
|
132
|
+
guardrails.append(
|
|
133
|
+
GeneratedGuardrail(
|
|
134
|
+
id=next_id(line_level, "STYLE"),
|
|
135
|
+
level=line_level,
|
|
136
|
+
category="Code Style",
|
|
137
|
+
description=f"Maximum line length: {line_length.max_length} characters",
|
|
138
|
+
verification="ruff check ."
|
|
139
|
+
if line_level == GuardrailLevel.MUST
|
|
140
|
+
else None,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return guardrails
|
|
145
|
+
|
|
146
|
+
def _generate_naming_guardrails(
|
|
147
|
+
self,
|
|
148
|
+
conventions: ConventionResult,
|
|
149
|
+
next_id: Callable[[GuardrailLevel, str], str],
|
|
150
|
+
) -> list[GeneratedGuardrail]:
|
|
151
|
+
"""Generate naming convention guardrails."""
|
|
152
|
+
guardrails: list[GeneratedGuardrail] = []
|
|
153
|
+
|
|
154
|
+
# Functions
|
|
155
|
+
func_naming = conventions.naming.functions
|
|
156
|
+
func_level = self.confidence_to_level(func_naming.confidence)
|
|
157
|
+
guardrails.append(
|
|
158
|
+
GeneratedGuardrail(
|
|
159
|
+
id=next_id(func_level, "NAMING"),
|
|
160
|
+
level=func_level,
|
|
161
|
+
category="Naming",
|
|
162
|
+
description=f"Function names: {func_naming.pattern}",
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Classes
|
|
167
|
+
class_naming = conventions.naming.classes
|
|
168
|
+
class_level = self.confidence_to_level(class_naming.confidence)
|
|
169
|
+
guardrails.append(
|
|
170
|
+
GeneratedGuardrail(
|
|
171
|
+
id=next_id(class_level, "NAMING"),
|
|
172
|
+
level=class_level,
|
|
173
|
+
category="Naming",
|
|
174
|
+
description=f"Class names: {class_naming.pattern}",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Constants (only if meaningful samples)
|
|
179
|
+
const_naming = conventions.naming.constants
|
|
180
|
+
if const_naming.sample_count >= 3:
|
|
181
|
+
const_level = self.confidence_to_level(const_naming.confidence)
|
|
182
|
+
guardrails.append(
|
|
183
|
+
GeneratedGuardrail(
|
|
184
|
+
id=next_id(const_level, "NAMING"),
|
|
185
|
+
level=const_level,
|
|
186
|
+
category="Naming",
|
|
187
|
+
description=f"Constant names: {const_naming.pattern}",
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return guardrails
|
|
192
|
+
|
|
193
|
+
def _generate_structure_guardrails(
|
|
194
|
+
self,
|
|
195
|
+
conventions: ConventionResult,
|
|
196
|
+
next_id: Callable[[GuardrailLevel, str], str],
|
|
197
|
+
) -> list[GeneratedGuardrail]:
|
|
198
|
+
"""Generate project structure guardrails."""
|
|
199
|
+
guardrails: list[GeneratedGuardrail] = []
|
|
200
|
+
structure = conventions.structure
|
|
201
|
+
|
|
202
|
+
if structure.has_src_layout and structure.source_dir:
|
|
203
|
+
guardrails.append(
|
|
204
|
+
GeneratedGuardrail(
|
|
205
|
+
id=next_id(GuardrailLevel.SHOULD, "STRUCTURE"),
|
|
206
|
+
level=GuardrailLevel.SHOULD,
|
|
207
|
+
category="Structure",
|
|
208
|
+
description=f"Source code in src/ layout ({structure.source_dir})",
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if structure.test_dir:
|
|
213
|
+
guardrails.append(
|
|
214
|
+
GeneratedGuardrail(
|
|
215
|
+
id=next_id(GuardrailLevel.SHOULD, "STRUCTURE"),
|
|
216
|
+
level=GuardrailLevel.SHOULD,
|
|
217
|
+
category="Structure",
|
|
218
|
+
description=f"Tests in {structure.test_dir}/ directory",
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return guardrails
|
|
223
|
+
|
|
224
|
+
def generate(self, conventions: ConventionResult) -> list[GeneratedGuardrail]:
|
|
225
|
+
"""Generate guardrails from conventions.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
conventions: Detected conventions from analysis.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
List of generated guardrails.
|
|
232
|
+
"""
|
|
233
|
+
counters: dict[str, int] = {}
|
|
234
|
+
|
|
235
|
+
def next_id(level: GuardrailLevel, category: str) -> str:
|
|
236
|
+
"""Generate next ID for a category."""
|
|
237
|
+
key = f"{level.value}-{category.upper()}"
|
|
238
|
+
counters[key] = counters.get(key, 0) + 1
|
|
239
|
+
return f"{key}-{counters[key]:03d}"
|
|
240
|
+
|
|
241
|
+
guardrails: list[GeneratedGuardrail] = []
|
|
242
|
+
guardrails.extend(self._generate_style_guardrails(conventions, next_id))
|
|
243
|
+
guardrails.extend(self._generate_naming_guardrails(conventions, next_id))
|
|
244
|
+
guardrails.extend(self._generate_structure_guardrails(conventions, next_id))
|
|
245
|
+
return guardrails
|
|
246
|
+
|
|
247
|
+
def to_markdown(
|
|
248
|
+
self,
|
|
249
|
+
conventions: ConventionResult,
|
|
250
|
+
project_name: str | None = None,
|
|
251
|
+
) -> str:
|
|
252
|
+
"""Generate markdown guardrails document.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
conventions: Detected conventions.
|
|
256
|
+
project_name: Optional project name for context.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Markdown content for guardrails.md.
|
|
260
|
+
"""
|
|
261
|
+
guardrails = self.generate(conventions)
|
|
262
|
+
by_category = self._group_by_category(guardrails)
|
|
263
|
+
|
|
264
|
+
lines: list[str] = []
|
|
265
|
+
self._add_md_header(lines, project_name)
|
|
266
|
+
self._add_md_context(lines, conventions)
|
|
267
|
+
self._add_md_guardrail_tables(lines, by_category)
|
|
268
|
+
self._add_md_footer(lines)
|
|
269
|
+
return "\n".join(lines)
|
|
270
|
+
|
|
271
|
+
def _group_by_category(
|
|
272
|
+
self, guardrails: list[GeneratedGuardrail]
|
|
273
|
+
) -> dict[str, list[GeneratedGuardrail]]:
|
|
274
|
+
"""Group guardrails by category."""
|
|
275
|
+
by_category: dict[str, list[GeneratedGuardrail]] = {}
|
|
276
|
+
for g in guardrails:
|
|
277
|
+
by_category.setdefault(g.category, []).append(g)
|
|
278
|
+
return by_category
|
|
279
|
+
|
|
280
|
+
def _add_md_header(self, lines: list[str], project_name: str | None) -> None:
|
|
281
|
+
"""Add markdown document header with YAML frontmatter.
|
|
282
|
+
|
|
283
|
+
Frontmatter is required by the guardrails parser for type identification.
|
|
284
|
+
"""
|
|
285
|
+
lines.append("---")
|
|
286
|
+
lines.append("type: guardrails")
|
|
287
|
+
lines.append('version: "1.0.0"')
|
|
288
|
+
lines.append("---")
|
|
289
|
+
lines.append("")
|
|
290
|
+
title = f"Guardrails: {project_name}" if project_name else "Guardrails"
|
|
291
|
+
lines.append(f"# {title}")
|
|
292
|
+
lines.append("")
|
|
293
|
+
lines.append("> Auto-generated from detected conventions")
|
|
294
|
+
lines.append("")
|
|
295
|
+
|
|
296
|
+
def _add_md_context(self, lines: list[str], conventions: ConventionResult) -> None:
|
|
297
|
+
"""Add context section with confidence info."""
|
|
298
|
+
lines.append("## Context")
|
|
299
|
+
lines.append("")
|
|
300
|
+
lines.append(f"- **Files analyzed:** {conventions.files_analyzed}")
|
|
301
|
+
lines.append(
|
|
302
|
+
f"- **Overall confidence:** {conventions.overall_confidence.value}"
|
|
303
|
+
)
|
|
304
|
+
lines.append(f"- **Generated:** {date.today().isoformat()}")
|
|
305
|
+
lines.append("")
|
|
306
|
+
|
|
307
|
+
if conventions.overall_confidence == Confidence.LOW:
|
|
308
|
+
lines.append(
|
|
309
|
+
"> ⚠️ **Low confidence:** Limited samples analyzed. "
|
|
310
|
+
"Review guardrails carefully before adopting."
|
|
311
|
+
)
|
|
312
|
+
lines.append("")
|
|
313
|
+
|
|
314
|
+
def _add_md_guardrail_tables(
|
|
315
|
+
self, lines: list[str], by_category: dict[str, list[GeneratedGuardrail]]
|
|
316
|
+
) -> None:
|
|
317
|
+
"""Add guardrail tables grouped by category.
|
|
318
|
+
|
|
319
|
+
Uses ### headings and 5-column tables to match the guardrails
|
|
320
|
+
parser contract (parser looks for ``^###`` sections and extracts
|
|
321
|
+
the ``Derived from`` column).
|
|
322
|
+
"""
|
|
323
|
+
for category, cat_guardrails in by_category.items():
|
|
324
|
+
lines.append(f"### {category}")
|
|
325
|
+
lines.append("")
|
|
326
|
+
lines.append("| ID | Level | Guardrail | Verification | Derived from |")
|
|
327
|
+
lines.append("|----|-------|-----------|--------------|--------------|")
|
|
328
|
+
|
|
329
|
+
for g in cat_guardrails:
|
|
330
|
+
verification = g.verification or "Manual review"
|
|
331
|
+
guardrail_id = g.id.lower()
|
|
332
|
+
lines.append(
|
|
333
|
+
f"| {guardrail_id} | {g.level.value} | "
|
|
334
|
+
f"{g.description} | {verification} | Convention |"
|
|
335
|
+
)
|
|
336
|
+
lines.append("")
|
|
337
|
+
|
|
338
|
+
def _add_md_footer(self, lines: list[str]) -> None:
|
|
339
|
+
"""Add document footer."""
|
|
340
|
+
lines.append("---")
|
|
341
|
+
lines.append("")
|
|
342
|
+
lines.append(
|
|
343
|
+
"*Generated by `raise init --detect`. Review and adjust as needed.*"
|
|
344
|
+
)
|
|
345
|
+
lines.append("")
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def generate_guardrails(
|
|
349
|
+
conventions: ConventionResult,
|
|
350
|
+
project_name: str | None = None,
|
|
351
|
+
) -> str:
|
|
352
|
+
"""Convenience function to generate guardrails markdown.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
conventions: Detected conventions from analysis.
|
|
356
|
+
project_name: Optional project name for context.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Markdown content for guardrails.md.
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
>>> result = detect_conventions(Path("/my/project"))
|
|
363
|
+
>>> markdown = generate_guardrails(result, project_name="my-api")
|
|
364
|
+
>>> Path("guardrails.md").write_text(markdown)
|
|
365
|
+
"""
|
|
366
|
+
generator = GuardrailGenerator()
|
|
367
|
+
return generator.to_markdown(conventions, project_name=project_name)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# =============================================================================
|
|
371
|
+
# Governance Scaffolding (from bundled templates)
|
|
372
|
+
# =============================================================================
|
|
373
|
+
|
|
374
|
+
# Template files in rai_base/governance/, relative to package root.
|
|
375
|
+
# Tuples of (source_path_in_package, dest_path_in_governance).
|
|
376
|
+
_GOVERNANCE_TEMPLATES: list[tuple[str, str]] = [
|
|
377
|
+
("prd.md", "prd.md"),
|
|
378
|
+
("vision.md", "vision.md"),
|
|
379
|
+
("guardrails.md", "guardrails.md"),
|
|
380
|
+
("backlog.md", "backlog.md"),
|
|
381
|
+
("architecture/system-context.md", "architecture/system-context.md"),
|
|
382
|
+
("architecture/system-design.md", "architecture/system-design.md"),
|
|
383
|
+
("architecture/domain-model.md", "architecture/domain-model.md"),
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class GovernanceScaffoldResult(BaseModel):
|
|
388
|
+
"""Result of governance scaffolding.
|
|
389
|
+
|
|
390
|
+
Attributes:
|
|
391
|
+
already_existed: True if all files already existed (nothing created).
|
|
392
|
+
files_created: Number of template files created.
|
|
393
|
+
files_skipped: Number of files skipped (already existed).
|
|
394
|
+
path: Path to the governance/ directory.
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
already_existed: bool = False
|
|
398
|
+
files_created: int = 0
|
|
399
|
+
files_skipped: int = 0
|
|
400
|
+
path: Path
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def scaffold_governance(
|
|
404
|
+
project_path: Path,
|
|
405
|
+
project_name: str,
|
|
406
|
+
) -> GovernanceScaffoldResult:
|
|
407
|
+
"""Scaffold governance/ from bundled rai_base templates.
|
|
408
|
+
|
|
409
|
+
Copies template files via importlib.resources, rendering
|
|
410
|
+
``{project_name}`` placeholders. Per-file idempotency: existing
|
|
411
|
+
files are never overwritten.
|
|
412
|
+
|
|
413
|
+
Follows bootstrap.py pattern for asset distribution.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
project_path: Project root directory.
|
|
417
|
+
project_name: Project name for template rendering.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
GovernanceScaffoldResult with details of what was created.
|
|
421
|
+
"""
|
|
422
|
+
gov_dir = project_path / "governance"
|
|
423
|
+
base = files("raise_cli.rai_base") / "governance"
|
|
424
|
+
result = GovernanceScaffoldResult(path=gov_dir)
|
|
425
|
+
|
|
426
|
+
for src_rel, dest_rel in _GOVERNANCE_TEMPLATES:
|
|
427
|
+
dest = gov_dir / dest_rel
|
|
428
|
+
if dest.exists():
|
|
429
|
+
result.files_skipped += 1
|
|
430
|
+
logger.debug("Skipped (exists): %s", dest)
|
|
431
|
+
continue
|
|
432
|
+
|
|
433
|
+
# Read bundled template and render placeholders
|
|
434
|
+
content = (base / src_rel).read_text(encoding="utf-8")
|
|
435
|
+
content = content.replace("{project_name}", project_name)
|
|
436
|
+
|
|
437
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
438
|
+
dest.write_text(content, encoding="utf-8")
|
|
439
|
+
result.files_created += 1
|
|
440
|
+
logger.debug("Created: %s", dest)
|
|
441
|
+
|
|
442
|
+
result.already_existed = result.files_created == 0
|
|
443
|
+
return result
|