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,672 @@
|
|
|
1
|
+
"""Instructions file generation for project onboarding.
|
|
2
|
+
|
|
3
|
+
Generates project-specific instructions content (CLAUDE.md, .cursor/rules/raise.mdc,
|
|
4
|
+
.windsurf/rules/raise.md, etc.) from .raise/ canonical sources. A .raise/ directory
|
|
5
|
+
must exist before generation — ``rai init`` always creates it first.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
|
|
16
|
+
from raise_cli.config.agents import AgentConfig
|
|
17
|
+
from raise_cli.onboarding.conventions import ConventionResult
|
|
18
|
+
from raise_cli.onboarding.detection import DetectionResult
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _str_from_nested(data: dict[str, Any], key1: str, key2: str) -> str | None:
|
|
22
|
+
"""Safely extract a string from a nested dict (YAML-loaded data).
|
|
23
|
+
|
|
24
|
+
Works with YAML-loaded dicts which have ``Any``-typed values.
|
|
25
|
+
"""
|
|
26
|
+
outer = data.get(key1)
|
|
27
|
+
if not isinstance(outer, dict):
|
|
28
|
+
return None
|
|
29
|
+
typed: dict[str, object] = {str(k): v for k, v in outer.items()} # type: ignore[union-attr]
|
|
30
|
+
raw = typed.get(key2)
|
|
31
|
+
return raw if isinstance(raw, str) else None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class InstructionsGenerator:
|
|
35
|
+
"""Generates agent instructions file content from .raise/ sources.
|
|
36
|
+
|
|
37
|
+
Reads identity, methodology, manifest, and optional integration configs
|
|
38
|
+
to produce a comprehensive instructions file (e.g. CLAUDE.md).
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def generate(
|
|
42
|
+
self,
|
|
43
|
+
project_name: str,
|
|
44
|
+
detection: DetectionResult,
|
|
45
|
+
conventions: ConventionResult | None = None,
|
|
46
|
+
*,
|
|
47
|
+
project_path: Path,
|
|
48
|
+
) -> str:
|
|
49
|
+
"""Generate CLAUDE.md content from .raise/ canonical sources.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
project_name: Name of the project.
|
|
53
|
+
detection: Project detection result (type, file count).
|
|
54
|
+
conventions: Optional convention detection result (unused, kept
|
|
55
|
+
for backward compatibility).
|
|
56
|
+
project_path: Project root path containing .raise/ directory.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Markdown content for CLAUDE.md.
|
|
60
|
+
"""
|
|
61
|
+
raise_dir = project_path / ".raise"
|
|
62
|
+
return self._generate_raise_project(project_name, raise_dir)
|
|
63
|
+
|
|
64
|
+
# =========================================================================
|
|
65
|
+
# RaiSE Project Generation
|
|
66
|
+
# =========================================================================
|
|
67
|
+
|
|
68
|
+
def _generate_raise_project(
|
|
69
|
+
self,
|
|
70
|
+
project_name: str,
|
|
71
|
+
raise_dir: Path,
|
|
72
|
+
) -> str:
|
|
73
|
+
"""Generate CLAUDE.md for a RaiSE project from .raise/ sources.
|
|
74
|
+
|
|
75
|
+
Reads identity, methodology, manifest, and optional integration
|
|
76
|
+
configs to produce a comprehensive CLAUDE.md.
|
|
77
|
+
"""
|
|
78
|
+
lines: list[str] = []
|
|
79
|
+
|
|
80
|
+
# Header comment
|
|
81
|
+
lines.append(
|
|
82
|
+
"<!-- Generated from .raise/ canonical source. "
|
|
83
|
+
"Do not edit manually. Regenerate with: rai init -->"
|
|
84
|
+
)
|
|
85
|
+
lines.append("")
|
|
86
|
+
|
|
87
|
+
# Project title + session start
|
|
88
|
+
lines.append("# RaiSE Project")
|
|
89
|
+
lines.append("")
|
|
90
|
+
lines.append(
|
|
91
|
+
"Run `/rai-session-start` at the beginning of each session "
|
|
92
|
+
"to load full context (patterns, coaching, session continuity)."
|
|
93
|
+
)
|
|
94
|
+
lines.append("")
|
|
95
|
+
|
|
96
|
+
# Load sources
|
|
97
|
+
identity_path = raise_dir / "rai" / "identity" / "core.md"
|
|
98
|
+
methodology_path = raise_dir / "rai" / "framework" / "methodology.yaml"
|
|
99
|
+
manifest_path = raise_dir / "manifest.yaml"
|
|
100
|
+
jira_path = raise_dir / "jira.yaml"
|
|
101
|
+
|
|
102
|
+
methodology: dict[str, Any] = {}
|
|
103
|
+
if methodology_path.is_file():
|
|
104
|
+
methodology = yaml.safe_load(methodology_path.read_text(encoding="utf-8"))
|
|
105
|
+
|
|
106
|
+
manifest: dict[str, Any] = {}
|
|
107
|
+
if manifest_path.is_file():
|
|
108
|
+
manifest = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
|
|
109
|
+
|
|
110
|
+
dev_branch = _str_from_nested(manifest, "branches", "development") or "dev"
|
|
111
|
+
|
|
112
|
+
# Identity section
|
|
113
|
+
if identity_path.is_file():
|
|
114
|
+
identity_text = identity_path.read_text(encoding="utf-8")
|
|
115
|
+
self._add_identity_section(lines, identity_text)
|
|
116
|
+
|
|
117
|
+
# Process Rules section
|
|
118
|
+
if methodology:
|
|
119
|
+
self._add_process_rules_section(lines, methodology)
|
|
120
|
+
|
|
121
|
+
# Branch Model section
|
|
122
|
+
if methodology.get("branches"):
|
|
123
|
+
self._add_branch_model_section(lines, methodology, dev_branch)
|
|
124
|
+
|
|
125
|
+
# CLI Quick Reference (static content)
|
|
126
|
+
self._add_cli_reference_section(lines)
|
|
127
|
+
|
|
128
|
+
# External Integrations (only if jira.yaml exists)
|
|
129
|
+
if jira_path.is_file():
|
|
130
|
+
self._add_integrations_section(lines)
|
|
131
|
+
|
|
132
|
+
# File Operations (static)
|
|
133
|
+
self._add_file_operations_section(lines)
|
|
134
|
+
|
|
135
|
+
# Post-Compaction (static)
|
|
136
|
+
self._add_post_compaction_section(lines)
|
|
137
|
+
|
|
138
|
+
return "\n".join(lines)
|
|
139
|
+
|
|
140
|
+
def _add_identity_section(self, lines: list[str], identity_text: str) -> None:
|
|
141
|
+
"""Extract and add Rai Identity section from core.md."""
|
|
142
|
+
lines.append("## Rai Identity")
|
|
143
|
+
lines.append("")
|
|
144
|
+
|
|
145
|
+
# Extract Values
|
|
146
|
+
values = self._extract_values(identity_text)
|
|
147
|
+
if values:
|
|
148
|
+
lines.append("### Values")
|
|
149
|
+
for i, (name, summary) in enumerate(values, 1):
|
|
150
|
+
lines.append(f"{i}. {name} \u2014 {summary}")
|
|
151
|
+
lines.append("")
|
|
152
|
+
|
|
153
|
+
# Extract Boundaries
|
|
154
|
+
i_will, i_wont = self._extract_boundaries(identity_text)
|
|
155
|
+
if i_will or i_wont:
|
|
156
|
+
lines.append("### Boundaries")
|
|
157
|
+
if i_will:
|
|
158
|
+
lines.append("I Will: " + ", ".join(i_will))
|
|
159
|
+
if i_wont:
|
|
160
|
+
lines.append("I Won't: " + ", ".join(i_wont))
|
|
161
|
+
lines.append("")
|
|
162
|
+
|
|
163
|
+
# Extract Principles (from the Internalized Philosophy table if present)
|
|
164
|
+
principles = self._extract_principles_from_identity(identity_text)
|
|
165
|
+
if principles:
|
|
166
|
+
lines.append("### Principles")
|
|
167
|
+
for i, (name, summary) in enumerate(principles, 1):
|
|
168
|
+
lines.append(f"{i}. {name} \u2014 {summary}")
|
|
169
|
+
lines.append("")
|
|
170
|
+
|
|
171
|
+
def _extract_values(self, text: str) -> list[tuple[str, str]]:
|
|
172
|
+
"""Extract value name + summary pairs from identity core.md.
|
|
173
|
+
|
|
174
|
+
Looks for ### N. Name headings followed by bullet points.
|
|
175
|
+
Condenses all bullets into a comma-separated summary.
|
|
176
|
+
"""
|
|
177
|
+
values: list[tuple[str, str]] = []
|
|
178
|
+
# Match ### N. Name followed by bullet list (within Values section)
|
|
179
|
+
pattern = r"###\s+\d+\.\s+(.+)\n((?:- .+\n)*)"
|
|
180
|
+
for match in re.finditer(pattern, text):
|
|
181
|
+
name = match.group(1).strip()
|
|
182
|
+
bullets = match.group(2).strip()
|
|
183
|
+
# Condense ALL bullets into a comma-separated short summary
|
|
184
|
+
bullet_texts = [
|
|
185
|
+
b.lstrip("- ").strip() for b in bullets.split("\n") if b.strip()
|
|
186
|
+
]
|
|
187
|
+
if bullet_texts:
|
|
188
|
+
# Strip leading pronouns and clean up each bullet
|
|
189
|
+
cleaned: list[str] = []
|
|
190
|
+
for bt in bullet_texts:
|
|
191
|
+
# Remove "I'll " / "I'm " prefix for conciseness
|
|
192
|
+
bt = re.sub(r"^I'll\s+", "", bt)
|
|
193
|
+
bt = re.sub(r"^I'm not .+ — I'm ", "", bt)
|
|
194
|
+
bt = bt.rstrip(".")
|
|
195
|
+
# Remove surrounding quotes (only if both present)
|
|
196
|
+
if bt.startswith('"') and bt.endswith('"'):
|
|
197
|
+
bt = bt[1:-1]
|
|
198
|
+
# Lower-case the first char for inline style
|
|
199
|
+
if bt and bt[0].isupper():
|
|
200
|
+
bt = bt[0].lower() + bt[1:]
|
|
201
|
+
cleaned.append(bt)
|
|
202
|
+
summary = ", ".join(cleaned)
|
|
203
|
+
else:
|
|
204
|
+
summary = name
|
|
205
|
+
values.append((name, summary))
|
|
206
|
+
return values
|
|
207
|
+
|
|
208
|
+
def _extract_boundaries(self, text: str) -> tuple[list[str], list[str]]:
|
|
209
|
+
"""Extract I Will / I Won't lists from identity core.md."""
|
|
210
|
+
i_will: list[str] = []
|
|
211
|
+
i_wont: list[str] = []
|
|
212
|
+
|
|
213
|
+
# Find "### I Will" section
|
|
214
|
+
will_match = re.search(
|
|
215
|
+
r"###\s+I Will\s*\n((?:- .+\n)*)", text, re.IGNORECASE
|
|
216
|
+
)
|
|
217
|
+
if will_match:
|
|
218
|
+
for line in will_match.group(1).strip().split("\n"):
|
|
219
|
+
item = line.lstrip("- ").strip()
|
|
220
|
+
if item:
|
|
221
|
+
# Lower-case and remove trailing periods for compact format
|
|
222
|
+
item = item[0].lower() + item[1:] if item else item
|
|
223
|
+
item = item.rstrip(".")
|
|
224
|
+
i_will.append(item)
|
|
225
|
+
|
|
226
|
+
# Find "### I Won't" section
|
|
227
|
+
wont_match = re.search(
|
|
228
|
+
r"###\s+I Won't\s*\n((?:- .+\n)*)", text, re.IGNORECASE
|
|
229
|
+
)
|
|
230
|
+
if wont_match:
|
|
231
|
+
for line in wont_match.group(1).strip().split("\n"):
|
|
232
|
+
item = line.lstrip("- ").strip()
|
|
233
|
+
if item:
|
|
234
|
+
item = item[0].lower() + item[1:] if item else item
|
|
235
|
+
item = item.rstrip(".")
|
|
236
|
+
i_wont.append(item)
|
|
237
|
+
|
|
238
|
+
return i_will, i_wont
|
|
239
|
+
|
|
240
|
+
def _extract_principles_from_identity(
|
|
241
|
+
self, text: str
|
|
242
|
+
) -> list[tuple[str, str]]:
|
|
243
|
+
"""Extract principles from the Internalized Philosophy table."""
|
|
244
|
+
principles: list[tuple[str, str]] = []
|
|
245
|
+
# Find the "Internalized Philosophy" section first to avoid
|
|
246
|
+
# matching other tables (e.g. comparison tables) in the file
|
|
247
|
+
philosophy_match = re.search(
|
|
248
|
+
r"##\s+Internalized Philosophy\s*\n(.*?)(?=\n---|\n##|\Z)",
|
|
249
|
+
text,
|
|
250
|
+
re.DOTALL,
|
|
251
|
+
)
|
|
252
|
+
if not philosophy_match:
|
|
253
|
+
return principles
|
|
254
|
+
|
|
255
|
+
section_text = philosophy_match.group(1)
|
|
256
|
+
|
|
257
|
+
# Look for table rows with | **Name** | description |
|
|
258
|
+
pattern = r"\|\s*\*\*(.+?)\*\*\s*\|\s*(.+?)\s*\|"
|
|
259
|
+
for match in re.finditer(pattern, section_text):
|
|
260
|
+
name = match.group(1).strip()
|
|
261
|
+
desc = match.group(2).strip()
|
|
262
|
+
# Skip table header separators
|
|
263
|
+
if name.startswith("-"):
|
|
264
|
+
continue
|
|
265
|
+
# Clean up description
|
|
266
|
+
desc = desc.rstrip(".")
|
|
267
|
+
if desc.startswith("I "):
|
|
268
|
+
# Condense: "I push back on over-engineering..." -> short form
|
|
269
|
+
desc = desc[0].lower() + desc[1:]
|
|
270
|
+
principles.append((name, desc))
|
|
271
|
+
return principles
|
|
272
|
+
|
|
273
|
+
def _add_process_rules_section(
|
|
274
|
+
self, lines: list[str], methodology: dict[str, Any]
|
|
275
|
+
) -> None:
|
|
276
|
+
"""Add Process Rules section from methodology.yaml."""
|
|
277
|
+
lines.append("## Process Rules")
|
|
278
|
+
lines.append("")
|
|
279
|
+
|
|
280
|
+
# Work Lifecycle
|
|
281
|
+
lifecycle = methodology.get("lifecycle", {})
|
|
282
|
+
if lifecycle:
|
|
283
|
+
lines.append("### Work Lifecycle")
|
|
284
|
+
for work_type in ["epic", "story", "session"]:
|
|
285
|
+
cfg = lifecycle.get(work_type, {})
|
|
286
|
+
flow = cfg.get("flow", "")
|
|
287
|
+
if flow:
|
|
288
|
+
lines.append(f"{work_type.upper()}: {flow}")
|
|
289
|
+
lines.append("")
|
|
290
|
+
|
|
291
|
+
# Gates
|
|
292
|
+
gates = methodology.get("gates", {})
|
|
293
|
+
blocking = gates.get("blocking", [])
|
|
294
|
+
quality = gates.get("quality", [])
|
|
295
|
+
if blocking or quality:
|
|
296
|
+
lines.append("### Gates")
|
|
297
|
+
for gate in blocking:
|
|
298
|
+
require = gate.get("require", "")
|
|
299
|
+
before = gate.get("before", "")
|
|
300
|
+
if require and before:
|
|
301
|
+
lines.append(f"- {require} before {before.lower()}")
|
|
302
|
+
for gate in quality:
|
|
303
|
+
gate_name = gate.get("gate", "")
|
|
304
|
+
when = gate.get("when", "")
|
|
305
|
+
if gate_name:
|
|
306
|
+
desc = f"- {gate_name}"
|
|
307
|
+
if when:
|
|
308
|
+
when_lower = when[0].lower() + when[1:] if when else when
|
|
309
|
+
desc += f" {when_lower}"
|
|
310
|
+
lines.append(desc)
|
|
311
|
+
lines.append("")
|
|
312
|
+
|
|
313
|
+
# Critical Rules from principles
|
|
314
|
+
principles = methodology.get("principles", {})
|
|
315
|
+
if principles:
|
|
316
|
+
lines.append("### Critical Rules")
|
|
317
|
+
for category in ["process", "collaboration", "technical"]:
|
|
318
|
+
category_principles = principles.get(category, [])
|
|
319
|
+
for p in category_principles:
|
|
320
|
+
name = p.get("name", "")
|
|
321
|
+
rule = p.get("rule", "")
|
|
322
|
+
rationale = p.get("rationale", "")
|
|
323
|
+
if name and rule:
|
|
324
|
+
line = f"- {name} \u2014 {rule}"
|
|
325
|
+
if rationale:
|
|
326
|
+
line += f" ({rationale.rstrip('.')})"
|
|
327
|
+
lines.append(line)
|
|
328
|
+
lines.append("")
|
|
329
|
+
|
|
330
|
+
def _add_branch_model_section(
|
|
331
|
+
self,
|
|
332
|
+
lines: list[str],
|
|
333
|
+
methodology: dict[str, Any],
|
|
334
|
+
dev_branch: str,
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Add Branch Model section."""
|
|
337
|
+
lines.append("## Branch Model")
|
|
338
|
+
|
|
339
|
+
branches = methodology.get("branches", {})
|
|
340
|
+
flow_items = branches.get("flow", [])
|
|
341
|
+
|
|
342
|
+
# Build the one-liner: main (stable) -> dev (development) -> story/...
|
|
343
|
+
lines.append(
|
|
344
|
+
f"main (stable) \u2192 {dev_branch} (development) "
|
|
345
|
+
"\u2192 story/s{N}.{M}/{name}"
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# Flow description
|
|
349
|
+
for flow_item in flow_items:
|
|
350
|
+
resolved = flow_item.replace("{development_branch}", dev_branch)
|
|
351
|
+
lines.append(resolved)
|
|
352
|
+
lines.append("")
|
|
353
|
+
|
|
354
|
+
def _add_cli_reference_section(self, lines: list[str]) -> None:
|
|
355
|
+
"""Add CLI Quick Reference section (static content)."""
|
|
356
|
+
lines.append("## CLI Quick Reference")
|
|
357
|
+
lines.append("")
|
|
358
|
+
lines.append("### Core")
|
|
359
|
+
lines.append(
|
|
360
|
+
"- cmd: rai init | sig: [--name TEXT] [--path PATH] [--detect] "
|
|
361
|
+
"| notes: --detect analyzes conventions"
|
|
362
|
+
)
|
|
363
|
+
lines.append("")
|
|
364
|
+
lines.append("### Session")
|
|
365
|
+
lines.append(
|
|
366
|
+
"- cmd: rai session start | sig: [--name TEXT] [--project TEXT] "
|
|
367
|
+
"[--agent TEXT] [--context] | notes: --name first-time only, --context for bundle"
|
|
368
|
+
)
|
|
369
|
+
lines.append(
|
|
370
|
+
"- cmd: rai session close | sig: [--summary TEXT] [--type TEXT] "
|
|
371
|
+
"[--pattern TEXT] [--state-file TEXT] [--session TEXT] "
|
|
372
|
+
"| notes: --state-file for structured close, --pattern repeatable"
|
|
373
|
+
)
|
|
374
|
+
lines.append(
|
|
375
|
+
"- cmd: rai session context | sig: --sections/-s TEXT --project/-p TEXT "
|
|
376
|
+
"| notes: sections: governance,behavioral,coaching,deadlines,progress"
|
|
377
|
+
)
|
|
378
|
+
lines.append(
|
|
379
|
+
"- cmd: rai session journal add | sig: TEXT [--type TYPE] "
|
|
380
|
+
"| notes: add decision/insight/task to session"
|
|
381
|
+
)
|
|
382
|
+
lines.append(
|
|
383
|
+
"- cmd: rai session journal show | sig: [--compact] [--project TEXT] "
|
|
384
|
+
"| notes: --compact for post-compaction restore"
|
|
385
|
+
)
|
|
386
|
+
lines.append("")
|
|
387
|
+
lines.append("### Graph")
|
|
388
|
+
lines.append(
|
|
389
|
+
"- cmd: rai graph build | sig: [--output PATH] [--no-diff] "
|
|
390
|
+
"| notes: NO --project flag, runs from CWD"
|
|
391
|
+
)
|
|
392
|
+
lines.append(
|
|
393
|
+
"- cmd: rai graph query | sig: QUERY_STR [--types TYPE] "
|
|
394
|
+
"[--strategy keyword_search|concept_lookup] [--limit N] "
|
|
395
|
+
"[--format human|json|compact] | notes: QUERY_STR positional"
|
|
396
|
+
)
|
|
397
|
+
lines.append(
|
|
398
|
+
"- cmd: rai graph context | sig: MODULE_ID [--format human|json] "
|
|
399
|
+
"| notes: MODULE_ID positional (e.g. mod-memory)"
|
|
400
|
+
)
|
|
401
|
+
lines.append("")
|
|
402
|
+
lines.append("### Pattern")
|
|
403
|
+
lines.append(
|
|
404
|
+
"- cmd: rai pattern add | sig: CONTENT [--context KEYWORDS] "
|
|
405
|
+
"[--type TYPE] [--from STORY_ID] [--scope SCOPE] "
|
|
406
|
+
"| notes: CONTENT positional, --from NOT --source"
|
|
407
|
+
)
|
|
408
|
+
lines.append("")
|
|
409
|
+
lines.append("### Signal")
|
|
410
|
+
lines.append(
|
|
411
|
+
"- cmd: rai signal emit-work | sig: WORK_TYPE WORK_ID "
|
|
412
|
+
"[--event EVENT] [--phase PHASE] "
|
|
413
|
+
"| notes: WORK_TYPE=epic|story, EVENT=start|complete|blocked"
|
|
414
|
+
)
|
|
415
|
+
lines.append("")
|
|
416
|
+
lines.append("### Discovery")
|
|
417
|
+
lines.append(
|
|
418
|
+
"- cmd: rai discover scan | sig: [PATH] [--language LANG] "
|
|
419
|
+
"[--output human|json|summary] [--exclude PATTERN] "
|
|
420
|
+
"| notes: PATH positional, --exclude repeatable"
|
|
421
|
+
)
|
|
422
|
+
lines.append("")
|
|
423
|
+
lines.append("### Skill")
|
|
424
|
+
lines.append(
|
|
425
|
+
"- cmd: rai skill list|validate|check-name|scaffold "
|
|
426
|
+
"| sig: [SKILL_NAME] | notes: validate checks skill structure"
|
|
427
|
+
)
|
|
428
|
+
lines.append(
|
|
429
|
+
"- cmd: rai skill set create|list|diff "
|
|
430
|
+
"| sig: [SET_NAME] | notes: manage skill sets"
|
|
431
|
+
)
|
|
432
|
+
lines.append("")
|
|
433
|
+
lines.append("### Backlog (requires -a jira when multiple adapters)")
|
|
434
|
+
lines.append(
|
|
435
|
+
"- cmd: rai backlog create | sig: SUMMARY -p PROJECT [-t TYPE] "
|
|
436
|
+
"[-d DESC] [-l LABELS] [--parent KEY] "
|
|
437
|
+
"| notes: SUMMARY positional, -p required"
|
|
438
|
+
)
|
|
439
|
+
lines.append(
|
|
440
|
+
"- cmd: rai backlog search | sig: QUERY [-n LIMIT] [-a ADAPTER] "
|
|
441
|
+
"[-f FORMAT] | notes: QUERY positional, JQL for Jira"
|
|
442
|
+
)
|
|
443
|
+
lines.append(
|
|
444
|
+
"- cmd: rai backlog get | sig: KEY [-a ADAPTER] "
|
|
445
|
+
"| notes: single issue details"
|
|
446
|
+
)
|
|
447
|
+
lines.append(
|
|
448
|
+
"- cmd: rai backlog get-comments | sig: KEY [-a ADAPTER] "
|
|
449
|
+
"| notes: issue comments"
|
|
450
|
+
)
|
|
451
|
+
lines.append(
|
|
452
|
+
"- cmd: rai backlog transition | sig: KEY STATUS [-a ADAPTER] "
|
|
453
|
+
"| notes: both positional"
|
|
454
|
+
)
|
|
455
|
+
lines.append(
|
|
456
|
+
"- cmd: rai backlog batch-transition | sig: KEYS STATUS [-a ADAPTER] "
|
|
457
|
+
"| notes: KEYS comma-separated"
|
|
458
|
+
)
|
|
459
|
+
lines.append(
|
|
460
|
+
"- cmd: rai backlog comment | sig: KEY BODY [-a ADAPTER] "
|
|
461
|
+
"| notes: both positional"
|
|
462
|
+
)
|
|
463
|
+
lines.append(
|
|
464
|
+
"- cmd: rai backlog link | sig: SOURCE TARGET LINK_TYPE [-a ADAPTER] "
|
|
465
|
+
"| notes: all 3 positional"
|
|
466
|
+
)
|
|
467
|
+
lines.append(
|
|
468
|
+
"- cmd: rai backlog update | sig: KEY [-s SUMMARY] [-l LABELS] "
|
|
469
|
+
"[--priority TEXT] [--assignee TEXT] "
|
|
470
|
+
"| notes: KEY positional, named flags for fields"
|
|
471
|
+
)
|
|
472
|
+
lines.append("")
|
|
473
|
+
lines.append("### Docs (documentation targets \u2014 Confluence etc.)")
|
|
474
|
+
lines.append(
|
|
475
|
+
"- cmd: rai docs publish | sig: ARTIFACT_TYPE [--title TEXT] "
|
|
476
|
+
"[-t TARGET] | notes: ARTIFACT_TYPE positional (roadmap, adr, etc.)"
|
|
477
|
+
)
|
|
478
|
+
lines.append(
|
|
479
|
+
"- cmd: rai docs get | sig: IDENTIFIER [-t TARGET] "
|
|
480
|
+
"| notes: page ID on remote target"
|
|
481
|
+
)
|
|
482
|
+
lines.append(
|
|
483
|
+
"- cmd: rai docs search | sig: QUERY [-n LIMIT] [-t TARGET] "
|
|
484
|
+
"| notes: QUERY positional"
|
|
485
|
+
)
|
|
486
|
+
lines.append("")
|
|
487
|
+
lines.append("### MCP")
|
|
488
|
+
lines.append(
|
|
489
|
+
"- cmd: rai mcp list | notes: registered servers in .raise/mcp/"
|
|
490
|
+
)
|
|
491
|
+
lines.append(
|
|
492
|
+
"- cmd: rai mcp health | sig: SERVER | notes: SERVER positional"
|
|
493
|
+
)
|
|
494
|
+
lines.append(
|
|
495
|
+
"- cmd: rai mcp tools | sig: SERVER | notes: list tools on server"
|
|
496
|
+
)
|
|
497
|
+
lines.append(
|
|
498
|
+
"- cmd: rai mcp call | sig: SERVER TOOL [--args JSON] [--verbose] "
|
|
499
|
+
"| notes: both positional"
|
|
500
|
+
)
|
|
501
|
+
lines.append(
|
|
502
|
+
"- cmd: rai mcp install | sig: PACKAGE --type uvx|npx|pip --name TEXT "
|
|
503
|
+
"[--env TEXT] [--module TEXT] | notes: PACKAGE positional"
|
|
504
|
+
)
|
|
505
|
+
lines.append(
|
|
506
|
+
"- cmd: rai mcp scaffold | sig: NAME --command TEXT [--args TEXT] "
|
|
507
|
+
"[--env TEXT] | notes: NAME positional"
|
|
508
|
+
)
|
|
509
|
+
lines.append("")
|
|
510
|
+
lines.append("### Gate")
|
|
511
|
+
lines.append(
|
|
512
|
+
"- cmd: rai gate list | sig: [-f FORMAT] "
|
|
513
|
+
"| notes: discovered workflow gates"
|
|
514
|
+
)
|
|
515
|
+
lines.append(
|
|
516
|
+
"- cmd: rai gate check | sig: [GATE_ID] [--all/-a] [-f FORMAT] "
|
|
517
|
+
"| notes: exit 0 all pass, 1 any fail"
|
|
518
|
+
)
|
|
519
|
+
lines.append("")
|
|
520
|
+
lines.append("### Adapter")
|
|
521
|
+
lines.append(
|
|
522
|
+
"- cmd: rai adapter list | sig: [-f FORMAT] "
|
|
523
|
+
"| notes: registered adapters by entry point"
|
|
524
|
+
)
|
|
525
|
+
lines.append(
|
|
526
|
+
"- cmd: rai adapter check | sig: [-f FORMAT] "
|
|
527
|
+
"| notes: validate against Protocol contracts"
|
|
528
|
+
)
|
|
529
|
+
lines.append(
|
|
530
|
+
"- cmd: rai adapter validate | sig: FILE "
|
|
531
|
+
"| notes: validate declarative YAML adapter config"
|
|
532
|
+
)
|
|
533
|
+
lines.append("")
|
|
534
|
+
lines.append("### Release")
|
|
535
|
+
lines.append(
|
|
536
|
+
"- cmd: rai release check | sig: [-p PATH] "
|
|
537
|
+
"| notes: run 10 quality gates"
|
|
538
|
+
)
|
|
539
|
+
lines.append(
|
|
540
|
+
"- cmd: rai release publish | sig: --bump/-b "
|
|
541
|
+
"major|minor|patch|alpha|beta|rc|release "
|
|
542
|
+
"[--version/-v TEXT] [--dry-run] [--skip-check] "
|
|
543
|
+
"| notes: --bump or --version required"
|
|
544
|
+
)
|
|
545
|
+
lines.append("")
|
|
546
|
+
lines.append("### Common Mistakes")
|
|
547
|
+
lines.append(
|
|
548
|
+
"- wrong: rai graph build --project . | right: rai graph build "
|
|
549
|
+
"| why: no --project flag"
|
|
550
|
+
)
|
|
551
|
+
lines.append(
|
|
552
|
+
"- wrong: rai pattern add --content \"...\" | right: rai pattern add \"...\" "
|
|
553
|
+
"| why: CONTENT positional"
|
|
554
|
+
)
|
|
555
|
+
lines.append(
|
|
556
|
+
"- wrong: rai pattern add --source F1 | right: --from F1 "
|
|
557
|
+
"| why: flag is --from"
|
|
558
|
+
)
|
|
559
|
+
lines.append(
|
|
560
|
+
"- wrong: rai discover scan --input dir | right: rai discover scan dir "
|
|
561
|
+
"| why: PATH positional"
|
|
562
|
+
)
|
|
563
|
+
lines.append(
|
|
564
|
+
"- wrong: rai backlog create RAISE --summary \"Title\" "
|
|
565
|
+
"| right: rai backlog create \"Title\" -p RAISE "
|
|
566
|
+
"| why: SUMMARY positional, project is -p flag"
|
|
567
|
+
)
|
|
568
|
+
lines.append(
|
|
569
|
+
"- wrong: rai backlog link X Y --type blocks "
|
|
570
|
+
"| right: rai backlog link X Y blocks "
|
|
571
|
+
"| why: LINK_TYPE positional"
|
|
572
|
+
)
|
|
573
|
+
lines.append(
|
|
574
|
+
"- wrong: rai backlog update KEY --field summary=\"X\" "
|
|
575
|
+
"| right: rai backlog update KEY -s \"X\" "
|
|
576
|
+
"| why: named flags (-s, -l, --priority, --assignee)"
|
|
577
|
+
)
|
|
578
|
+
lines.append("")
|
|
579
|
+
|
|
580
|
+
def _add_integrations_section(self, lines: list[str]) -> None:
|
|
581
|
+
"""Add External Integrations section."""
|
|
582
|
+
lines.append("## External Integrations")
|
|
583
|
+
lines.append(
|
|
584
|
+
"- Jira config: `.raise/jira.yaml` \u2014 team identifiers, workflows, "
|
|
585
|
+
"transition IDs. Read just-in-time via `rai backlog` CLI."
|
|
586
|
+
)
|
|
587
|
+
lines.append(
|
|
588
|
+
"- MCP servers: `.raise/mcp/*.yaml` \u2014 managed via "
|
|
589
|
+
"`rai mcp install|scaffold|list|health`."
|
|
590
|
+
)
|
|
591
|
+
lines.append(
|
|
592
|
+
"- Documentation targets: configured per adapter. "
|
|
593
|
+
"Use `rai docs publish|get|search`."
|
|
594
|
+
)
|
|
595
|
+
lines.append("")
|
|
596
|
+
|
|
597
|
+
def _add_file_operations_section(self, lines: list[str]) -> None:
|
|
598
|
+
"""Add File Operations section (static content)."""
|
|
599
|
+
lines.append("## File Operations")
|
|
600
|
+
lines.append("- ALWAYS read files explicitly before editing them")
|
|
601
|
+
lines.append("- Use read tool first, then edit/write tools")
|
|
602
|
+
lines.append("- Never assume file context is loaded from previous turns")
|
|
603
|
+
lines.append("- After `/clear`, re-read all files you need to modify")
|
|
604
|
+
lines.append("")
|
|
605
|
+
|
|
606
|
+
def _add_post_compaction_section(self, lines: list[str]) -> None:
|
|
607
|
+
"""Add Post-Compaction Context Restoration section (static content)."""
|
|
608
|
+
lines.append("## Post-Compaction Context Restoration")
|
|
609
|
+
lines.append(
|
|
610
|
+
"When you detect context was compacted "
|
|
611
|
+
"(continuation summary present), restore working state:"
|
|
612
|
+
)
|
|
613
|
+
lines.append(
|
|
614
|
+
"1. Read the session journal: "
|
|
615
|
+
"`uv run rai session journal show --compact --project .`"
|
|
616
|
+
)
|
|
617
|
+
lines.append(
|
|
618
|
+
"2. Read the current epic/story scope doc if referenced in journal"
|
|
619
|
+
)
|
|
620
|
+
lines.append("3. Summarize: where we are, what was decided, what's next")
|
|
621
|
+
lines.append(
|
|
622
|
+
"4. Continue work \u2014 do NOT re-run "
|
|
623
|
+
"`/rai-session-start` (session is already active)"
|
|
624
|
+
)
|
|
625
|
+
lines.append("")
|
|
626
|
+
lines.append(
|
|
627
|
+
"The PreCompact hook logs journal state "
|
|
628
|
+
"before compaction (side-effect only)."
|
|
629
|
+
)
|
|
630
|
+
lines.append(
|
|
631
|
+
"Post-compaction injection via hooks is broken "
|
|
632
|
+
"(Claude Code bugs #12671, #15174)."
|
|
633
|
+
)
|
|
634
|
+
lines.append("")
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def generate_instructions(
|
|
638
|
+
project_name: str,
|
|
639
|
+
detection: DetectionResult,
|
|
640
|
+
conventions: ConventionResult | None = None,
|
|
641
|
+
*,
|
|
642
|
+
agent_config: AgentConfig | None = None,
|
|
643
|
+
project_path: Path,
|
|
644
|
+
) -> str:
|
|
645
|
+
"""Convenience function to generate agent instructions file content.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
project_name: Name of the project.
|
|
649
|
+
detection: Project detection result.
|
|
650
|
+
conventions: Optional convention detection result (unused, kept
|
|
651
|
+
for backward compatibility).
|
|
652
|
+
agent_config: Agent configuration (unused, kept for backward
|
|
653
|
+
compatibility).
|
|
654
|
+
project_path: Project root path containing .raise/ directory.
|
|
655
|
+
|
|
656
|
+
Returns:
|
|
657
|
+
Markdown content for the agent's instructions file.
|
|
658
|
+
|
|
659
|
+
Example:
|
|
660
|
+
>>> detection = detect_project_type(Path("/my/project"))
|
|
661
|
+
>>> content = generate_instructions("my-api", detection, project_path=Path("/my/project"))
|
|
662
|
+
>>> Path("CLAUDE.md").write_text(content)
|
|
663
|
+
"""
|
|
664
|
+
generator = InstructionsGenerator()
|
|
665
|
+
return generator.generate(
|
|
666
|
+
project_name, detection, conventions, project_path=project_path
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
# Backward-compat alias
|
|
671
|
+
ClaudeMdGenerator = InstructionsGenerator
|
|
672
|
+
generate_claude_md = generate_instructions
|