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,348 @@
|
|
|
1
|
+
"""CLI commands for Rai's pattern memory: add, reinforce, promote.
|
|
2
|
+
|
|
3
|
+
The pattern group owns commands that write to pattern memory (JSONL files).
|
|
4
|
+
These were extracted from the `memory` God Object in RAISE-247 (ADR-038).
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
- add: Add a new learned pattern to memory
|
|
8
|
+
- reinforce: Reinforce a pattern with a vote signal (applied/N/A/contradicted)
|
|
9
|
+
- promote: Move a pattern from personal scope to project scope
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Annotated
|
|
18
|
+
|
|
19
|
+
import typer
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
|
|
22
|
+
from raise_cli.cli.error_handler import cli_error
|
|
23
|
+
from raise_cli.hooks.emitter import create_emitter
|
|
24
|
+
from raise_cli.hooks.events import PatternAddedEvent
|
|
25
|
+
from raise_cli.memory import (
|
|
26
|
+
MemoryScope,
|
|
27
|
+
PatternInput,
|
|
28
|
+
PatternSubType,
|
|
29
|
+
ReinforceResult,
|
|
30
|
+
append_pattern,
|
|
31
|
+
get_memory_dir_for_scope,
|
|
32
|
+
reinforce_pattern,
|
|
33
|
+
)
|
|
34
|
+
from raise_cli.onboarding.profile import load_developer_profile
|
|
35
|
+
from raise_core.graph.query import (
|
|
36
|
+
SCORING_LOW_WILSON_THRESHOLD,
|
|
37
|
+
wilson_lower_bound,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
pattern_app = typer.Typer(
|
|
41
|
+
name="pattern",
|
|
42
|
+
help="Manage learned patterns",
|
|
43
|
+
no_args_is_help=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
console = Console()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pattern_app.command("reinforce")
|
|
50
|
+
def reinforce_cmd(
|
|
51
|
+
pattern_id: Annotated[
|
|
52
|
+
str, typer.Argument(help="Pattern ID to reinforce (e.g., PAT-E-183)")
|
|
53
|
+
],
|
|
54
|
+
vote: Annotated[
|
|
55
|
+
int,
|
|
56
|
+
typer.Option(
|
|
57
|
+
"--vote",
|
|
58
|
+
"-v",
|
|
59
|
+
help="Vote: 1 (applied), 0 (N/A — not counted), -1 (contradicted)",
|
|
60
|
+
),
|
|
61
|
+
],
|
|
62
|
+
story_id: Annotated[
|
|
63
|
+
str | None,
|
|
64
|
+
typer.Option(
|
|
65
|
+
"--from", "-f", help="Story ID for traceability (e.g., RAISE-170)"
|
|
66
|
+
),
|
|
67
|
+
] = None,
|
|
68
|
+
scope: Annotated[
|
|
69
|
+
str,
|
|
70
|
+
typer.Option("--scope", "-s", help="Memory scope (global, project, personal)"),
|
|
71
|
+
] = "project",
|
|
72
|
+
memory_dir: Annotated[
|
|
73
|
+
Path | None,
|
|
74
|
+
typer.Option(
|
|
75
|
+
"--memory-dir", "-m", help="Memory directory path (overrides scope)"
|
|
76
|
+
),
|
|
77
|
+
] = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Reinforce a pattern with a vote signal.
|
|
80
|
+
|
|
81
|
+
Called at story-review to record whether a pattern was applied (1),
|
|
82
|
+
not relevant (0), or contradicted (-1) during implementation.
|
|
83
|
+
Vote 0 (N/A) does not modify evaluations count.
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
$ rai pattern reinforce PAT-E-183 --vote 1 --from RAISE-170
|
|
87
|
+
$ rai pattern reinforce PAT-E-094 --vote -1 --from RAISE-170
|
|
88
|
+
$ rai pattern reinforce PAT-E-151 --vote 0 --from RAISE-170
|
|
89
|
+
"""
|
|
90
|
+
if vote not in (1, 0, -1):
|
|
91
|
+
cli_error(
|
|
92
|
+
f"Invalid vote: {vote}",
|
|
93
|
+
hint="Valid values: 1 (applied), 0 (N/A), -1 (contradicted)",
|
|
94
|
+
exit_code=7,
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
vote_int = vote
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
memory_scope = MemoryScope(scope)
|
|
102
|
+
except ValueError:
|
|
103
|
+
cli_error(
|
|
104
|
+
f"Invalid scope: {scope}",
|
|
105
|
+
hint="Valid scopes: global, project, personal",
|
|
106
|
+
exit_code=7,
|
|
107
|
+
)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
mem_dir = memory_dir or get_memory_dir_for_scope(memory_scope)
|
|
111
|
+
patterns_file = mem_dir / "patterns.jsonl"
|
|
112
|
+
|
|
113
|
+
if not patterns_file.exists():
|
|
114
|
+
cli_error(
|
|
115
|
+
f"Patterns file not found: {patterns_file}",
|
|
116
|
+
hint="Run 'rai pattern add' first or check --memory-dir",
|
|
117
|
+
exit_code=4,
|
|
118
|
+
)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
result: ReinforceResult = reinforce_pattern(
|
|
123
|
+
patterns_file, pattern_id, vote=vote_int, story_id=story_id
|
|
124
|
+
)
|
|
125
|
+
except KeyError:
|
|
126
|
+
cli_error(
|
|
127
|
+
f"Pattern '{pattern_id}' not found in {patterns_file}",
|
|
128
|
+
hint="Check the pattern ID with 'rai graph query'",
|
|
129
|
+
exit_code=4,
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if not result.was_updated:
|
|
134
|
+
console.print(f"\n[green]✓[/green] {pattern_id}: N/A (not counted)\n")
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
summary = (
|
|
138
|
+
f"positives={result.positives}, "
|
|
139
|
+
f"negatives={result.negatives}, "
|
|
140
|
+
f"evaluations={result.evaluations}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if result.evaluations > 0 and (result.positives + result.negatives) > 0:
|
|
144
|
+
wilson = wilson_lower_bound(result.positives, result.negatives)
|
|
145
|
+
wilson_str = f"wilson≈{wilson:.2f}"
|
|
146
|
+
if wilson < SCORING_LOW_WILSON_THRESHOLD:
|
|
147
|
+
wilson_str += " [yellow]↓ consider reviewing[/yellow]"
|
|
148
|
+
summary += f", {wilson_str}"
|
|
149
|
+
|
|
150
|
+
console.print(f"\n[green]✓[/green] {pattern_id}: {summary}\n")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@pattern_app.command("add")
|
|
154
|
+
def add_pattern(
|
|
155
|
+
content: Annotated[str, typer.Argument(help="Pattern description")],
|
|
156
|
+
context: Annotated[
|
|
157
|
+
str,
|
|
158
|
+
typer.Option("--context", "-c", help="Context keywords (comma-separated)"),
|
|
159
|
+
] = "",
|
|
160
|
+
sub_type: Annotated[
|
|
161
|
+
str,
|
|
162
|
+
typer.Option(
|
|
163
|
+
"--type",
|
|
164
|
+
"-t",
|
|
165
|
+
help="Pattern type (codebase, process, architecture, technical)",
|
|
166
|
+
),
|
|
167
|
+
] = "process",
|
|
168
|
+
learned_from: Annotated[
|
|
169
|
+
str | None,
|
|
170
|
+
typer.Option("--from", "-f", help="Story/session where learned"),
|
|
171
|
+
] = None,
|
|
172
|
+
scope: Annotated[
|
|
173
|
+
str,
|
|
174
|
+
typer.Option("--scope", "-s", help="Memory scope (global, project, personal)"),
|
|
175
|
+
] = "personal",
|
|
176
|
+
memory_dir: Annotated[
|
|
177
|
+
Path | None,
|
|
178
|
+
typer.Option(
|
|
179
|
+
"--memory-dir", "-m", help="Memory directory path (overrides scope)"
|
|
180
|
+
),
|
|
181
|
+
] = None,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Add a new pattern to memory.
|
|
184
|
+
|
|
185
|
+
Examples:
|
|
186
|
+
# Add a process pattern (default: personal scope)
|
|
187
|
+
$ rai pattern add "HITL before commits" -c "git,workflow"
|
|
188
|
+
|
|
189
|
+
# Add a technical pattern
|
|
190
|
+
$ rai pattern add "Use capsys for stdout tests" -t technical -c "pytest,testing"
|
|
191
|
+
|
|
192
|
+
# Add with source reference
|
|
193
|
+
$ rai pattern add "BFS reuse across modules" -t architecture --from F2.3
|
|
194
|
+
|
|
195
|
+
# Add to global scope (universal pattern)
|
|
196
|
+
$ rai pattern add "Universal TDD pattern" --scope global
|
|
197
|
+
|
|
198
|
+
# Add to personal scope (my learnings)
|
|
199
|
+
$ rai pattern add "My workflow preference" --scope personal
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
memory_scope = MemoryScope(scope)
|
|
203
|
+
except ValueError:
|
|
204
|
+
cli_error(
|
|
205
|
+
f"Invalid scope: {scope}",
|
|
206
|
+
hint="Valid scopes: global, project, personal",
|
|
207
|
+
exit_code=7,
|
|
208
|
+
)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
mem_dir = memory_dir or get_memory_dir_for_scope(memory_scope)
|
|
212
|
+
if not mem_dir.exists():
|
|
213
|
+
mem_dir.mkdir(parents=True, exist_ok=True)
|
|
214
|
+
|
|
215
|
+
context_list = [c.strip() for c in context.split(",") if c.strip()]
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
pattern_type = PatternSubType(sub_type)
|
|
219
|
+
except ValueError:
|
|
220
|
+
cli_error(
|
|
221
|
+
f"Invalid pattern type: {sub_type}",
|
|
222
|
+
hint="Valid types: codebase, process, architecture, technical",
|
|
223
|
+
exit_code=7,
|
|
224
|
+
)
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
input_data = PatternInput(
|
|
228
|
+
content=content,
|
|
229
|
+
sub_type=pattern_type,
|
|
230
|
+
context=context_list,
|
|
231
|
+
learned_from=learned_from,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
profile = load_developer_profile()
|
|
235
|
+
dev_prefix = profile.get_pattern_prefix() if profile else None
|
|
236
|
+
|
|
237
|
+
result = append_pattern(
|
|
238
|
+
mem_dir, input_data, scope=memory_scope, developer_prefix=dev_prefix
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if result.success:
|
|
242
|
+
emitter = create_emitter()
|
|
243
|
+
emitter.emit(
|
|
244
|
+
PatternAddedEvent(
|
|
245
|
+
pattern_id=result.id or "",
|
|
246
|
+
content=content,
|
|
247
|
+
context=context,
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
console.print(f"\n[green]✓[/green] {result.message}")
|
|
251
|
+
console.print(f" ID: [cyan]{result.id}[/cyan]")
|
|
252
|
+
console.print(f" Content: {content[:60]}...")
|
|
253
|
+
if context_list:
|
|
254
|
+
console.print(f" Context: {', '.join(context_list)}")
|
|
255
|
+
console.print("\n[dim]Index will rebuild on next query.[/dim]\n")
|
|
256
|
+
else:
|
|
257
|
+
cli_error(result.message)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@pattern_app.command("promote")
|
|
261
|
+
def promote_pattern(
|
|
262
|
+
pattern_id: Annotated[
|
|
263
|
+
str, typer.Argument(help="Pattern ID to promote (e.g., PAT-E-123)")
|
|
264
|
+
],
|
|
265
|
+
memory_dir: Annotated[
|
|
266
|
+
Path | None,
|
|
267
|
+
typer.Option(
|
|
268
|
+
"--memory-dir",
|
|
269
|
+
"-m",
|
|
270
|
+
help="Memory directory path (overrides default)",
|
|
271
|
+
),
|
|
272
|
+
] = None,
|
|
273
|
+
) -> None:
|
|
274
|
+
"""Promote a pattern from personal scope to project scope.
|
|
275
|
+
|
|
276
|
+
Moves the pattern entry from personal patterns.jsonl to project patterns.jsonl.
|
|
277
|
+
The pattern ID is preserved.
|
|
278
|
+
|
|
279
|
+
Examples:
|
|
280
|
+
$ rai pattern promote PAT-E-123
|
|
281
|
+
"""
|
|
282
|
+
personal_dir = memory_dir or get_memory_dir_for_scope(MemoryScope.PERSONAL)
|
|
283
|
+
personal_file = personal_dir / "patterns.jsonl"
|
|
284
|
+
|
|
285
|
+
if not personal_file.exists():
|
|
286
|
+
cli_error(
|
|
287
|
+
f"Personal patterns file not found: {personal_file}",
|
|
288
|
+
hint="No personal patterns to promote. Add patterns first with 'rai pattern add'.",
|
|
289
|
+
exit_code=4,
|
|
290
|
+
)
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
# Read all personal patterns and find the target
|
|
294
|
+
lines = personal_file.read_text(encoding="utf-8").strip().splitlines()
|
|
295
|
+
target: dict[str, object] | None = None
|
|
296
|
+
remaining: list[str] = []
|
|
297
|
+
|
|
298
|
+
for line in lines:
|
|
299
|
+
if not line.strip():
|
|
300
|
+
continue
|
|
301
|
+
entry = json.loads(line)
|
|
302
|
+
if entry.get("id") == pattern_id:
|
|
303
|
+
target = entry
|
|
304
|
+
else:
|
|
305
|
+
remaining.append(line)
|
|
306
|
+
|
|
307
|
+
if target is None:
|
|
308
|
+
cli_error(
|
|
309
|
+
f"Pattern '{pattern_id}' not found in personal patterns",
|
|
310
|
+
hint="Check the pattern ID. Only personal-scope patterns can be promoted.",
|
|
311
|
+
exit_code=4,
|
|
312
|
+
)
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
# Append to project patterns
|
|
316
|
+
project_dir = get_memory_dir_for_scope(MemoryScope.PROJECT)
|
|
317
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
|
318
|
+
project_file = project_dir / "patterns.jsonl"
|
|
319
|
+
with project_file.open("a", encoding="utf-8") as f:
|
|
320
|
+
f.write(json.dumps(dict(target)) + "\n")
|
|
321
|
+
|
|
322
|
+
# Rewrite personal file without the promoted pattern (atomic: temp + rename)
|
|
323
|
+
tmp_fd, tmp_path = tempfile.mkstemp(
|
|
324
|
+
dir=personal_file.parent, suffix=".tmp"
|
|
325
|
+
)
|
|
326
|
+
try:
|
|
327
|
+
with open(tmp_fd, "w", encoding="utf-8") as tmp_f:
|
|
328
|
+
for line in remaining:
|
|
329
|
+
tmp_f.write(line + "\n")
|
|
330
|
+
Path(tmp_path).replace(personal_file)
|
|
331
|
+
except Exception:
|
|
332
|
+
Path(tmp_path).unlink(missing_ok=True)
|
|
333
|
+
raise
|
|
334
|
+
|
|
335
|
+
# Emit hook
|
|
336
|
+
content_str = str(target.get("content", ""))
|
|
337
|
+
emitter = create_emitter()
|
|
338
|
+
emitter.emit(
|
|
339
|
+
PatternAddedEvent(
|
|
340
|
+
pattern_id=pattern_id,
|
|
341
|
+
content=content_str,
|
|
342
|
+
context=str(target.get("context", "")),
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
console.print(f"\n[green]✓[/green] Promoted {pattern_id} to project scope")
|
|
347
|
+
console.print(f" Content: {content_str[:60]}")
|
|
348
|
+
console.print()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""CLI commands for developer profile management.
|
|
2
|
+
|
|
3
|
+
This module provides the `rai profile` command for viewing
|
|
4
|
+
the developer profile stored in ~/.rai/developer.yaml.
|
|
5
|
+
|
|
6
|
+
For session management, use `rai session start/close`.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
$ rai profile # View current profile in YAML format
|
|
10
|
+
$ rai profile show # Same (backward-compat subcommand)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
from raise_cli.onboarding.profile import (
|
|
19
|
+
load_developer_profile,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
profile_app = typer.Typer(
|
|
23
|
+
name="profile",
|
|
24
|
+
help="View developer profile",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@profile_app.callback(invoke_without_command=True)
|
|
29
|
+
def profile_callback(ctx: typer.Context) -> None:
|
|
30
|
+
"""View developer profile. Runs 'show' when called without subcommand."""
|
|
31
|
+
if ctx.invoked_subcommand is None:
|
|
32
|
+
show()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@profile_app.command()
|
|
36
|
+
def show() -> None:
|
|
37
|
+
"""Display the developer profile in YAML format.
|
|
38
|
+
|
|
39
|
+
Outputs the contents of ~/.rai/developer.yaml in human-readable YAML format.
|
|
40
|
+
If no profile exists, shows a helpful message guiding the user to create one.
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
$ rai profile
|
|
44
|
+
$ rai profile show
|
|
45
|
+
"""
|
|
46
|
+
profile = load_developer_profile()
|
|
47
|
+
|
|
48
|
+
if profile is None:
|
|
49
|
+
typer.echo(
|
|
50
|
+
"No developer profile found. Run `raise init` in a project to create one."
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
# Convert to dict and output as YAML
|
|
55
|
+
data = profile.model_dump(mode="json")
|
|
56
|
+
output = yaml.dump(
|
|
57
|
+
data, default_flow_style=False, allow_unicode=True, sort_keys=False
|
|
58
|
+
)
|
|
59
|
+
typer.echo(output.rstrip())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Backward-compatible aliases for commands moved to the release group.
|
|
2
|
+
|
|
3
|
+
All active commands have been moved to the `release` group (RAISE-247/S5):
|
|
4
|
+
- publish check → release check
|
|
5
|
+
- publish release → release publish
|
|
6
|
+
|
|
7
|
+
These aliases print deprecation warnings and delegate to the canonical commands.
|
|
8
|
+
They will be removed in a future release (RAISE-247/S9).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
|
|
19
|
+
from raise_cli.publish.version import BumpType
|
|
20
|
+
|
|
21
|
+
publish_app = typer.Typer(help="Publish and release management commands")
|
|
22
|
+
|
|
23
|
+
_stderr_console = Console(stderr=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _deprecation_warning(old_cmd: str, new_cmd: str) -> None:
|
|
27
|
+
"""Print deprecation warning to stderr."""
|
|
28
|
+
_stderr_console.print(
|
|
29
|
+
f"[yellow]DEPRECATED:[/yellow] 'rai {old_cmd}' → use 'rai {new_cmd}' instead",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@publish_app.command("check")
|
|
34
|
+
def check_shim(
|
|
35
|
+
project: Annotated[
|
|
36
|
+
Path,
|
|
37
|
+
typer.Option("--project", "-p", help="Project root path"),
|
|
38
|
+
] = Path("."),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Deprecated: use 'rai release check'."""
|
|
41
|
+
_deprecation_warning("publish check", "release check")
|
|
42
|
+
from raise_cli.cli.commands.release import check_command
|
|
43
|
+
|
|
44
|
+
check_command(project=project)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@publish_app.command("release")
|
|
48
|
+
def release_shim(
|
|
49
|
+
bump: Annotated[
|
|
50
|
+
BumpType | None,
|
|
51
|
+
typer.Option("--bump", "-b", help="Version bump type"),
|
|
52
|
+
] = None,
|
|
53
|
+
version: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
typer.Option("--version", "-v", help="Explicit version (overrides --bump)"),
|
|
56
|
+
] = None,
|
|
57
|
+
dry_run: Annotated[
|
|
58
|
+
bool,
|
|
59
|
+
typer.Option("--dry-run", help="Show what would happen without executing"),
|
|
60
|
+
] = False,
|
|
61
|
+
skip_check: Annotated[
|
|
62
|
+
bool,
|
|
63
|
+
typer.Option("--skip-check", help="Skip quality gates (dangerous)"),
|
|
64
|
+
] = False,
|
|
65
|
+
project: Annotated[
|
|
66
|
+
Path,
|
|
67
|
+
typer.Option("--project", "-p", help="Project root path"),
|
|
68
|
+
] = Path("."),
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Deprecated: use 'rai release publish'."""
|
|
71
|
+
_deprecation_warning("publish release", "release publish")
|
|
72
|
+
from raise_cli.cli.commands.release import publish_command
|
|
73
|
+
|
|
74
|
+
publish_command(
|
|
75
|
+
bump=bump,
|
|
76
|
+
version=version,
|
|
77
|
+
dry_run=dry_run,
|
|
78
|
+
skip_check=skip_check,
|
|
79
|
+
project=project,
|
|
80
|
+
)
|