prizmkit 1.0.102 → 1.0.104
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.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/run.sh +2 -2
- package/bundled/dev-pipeline/scripts/utils.py +333 -31
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +10 -1
- package/bundled/dev-pipeline/templates/feature-list-schema.json +10 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +8 -1
- package/bundled/skills/bug-planner/SKILL.md +2 -2
- package/bundled/skills/prizmkit-init/SKILL.md +125 -5
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -1125,8 +1125,8 @@ case "${1:-run}" in
|
|
|
1125
1125
|
run_one "$@"
|
|
1126
1126
|
else
|
|
1127
1127
|
# Parse positional and --features flag
|
|
1128
|
-
|
|
1129
|
-
|
|
1128
|
+
_run_feature_list="feature-list.json"
|
|
1129
|
+
_run_features_filter=""
|
|
1130
1130
|
while [[ $# -gt 0 ]]; do
|
|
1131
1131
|
case "$1" in
|
|
1132
1132
|
--features)
|
|
@@ -8,6 +8,7 @@ to avoid duplication across pipeline scripts.
|
|
|
8
8
|
import json
|
|
9
9
|
import logging
|
|
10
10
|
import os
|
|
11
|
+
import re
|
|
11
12
|
import sys
|
|
12
13
|
|
|
13
14
|
|
|
@@ -107,26 +108,95 @@ def _build_progress_bar(percent, width=20):
|
|
|
107
108
|
return "{} {:>3}%".format(bar, int(percent))
|
|
108
109
|
|
|
109
110
|
|
|
111
|
+
def _read_file_safe(filepath):
|
|
112
|
+
"""Read a file and return its content, or empty string on error."""
|
|
113
|
+
try:
|
|
114
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
115
|
+
return f.read()
|
|
116
|
+
except (IOError, OSError):
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _detect_node_runtime(project_root, pkg):
|
|
121
|
+
"""Detect Node.js runtime version from engines, .nvmrc, or .node-version."""
|
|
122
|
+
engines = pkg.get("engines", {})
|
|
123
|
+
node_ver = engines.get("node", "")
|
|
124
|
+
if node_ver:
|
|
125
|
+
return "Node.js {}".format(node_ver)
|
|
126
|
+
|
|
127
|
+
for version_file in [".nvmrc", ".node-version"]:
|
|
128
|
+
vpath = os.path.join(project_root, version_file)
|
|
129
|
+
if os.path.isfile(vpath):
|
|
130
|
+
content = _read_file_safe(vpath).strip()
|
|
131
|
+
if content:
|
|
132
|
+
return "Node.js {}".format(content)
|
|
133
|
+
|
|
134
|
+
return "Node.js"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _parse_python_deps(py_content, req_content):
|
|
138
|
+
"""Extract Python package names from pyproject.toml and requirements.txt.
|
|
139
|
+
|
|
140
|
+
Returns a set of lowercased package names (without version specifiers).
|
|
141
|
+
"""
|
|
142
|
+
deps = set()
|
|
143
|
+
|
|
144
|
+
# Parse requirements.txt lines: "package==1.0", "package>=2.0", "package"
|
|
145
|
+
for line in req_content.splitlines():
|
|
146
|
+
line = line.strip()
|
|
147
|
+
if not line or line.startswith("#") or line.startswith("-"):
|
|
148
|
+
continue
|
|
149
|
+
# Split on version specifiers and extras
|
|
150
|
+
name = re.split(r"[>=<!~;\[\s]", line, 1)[0].strip()
|
|
151
|
+
if name:
|
|
152
|
+
deps.add(name.lower())
|
|
153
|
+
|
|
154
|
+
# Parse pyproject.toml dependencies (simplified: look for quoted strings
|
|
155
|
+
# in [project.dependencies] and [project.optional-dependencies.*] sections)
|
|
156
|
+
in_deps_section = False
|
|
157
|
+
for line in py_content.splitlines():
|
|
158
|
+
stripped = line.strip()
|
|
159
|
+
if stripped.startswith("["):
|
|
160
|
+
in_deps_section = (
|
|
161
|
+
"dependencies" in stripped.lower()
|
|
162
|
+
and "optional" not in stripped.lower()
|
|
163
|
+
) or "dependencies" in stripped.lower()
|
|
164
|
+
continue
|
|
165
|
+
if in_deps_section:
|
|
166
|
+
# Match quoted dependency: "flask>=2.0" or 'django~=4.0'
|
|
167
|
+
match = re.match(r"""^[\s"']*([a-zA-Z0-9_-]+)""", stripped)
|
|
168
|
+
if match:
|
|
169
|
+
deps.add(match.group(1).lower())
|
|
170
|
+
|
|
171
|
+
return deps
|
|
172
|
+
|
|
173
|
+
|
|
110
174
|
def detect_project_context(project_root):
|
|
111
175
|
"""Auto-detect project tech stack from project files.
|
|
112
176
|
|
|
113
|
-
Reads package.json, pyproject.toml,
|
|
114
|
-
to infer language,
|
|
115
|
-
|
|
177
|
+
Reads package.json, pyproject.toml, requirements.txt, docker-compose,
|
|
178
|
+
and common config files to infer language, frameworks, styling,
|
|
179
|
+
database, ORM, bundler, testing, and project type.
|
|
180
|
+
|
|
181
|
+
Returns a dict of detected key-value pairs. Only detected fields are
|
|
182
|
+
included — no empty or null values. Adapts to any project type
|
|
183
|
+
(frontend-only, backend-only, fullstack, library, CLI, monorepo).
|
|
116
184
|
"""
|
|
117
185
|
detected = {}
|
|
118
186
|
|
|
119
|
-
# 1. Node.js / JavaScript / TypeScript project
|
|
187
|
+
# ── 1. Node.js / JavaScript / TypeScript project ──
|
|
120
188
|
pkg_path = os.path.join(project_root, "package.json")
|
|
121
189
|
if os.path.isfile(pkg_path):
|
|
122
190
|
try:
|
|
123
191
|
with open(pkg_path, "r", encoding="utf-8") as f:
|
|
124
192
|
pkg = json.load(f)
|
|
125
193
|
|
|
126
|
-
#
|
|
194
|
+
# All dependencies combined for detection
|
|
127
195
|
deps = {}
|
|
128
196
|
deps.update(pkg.get("dependencies", {}))
|
|
129
197
|
deps.update(pkg.get("devDependencies", {}))
|
|
198
|
+
|
|
199
|
+
# Language
|
|
130
200
|
if "typescript" in deps or os.path.isfile(
|
|
131
201
|
os.path.join(project_root, "tsconfig.json")
|
|
132
202
|
):
|
|
@@ -134,7 +204,10 @@ def detect_project_context(project_root):
|
|
|
134
204
|
else:
|
|
135
205
|
detected["language"] = "JavaScript"
|
|
136
206
|
|
|
137
|
-
#
|
|
207
|
+
# Runtime
|
|
208
|
+
detected["runtime"] = _detect_node_runtime(project_root, pkg)
|
|
209
|
+
|
|
210
|
+
# Test framework (more specific first)
|
|
138
211
|
scripts = pkg.get("scripts", {})
|
|
139
212
|
test_script = (
|
|
140
213
|
scripts.get("test", "")
|
|
@@ -150,35 +223,256 @@ def detect_project_context(project_root):
|
|
|
150
223
|
elif "--test" in test_script or "node:test" in test_script:
|
|
151
224
|
detected["testing_framework"] = "Node.js built-in test runner"
|
|
152
225
|
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
226
|
+
# ── Frontend framework ──
|
|
227
|
+
frontend_frameworks = [
|
|
228
|
+
("next", "Next.js"),
|
|
229
|
+
("nuxt", "Nuxt"),
|
|
230
|
+
("@angular/core", "Angular"),
|
|
231
|
+
("svelte", "Svelte"),
|
|
232
|
+
("solid-js", "Solid.js"),
|
|
233
|
+
("react", "React"),
|
|
234
|
+
("vue", "Vue.js"),
|
|
235
|
+
]
|
|
236
|
+
for dep_name, fw_name in frontend_frameworks:
|
|
237
|
+
if dep_name in deps:
|
|
238
|
+
detected["frontend_framework"] = fw_name
|
|
239
|
+
break
|
|
240
|
+
|
|
241
|
+
# ── Backend framework ──
|
|
242
|
+
backend_frameworks = [
|
|
243
|
+
("@nestjs/core", "NestJS"),
|
|
244
|
+
("express", "Express.js"),
|
|
245
|
+
("fastify", "Fastify"),
|
|
246
|
+
("koa", "Koa"),
|
|
247
|
+
("hapi", "Hapi"),
|
|
248
|
+
("hono", "Hono"),
|
|
249
|
+
]
|
|
250
|
+
for dep_name, fw_name in backend_frameworks:
|
|
251
|
+
if dep_name in deps:
|
|
252
|
+
detected["backend_framework"] = fw_name
|
|
253
|
+
break
|
|
254
|
+
|
|
255
|
+
# Legacy "framework" field for backward compatibility
|
|
256
|
+
if "frontend_framework" in detected:
|
|
257
|
+
detected["framework"] = detected["frontend_framework"]
|
|
258
|
+
elif "backend_framework" in detected:
|
|
259
|
+
detected["framework"] = detected["backend_framework"]
|
|
260
|
+
|
|
261
|
+
# ── Frontend styling ──
|
|
262
|
+
styling_libs = [
|
|
263
|
+
("tailwindcss", "Tailwind CSS"),
|
|
264
|
+
("@tailwindcss/vite", "Tailwind CSS"),
|
|
265
|
+
("styled-components", "Styled Components"),
|
|
266
|
+
("@emotion/react", "Emotion"),
|
|
267
|
+
("@emotion/styled", "Emotion"),
|
|
268
|
+
("@mui/material", "Material UI"),
|
|
269
|
+
("@chakra-ui/react", "Chakra UI"),
|
|
270
|
+
("antd", "Ant Design"),
|
|
271
|
+
("sass", "SCSS/Sass"),
|
|
272
|
+
("node-sass", "SCSS/Sass"),
|
|
273
|
+
("less", "Less"),
|
|
274
|
+
]
|
|
275
|
+
for dep_name, style_name in styling_libs:
|
|
276
|
+
if dep_name in deps:
|
|
277
|
+
detected["frontend_styling"] = style_name
|
|
278
|
+
break
|
|
279
|
+
|
|
280
|
+
# ── Database ──
|
|
281
|
+
db_libs = [
|
|
282
|
+
("pg", "PostgreSQL"),
|
|
283
|
+
("postgres", "PostgreSQL"),
|
|
284
|
+
("mysql2", "MySQL"),
|
|
285
|
+
("mysql", "MySQL"),
|
|
286
|
+
("better-sqlite3", "SQLite"),
|
|
287
|
+
("mongodb", "MongoDB"),
|
|
288
|
+
("mongoose", "MongoDB"),
|
|
289
|
+
("redis", "Redis"),
|
|
290
|
+
("ioredis", "Redis"),
|
|
291
|
+
]
|
|
292
|
+
for dep_name, db_name in db_libs:
|
|
293
|
+
if dep_name in deps:
|
|
294
|
+
detected["database"] = db_name
|
|
295
|
+
break
|
|
296
|
+
|
|
297
|
+
# ── ORM ──
|
|
298
|
+
orm_libs = [
|
|
299
|
+
("@prisma/client", "Prisma"),
|
|
300
|
+
("prisma", "Prisma"),
|
|
301
|
+
("drizzle-orm", "Drizzle"),
|
|
302
|
+
("typeorm", "TypeORM"),
|
|
303
|
+
("sequelize", "Sequelize"),
|
|
304
|
+
("mongoose", "Mongoose"),
|
|
305
|
+
("knex", "Knex.js"),
|
|
306
|
+
("@mikro-orm/core", "MikroORM"),
|
|
307
|
+
]
|
|
308
|
+
for dep_name, orm_name in orm_libs:
|
|
309
|
+
if dep_name in deps:
|
|
310
|
+
detected["orm"] = orm_name
|
|
311
|
+
break
|
|
312
|
+
|
|
313
|
+
# ── Bundler ──
|
|
314
|
+
bundler_libs = [
|
|
315
|
+
("vite", "Vite"),
|
|
316
|
+
("webpack", "Webpack"),
|
|
317
|
+
("esbuild", "esbuild"),
|
|
318
|
+
("rollup", "Rollup"),
|
|
319
|
+
("parcel", "Parcel"),
|
|
320
|
+
("turbo", "Turborepo"),
|
|
321
|
+
("@rspack/core", "Rspack"),
|
|
322
|
+
]
|
|
323
|
+
for dep_name, bundler_name in bundler_libs:
|
|
324
|
+
if dep_name in deps:
|
|
325
|
+
detected["bundler"] = bundler_name
|
|
326
|
+
break
|
|
327
|
+
|
|
328
|
+
# ── Project type inference ──
|
|
329
|
+
has_frontend = "frontend_framework" in detected
|
|
330
|
+
has_backend = "backend_framework" in detected
|
|
331
|
+
has_workspaces = "workspaces" in pkg
|
|
332
|
+
has_bin = "bin" in pkg
|
|
333
|
+
|
|
334
|
+
if has_workspaces:
|
|
335
|
+
detected["project_type"] = "monorepo"
|
|
336
|
+
elif has_frontend and has_backend:
|
|
337
|
+
detected["project_type"] = "fullstack"
|
|
338
|
+
elif has_frontend:
|
|
339
|
+
detected["project_type"] = "frontend"
|
|
340
|
+
elif has_backend:
|
|
341
|
+
detected["project_type"] = "backend"
|
|
342
|
+
elif has_bin:
|
|
343
|
+
detected["project_type"] = "cli"
|
|
344
|
+
elif "main" in pkg or "exports" in pkg:
|
|
345
|
+
detected["project_type"] = "library"
|
|
346
|
+
|
|
164
347
|
except (json.JSONDecodeError, IOError):
|
|
165
348
|
pass
|
|
166
349
|
|
|
167
|
-
# 2. Python project detection
|
|
168
|
-
if not detected:
|
|
350
|
+
# ── 2. Python project detection ──
|
|
351
|
+
if "language" not in detected:
|
|
352
|
+
py_content = ""
|
|
169
353
|
for marker in ["pyproject.toml", "setup.py", "requirements.txt"]:
|
|
170
|
-
|
|
354
|
+
marker_path = os.path.join(project_root, marker)
|
|
355
|
+
if os.path.isfile(marker_path):
|
|
171
356
|
detected["language"] = "Python"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
357
|
+
if marker == "pyproject.toml":
|
|
358
|
+
py_content = _read_file_safe(marker_path)
|
|
359
|
+
break
|
|
360
|
+
|
|
361
|
+
if detected.get("language") == "Python":
|
|
362
|
+
req_path = os.path.join(project_root, "requirements.txt")
|
|
363
|
+
req_content = _read_file_safe(req_path) if os.path.isfile(req_path) else ""
|
|
364
|
+
py_deps = _parse_python_deps(py_content, req_content)
|
|
365
|
+
|
|
366
|
+
# Runtime version (regex for requires-python = ">=3.11")
|
|
367
|
+
if py_content:
|
|
368
|
+
ver_match = re.search(
|
|
369
|
+
r'requires-python\s*=\s*["\']([^"\']+)["\']', py_content
|
|
370
|
+
)
|
|
371
|
+
if ver_match:
|
|
372
|
+
detected["runtime"] = "Python {}".format(ver_match.group(1))
|
|
373
|
+
|
|
374
|
+
# Testing
|
|
375
|
+
if "pytest" in py_deps:
|
|
376
|
+
detected["testing_framework"] = "pytest"
|
|
377
|
+
|
|
378
|
+
# Backend framework
|
|
379
|
+
py_backend = [
|
|
380
|
+
("django", "Django"),
|
|
381
|
+
("fastapi", "FastAPI"),
|
|
382
|
+
("flask", "Flask"),
|
|
383
|
+
("starlette", "Starlette"),
|
|
384
|
+
("tornado", "Tornado"),
|
|
385
|
+
("aiohttp", "aiohttp"),
|
|
386
|
+
]
|
|
387
|
+
for dep_name, fw_name in py_backend:
|
|
388
|
+
if dep_name in py_deps:
|
|
389
|
+
detected["backend_framework"] = fw_name
|
|
390
|
+
detected["framework"] = fw_name
|
|
391
|
+
break
|
|
392
|
+
|
|
393
|
+
# Database / ORM
|
|
394
|
+
py_db = [
|
|
395
|
+
("psycopg2", "PostgreSQL"),
|
|
396
|
+
("psycopg", "PostgreSQL"),
|
|
397
|
+
("asyncpg", "PostgreSQL"),
|
|
398
|
+
("pymysql", "MySQL"),
|
|
399
|
+
("pymongo", "MongoDB"),
|
|
400
|
+
("motor", "MongoDB"),
|
|
401
|
+
]
|
|
402
|
+
for dep_name, db_name in py_db:
|
|
403
|
+
if dep_name in py_deps:
|
|
404
|
+
detected["database"] = db_name
|
|
405
|
+
break
|
|
406
|
+
|
|
407
|
+
py_orm = [
|
|
408
|
+
("sqlalchemy", "SQLAlchemy"),
|
|
409
|
+
("tortoise-orm", "Tortoise ORM"),
|
|
410
|
+
("peewee", "Peewee"),
|
|
411
|
+
]
|
|
412
|
+
for dep_name, orm_name in py_orm:
|
|
413
|
+
if dep_name in py_deps:
|
|
414
|
+
detected["orm"] = orm_name
|
|
415
|
+
break
|
|
416
|
+
# Django ORM: if django is a dep and no other ORM detected
|
|
417
|
+
if "django" in py_deps and "orm" not in detected:
|
|
418
|
+
detected["orm"] = "Django ORM"
|
|
419
|
+
|
|
420
|
+
# Project type
|
|
421
|
+
if "backend_framework" in detected:
|
|
422
|
+
detected["project_type"] = "backend"
|
|
423
|
+
|
|
424
|
+
# ── 3. Go project detection ──
|
|
425
|
+
if "language" not in detected:
|
|
426
|
+
go_mod_path = os.path.join(project_root, "go.mod")
|
|
427
|
+
if os.path.isfile(go_mod_path):
|
|
428
|
+
detected["language"] = "Go"
|
|
429
|
+
detected["runtime"] = "Go"
|
|
430
|
+
go_content = _read_file_safe(go_mod_path)
|
|
431
|
+
if "gin-gonic" in go_content:
|
|
432
|
+
detected["backend_framework"] = "Gin"
|
|
433
|
+
elif "labstack/echo" in go_content:
|
|
434
|
+
detected["backend_framework"] = "Echo"
|
|
435
|
+
elif "go-chi/chi" in go_content:
|
|
436
|
+
detected["backend_framework"] = "Chi"
|
|
437
|
+
if "backend_framework" in detected:
|
|
438
|
+
detected["framework"] = detected["backend_framework"]
|
|
439
|
+
detected["project_type"] = "backend"
|
|
440
|
+
|
|
441
|
+
# ── 4. Rust / Java / other languages (basic detection) ──
|
|
442
|
+
if "language" not in detected:
|
|
443
|
+
if os.path.isfile(os.path.join(project_root, "Cargo.toml")):
|
|
444
|
+
detected["language"] = "Rust"
|
|
445
|
+
detected["runtime"] = "Rust"
|
|
446
|
+
elif os.path.isfile(os.path.join(project_root, "pom.xml")):
|
|
447
|
+
detected["language"] = "Java"
|
|
448
|
+
detected["runtime"] = "Java (Maven)"
|
|
449
|
+
elif os.path.isfile(os.path.join(project_root, "build.gradle")):
|
|
450
|
+
detected["language"] = "Java/Kotlin"
|
|
451
|
+
detected["runtime"] = "Java (Gradle)"
|
|
452
|
+
|
|
453
|
+
# ── 5. Database from docker-compose (cross-language) ──
|
|
454
|
+
if "database" not in detected:
|
|
455
|
+
for dc_name in [
|
|
456
|
+
"docker-compose.yml",
|
|
457
|
+
"docker-compose.yaml",
|
|
458
|
+
"compose.yml",
|
|
459
|
+
"compose.yaml",
|
|
460
|
+
]:
|
|
461
|
+
dc_path = os.path.join(project_root, dc_name)
|
|
462
|
+
if os.path.isfile(dc_path):
|
|
463
|
+
dc_content = _read_file_safe(dc_path).lower()
|
|
464
|
+
dc_db = [
|
|
465
|
+
("postgres", "PostgreSQL"),
|
|
466
|
+
("mysql", "MySQL"),
|
|
467
|
+
("mariadb", "MariaDB"),
|
|
468
|
+
("mongo", "MongoDB"),
|
|
469
|
+
("redis", "Redis"),
|
|
470
|
+
("sqlite", "SQLite"),
|
|
471
|
+
]
|
|
472
|
+
for pattern, db_name in dc_db:
|
|
473
|
+
if pattern in dc_content:
|
|
474
|
+
detected["database"] = db_name
|
|
475
|
+
break
|
|
182
476
|
break
|
|
183
477
|
|
|
184
478
|
return detected
|
|
@@ -199,10 +493,18 @@ def enrich_global_context(global_context, project_root):
|
|
|
199
493
|
"language": "language",
|
|
200
494
|
"testing_framework": "testing_strategy",
|
|
201
495
|
"framework": "framework",
|
|
496
|
+
"frontend_framework": "frontend_framework",
|
|
497
|
+
"frontend_styling": "frontend_styling",
|
|
498
|
+
"backend_framework": "backend_framework",
|
|
499
|
+
"database": "database",
|
|
500
|
+
"orm": "orm",
|
|
501
|
+
"bundler": "bundler",
|
|
502
|
+
"project_type": "project_type",
|
|
503
|
+
"runtime": "runtime",
|
|
202
504
|
}
|
|
203
505
|
# Alternate key names that should block auto-detection
|
|
204
506
|
alt_keys = {
|
|
205
|
-
"testing_strategy": ["testing_framework", "test_framework"],
|
|
507
|
+
"testing_strategy": ["testing_framework", "test_framework", "testing"],
|
|
206
508
|
}
|
|
207
509
|
for det_key, ctx_key in key_mapping.items():
|
|
208
510
|
if det_key not in detected:
|
|
@@ -151,7 +151,16 @@
|
|
|
151
151
|
"description": "Global context for all bug fixes in this batch",
|
|
152
152
|
"properties": {
|
|
153
153
|
"tech_stack": { "type": "string" },
|
|
154
|
-
"
|
|
154
|
+
"language": { "type": "string" },
|
|
155
|
+
"runtime": { "type": "string" },
|
|
156
|
+
"frontend_framework": { "type": "string" },
|
|
157
|
+
"frontend_styling": { "type": "string" },
|
|
158
|
+
"backend_framework": { "type": "string" },
|
|
159
|
+
"database": { "type": "string" },
|
|
160
|
+
"orm": { "type": "string" },
|
|
161
|
+
"testing_strategy": { "type": "string" },
|
|
162
|
+
"bundler": { "type": "string" },
|
|
163
|
+
"project_type": { "type": "string" },
|
|
155
164
|
"ci_pipeline": { "type": "string" }
|
|
156
165
|
}
|
|
157
166
|
}
|
|
@@ -108,8 +108,17 @@
|
|
|
108
108
|
"type": "object",
|
|
109
109
|
"properties": {
|
|
110
110
|
"tech_stack": { "type": "string" },
|
|
111
|
+
"language": { "type": "string" },
|
|
112
|
+
"runtime": { "type": "string" },
|
|
113
|
+
"frontend_framework": { "type": "string" },
|
|
114
|
+
"frontend_styling": { "type": "string" },
|
|
115
|
+
"backend_framework": { "type": "string" },
|
|
116
|
+
"database": { "type": "string" },
|
|
117
|
+
"orm": { "type": "string" },
|
|
111
118
|
"design_system": { "type": "string" },
|
|
112
|
-
"testing_strategy": { "type": "string" }
|
|
119
|
+
"testing_strategy": { "type": "string" },
|
|
120
|
+
"bundler": { "type": "string" },
|
|
121
|
+
"project_type": { "type": "string" }
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
}
|
|
@@ -47,8 +47,15 @@ Do NOT use this skill when:
|
|
|
47
47
|
|
|
48
48
|
Before questions, check optional context files (never block if absent):
|
|
49
49
|
- `.prizm-docs/root.prizm` (architecture/project context)
|
|
50
|
-
- `.prizmkit/config.json` (existing stack preferences)
|
|
50
|
+
- `.prizmkit/config.json` (existing stack preferences and detected tech stack)
|
|
51
51
|
- existing `feature-list.json` (required for incremental mode)
|
|
52
|
+
|
|
53
|
+
**Tech stack auto-population from config.json:**
|
|
54
|
+
- If `.prizmkit/config.json` contains a `tech_stack` object, use it to pre-fill `global_context` fields in the generated `feature-list.json`.
|
|
55
|
+
- Map config fields to global_context: `language`, `runtime`, `frontend_framework`, `frontend_styling`, `backend_framework`, `database`, `orm`, `testing` → `testing_strategy`, `bundler`, `project_type`.
|
|
56
|
+
- Do NOT re-ask the user for tech stack info that is already present in config.json. Instead, show the detected stack and ask: "Is this correct? Any changes?"
|
|
57
|
+
- If config.json has no `tech_stack`, fall back to asking during Phase 2 (constraints and tech assumptions).
|
|
58
|
+
|
|
52
59
|
Note:
|
|
53
60
|
- This skill **reads** `.prizmkit/config.json` if present.
|
|
54
61
|
- This skill does **not** create `.prizmkit/config.json` directly.
|
|
@@ -43,8 +43,8 @@ Launch the interactive bug planning process through 4 phases.
|
|
|
43
43
|
### Phase 1: Project Context
|
|
44
44
|
|
|
45
45
|
1. **Identify project**: Read project name and description from existing `feature-list.json` or ask user
|
|
46
|
-
2. **Identify tech stack**: Read from `feature-list.json` global_context
|
|
47
|
-
3. **Identify testing framework**:
|
|
46
|
+
2. **Identify tech stack**: Read from `.prizmkit/config.json` `tech_stack` (preferred), then `feature-list.json` global_context, then `.prizm-docs/root.prizm`. Only ask user if none of these sources provide tech stack info.
|
|
47
|
+
3. **Identify testing framework**: Read from `.prizmkit/config.json` `tech_stack.testing`, or auto-detect from package.json/requirements.txt/etc., or ask user
|
|
48
48
|
|
|
49
49
|
Output: `project_name`, `project_description`, `global_context` fields populated.
|
|
50
50
|
|
|
@@ -35,6 +35,8 @@ Project takeover and bootstrap skill. Scans any project (brownfield or greenfiel
|
|
|
35
35
|
|
|
36
36
|
MODE DETECTION:
|
|
37
37
|
- If `.prizm-docs/` exists: Ask user if they want to reinitialize or update
|
|
38
|
+
- **Reinitialize**: overwrites `.prizm-docs/` and `config.json` tech_stack (fresh start)
|
|
39
|
+
- **Update**: re-scans tech stack and merges changes into existing `config.json` (see Step 3b merge strategy); updates `root.prizm` TECH_STACK if changed; preserves existing `.prizm-docs/` L1/L2 docs
|
|
38
40
|
- If project has source code: brownfield mode
|
|
39
41
|
- If project is nearly empty: greenfield mode
|
|
40
42
|
|
|
@@ -50,6 +52,18 @@ BROWNFIELD WORKFLOW (existing project):
|
|
|
50
52
|
3. Identify entry points by language convention
|
|
51
53
|
4. Catalog dependencies (external packages)
|
|
52
54
|
5. Count source files per directory
|
|
55
|
+
6. Detect detailed tech stack (adaptive — only include fields that apply to this project):
|
|
56
|
+
- **Language & Runtime**: e.g. TypeScript + Node.js 20, Python 3.11, Go 1.22
|
|
57
|
+
- **Frontend framework** (if applicable): React, Vue.js, Angular, Next.js, Svelte, etc.
|
|
58
|
+
- **Frontend styling** (if applicable): Tailwind CSS, SCSS, Styled Components, Material UI, etc.
|
|
59
|
+
- **Backend framework** (if applicable): Express.js, FastAPI, Django, NestJS, Gin, etc.
|
|
60
|
+
- **Database** (if applicable): PostgreSQL, MySQL, MongoDB, Redis, SQLite — detected from deps or `docker-compose.yml`
|
|
61
|
+
- **ORM** (if applicable): Prisma, Drizzle, TypeORM, SQLAlchemy, Mongoose, etc.
|
|
62
|
+
- **Bundler** (if applicable): Vite, Webpack, esbuild, Rollup, Turborepo
|
|
63
|
+
- **Testing**: Vitest, Jest, pytest, Go test, etc.
|
|
64
|
+
- **Project type** (inferred): `frontend` | `backend` | `fullstack` | `library` | `cli` | `monorepo`
|
|
65
|
+
|
|
66
|
+
**IMPORTANT**: Not all projects have all fields. A pure backend API will have no `frontend_framework` or `frontend_styling`. A library may have no database. Only record what is actually detected — never generate empty or placeholder values.
|
|
53
67
|
|
|
54
68
|
**Step 2: Prizm Documentation Generation**
|
|
55
69
|
Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structure from Step 1:
|
|
@@ -64,6 +78,57 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
|
|
|
64
78
|
- `.prizmkit/config.json` (adoption_mode, speckit_hooks_enabled, platform)
|
|
65
79
|
- `.prizmkit/specs/` (empty)
|
|
66
80
|
|
|
81
|
+
3b. Write detected tech stack to `.prizmkit/config.json`:
|
|
82
|
+
|
|
83
|
+
**Merge strategy** (handles re-init without losing user edits):
|
|
84
|
+
- Read existing `config.json` if present
|
|
85
|
+
- If `tech_stack` field exists AND `_auto_detected` is `false` or absent:
|
|
86
|
+
→ **SKIP** — user has manually configured tech stack, preserve their settings
|
|
87
|
+
- If `tech_stack` field exists AND `_auto_detected` is `true`:
|
|
88
|
+
→ **MERGE** — overwrite auto-detected values with new detection results, but preserve any keys the user added manually (keys not in the new detection result)
|
|
89
|
+
- If `tech_stack` field does NOT exist:
|
|
90
|
+
→ **WRITE** full detected tech stack with `"_auto_detected": true`
|
|
91
|
+
- Only include fields that were actually detected (no empty/null values)
|
|
92
|
+
|
|
93
|
+
Example config.json after init (fullstack project):
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"adoption_mode": "passive",
|
|
97
|
+
"platform": "claude",
|
|
98
|
+
"tech_stack": {
|
|
99
|
+
"language": "TypeScript",
|
|
100
|
+
"runtime": "Node.js 20",
|
|
101
|
+
"frontend_framework": "React",
|
|
102
|
+
"frontend_styling": "Tailwind CSS",
|
|
103
|
+
"backend_framework": "Express.js",
|
|
104
|
+
"database": "PostgreSQL",
|
|
105
|
+
"orm": "Prisma",
|
|
106
|
+
"testing": "Vitest",
|
|
107
|
+
"bundler": "Vite",
|
|
108
|
+
"project_type": "fullstack",
|
|
109
|
+
"_auto_detected": true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Example config.json after init (pure Python backend):
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"adoption_mode": "passive",
|
|
118
|
+
"platform": "claude",
|
|
119
|
+
"tech_stack": {
|
|
120
|
+
"language": "Python",
|
|
121
|
+
"runtime": "Python >=3.11",
|
|
122
|
+
"backend_framework": "FastAPI",
|
|
123
|
+
"database": "PostgreSQL",
|
|
124
|
+
"orm": "SQLAlchemy",
|
|
125
|
+
"testing": "pytest",
|
|
126
|
+
"project_type": "backend",
|
|
127
|
+
"_auto_detected": true
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
67
132
|
**Step 4: Hook & Settings Configuration**
|
|
68
133
|
|
|
69
134
|
4a. Read or create platform settings file (`.codebuddy/settings.json` or `.claude/settings.json`)
|
|
@@ -74,15 +139,44 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
|
|
|
74
139
|
4d. Preserve any existing hooks and settings — never overwrite user's custom configuration
|
|
75
140
|
|
|
76
141
|
**Step 5: Report**
|
|
77
|
-
Output summary: platform detected, tech stack detected, modules discovered, L1 docs generated, platform-specific configuration applied, next recommended steps.
|
|
142
|
+
Output summary: platform detected, tech stack detected (with detail), modules discovered, L1 docs generated, platform-specific configuration applied, next recommended steps.
|
|
143
|
+
|
|
144
|
+
Tech stack report format (only show detected fields, adapt to project type):
|
|
145
|
+
```
|
|
146
|
+
Tech stack detected:
|
|
147
|
+
Language: TypeScript
|
|
148
|
+
Runtime: Node.js 20
|
|
149
|
+
Frontend: React + Tailwind CSS
|
|
150
|
+
Backend: Express.js
|
|
151
|
+
Database: PostgreSQL (Prisma)
|
|
152
|
+
Testing: Vitest
|
|
153
|
+
Bundler: Vite
|
|
154
|
+
Project type: fullstack
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For a pure backend Python project, the report would show:
|
|
158
|
+
```
|
|
159
|
+
Tech stack detected:
|
|
160
|
+
Language: Python
|
|
161
|
+
Runtime: Python >=3.11
|
|
162
|
+
Backend: FastAPI
|
|
163
|
+
Database: PostgreSQL (SQLAlchemy)
|
|
164
|
+
Testing: pytest
|
|
165
|
+
Project type: backend
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Saved to: `.prizmkit/config.json` → `tech_stack` field
|
|
78
169
|
|
|
79
170
|
Include platform-specific guidance:
|
|
80
171
|
- CodeBuddy: "Use `/prizmkit-specify` to start your first feature"
|
|
81
172
|
- Claude Code: "Use `/prizmkit-specify` to start your first feature"
|
|
82
173
|
|
|
83
174
|
GREENFIELD WORKFLOW (new project):
|
|
84
|
-
- Skip Step 1 (no code to scan)
|
|
85
|
-
-
|
|
175
|
+
- Skip Step 1 (no code to scan) — but ask the user about their intended tech stack:
|
|
176
|
+
- "What language/framework will you use?" (e.g. React + Node.js, Python + FastAPI, etc.)
|
|
177
|
+
- Record answers in `config.json` `tech_stack` with `"_auto_detected": false` (user-provided, not auto-detected)
|
|
178
|
+
- If user is unsure, skip tech_stack — it can be populated later on re-init after code exists
|
|
179
|
+
- Step 2: Create minimal `.prizm-docs/` with just `root.prizm` skeleton (populate TECH_STACK from user answers if provided)
|
|
86
180
|
- Steps 3-5: Same as brownfield (Step 5 Report recommends starting with specify for first feature)
|
|
87
181
|
|
|
88
182
|
### Gradual Adoption Path
|
|
@@ -107,14 +201,23 @@ User can change mode in `.prizmkit/config.json`: `"adoption_mode": "passive" | "
|
|
|
107
201
|
|
|
108
202
|
## Example
|
|
109
203
|
|
|
110
|
-
**Brownfield init on a Node.js project:**
|
|
204
|
+
**Brownfield init on a fullstack Node.js project:**
|
|
111
205
|
```
|
|
112
206
|
$ /prizmkit-init
|
|
113
207
|
|
|
114
208
|
Platform detected: Claude Code
|
|
115
|
-
Tech stack: TypeScript, Node.js, Express
|
|
116
209
|
Mode: Brownfield (154 source files found)
|
|
117
210
|
|
|
211
|
+
Tech stack detected:
|
|
212
|
+
Language: TypeScript
|
|
213
|
+
Runtime: Node.js 20
|
|
214
|
+
Frontend: React + Tailwind CSS
|
|
215
|
+
Backend: Express.js
|
|
216
|
+
Database: PostgreSQL (Prisma)
|
|
217
|
+
Testing: Vitest
|
|
218
|
+
Bundler: Vite
|
|
219
|
+
Project type: fullstack
|
|
220
|
+
|
|
118
221
|
Modules discovered:
|
|
119
222
|
src/routes/ → .prizm-docs/routes.prizm (12 files)
|
|
120
223
|
src/models/ → .prizm-docs/models.prizm (8 files)
|
|
@@ -123,8 +226,25 @@ Modules discovered:
|
|
|
123
226
|
|
|
124
227
|
Generated: root.prizm + 4 L1 docs + changelog.prizm
|
|
125
228
|
Configured: .claude/rules/ (2 files), hooks in settings.json
|
|
229
|
+
Saved: .prizmkit/config.json (tech_stack recorded)
|
|
126
230
|
|
|
127
231
|
Next: Use /prizmkit-specify to start your first feature
|
|
128
232
|
```
|
|
129
233
|
|
|
234
|
+
**Re-init after PrizmKit upgrade (existing config preserved):**
|
|
235
|
+
```
|
|
236
|
+
$ /prizmkit-init
|
|
237
|
+
|
|
238
|
+
.prizm-docs/ already exists. Reinitialize or update?
|
|
239
|
+
> Update (preserve existing docs)
|
|
240
|
+
|
|
241
|
+
Tech stack changes detected:
|
|
242
|
+
+ bundler: Vite (newly detected)
|
|
243
|
+
~ testing: Jest → Vitest (updated)
|
|
244
|
+
= language: TypeScript (unchanged)
|
|
245
|
+
= frontend: React (unchanged)
|
|
246
|
+
|
|
247
|
+
Merged into .prizmkit/config.json (2 fields updated, user overrides preserved)
|
|
248
|
+
```
|
|
249
|
+
|
|
130
250
|
IMPORTANT: Use `${SKILL_DIR}` placeholder for all path references. Never hardcode absolute paths.
|