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,374 @@
|
|
|
1
|
+
"""Project type detection for RaiSE initialization.
|
|
2
|
+
|
|
3
|
+
Detects whether a directory is greenfield (no code) or brownfield (existing code)
|
|
4
|
+
by counting source code files while excluding common non-project directories.
|
|
5
|
+
Also detects dominant language and suggests toolchain commands.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections import Counter
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from raise_cli.core.files import EXCLUDED_DIRS, should_exclude_dir
|
|
16
|
+
|
|
17
|
+
# Re-export for backward compatibility
|
|
18
|
+
__all__ = [
|
|
19
|
+
"CODE_EXTENSIONS",
|
|
20
|
+
"EXCLUDED_DIRS",
|
|
21
|
+
"LANGUAGE_TOOLCHAIN",
|
|
22
|
+
"ProjectType",
|
|
23
|
+
"DetectionResult",
|
|
24
|
+
"ToolchainInfo",
|
|
25
|
+
"detect_language",
|
|
26
|
+
"detect_project_type",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Common code file extensions to detect
|
|
30
|
+
CODE_EXTENSIONS: frozenset[str] = frozenset(
|
|
31
|
+
{
|
|
32
|
+
# Python
|
|
33
|
+
".py",
|
|
34
|
+
# JavaScript/TypeScript
|
|
35
|
+
".js",
|
|
36
|
+
".jsx",
|
|
37
|
+
".ts",
|
|
38
|
+
".tsx",
|
|
39
|
+
".mjs",
|
|
40
|
+
".cjs",
|
|
41
|
+
# JVM
|
|
42
|
+
".java",
|
|
43
|
+
".kt",
|
|
44
|
+
".scala",
|
|
45
|
+
# Systems
|
|
46
|
+
".c",
|
|
47
|
+
".cpp",
|
|
48
|
+
".cc",
|
|
49
|
+
".cxx",
|
|
50
|
+
".h",
|
|
51
|
+
".hpp",
|
|
52
|
+
".rs",
|
|
53
|
+
".go",
|
|
54
|
+
# Scripting
|
|
55
|
+
".rb",
|
|
56
|
+
".php",
|
|
57
|
+
".pl",
|
|
58
|
+
".pm",
|
|
59
|
+
# .NET
|
|
60
|
+
".cs",
|
|
61
|
+
".fs",
|
|
62
|
+
".vb",
|
|
63
|
+
# Other
|
|
64
|
+
".swift",
|
|
65
|
+
".m",
|
|
66
|
+
".mm",
|
|
67
|
+
".lua",
|
|
68
|
+
".r",
|
|
69
|
+
".R",
|
|
70
|
+
".jl",
|
|
71
|
+
".dart",
|
|
72
|
+
".ex",
|
|
73
|
+
".exs",
|
|
74
|
+
".erl",
|
|
75
|
+
".hrl",
|
|
76
|
+
".clj",
|
|
77
|
+
".cljs",
|
|
78
|
+
".elm",
|
|
79
|
+
".hs",
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Map file extensions to language names
|
|
85
|
+
EXTENSION_TO_LANGUAGE: dict[str, str] = {
|
|
86
|
+
".py": "python",
|
|
87
|
+
".pyi": "python",
|
|
88
|
+
".ts": "typescript",
|
|
89
|
+
".tsx": "typescript",
|
|
90
|
+
".js": "javascript",
|
|
91
|
+
".jsx": "javascript",
|
|
92
|
+
".mjs": "javascript",
|
|
93
|
+
".cjs": "javascript",
|
|
94
|
+
".cs": "csharp",
|
|
95
|
+
".fs": "fsharp",
|
|
96
|
+
".vb": "vb",
|
|
97
|
+
".java": "java",
|
|
98
|
+
".kt": "kotlin",
|
|
99
|
+
".scala": "scala",
|
|
100
|
+
".go": "go",
|
|
101
|
+
".rs": "rust",
|
|
102
|
+
".rb": "ruby",
|
|
103
|
+
".php": "php",
|
|
104
|
+
".dart": "dart",
|
|
105
|
+
".swift": "swift",
|
|
106
|
+
".c": "c",
|
|
107
|
+
".cpp": "cpp",
|
|
108
|
+
".cc": "cpp",
|
|
109
|
+
".cxx": "cpp",
|
|
110
|
+
".h": "c",
|
|
111
|
+
".hpp": "cpp",
|
|
112
|
+
".ex": "elixir",
|
|
113
|
+
".exs": "elixir",
|
|
114
|
+
".erl": "erlang",
|
|
115
|
+
".hrl": "erlang",
|
|
116
|
+
".hs": "haskell",
|
|
117
|
+
".elm": "elm",
|
|
118
|
+
".clj": "clojure",
|
|
119
|
+
".cljs": "clojure",
|
|
120
|
+
".jl": "julia",
|
|
121
|
+
".lua": "lua",
|
|
122
|
+
".r": "r",
|
|
123
|
+
".R": "r",
|
|
124
|
+
".pl": "perl",
|
|
125
|
+
".pm": "perl",
|
|
126
|
+
".m": "objective-c",
|
|
127
|
+
".mm": "objective-c",
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(frozen=True)
|
|
132
|
+
class ToolchainInfo:
|
|
133
|
+
"""Suggested toolchain commands for a language.
|
|
134
|
+
|
|
135
|
+
Attributes:
|
|
136
|
+
language: Detected language name.
|
|
137
|
+
test_command: Suggested test runner command.
|
|
138
|
+
lint_command: Suggested linter command, if known.
|
|
139
|
+
type_check_command: Suggested type checker command, if known.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
language: str
|
|
143
|
+
test_command: str | None = None
|
|
144
|
+
lint_command: str | None = None
|
|
145
|
+
type_check_command: str | None = None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Default toolchain commands per language
|
|
149
|
+
LANGUAGE_TOOLCHAIN: dict[str, ToolchainInfo] = {
|
|
150
|
+
"python": ToolchainInfo(
|
|
151
|
+
language="python",
|
|
152
|
+
test_command="uv run pytest --tb=short",
|
|
153
|
+
lint_command="uv run ruff check",
|
|
154
|
+
type_check_command="uv run pyright",
|
|
155
|
+
),
|
|
156
|
+
"typescript": ToolchainInfo(
|
|
157
|
+
language="typescript",
|
|
158
|
+
test_command="npx vitest run",
|
|
159
|
+
lint_command="npx eslint .",
|
|
160
|
+
type_check_command="npx tsc --noEmit",
|
|
161
|
+
),
|
|
162
|
+
"javascript": ToolchainInfo(
|
|
163
|
+
language="javascript",
|
|
164
|
+
test_command="npx vitest run",
|
|
165
|
+
lint_command="npx eslint .",
|
|
166
|
+
),
|
|
167
|
+
"csharp": ToolchainInfo(
|
|
168
|
+
language="csharp",
|
|
169
|
+
test_command="dotnet test --verbosity quiet",
|
|
170
|
+
lint_command="dotnet format --verify-no-changes",
|
|
171
|
+
type_check_command="dotnet build --no-restore",
|
|
172
|
+
),
|
|
173
|
+
"java": ToolchainInfo(
|
|
174
|
+
language="java",
|
|
175
|
+
test_command="mvn test",
|
|
176
|
+
lint_command="mvn checkstyle:check",
|
|
177
|
+
),
|
|
178
|
+
"go": ToolchainInfo(
|
|
179
|
+
language="go",
|
|
180
|
+
test_command="go test ./...",
|
|
181
|
+
lint_command="golangci-lint run",
|
|
182
|
+
type_check_command="go vet ./...",
|
|
183
|
+
),
|
|
184
|
+
"rust": ToolchainInfo(
|
|
185
|
+
language="rust",
|
|
186
|
+
test_command="cargo test",
|
|
187
|
+
lint_command="cargo clippy",
|
|
188
|
+
type_check_command="cargo check",
|
|
189
|
+
),
|
|
190
|
+
"php": ToolchainInfo(
|
|
191
|
+
language="php",
|
|
192
|
+
test_command="vendor/bin/phpunit",
|
|
193
|
+
lint_command="vendor/bin/php-cs-fixer fix --dry-run",
|
|
194
|
+
type_check_command="vendor/bin/phpstan analyse",
|
|
195
|
+
),
|
|
196
|
+
"dart": ToolchainInfo(
|
|
197
|
+
language="dart",
|
|
198
|
+
test_command="flutter test",
|
|
199
|
+
lint_command="dart fix --dry-run",
|
|
200
|
+
type_check_command="dart analyze",
|
|
201
|
+
),
|
|
202
|
+
"ruby": ToolchainInfo(
|
|
203
|
+
language="ruby",
|
|
204
|
+
test_command="bundle exec rspec",
|
|
205
|
+
lint_command="bundle exec rubocop",
|
|
206
|
+
),
|
|
207
|
+
"kotlin": ToolchainInfo(
|
|
208
|
+
language="kotlin",
|
|
209
|
+
test_command="./gradlew test",
|
|
210
|
+
lint_command="./gradlew ktlintCheck",
|
|
211
|
+
),
|
|
212
|
+
"swift": ToolchainInfo(
|
|
213
|
+
language="swift",
|
|
214
|
+
test_command="swift test",
|
|
215
|
+
lint_command="swiftlint",
|
|
216
|
+
),
|
|
217
|
+
"elixir": ToolchainInfo(
|
|
218
|
+
language="elixir",
|
|
219
|
+
test_command="mix test",
|
|
220
|
+
lint_command="mix credo",
|
|
221
|
+
type_check_command="mix dialyzer",
|
|
222
|
+
),
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ProjectType(StrEnum):
|
|
227
|
+
"""Type of project based on existing code.
|
|
228
|
+
|
|
229
|
+
Values:
|
|
230
|
+
GREENFIELD: No existing code files (new project)
|
|
231
|
+
BROWNFIELD: Has existing code files
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
GREENFIELD = "greenfield"
|
|
235
|
+
BROWNFIELD = "brownfield"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True)
|
|
239
|
+
class DetectionResult:
|
|
240
|
+
"""Result of project type detection.
|
|
241
|
+
|
|
242
|
+
Attributes:
|
|
243
|
+
project_type: Whether the project is greenfield or brownfield.
|
|
244
|
+
code_file_count: Number of code files detected.
|
|
245
|
+
language: Dominant language detected, if any.
|
|
246
|
+
toolchain: Suggested toolchain commands for the detected language.
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
project_type: ProjectType
|
|
250
|
+
code_file_count: int
|
|
251
|
+
language: str | None = None
|
|
252
|
+
toolchain: ToolchainInfo | None = None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _count_extensions(directory: Path) -> Counter[str]:
|
|
256
|
+
"""Count code file extensions in a directory recursively.
|
|
257
|
+
|
|
258
|
+
Excludes hidden directories, node_modules, __pycache__, etc.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
directory: Root directory to scan.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Counter mapping file extensions to their counts.
|
|
265
|
+
"""
|
|
266
|
+
counts: Counter[str] = Counter()
|
|
267
|
+
if not directory.is_dir():
|
|
268
|
+
return counts
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
for item in directory.iterdir():
|
|
272
|
+
if item.is_dir():
|
|
273
|
+
if not should_exclude_dir(item):
|
|
274
|
+
counts += _count_extensions(item)
|
|
275
|
+
elif item.is_file() and item.suffix in CODE_EXTENSIONS:
|
|
276
|
+
counts[item.suffix] += 1
|
|
277
|
+
except PermissionError:
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
return counts
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def count_code_files(directory: Path) -> int:
|
|
284
|
+
"""Count code files in a directory recursively.
|
|
285
|
+
|
|
286
|
+
Excludes hidden directories, node_modules, __pycache__, etc.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
directory: Root directory to scan.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Number of code files found.
|
|
293
|
+
"""
|
|
294
|
+
return sum(_count_extensions(directory).values())
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def detect_language(directory: Path) -> ToolchainInfo | None:
|
|
298
|
+
"""Detect the dominant language in a directory.
|
|
299
|
+
|
|
300
|
+
Counts file extensions, maps them to languages, and returns the
|
|
301
|
+
toolchain info for the most common language.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
directory: Root directory to scan.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
ToolchainInfo for the dominant language, or None if no code files found.
|
|
308
|
+
"""
|
|
309
|
+
ext_counts = _count_extensions(directory)
|
|
310
|
+
if not ext_counts:
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
# Map extensions to language counts
|
|
314
|
+
lang_counts: Counter[str] = Counter()
|
|
315
|
+
for ext, count in ext_counts.items():
|
|
316
|
+
lang = EXTENSION_TO_LANGUAGE.get(ext)
|
|
317
|
+
if lang:
|
|
318
|
+
lang_counts[lang] += count
|
|
319
|
+
|
|
320
|
+
if not lang_counts:
|
|
321
|
+
return None
|
|
322
|
+
|
|
323
|
+
dominant_lang = lang_counts.most_common(1)[0][0]
|
|
324
|
+
return LANGUAGE_TOOLCHAIN.get(
|
|
325
|
+
dominant_lang,
|
|
326
|
+
ToolchainInfo(language=dominant_lang),
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def detect_project_type(directory: Path) -> DetectionResult:
|
|
331
|
+
"""Detect whether a directory is greenfield or brownfield.
|
|
332
|
+
|
|
333
|
+
A greenfield project has no code files.
|
|
334
|
+
A brownfield project has at least one code file.
|
|
335
|
+
For brownfield projects, also detects the dominant language
|
|
336
|
+
and suggests toolchain commands.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
directory: Directory to analyze.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
DetectionResult with project type, file count, and language info.
|
|
343
|
+
"""
|
|
344
|
+
ext_counts = _count_extensions(directory)
|
|
345
|
+
code_file_count = sum(ext_counts.values())
|
|
346
|
+
|
|
347
|
+
if code_file_count == 0:
|
|
348
|
+
return DetectionResult(
|
|
349
|
+
project_type=ProjectType.GREENFIELD,
|
|
350
|
+
code_file_count=0,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Detect dominant language from extension counts
|
|
354
|
+
lang_counts: Counter[str] = Counter()
|
|
355
|
+
for ext, count in ext_counts.items():
|
|
356
|
+
lang = EXTENSION_TO_LANGUAGE.get(ext)
|
|
357
|
+
if lang:
|
|
358
|
+
lang_counts[lang] += count
|
|
359
|
+
|
|
360
|
+
language: str | None = None
|
|
361
|
+
toolchain: ToolchainInfo | None = None
|
|
362
|
+
if lang_counts:
|
|
363
|
+
language = lang_counts.most_common(1)[0][0]
|
|
364
|
+
toolchain = LANGUAGE_TOOLCHAIN.get(
|
|
365
|
+
language,
|
|
366
|
+
ToolchainInfo(language=language),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return DetectionResult(
|
|
370
|
+
project_type=ProjectType.BROWNFIELD,
|
|
371
|
+
code_file_count=code_file_count,
|
|
372
|
+
language=language,
|
|
373
|
+
toolchain=toolchain,
|
|
374
|
+
)
|