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
raise_cli/mcp/models.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""MCP infrastructure models — independent of domain adapters.
|
|
2
|
+
|
|
3
|
+
These models belong to the MCP infrastructure layer. Domain adapters
|
|
4
|
+
(PM, Docs) have their own models in ``raise_cli.adapters.models``.
|
|
5
|
+
|
|
6
|
+
Architecture: ADR-042, E338 (AR-C1)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class McpToolResult(BaseModel):
|
|
17
|
+
"""Parsed result from an MCP tool call."""
|
|
18
|
+
|
|
19
|
+
text: str = ""
|
|
20
|
+
data: dict[str, Any] = Field(default_factory=dict)
|
|
21
|
+
is_error: bool = False
|
|
22
|
+
error_message: str = ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class McpToolInfo(BaseModel):
|
|
26
|
+
"""Tool metadata from server discovery."""
|
|
27
|
+
|
|
28
|
+
name: str
|
|
29
|
+
description: str = ""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class McpHealthResult(BaseModel):
|
|
33
|
+
"""Health check result for an MCP server.
|
|
34
|
+
|
|
35
|
+
Independent of AdapterHealth (domain layer). Adapters convert
|
|
36
|
+
McpHealthResult → AdapterHealth when needed.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
server_name: str = Field(..., description="MCP server identifier")
|
|
40
|
+
healthy: bool = Field(..., description="Whether server responded")
|
|
41
|
+
message: str = Field(default="", description="Status or error message")
|
|
42
|
+
latency_ms: int | None = Field(default=None, description="Response latency in ms")
|
|
43
|
+
tool_count: int = Field(default=0, description="Number of tools discovered")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""MCP server registry — discovers servers from .raise/mcp/*.yaml.
|
|
2
|
+
|
|
3
|
+
Scans the MCP config directory for YAML files, parses each as
|
|
4
|
+
``McpServerConfig``, and returns a name → config mapping.
|
|
5
|
+
|
|
6
|
+
Architecture: ADR-042, E338
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
from pydantic import ValidationError
|
|
16
|
+
|
|
17
|
+
from raise_cli.mcp.schema import McpServerConfig
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def discover_mcp_servers(
|
|
23
|
+
mcp_dir: Path | None = None,
|
|
24
|
+
) -> dict[str, McpServerConfig]:
|
|
25
|
+
"""Discover MCP servers registered in .raise/mcp/*.yaml.
|
|
26
|
+
|
|
27
|
+
Scans ``mcp_dir`` (default: ``.raise/mcp/``) for ``*.yaml`` files,
|
|
28
|
+
parses each as ``McpServerConfig``, and returns a name → config mapping.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
mcp_dir: Directory to scan. Defaults to ``.raise/mcp/``.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Mapping of server name to parsed config.
|
|
35
|
+
"""
|
|
36
|
+
if mcp_dir is None:
|
|
37
|
+
mcp_dir = Path.cwd() / ".raise" / "mcp"
|
|
38
|
+
|
|
39
|
+
if not mcp_dir.is_dir():
|
|
40
|
+
return {}
|
|
41
|
+
|
|
42
|
+
result: dict[str, McpServerConfig] = {}
|
|
43
|
+
|
|
44
|
+
for yaml_path in sorted(mcp_dir.glob("*.yaml")):
|
|
45
|
+
if yaml_path.name == "catalog.yaml":
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
raw = yaml.safe_load(yaml_path.read_text(encoding="utf-8"))
|
|
50
|
+
except Exception as exc: # noqa: BLE001
|
|
51
|
+
logger.warning("Skipping %s: YAML parse error: %s", yaml_path.name, exc)
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
if not isinstance(raw, dict):
|
|
55
|
+
logger.warning("Skipping %s: YAML content is not a mapping", yaml_path.name)
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
config = McpServerConfig.model_validate(raw)
|
|
60
|
+
except ValidationError as exc:
|
|
61
|
+
logger.warning(
|
|
62
|
+
"Skipping %s: schema validation error: %s", yaml_path.name, exc
|
|
63
|
+
)
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
name = config.name
|
|
67
|
+
if name in result:
|
|
68
|
+
logger.warning(
|
|
69
|
+
"Skipping %s: server name '%s' already defined by another YAML file",
|
|
70
|
+
yaml_path.name,
|
|
71
|
+
name,
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
result[name] = config
|
|
76
|
+
|
|
77
|
+
return result
|
raise_cli/mcp/schema.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""MCP server configuration schema.
|
|
2
|
+
|
|
3
|
+
Defines the YAML config format for ``.raise/mcp/*.yaml`` and the shared
|
|
4
|
+
``ServerConnection`` model used by both MCP registry and declarative adapters.
|
|
5
|
+
|
|
6
|
+
Architecture: ADR-042, E338 (AR-C2)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ServerConnection(BaseModel):
|
|
15
|
+
"""MCP server connection configuration.
|
|
16
|
+
|
|
17
|
+
Shared between MCP registry (``McpServerConfig``) and declarative
|
|
18
|
+
adapters (``DeclarativeAdapterConfig``). Eliminates duplication (AR-C2).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
command: str = Field(..., description="Server command (e.g. 'uvx', 'npx')")
|
|
22
|
+
args: list[str] = Field(
|
|
23
|
+
default_factory=list, description="Server command arguments"
|
|
24
|
+
)
|
|
25
|
+
env: list[str] | None = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Env var names to pass to server subprocess",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class McpServerConfig(BaseModel):
|
|
32
|
+
"""Root config model for a generic MCP server.
|
|
33
|
+
|
|
34
|
+
Parsed from ``.raise/mcp/<name>.yaml``. Not tied to any domain protocol.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
name: str = Field(..., description="Server name (used in rai mcp commands)")
|
|
38
|
+
description: str | None = Field(
|
|
39
|
+
default=None, description="Human-readable description"
|
|
40
|
+
)
|
|
41
|
+
server: ServerConnection
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Memory module for Rai's persistent memory.
|
|
2
|
+
|
|
3
|
+
This module provides infrastructure for loading, querying, and managing
|
|
4
|
+
Rai's accumulated memories stored in JSONL format.
|
|
5
|
+
|
|
6
|
+
For queries, use the Graph from raise_cli.context:
|
|
7
|
+
- CLI: `raise context query "keywords" --types pattern,calibration,session`
|
|
8
|
+
- Programmatic: `QueryEngine` from `raise_cli.context.query`
|
|
9
|
+
|
|
10
|
+
The JSONL files (.raise/rai/memory/*.jsonl) remain the source of truth.
|
|
11
|
+
The unified graph consolidates memory with governance, skills, and work items.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from raise_cli.memory.migration import (
|
|
17
|
+
MigrationResult,
|
|
18
|
+
migrate_to_personal,
|
|
19
|
+
needs_migration,
|
|
20
|
+
)
|
|
21
|
+
from raise_cli.memory.models import (
|
|
22
|
+
MemoryConcept,
|
|
23
|
+
MemoryConceptType,
|
|
24
|
+
MemoryRelationship,
|
|
25
|
+
MemoryRelationshipType,
|
|
26
|
+
MemoryScope,
|
|
27
|
+
PatternSubType,
|
|
28
|
+
)
|
|
29
|
+
from raise_cli.memory.writer import (
|
|
30
|
+
PatternInput,
|
|
31
|
+
ReinforceResult,
|
|
32
|
+
SessionInput,
|
|
33
|
+
WriteResult,
|
|
34
|
+
append_pattern,
|
|
35
|
+
append_session,
|
|
36
|
+
get_memory_dir_for_scope,
|
|
37
|
+
reinforce_pattern,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"MemoryConcept",
|
|
42
|
+
"MemoryConceptType",
|
|
43
|
+
"MemoryRelationship",
|
|
44
|
+
"MemoryRelationshipType",
|
|
45
|
+
"MemoryScope",
|
|
46
|
+
"MigrationResult",
|
|
47
|
+
"PatternInput",
|
|
48
|
+
"PatternSubType",
|
|
49
|
+
"ReinforceResult",
|
|
50
|
+
"SessionInput",
|
|
51
|
+
"WriteResult",
|
|
52
|
+
"append_pattern",
|
|
53
|
+
"append_session",
|
|
54
|
+
"get_memory_dir_for_scope",
|
|
55
|
+
"migrate_to_personal",
|
|
56
|
+
"needs_migration",
|
|
57
|
+
"reinforce_pattern",
|
|
58
|
+
]
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""JSONL loader for memory concepts.
|
|
2
|
+
|
|
3
|
+
This module provides functions to load memory concepts from JSONL files
|
|
4
|
+
in the .raise/rai/memory/ directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from datetime import date
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from raise_cli.memory.models import (
|
|
15
|
+
MemoryConcept,
|
|
16
|
+
MemoryConceptType,
|
|
17
|
+
MemoryLoadResult,
|
|
18
|
+
MemoryScope,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_date(date_str: str) -> date:
|
|
23
|
+
"""Parse a date string in YYYY-MM-DD format.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
date_str: Date string to parse.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Parsed date object.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: If date string is invalid.
|
|
33
|
+
"""
|
|
34
|
+
return date.fromisoformat(date_str)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_pattern(
|
|
38
|
+
data: dict[str, Any], scope: MemoryScope = MemoryScope.PROJECT
|
|
39
|
+
) -> MemoryConcept:
|
|
40
|
+
"""Load a pattern concept from JSONL data.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data: Dictionary from JSONL line.
|
|
44
|
+
scope: Memory scope (global, project, or personal).
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
MemoryConcept for the pattern.
|
|
48
|
+
"""
|
|
49
|
+
# Handle schema variations: 'content' or 'pattern' field
|
|
50
|
+
content = data.get("content") or data.get("pattern", "")
|
|
51
|
+
# Handle date field variations: 'created' or 'date'
|
|
52
|
+
date_str = data.get("created") or data.get("date", "")
|
|
53
|
+
|
|
54
|
+
metadata: dict[str, Any] = {
|
|
55
|
+
"sub_type": data.get("type", "unknown"),
|
|
56
|
+
"learned_from": data.get("learned_from"),
|
|
57
|
+
"scope": scope.value,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Surface base/version for pattern versioning (F14.6)
|
|
61
|
+
if data.get("base") is not None:
|
|
62
|
+
metadata["base"] = data["base"]
|
|
63
|
+
if data.get("version") is not None:
|
|
64
|
+
metadata["version"] = data["version"]
|
|
65
|
+
|
|
66
|
+
return MemoryConcept(
|
|
67
|
+
id=data["id"],
|
|
68
|
+
type=MemoryConceptType.PATTERN,
|
|
69
|
+
content=content,
|
|
70
|
+
context=data.get("context", []),
|
|
71
|
+
created=parse_date(date_str),
|
|
72
|
+
metadata=metadata,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load_calibration(
|
|
77
|
+
data: dict[str, Any], scope: MemoryScope = MemoryScope.PROJECT
|
|
78
|
+
) -> MemoryConcept:
|
|
79
|
+
"""Load a calibration concept from JSONL data.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
data: Dictionary from JSONL line.
|
|
83
|
+
scope: Memory scope (global, project, or personal).
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
MemoryConcept for the calibration.
|
|
87
|
+
"""
|
|
88
|
+
# Handle schema variations for name and story fields
|
|
89
|
+
name = data.get("name") or data.get("feature_name", "unknown")
|
|
90
|
+
# Backward compat: try "story" (new), then "feature" (old data)
|
|
91
|
+
story = data.get("story") or data.get("feature") or data.get("story_id", "unknown")
|
|
92
|
+
# Handle date field variations: 'created' or 'date'
|
|
93
|
+
date_str = data.get("created") or data.get("date", "")
|
|
94
|
+
# Handle velocity field variations: 'ratio' or 'velocity'
|
|
95
|
+
velocity = data.get("ratio") or data.get("velocity")
|
|
96
|
+
|
|
97
|
+
# Build content summary from calibration data
|
|
98
|
+
content_parts = [f"{name} ({story})"]
|
|
99
|
+
if data.get("actual_min"):
|
|
100
|
+
content_parts.append(f"actual: {data['actual_min']}min")
|
|
101
|
+
if velocity:
|
|
102
|
+
content_parts.append(f"velocity: {velocity}x")
|
|
103
|
+
content = " - ".join(content_parts)
|
|
104
|
+
|
|
105
|
+
# Build context from story and size
|
|
106
|
+
context = [story, data["size"].lower()]
|
|
107
|
+
if data.get("kata_cycle"):
|
|
108
|
+
context.append("kata-cycle")
|
|
109
|
+
|
|
110
|
+
return MemoryConcept(
|
|
111
|
+
id=data.get("id") or data.get("story_id", "unknown"),
|
|
112
|
+
type=MemoryConceptType.CALIBRATION,
|
|
113
|
+
content=content,
|
|
114
|
+
context=context,
|
|
115
|
+
created=parse_date(date_str),
|
|
116
|
+
metadata={
|
|
117
|
+
"story": story,
|
|
118
|
+
"name": name,
|
|
119
|
+
"size": data["size"],
|
|
120
|
+
"sp": data.get("sp"),
|
|
121
|
+
"estimated_min": data.get("estimated_min"),
|
|
122
|
+
"actual_min": data.get("actual_min"),
|
|
123
|
+
"ratio": velocity,
|
|
124
|
+
"kata_cycle": data.get("kata_cycle", False),
|
|
125
|
+
"notes": data.get("notes"),
|
|
126
|
+
"scope": scope.value,
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def load_session(
|
|
132
|
+
data: dict[str, Any], scope: MemoryScope = MemoryScope.PROJECT
|
|
133
|
+
) -> MemoryConcept:
|
|
134
|
+
"""Load a session concept from JSONL data.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
data: Dictionary from JSONL line.
|
|
138
|
+
scope: Memory scope (global, project, or personal).
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
MemoryConcept for the session.
|
|
142
|
+
"""
|
|
143
|
+
# Handle schema variations: 'topic' or 'summary'
|
|
144
|
+
topic = data.get("topic") or data.get("summary", "unknown")
|
|
145
|
+
|
|
146
|
+
# Build content from topic and outcomes
|
|
147
|
+
outcomes_str = ", ".join(data.get("outcomes", [])[:3])
|
|
148
|
+
content = f"{topic}: {outcomes_str}"
|
|
149
|
+
|
|
150
|
+
# Build context from type and outcomes keywords
|
|
151
|
+
context = [data["type"]]
|
|
152
|
+
# Extract keywords from topic
|
|
153
|
+
topic_words = topic.lower().split()
|
|
154
|
+
context.extend([w for w in topic_words if len(w) > 3][:3])
|
|
155
|
+
|
|
156
|
+
return MemoryConcept(
|
|
157
|
+
id=data["id"],
|
|
158
|
+
type=MemoryConceptType.SESSION,
|
|
159
|
+
content=content,
|
|
160
|
+
context=context,
|
|
161
|
+
created=parse_date(data["date"]),
|
|
162
|
+
metadata={
|
|
163
|
+
"session_type": data["type"],
|
|
164
|
+
"topic": topic,
|
|
165
|
+
"outcomes": data.get("outcomes", []),
|
|
166
|
+
"log_path": data.get("log_path"),
|
|
167
|
+
"scope": scope.value,
|
|
168
|
+
},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def load_jsonl_file(
|
|
173
|
+
file_path: Path,
|
|
174
|
+
concept_type: MemoryConceptType,
|
|
175
|
+
scope: MemoryScope = MemoryScope.PROJECT,
|
|
176
|
+
) -> tuple[list[MemoryConcept], list[str]]:
|
|
177
|
+
"""Load concepts from a single JSONL file.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
file_path: Path to the JSONL file.
|
|
181
|
+
concept_type: Type of concepts in the file.
|
|
182
|
+
scope: Memory scope to assign to loaded concepts.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Tuple of (concepts list, errors list).
|
|
186
|
+
"""
|
|
187
|
+
concepts: list[MemoryConcept] = []
|
|
188
|
+
errors: list[str] = []
|
|
189
|
+
|
|
190
|
+
if not file_path.exists():
|
|
191
|
+
return concepts, errors
|
|
192
|
+
|
|
193
|
+
loader_map = {
|
|
194
|
+
MemoryConceptType.PATTERN: load_pattern,
|
|
195
|
+
MemoryConceptType.CALIBRATION: load_calibration,
|
|
196
|
+
MemoryConceptType.SESSION: load_session,
|
|
197
|
+
}
|
|
198
|
+
loader = loader_map[concept_type]
|
|
199
|
+
|
|
200
|
+
with file_path.open("r", encoding="utf-8") as f:
|
|
201
|
+
for line_num, line in enumerate(f, start=1):
|
|
202
|
+
line = line.strip()
|
|
203
|
+
if not line:
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
data = json.loads(line)
|
|
207
|
+
concept = loader(data, scope=scope)
|
|
208
|
+
concepts.append(concept)
|
|
209
|
+
except (json.JSONDecodeError, KeyError, ValueError) as e:
|
|
210
|
+
errors.append(f"{file_path.name}:{line_num}: {e}")
|
|
211
|
+
|
|
212
|
+
return concepts, errors
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def load_memory_from_directory(memory_dir: Path) -> MemoryLoadResult:
|
|
216
|
+
"""Load all memory concepts from a .raise/rai/memory/ directory.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
memory_dir: Path to the memory directory.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
MemoryLoadResult with all loaded concepts.
|
|
223
|
+
"""
|
|
224
|
+
all_concepts: list[MemoryConcept] = []
|
|
225
|
+
all_errors: list[str] = []
|
|
226
|
+
files_processed = 0
|
|
227
|
+
|
|
228
|
+
# Define files and their types
|
|
229
|
+
file_mappings = [
|
|
230
|
+
(memory_dir / "patterns.jsonl", MemoryConceptType.PATTERN),
|
|
231
|
+
(memory_dir / "calibration.jsonl", MemoryConceptType.CALIBRATION),
|
|
232
|
+
(memory_dir / "sessions" / "index.jsonl", MemoryConceptType.SESSION),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
for file_path, concept_type in file_mappings:
|
|
236
|
+
if file_path.exists():
|
|
237
|
+
concepts, errors = load_jsonl_file(file_path, concept_type)
|
|
238
|
+
all_concepts.extend(concepts)
|
|
239
|
+
all_errors.extend(errors)
|
|
240
|
+
files_processed += 1
|
|
241
|
+
|
|
242
|
+
return MemoryLoadResult(
|
|
243
|
+
concepts=all_concepts,
|
|
244
|
+
total=len(all_concepts),
|
|
245
|
+
files_processed=files_processed,
|
|
246
|
+
errors=all_errors,
|
|
247
|
+
)
|