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,350 @@
|
|
|
1
|
+
"""CLI commands for skill management.
|
|
2
|
+
|
|
3
|
+
Provides deterministic operations for listing, validating, and scaffolding
|
|
4
|
+
RaiSE skills, following the inference economy principle.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Annotated
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from raise_cli.cli.commands.skill_set import skill_set_app
|
|
16
|
+
from raise_cli.onboarding.skills import SkillScaffoldResult, scaffold_skills
|
|
17
|
+
from raise_cli.output.formatters.skill import (
|
|
18
|
+
format_name_check_human,
|
|
19
|
+
format_name_check_json,
|
|
20
|
+
format_scaffold_human,
|
|
21
|
+
format_scaffold_json,
|
|
22
|
+
format_skill_list_human,
|
|
23
|
+
format_skill_list_json,
|
|
24
|
+
format_validation_human,
|
|
25
|
+
format_validation_json,
|
|
26
|
+
)
|
|
27
|
+
from raise_cli.skills.locator import SkillLocator, get_default_skill_dir
|
|
28
|
+
from raise_cli.skills.name_checker import check_name
|
|
29
|
+
from raise_cli.skills.scaffold import scaffold_skill
|
|
30
|
+
from raise_cli.skills.validator import (
|
|
31
|
+
ValidationResult,
|
|
32
|
+
validate_skill,
|
|
33
|
+
validate_skill_file,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
skill_app = typer.Typer(
|
|
37
|
+
name="skill",
|
|
38
|
+
help="Manage RaiSE skills",
|
|
39
|
+
no_args_is_help=True,
|
|
40
|
+
)
|
|
41
|
+
skill_app.add_typer(skill_set_app, name="set")
|
|
42
|
+
|
|
43
|
+
console = Console()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@skill_app.command("list")
|
|
47
|
+
def list_command(
|
|
48
|
+
format: Annotated[
|
|
49
|
+
str,
|
|
50
|
+
typer.Option(
|
|
51
|
+
"--format",
|
|
52
|
+
"-f",
|
|
53
|
+
help="Output format: human or json",
|
|
54
|
+
),
|
|
55
|
+
] = "human",
|
|
56
|
+
) -> None:
|
|
57
|
+
"""List all skills in the skill directory.
|
|
58
|
+
|
|
59
|
+
Shows skills grouped by lifecycle with version and description.
|
|
60
|
+
"""
|
|
61
|
+
skill_dir = get_default_skill_dir()
|
|
62
|
+
locator = SkillLocator(skill_dir)
|
|
63
|
+
skills = locator.load_all_skills()
|
|
64
|
+
grouped = locator.group_by_lifecycle(skills)
|
|
65
|
+
|
|
66
|
+
if format == "json":
|
|
67
|
+
output = format_skill_list_json(skills, str(skill_dir))
|
|
68
|
+
print(output) # Plain print for valid JSON
|
|
69
|
+
else:
|
|
70
|
+
format_skill_list_human(skills, grouped, console)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@skill_app.command("validate")
|
|
74
|
+
def validate_command(
|
|
75
|
+
path: Annotated[
|
|
76
|
+
str | None,
|
|
77
|
+
typer.Argument(
|
|
78
|
+
help="Path to skill file or directory. Validates all skills if not specified.",
|
|
79
|
+
),
|
|
80
|
+
] = None,
|
|
81
|
+
format: Annotated[
|
|
82
|
+
str,
|
|
83
|
+
typer.Option(
|
|
84
|
+
"--format",
|
|
85
|
+
"-f",
|
|
86
|
+
help="Output format: human or json",
|
|
87
|
+
),
|
|
88
|
+
] = "human",
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Validate skill structure against RaiSE schema.
|
|
91
|
+
|
|
92
|
+
Checks frontmatter, required fields, sections, and naming conventions.
|
|
93
|
+
"""
|
|
94
|
+
results: list[ValidationResult] = []
|
|
95
|
+
|
|
96
|
+
if path:
|
|
97
|
+
# Validate specific file or directory
|
|
98
|
+
target = Path(path)
|
|
99
|
+
if target.is_file():
|
|
100
|
+
results.append(validate_skill_file(target))
|
|
101
|
+
elif target.is_dir():
|
|
102
|
+
# Look for SKILL.md in directory
|
|
103
|
+
skill_file = target / "SKILL.md"
|
|
104
|
+
if skill_file.exists():
|
|
105
|
+
results.append(validate_skill_file(skill_file))
|
|
106
|
+
else:
|
|
107
|
+
results.append(
|
|
108
|
+
ValidationResult(
|
|
109
|
+
path=str(target),
|
|
110
|
+
errors=[f"No SKILL.md found in {target}"],
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
else:
|
|
114
|
+
results.append(
|
|
115
|
+
ValidationResult(
|
|
116
|
+
path=str(target),
|
|
117
|
+
errors=[f"Path not found: {target}"],
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
# Validate all skills
|
|
122
|
+
skill_dir = get_default_skill_dir()
|
|
123
|
+
locator = SkillLocator(skill_dir)
|
|
124
|
+
skills = locator.load_all_skills()
|
|
125
|
+
|
|
126
|
+
for skill in skills:
|
|
127
|
+
results.append(validate_skill(skill))
|
|
128
|
+
|
|
129
|
+
# Output results
|
|
130
|
+
if format == "json":
|
|
131
|
+
print(format_validation_json(results))
|
|
132
|
+
else:
|
|
133
|
+
format_validation_human(results, console)
|
|
134
|
+
|
|
135
|
+
# Exit with error code if any validation failed
|
|
136
|
+
if not all(r.is_valid for r in results):
|
|
137
|
+
raise typer.Exit(code=1)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@skill_app.command("check-name")
|
|
141
|
+
def check_name_command(
|
|
142
|
+
name: Annotated[
|
|
143
|
+
str,
|
|
144
|
+
typer.Argument(
|
|
145
|
+
help="Proposed skill name to check (e.g., 'story-validate').",
|
|
146
|
+
),
|
|
147
|
+
],
|
|
148
|
+
format: Annotated[
|
|
149
|
+
str,
|
|
150
|
+
typer.Option(
|
|
151
|
+
"--format",
|
|
152
|
+
"-f",
|
|
153
|
+
help="Output format: human or json",
|
|
154
|
+
),
|
|
155
|
+
] = "human",
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Check a proposed skill name against naming conventions.
|
|
158
|
+
|
|
159
|
+
Validates that the name follows {domain}-{action} pattern,
|
|
160
|
+
doesn't conflict with existing skills or CLI commands,
|
|
161
|
+
and uses a known lifecycle domain.
|
|
162
|
+
"""
|
|
163
|
+
result = check_name(name)
|
|
164
|
+
|
|
165
|
+
# Output results
|
|
166
|
+
if format == "json":
|
|
167
|
+
print(format_name_check_json(result))
|
|
168
|
+
else:
|
|
169
|
+
format_name_check_human(result, console)
|
|
170
|
+
|
|
171
|
+
# Exit with error code if name is invalid
|
|
172
|
+
if not result.is_valid:
|
|
173
|
+
raise typer.Exit(code=1)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@skill_app.command("scaffold")
|
|
177
|
+
def scaffold_command(
|
|
178
|
+
name: Annotated[
|
|
179
|
+
str,
|
|
180
|
+
typer.Argument(
|
|
181
|
+
help="Skill name to create (e.g., 'story-validate').",
|
|
182
|
+
),
|
|
183
|
+
],
|
|
184
|
+
lifecycle: Annotated[
|
|
185
|
+
str | None,
|
|
186
|
+
typer.Option(
|
|
187
|
+
"--lifecycle",
|
|
188
|
+
"-l",
|
|
189
|
+
help="Lifecycle: session, epic, story, discovery, utility, meta. Inferred from name if not specified.",
|
|
190
|
+
),
|
|
191
|
+
] = None,
|
|
192
|
+
after: Annotated[
|
|
193
|
+
str | None,
|
|
194
|
+
typer.Option(
|
|
195
|
+
"--after",
|
|
196
|
+
help="Skill that should come before this one (prerequisites).",
|
|
197
|
+
),
|
|
198
|
+
] = None,
|
|
199
|
+
before: Annotated[
|
|
200
|
+
str | None,
|
|
201
|
+
typer.Option(
|
|
202
|
+
"--before",
|
|
203
|
+
help="Skill that should come after this one (next).",
|
|
204
|
+
),
|
|
205
|
+
] = None,
|
|
206
|
+
skill_set: Annotated[
|
|
207
|
+
str | None,
|
|
208
|
+
typer.Option(
|
|
209
|
+
"--set",
|
|
210
|
+
help="Skill set to create in (e.g., 'my-team'). Creates in .raise/skills/{set}/.",
|
|
211
|
+
),
|
|
212
|
+
] = None,
|
|
213
|
+
from_builtin: Annotated[
|
|
214
|
+
bool,
|
|
215
|
+
typer.Option(
|
|
216
|
+
"--from-builtin",
|
|
217
|
+
help="Copy from deployed builtin skill as starting point. Requires --set.",
|
|
218
|
+
),
|
|
219
|
+
] = False,
|
|
220
|
+
format: Annotated[
|
|
221
|
+
str,
|
|
222
|
+
typer.Option(
|
|
223
|
+
"--format",
|
|
224
|
+
"-f",
|
|
225
|
+
help="Output format: human or json",
|
|
226
|
+
),
|
|
227
|
+
] = "human",
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Create a new skill from template.
|
|
230
|
+
|
|
231
|
+
Generates a SKILL.md file with proper structure.
|
|
232
|
+
Without --set: creates in .claude/skills/<name>/.
|
|
233
|
+
With --set: creates in .raise/skills/<set>/<name>/.
|
|
234
|
+
"""
|
|
235
|
+
result = scaffold_skill(
|
|
236
|
+
name,
|
|
237
|
+
lifecycle=lifecycle,
|
|
238
|
+
after=after,
|
|
239
|
+
before=before,
|
|
240
|
+
skill_set=skill_set,
|
|
241
|
+
from_builtin=from_builtin,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Output results
|
|
245
|
+
if format == "json":
|
|
246
|
+
print(format_scaffold_json(result))
|
|
247
|
+
else:
|
|
248
|
+
format_scaffold_human(result, console)
|
|
249
|
+
|
|
250
|
+
# Exit with error code if creation failed
|
|
251
|
+
if not result.created:
|
|
252
|
+
raise typer.Exit(code=1)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _print_sync_table(result: SkillScaffoldResult) -> None:
|
|
256
|
+
"""Print a summary table of skill sync status."""
|
|
257
|
+
from rich.table import Table
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
from raise_cli.skills_base import __version__ as cli_version
|
|
261
|
+
except ImportError:
|
|
262
|
+
cli_version = "unknown"
|
|
263
|
+
|
|
264
|
+
console.print(f"\n[bold]Skill sync check: raise-cli {cli_version}[/bold]\n")
|
|
265
|
+
|
|
266
|
+
rows: list[tuple[str, str]] = []
|
|
267
|
+
for name in result.skills_installed:
|
|
268
|
+
rows.append((name, "[green]new — not deployed[/green]"))
|
|
269
|
+
for name in result.skills_updated:
|
|
270
|
+
rows.append((name, "[yellow]outdated — update available[/yellow]"))
|
|
271
|
+
for name in result.skills_conflicted:
|
|
272
|
+
rows.append((name, "[yellow]conflict — both changed[/yellow]"))
|
|
273
|
+
for name in result.skills_current:
|
|
274
|
+
rows.append((name, "[green]current[/green]"))
|
|
275
|
+
|
|
276
|
+
rows.sort(key=lambda r: r[0])
|
|
277
|
+
|
|
278
|
+
table = Table(show_header=True)
|
|
279
|
+
table.add_column("Skill", style="bold")
|
|
280
|
+
table.add_column("Status")
|
|
281
|
+
for name, status in rows:
|
|
282
|
+
table.add_row(name, status)
|
|
283
|
+
|
|
284
|
+
console.print(table)
|
|
285
|
+
|
|
286
|
+
n_stale = (
|
|
287
|
+
len(result.skills_updated)
|
|
288
|
+
+ len(result.skills_installed)
|
|
289
|
+
+ len(result.skills_conflicted)
|
|
290
|
+
)
|
|
291
|
+
n_current = len(result.skills_current)
|
|
292
|
+
if n_stale == 0:
|
|
293
|
+
console.print(f"\n All {n_current} skills are current.\n")
|
|
294
|
+
else:
|
|
295
|
+
console.print(
|
|
296
|
+
f"\n {n_stale} skill(s) need attention, "
|
|
297
|
+
f"{n_current} current. Run [bold]rai init[/bold] to update.\n"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@skill_app.command("sync")
|
|
302
|
+
def sync_cmd(
|
|
303
|
+
path: Annotated[
|
|
304
|
+
Path | None, typer.Option("--path", "-p", help="Project path")
|
|
305
|
+
] = None,
|
|
306
|
+
) -> None:
|
|
307
|
+
"""Check skill freshness against installed package version.
|
|
308
|
+
|
|
309
|
+
Reports which skills are current, outdated, or have conflicts.
|
|
310
|
+
Exit code 0 = all current, 1 = updates available.
|
|
311
|
+
|
|
312
|
+
Examples:
|
|
313
|
+
$ rai skill sync
|
|
314
|
+
$ rai skill sync --path /path/to/project
|
|
315
|
+
"""
|
|
316
|
+
from raise_cli.config.agent_registry import load_registry
|
|
317
|
+
|
|
318
|
+
project_path = (path or Path.cwd()).resolve()
|
|
319
|
+
registry = load_registry(project_root=project_path)
|
|
320
|
+
|
|
321
|
+
# Determine agent type from manifest, fall back to "claude"
|
|
322
|
+
agent_type = "claude"
|
|
323
|
+
try:
|
|
324
|
+
from raise_cli.onboarding.manifest import load_manifest
|
|
325
|
+
|
|
326
|
+
manifest = load_manifest(project_path)
|
|
327
|
+
if manifest and manifest.agents.types:
|
|
328
|
+
agent_type = manifest.agents.types[0]
|
|
329
|
+
except Exception:
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
config = registry.get_config(agent_type)
|
|
333
|
+
plugin = registry.get_plugin(agent_type)
|
|
334
|
+
|
|
335
|
+
result = scaffold_skills(
|
|
336
|
+
project_path,
|
|
337
|
+
agent_config=config,
|
|
338
|
+
plugin=plugin,
|
|
339
|
+
dry_run=True,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
_print_sync_table(result)
|
|
343
|
+
|
|
344
|
+
has_updates = bool(
|
|
345
|
+
result.skills_updated
|
|
346
|
+
or result.skills_installed
|
|
347
|
+
or result.skills_conflicted
|
|
348
|
+
)
|
|
349
|
+
if has_updates:
|
|
350
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""CLI commands for skill set management.
|
|
2
|
+
|
|
3
|
+
Provides ``rai skill set create|list|diff`` for observable skill set
|
|
4
|
+
operations. These commands are the foundation for ``/rai-skillset-manage``.
|
|
5
|
+
(S340.4, RAISE-344)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Annotated
|
|
13
|
+
|
|
14
|
+
import typer
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
|
|
18
|
+
from raise_cli.skills.skillsets import (
|
|
19
|
+
create_skill_set,
|
|
20
|
+
diff_skill_set,
|
|
21
|
+
list_skill_sets,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
skill_set_app = typer.Typer(
|
|
25
|
+
name="set",
|
|
26
|
+
help="Manage skill sets",
|
|
27
|
+
no_args_is_help=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
console = Console()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@skill_set_app.command("create")
|
|
34
|
+
def create_command(
|
|
35
|
+
name: Annotated[
|
|
36
|
+
str,
|
|
37
|
+
typer.Argument(help="Skill set name (e.g., 'my-team')."),
|
|
38
|
+
],
|
|
39
|
+
empty: Annotated[
|
|
40
|
+
bool,
|
|
41
|
+
typer.Option("--empty", help="Create empty set (no builtins copied)."),
|
|
42
|
+
] = False,
|
|
43
|
+
format: Annotated[
|
|
44
|
+
str,
|
|
45
|
+
typer.Option("--format", "-f", help="Output format: human or json"),
|
|
46
|
+
] = "human",
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Create a new skill set from builtins.
|
|
49
|
+
|
|
50
|
+
Copies all builtin skills to .raise/skills/<name>/ as a starting
|
|
51
|
+
base for customization. Use --empty for a blank set.
|
|
52
|
+
"""
|
|
53
|
+
project_root = Path.cwd()
|
|
54
|
+
result = create_skill_set(name, project_root, empty=empty)
|
|
55
|
+
|
|
56
|
+
if format == "json":
|
|
57
|
+
print(result.model_dump_json(indent=2))
|
|
58
|
+
elif result.created:
|
|
59
|
+
console.print(f"[green]✓[/green] Skill set '{name}' created at {result.path}")
|
|
60
|
+
console.print(f" {result.skill_count} skills copied from builtins")
|
|
61
|
+
console.print("\n Next: customize skills, then deploy with:")
|
|
62
|
+
console.print(f" [bold]rai init --skill-set {name}[/bold]")
|
|
63
|
+
else:
|
|
64
|
+
console.print(f"[red]✗[/red] {result.error}")
|
|
65
|
+
raise typer.Exit(code=1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@skill_set_app.command("list")
|
|
69
|
+
def list_command(
|
|
70
|
+
format: Annotated[
|
|
71
|
+
str,
|
|
72
|
+
typer.Option("--format", "-f", help="Output format: human or json"),
|
|
73
|
+
] = "human",
|
|
74
|
+
) -> None:
|
|
75
|
+
"""List all skill sets in .raise/skills/."""
|
|
76
|
+
project_root = Path.cwd()
|
|
77
|
+
sets = list_skill_sets(project_root)
|
|
78
|
+
|
|
79
|
+
if format == "json":
|
|
80
|
+
print(json.dumps([s.model_dump() for s in sets], indent=2))
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
if not sets:
|
|
84
|
+
console.print("No skill sets found in .raise/skills/")
|
|
85
|
+
console.print(" Create one with: [bold]rai skill set create <name>[/bold]")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
table = Table(title="Skill Sets")
|
|
89
|
+
table.add_column("Name", style="bold")
|
|
90
|
+
table.add_column("Skills", justify="right")
|
|
91
|
+
table.add_column("Path")
|
|
92
|
+
|
|
93
|
+
for s in sets:
|
|
94
|
+
table.add_row(s.name, str(s.skill_count), s.path)
|
|
95
|
+
|
|
96
|
+
console.print(table)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@skill_set_app.command("diff")
|
|
100
|
+
def diff_command(
|
|
101
|
+
name: Annotated[
|
|
102
|
+
str,
|
|
103
|
+
typer.Argument(help="Skill set name to compare against builtins."),
|
|
104
|
+
],
|
|
105
|
+
format: Annotated[
|
|
106
|
+
str,
|
|
107
|
+
typer.Option("--format", "-f", help="Output format: human or json"),
|
|
108
|
+
] = "human",
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Compare a skill set against builtins.
|
|
111
|
+
|
|
112
|
+
Shows which skills are added, modified, or unchanged relative
|
|
113
|
+
to the builtin skill set.
|
|
114
|
+
"""
|
|
115
|
+
project_root = Path.cwd()
|
|
116
|
+
diff = diff_skill_set(name, project_root)
|
|
117
|
+
|
|
118
|
+
if diff is None:
|
|
119
|
+
console.print(f"[red]✗[/red] Skill set '{name}' not found")
|
|
120
|
+
raise typer.Exit(code=1)
|
|
121
|
+
|
|
122
|
+
if format == "json":
|
|
123
|
+
print(diff.model_dump_json(indent=2))
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
console.print(f"[bold]Skill set:[/bold] {name}\n")
|
|
127
|
+
|
|
128
|
+
if diff.added:
|
|
129
|
+
console.print(f"[green]Added ({len(diff.added)}):[/green]")
|
|
130
|
+
for s in diff.added:
|
|
131
|
+
console.print(f" + {s}")
|
|
132
|
+
|
|
133
|
+
if diff.modified:
|
|
134
|
+
console.print(f"[yellow]Modified ({len(diff.modified)}):[/yellow]")
|
|
135
|
+
for s in diff.modified:
|
|
136
|
+
console.print(f" ~ {s}")
|
|
137
|
+
|
|
138
|
+
if diff.unchanged:
|
|
139
|
+
console.print(f"[dim]Unchanged ({len(diff.unchanged)}):[/dim]")
|
|
140
|
+
for s in diff.unchanged:
|
|
141
|
+
console.print(f" = {s}")
|
|
142
|
+
|
|
143
|
+
total = len(diff.added) + len(diff.modified) + len(diff.unchanged)
|
|
144
|
+
added, modified = len(diff.added), len(diff.modified)
|
|
145
|
+
console.print(f"\n[bold]Total:[/bold] {total} skills ({added} added, {modified} modified)")
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Error presentation with Rich formatting.
|
|
2
|
+
|
|
3
|
+
This module provides error display for raise-cli, supporting both
|
|
4
|
+
human-friendly Rich output and JSON format for scripting.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from raise_cli.exceptions import KataNotFoundError
|
|
8
|
+
>>> from raise_cli.cli.error_handler import handle_error
|
|
9
|
+
>>>
|
|
10
|
+
>>> error = KataNotFoundError("Kata 'foo' not found", hint="Check .raise/katas/")
|
|
11
|
+
>>> exit_code = handle_error(error, format="human")
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import sys
|
|
18
|
+
from typing import TYPE_CHECKING, Literal, NoReturn
|
|
19
|
+
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from raise_cli.exceptions import RaiError
|
|
25
|
+
|
|
26
|
+
# Stderr console for error output
|
|
27
|
+
_error_console: Console | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_error_console() -> Console:
|
|
31
|
+
"""Get or create the stderr console singleton.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Rich Console configured for stderr output.
|
|
35
|
+
"""
|
|
36
|
+
global _error_console # noqa: PLW0603
|
|
37
|
+
if _error_console is None:
|
|
38
|
+
_error_console = Console(stderr=True)
|
|
39
|
+
return _error_console
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def set_error_console(console: Console | None) -> None:
|
|
43
|
+
"""Set the error console (for testing).
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
console: Console to use, or None to reset to default.
|
|
47
|
+
"""
|
|
48
|
+
global _error_console # noqa: PLW0603
|
|
49
|
+
_error_console = console
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def handle_error(
|
|
53
|
+
error: RaiError,
|
|
54
|
+
*,
|
|
55
|
+
output_format: Literal["human", "json", "table"] = "human",
|
|
56
|
+
) -> int:
|
|
57
|
+
"""Format and display error, return exit code.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
error: The RaiError to display.
|
|
61
|
+
output_format: Output format (human uses Rich, json outputs JSON).
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The error's exit_code for use with sys.exit().
|
|
65
|
+
"""
|
|
66
|
+
if output_format == "json":
|
|
67
|
+
_handle_error_json(error)
|
|
68
|
+
else:
|
|
69
|
+
_handle_error_human(error)
|
|
70
|
+
|
|
71
|
+
return error.exit_code
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _handle_error_human(error: RaiError) -> None:
|
|
75
|
+
"""Display error with Rich formatting.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
error: The RaiError to display.
|
|
79
|
+
"""
|
|
80
|
+
console = get_error_console()
|
|
81
|
+
|
|
82
|
+
# Main error panel
|
|
83
|
+
console.print(
|
|
84
|
+
Panel(
|
|
85
|
+
f"[bold red]{error.message}[/]",
|
|
86
|
+
title=f"[red]Error {error.error_code}[/]",
|
|
87
|
+
border_style="red",
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Details section
|
|
92
|
+
if error.details:
|
|
93
|
+
console.print("\n[dim]Details:[/]")
|
|
94
|
+
for key, value in error.details.items():
|
|
95
|
+
console.print(f" [dim]\u2022[/] {key}: {value}")
|
|
96
|
+
|
|
97
|
+
# Hint section
|
|
98
|
+
if error.hint:
|
|
99
|
+
console.print(f"\n[cyan]Hint:[/] {error.hint}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _handle_error_json(error: RaiError) -> None:
|
|
103
|
+
"""Output error as JSON to stderr.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
error: The RaiError to display.
|
|
107
|
+
"""
|
|
108
|
+
output = json.dumps(error.to_dict(), indent=2)
|
|
109
|
+
print(output, file=sys.stderr)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def cli_error(
|
|
113
|
+
message: str,
|
|
114
|
+
*,
|
|
115
|
+
hint: str | None = None,
|
|
116
|
+
exit_code: int = 1,
|
|
117
|
+
) -> NoReturn:
|
|
118
|
+
"""Print error message and exit with code.
|
|
119
|
+
|
|
120
|
+
This is the standard pattern for CLI error handling. Use this instead of
|
|
121
|
+
manually printing errors and calling typer.Exit().
|
|
122
|
+
|
|
123
|
+
Exit codes (from exceptions.py):
|
|
124
|
+
1 - General error
|
|
125
|
+
2 - Configuration error
|
|
126
|
+
3 - Resource not found (kata, gate)
|
|
127
|
+
4 - Artifact not found
|
|
128
|
+
5 - Dependency unavailable
|
|
129
|
+
6 - State corruption
|
|
130
|
+
7 - Validation error
|
|
131
|
+
10 - Gate failed
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
message: Error message to display.
|
|
135
|
+
hint: Optional suggestion for resolution.
|
|
136
|
+
exit_code: Process exit code (default 1).
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
typer.Exit: Always raises to exit the CLI.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> cli_error("File not found", hint="Check the path", exit_code=4)
|
|
143
|
+
"""
|
|
144
|
+
import typer
|
|
145
|
+
|
|
146
|
+
console = get_error_console()
|
|
147
|
+
console.print(f"[red]Error:[/red] {message}")
|
|
148
|
+
if hint:
|
|
149
|
+
console.print(f"[dim]Hint: {hint}[/dim]")
|
|
150
|
+
raise typer.Exit(exit_code)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
__all__ = [
|
|
154
|
+
"cli_error",
|
|
155
|
+
"handle_error",
|
|
156
|
+
"get_error_console",
|
|
157
|
+
"set_error_console",
|
|
158
|
+
]
|