osdlc-kit 0.3.0__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.
- osdlc/__init__.py +0 -0
- osdlc/__main__.py +5 -0
- osdlc/cli.py +188 -0
- osdlc/detector.py +67 -0
- osdlc/scaffold.py +206 -0
- osdlc/templates.py +615 -0
- osdlc/version.py +303 -0
- osdlc_kit-0.3.0.dist-info/METADATA +472 -0
- osdlc_kit-0.3.0.dist-info/RECORD +11 -0
- osdlc_kit-0.3.0.dist-info/WHEEL +4 -0
- osdlc_kit-0.3.0.dist-info/entry_points.txt +2 -0
osdlc/__init__.py
ADDED
|
File without changes
|
osdlc/__main__.py
ADDED
osdlc/cli.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
from osdlc.detector import detect_project_type, suggest_language_by_extension
|
|
5
|
+
from osdlc.scaffold import scaffold
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prompt(prompt_text, default=None):
|
|
9
|
+
if default is not None:
|
|
10
|
+
user_input = input(f"{prompt_text} [{default}]: ").strip()
|
|
11
|
+
return user_input if user_input else default
|
|
12
|
+
return input(f"{prompt_text}: ").strip()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def confirm(prompt_text, default=True):
|
|
16
|
+
default_str = "Y/n" if default else "y/N"
|
|
17
|
+
user_input = input(f"{prompt_text} [{default_str}]: ").strip().lower()
|
|
18
|
+
if not user_input:
|
|
19
|
+
return default
|
|
20
|
+
return user_input.startswith("y")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def print_banner():
|
|
24
|
+
print("=" * 60)
|
|
25
|
+
print(" AI Open SDLC Kit - Project Scaffolding")
|
|
26
|
+
print("=" * 60)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def print_summary(generated, skipped, errors):
|
|
30
|
+
print()
|
|
31
|
+
print("=" * 60)
|
|
32
|
+
print(" Summary")
|
|
33
|
+
print("=" * 60)
|
|
34
|
+
|
|
35
|
+
if generated:
|
|
36
|
+
print(f"\n Generated ({len(generated)} files):")
|
|
37
|
+
for f in generated:
|
|
38
|
+
print(f" + {f}")
|
|
39
|
+
|
|
40
|
+
if skipped:
|
|
41
|
+
print(f"\n Skipped ({len(skipped)} files - already exist):")
|
|
42
|
+
for f in skipped:
|
|
43
|
+
print(f" ~ {f}")
|
|
44
|
+
print("\n Use --force to overwrite existing files.")
|
|
45
|
+
|
|
46
|
+
if errors:
|
|
47
|
+
print(f"\n Errors ({len(errors)}):")
|
|
48
|
+
for f, err in errors:
|
|
49
|
+
print(f" ! {f}: {err}")
|
|
50
|
+
|
|
51
|
+
print()
|
|
52
|
+
print(" Quick Start:")
|
|
53
|
+
print(" -------------")
|
|
54
|
+
print(" Run /oc analyze on any issue to start the agent-driven SDLC cycle.")
|
|
55
|
+
print(" Create an issue and comment with '/oc analyze' to get started.")
|
|
56
|
+
print()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def check_git_repo(root="."):
|
|
60
|
+
try:
|
|
61
|
+
import subprocess
|
|
62
|
+
result = subprocess.run(
|
|
63
|
+
["git", "rev-parse", "--is-inside-work-tree"],
|
|
64
|
+
capture_output=True, text=True, timeout=5,
|
|
65
|
+
cwd=root
|
|
66
|
+
)
|
|
67
|
+
return result.returncode == 0
|
|
68
|
+
except Exception:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main():
|
|
73
|
+
parser = argparse.ArgumentParser(
|
|
74
|
+
description="AI Open SDLC Kit - Bootstrap your repository with SDLC methodology"
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"command", nargs="?", default="init",
|
|
78
|
+
choices=["init"],
|
|
79
|
+
help="Command to run (default: init)"
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--force", "-f", action="store_true",
|
|
83
|
+
help="Overwrite existing files"
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--target", "-t", default=".",
|
|
87
|
+
help="Target directory (default: current directory)"
|
|
88
|
+
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"--non-interactive", action="store_true",
|
|
91
|
+
help="Skip interactive prompts (use defaults/detected values)"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
args = parser.parse_args()
|
|
95
|
+
|
|
96
|
+
if args.command != "init":
|
|
97
|
+
print(f"Unknown command: {args.command}")
|
|
98
|
+
return 1
|
|
99
|
+
|
|
100
|
+
root = os.path.abspath(args.target)
|
|
101
|
+
|
|
102
|
+
if not os.path.isdir(root):
|
|
103
|
+
print(f"Error: target directory '{root}' does not exist.")
|
|
104
|
+
return 1
|
|
105
|
+
|
|
106
|
+
if not check_git_repo(root):
|
|
107
|
+
print("Warning: Not inside a Git repository. Some features may not work.")
|
|
108
|
+
print("Consider running 'git init' first.")
|
|
109
|
+
|
|
110
|
+
print_banner()
|
|
111
|
+
|
|
112
|
+
detected = detect_project_type(root)
|
|
113
|
+
ext_lang = suggest_language_by_extension(root)
|
|
114
|
+
|
|
115
|
+
if detected:
|
|
116
|
+
print(f"\n Detected: {detected['language']} ({detected['build_system']})")
|
|
117
|
+
elif ext_lang:
|
|
118
|
+
print(f"\n Suggested language (by file extension): {ext_lang}")
|
|
119
|
+
else:
|
|
120
|
+
print("\n No build system detected. You will be prompted for project details.")
|
|
121
|
+
|
|
122
|
+
print()
|
|
123
|
+
|
|
124
|
+
if args.non_interactive:
|
|
125
|
+
if detected:
|
|
126
|
+
config = detected.copy()
|
|
127
|
+
else:
|
|
128
|
+
config = {
|
|
129
|
+
"build_system": "unknown",
|
|
130
|
+
"language": ext_lang or "unknown",
|
|
131
|
+
"version_file": "VERSION",
|
|
132
|
+
"build_cmd": "echo 'no build command configured'",
|
|
133
|
+
"test_cmd": "echo 'no test command configured'",
|
|
134
|
+
"lint_cmd": "echo 'no lint command configured'",
|
|
135
|
+
"probe": "manual",
|
|
136
|
+
}
|
|
137
|
+
config.setdefault("project_name", os.path.basename(root))
|
|
138
|
+
config.setdefault("default_branch", "main")
|
|
139
|
+
config.setdefault("error_to_issue", False)
|
|
140
|
+
config.setdefault("provider_google", "")
|
|
141
|
+
config.setdefault("model", "opencode/deepseek-v4-flash-free")
|
|
142
|
+
config.setdefault("env_notes", "No special environment constraints.")
|
|
143
|
+
config.setdefault("architectural_notes", "")
|
|
144
|
+
else:
|
|
145
|
+
config = {}
|
|
146
|
+
|
|
147
|
+
template = detected or {}
|
|
148
|
+
|
|
149
|
+
project_name = prompt("Project name", default=os.path.basename(root))
|
|
150
|
+
config["project_name"] = project_name
|
|
151
|
+
|
|
152
|
+
config["default_branch"] = prompt("Default branch name", default="main")
|
|
153
|
+
|
|
154
|
+
version_file_default = template.get("version_file", "VERSION")
|
|
155
|
+
config["version_file"] = prompt("Version config file path", default=version_file_default)
|
|
156
|
+
|
|
157
|
+
config["build_cmd"] = prompt("Build command", default=template.get("build_cmd", "echo 'no build'"))
|
|
158
|
+
config["test_cmd"] = prompt("Test command", default=template.get("test_cmd", "echo 'no tests'"))
|
|
159
|
+
config["lint_cmd"] = prompt("Lint command", default=template.get("lint_cmd", "echo 'no linter'"))
|
|
160
|
+
|
|
161
|
+
config["language"] = template.get("language", ext_lang or prompt("Language", default="unknown"))
|
|
162
|
+
config["build_system"] = template.get("build_system", prompt("Build system", default="manual"))
|
|
163
|
+
config["probe"] = template.get("probe", "manual")
|
|
164
|
+
|
|
165
|
+
error_to_issue = confirm("Enable error-to-issue pipeline?", default=False)
|
|
166
|
+
config["error_to_issue"] = error_to_issue
|
|
167
|
+
|
|
168
|
+
if error_to_issue:
|
|
169
|
+
config["github_token_env"] = prompt("GITHUB_TOKEN environment variable name", default="GITHUB_TOKEN")
|
|
170
|
+
config["provider_google"] = ',\n "google": {\n "options": {\n "timeout": 300000,\n "chunkTimeout": 60000\n }\n }'
|
|
171
|
+
else:
|
|
172
|
+
config["provider_google"] = ""
|
|
173
|
+
|
|
174
|
+
config["model"] = prompt("Default model", default="opencode/deepseek-v4-flash-free")
|
|
175
|
+
|
|
176
|
+
config["env_notes"] = prompt("Environment notes (optional)", default="No special environment constraints.")
|
|
177
|
+
config["architectural_notes"] = prompt("Architectural notes (optional)", default="")
|
|
178
|
+
|
|
179
|
+
print("Scaffolding project...")
|
|
180
|
+
generated, skipped, errors = scaffold(config, root=root, force=args.force)
|
|
181
|
+
|
|
182
|
+
print_summary(generated, skipped, errors)
|
|
183
|
+
|
|
184
|
+
return 0 if not errors else 1
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
sys.exit(main())
|
osdlc/detector.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
PROBES = [
|
|
4
|
+
("pom.xml", "Maven", "Java", "pom.xml", "mvn compile", "mvn test", "mvn checkstyle:check"),
|
|
5
|
+
("build.gradle.kts", "Gradle Kotlin","Kotlin/Java","gradle.properties", "./gradlew build", "./gradlew test", "./gradlew ktlintCheck"),
|
|
6
|
+
("build.gradle", "Gradle", "Java", "gradle.properties", "./gradlew build", "./gradlew test", "./gradlew check"),
|
|
7
|
+
("yarn.lock", "Yarn", "JavaScript", "package.json", "yarn build", "yarn test", "yarn lint"),
|
|
8
|
+
("pnpm-lock.yaml", "pnpm", "JavaScript", "package.json", "pnpm build", "pnpm test", "pnpm lint"),
|
|
9
|
+
("package.json", "npm", "JavaScript", "package.json", "npm run build", "npm test", "npm run lint"),
|
|
10
|
+
("Cargo.toml", "Cargo", "Rust", "Cargo.toml", "cargo build", "cargo test", "cargo clippy"),
|
|
11
|
+
("pyproject.toml", "PEP 621", "Python", "pyproject.toml", "python -m build", "pytest", "ruff check ."),
|
|
12
|
+
("setup.py", "setuptools", "Python", "VERSION", "python setup.py sdist", "pytest", "ruff check ."),
|
|
13
|
+
("requirements.txt", "pip", "Python", "VERSION", "pip install -r requirements.txt", "pytest", "ruff check ."),
|
|
14
|
+
("go.mod", "Go modules", "Go", "version.go", "go build ./...", "go test ./...", "go vet ./..."),
|
|
15
|
+
("composer.json", "Composer", "PHP", "composer.json", "composer install", "phpunit", "phpcs"),
|
|
16
|
+
("Gemfile", "Bundler", "Ruby", "lib/version.rb", "bundle exec rake build", "bundle exec rspec", "bundle exec rubocop"),
|
|
17
|
+
("CMakeLists.txt", "CMake", "C/C++", "CMakeLists.txt", "cmake --build build", "ctest", "cmake --build build --target lint"),
|
|
18
|
+
("mix.exs", "Mix", "Elixir", "mix.exs", "mix compile", "mix test", "mix credo"),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
LANGUAGE_EXTENSIONS = {
|
|
22
|
+
".kt": "Kotlin",
|
|
23
|
+
".kts": "Kotlin",
|
|
24
|
+
".java": "Java",
|
|
25
|
+
".js": "JavaScript",
|
|
26
|
+
".ts": "TypeScript",
|
|
27
|
+
".jsx": "React",
|
|
28
|
+
".tsx": "React TypeScript",
|
|
29
|
+
".rs": "Rust",
|
|
30
|
+
".py": "Python",
|
|
31
|
+
".go": "Go",
|
|
32
|
+
".rb": "Ruby",
|
|
33
|
+
".ex": "Elixir",
|
|
34
|
+
".exs": "Elixir",
|
|
35
|
+
".php": "PHP",
|
|
36
|
+
".cs": "C#",
|
|
37
|
+
".swift":"Swift",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def detect_project_type(root="."):
|
|
42
|
+
for probe, build_system, language, version_file, build_cmd, test_cmd, lint_cmd in PROBES:
|
|
43
|
+
path = os.path.join(root, probe)
|
|
44
|
+
if os.path.exists(path):
|
|
45
|
+
return {
|
|
46
|
+
"build_system": build_system,
|
|
47
|
+
"language": language,
|
|
48
|
+
"version_file": version_file,
|
|
49
|
+
"build_cmd": build_cmd,
|
|
50
|
+
"test_cmd": test_cmd,
|
|
51
|
+
"lint_cmd": lint_cmd,
|
|
52
|
+
"probe": probe,
|
|
53
|
+
}
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def suggest_language_by_extension(root="."):
|
|
58
|
+
counts = {}
|
|
59
|
+
for entry in os.scandir(root):
|
|
60
|
+
if entry.is_file():
|
|
61
|
+
_, ext = os.path.splitext(entry.name)
|
|
62
|
+
if ext in LANGUAGE_EXTENSIONS:
|
|
63
|
+
lang = LANGUAGE_EXTENSIONS[ext]
|
|
64
|
+
counts[lang] = counts.get(lang, 0) + 1
|
|
65
|
+
if counts:
|
|
66
|
+
return max(counts, key=counts.get)
|
|
67
|
+
return None
|
osdlc/scaffold.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from osdlc.templates import ALL_TEMPLATES
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def detect_codeql_languages(language):
|
|
6
|
+
mapping = {
|
|
7
|
+
"Java": "java-kotlin",
|
|
8
|
+
"Kotlin": "java-kotlin",
|
|
9
|
+
"Kotlin/Java": "java-kotlin",
|
|
10
|
+
"JavaScript": "javascript-typescript",
|
|
11
|
+
"TypeScript": "javascript-typescript",
|
|
12
|
+
"Python": "python",
|
|
13
|
+
"Rust": "rust",
|
|
14
|
+
"Go": "go",
|
|
15
|
+
"Ruby": "ruby",
|
|
16
|
+
"PHP": "python",
|
|
17
|
+
"C/C++": "cpp",
|
|
18
|
+
"C#": "csharp",
|
|
19
|
+
"Swift": "swift",
|
|
20
|
+
"Elixir": "python",
|
|
21
|
+
}
|
|
22
|
+
return mapping.get(language, "python")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def detect_ecosystem(build_system):
|
|
26
|
+
mapping = {
|
|
27
|
+
"Maven": "maven",
|
|
28
|
+
"Gradle Kotlin": "gradle",
|
|
29
|
+
"Gradle": "gradle",
|
|
30
|
+
"npm": "npm",
|
|
31
|
+
"Yarn": "npm",
|
|
32
|
+
"pnpm": "npm",
|
|
33
|
+
"Cargo": "cargo",
|
|
34
|
+
"PEP 621": "pip",
|
|
35
|
+
"setuptools": "pip",
|
|
36
|
+
"pip": "pip",
|
|
37
|
+
"Go modules": "go_mod",
|
|
38
|
+
"Composer": "composer",
|
|
39
|
+
"Bundler": "bundler",
|
|
40
|
+
"CMake": "cmake",
|
|
41
|
+
"Mix": "mix",
|
|
42
|
+
}
|
|
43
|
+
return mapping.get(build_system, "pip")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def detect_language_setup_step(language):
|
|
47
|
+
mapping = {
|
|
48
|
+
"Java": """\
|
|
49
|
+
- name: Set up JDK 17
|
|
50
|
+
uses: actions/setup-java@v5
|
|
51
|
+
with:
|
|
52
|
+
java-version: '17'
|
|
53
|
+
distribution: 'temurin'
|
|
54
|
+
|
|
55
|
+
- name: Setup Gradle
|
|
56
|
+
uses: gradle/actions/setup-gradle@v3""",
|
|
57
|
+
"Kotlin/Java": """\
|
|
58
|
+
- name: Set up JDK 17
|
|
59
|
+
uses: actions/setup-java@v5
|
|
60
|
+
with:
|
|
61
|
+
java-version: '17'
|
|
62
|
+
distribution: 'temurin'
|
|
63
|
+
|
|
64
|
+
- name: Setup Gradle
|
|
65
|
+
uses: gradle/actions/setup-gradle@v3""",
|
|
66
|
+
"JavaScript": """\
|
|
67
|
+
- name: Setup Node.js
|
|
68
|
+
uses: actions/setup-node@v4
|
|
69
|
+
with:
|
|
70
|
+
node-version: 'latest'
|
|
71
|
+
|
|
72
|
+
- name: Install dependencies
|
|
73
|
+
run: npm ci""",
|
|
74
|
+
"Python": """\
|
|
75
|
+
- name: Setup Python
|
|
76
|
+
uses: actions/setup-python@v5
|
|
77
|
+
with:
|
|
78
|
+
python-version: '3.x'
|
|
79
|
+
|
|
80
|
+
- name: Install dependencies
|
|
81
|
+
run: pip install .""",
|
|
82
|
+
"Rust": """\
|
|
83
|
+
- name: Setup Rust
|
|
84
|
+
uses: dtolnay/action-rust-toolchain@stable""",
|
|
85
|
+
"Go": """\
|
|
86
|
+
- name: Setup Go
|
|
87
|
+
uses: actions/setup-go@v5
|
|
88
|
+
with:
|
|
89
|
+
go-version: 'stable'""",
|
|
90
|
+
"Ruby": """\
|
|
91
|
+
- name: Setup Ruby
|
|
92
|
+
uses: ruby/setup-ruby@v1
|
|
93
|
+
with:
|
|
94
|
+
ruby-version: '3.x'
|
|
95
|
+
bundler-cache: true""",
|
|
96
|
+
"PHP": """\
|
|
97
|
+
- name: Setup PHP
|
|
98
|
+
uses: shivammathur/setup-php@v2
|
|
99
|
+
with:
|
|
100
|
+
php-version: '8.2'""",
|
|
101
|
+
"C/C++": """\
|
|
102
|
+
- name: Setup CMake
|
|
103
|
+
uses: lukka/get-cmake@latest""",
|
|
104
|
+
"Elixir": """\
|
|
105
|
+
- name: Setup Elixir
|
|
106
|
+
uses: erlef/setup-beam@v1
|
|
107
|
+
with:
|
|
108
|
+
elixir-version: 'latest'
|
|
109
|
+
otp-version: 'latest'""",
|
|
110
|
+
}
|
|
111
|
+
return mapping.get(language, "")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def build_template_vars(config):
|
|
115
|
+
language = config["language"]
|
|
116
|
+
build_system = config["build_system"]
|
|
117
|
+
codeql_lang = detect_codeql_languages(language)
|
|
118
|
+
ecosystem = detect_ecosystem(build_system)
|
|
119
|
+
lang_setup = detect_language_setup_step(language)
|
|
120
|
+
|
|
121
|
+
l = language.lower()
|
|
122
|
+
validate_java = "true" if l in ("java", "kotlin", "kotlin/java") else "false"
|
|
123
|
+
validate_kotlin = "true" if l in ("kotlin", "kotlin/java") else "false"
|
|
124
|
+
validate_javascript = "true" if l in ("javascript", "typescript", "react", "react typescript") else "false"
|
|
125
|
+
validate_typescript = "true" if l in ("typescript", "react typescript") else "false"
|
|
126
|
+
validate_python = "true" if l == "python" else "false"
|
|
127
|
+
validate_go = "true" if l == "go" else "false"
|
|
128
|
+
validate_rust = "true" if l == "rust" else "false"
|
|
129
|
+
validate_ruby = "true" if l == "ruby" else "false"
|
|
130
|
+
validate_php = "true" if l == "php" else "false"
|
|
131
|
+
validate_xml = "true" if l in ("java", "kotlin", "kotlin/java") else "false"
|
|
132
|
+
validate_properties = "true" if l in ("java", "kotlin", "kotlin/java") else "false"
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"project_name": config.get("project_name", "my-project"),
|
|
136
|
+
"default_branch": config["default_branch"],
|
|
137
|
+
"version_file": config["version_file"],
|
|
138
|
+
"build_cmd": config["build_cmd"],
|
|
139
|
+
"test_cmd": config["test_cmd"],
|
|
140
|
+
"lint_cmd": config["lint_cmd"],
|
|
141
|
+
"language_description": f"This project uses {language} with the {build_system} build system ({config['probe']}).",
|
|
142
|
+
"env_notes": config.get("env_notes", "No special environment constraints."),
|
|
143
|
+
"architectural_notes": config.get("architectural_notes", ""),
|
|
144
|
+
"model": config.get("model", "opencode/deepseek-v4-flash-free"),
|
|
145
|
+
"provider_google": config.get("provider_google", ""),
|
|
146
|
+
"codeql_languages": repr(codeql_lang),
|
|
147
|
+
"ecosystem": ecosystem,
|
|
148
|
+
"language_setup": lang_setup,
|
|
149
|
+
"validate_java": validate_java,
|
|
150
|
+
"validate_kotlin": validate_kotlin,
|
|
151
|
+
"validate_javascript": validate_javascript,
|
|
152
|
+
"validate_typescript": validate_typescript,
|
|
153
|
+
"validate_python": validate_python,
|
|
154
|
+
"validate_go": validate_go,
|
|
155
|
+
"validate_rust": validate_rust,
|
|
156
|
+
"validate_ruby": validate_ruby,
|
|
157
|
+
"validate_php": validate_php,
|
|
158
|
+
"validate_xml": validate_xml,
|
|
159
|
+
"validate_properties": validate_properties,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def render_template(template, vars_dict):
|
|
164
|
+
result = template
|
|
165
|
+
for key, value in vars_dict.items():
|
|
166
|
+
placeholder = "{" + key + "}"
|
|
167
|
+
result = result.replace(placeholder, str(value))
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def scaffold(config, root=".", force=False):
|
|
172
|
+
vars_dict = build_template_vars(config)
|
|
173
|
+
generated = []
|
|
174
|
+
skipped = []
|
|
175
|
+
errors = []
|
|
176
|
+
|
|
177
|
+
templates = dict(ALL_TEMPLATES)
|
|
178
|
+
if not config.get("error_to_issue"):
|
|
179
|
+
templates.pop(".opencode/skills/error-handling/SKILL.md", None)
|
|
180
|
+
|
|
181
|
+
dirs_to_create = set()
|
|
182
|
+
for filepath in templates:
|
|
183
|
+
full_path = os.path.join(root, filepath)
|
|
184
|
+
parent = os.path.dirname(full_path)
|
|
185
|
+
if parent:
|
|
186
|
+
dirs_to_create.add(parent)
|
|
187
|
+
|
|
188
|
+
for d in sorted(dirs_to_create):
|
|
189
|
+
os.makedirs(d, exist_ok=True)
|
|
190
|
+
|
|
191
|
+
for filepath, template in templates.items():
|
|
192
|
+
full_path = os.path.join(root, filepath)
|
|
193
|
+
|
|
194
|
+
if os.path.exists(full_path) and not force:
|
|
195
|
+
skipped.append(filepath)
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
content = render_template(template, vars_dict)
|
|
200
|
+
with open(full_path, "w", newline="\n", encoding="utf-8") as f:
|
|
201
|
+
f.write(content)
|
|
202
|
+
generated.append(filepath)
|
|
203
|
+
except Exception as e:
|
|
204
|
+
errors.append((filepath, str(e)))
|
|
205
|
+
|
|
206
|
+
return generated, skipped, errors
|