prizmkit 1.0.14 → 1.0.15
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/retry-bug.sh +20 -2
- package/bundled/dev-pipeline/retry-feature.sh +20 -2
- package/bundled/rules/prizm/prizm-documentation.md +68 -7
- package/bundled/skills/_metadata.json +40 -8
- package/bundled/templates/hooks/commit-intent-claude.json +1 -1
- package/bundled/templates/hooks/commit-intent-codebuddy.json +1 -1
- package/bundled/templates/hooks/prizm-pre-commit.sh +58 -0
- package/bundled/templates/hooks/validate-prizm-docs.sh +84 -0
- package/package.json +1 -1
- package/src/detect-platform.js +20 -8
- package/src/index.js +8 -8
- package/src/scaffold.js +66 -1
package/bundled/VERSION.json
CHANGED
|
@@ -24,9 +24,27 @@ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
|
24
24
|
SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
|
|
25
25
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
26
26
|
|
|
27
|
-
# AI CLI detection
|
|
27
|
+
# AI CLI detection: AI_CLI env > .prizmkit/config.json > CODEBUDDY_CLI > auto-detect
|
|
28
28
|
if [[ -n "${AI_CLI:-}" ]]; then
|
|
29
29
|
CLI_CMD="$AI_CLI"
|
|
30
|
+
elif [[ -f ".prizmkit/config.json" ]]; then
|
|
31
|
+
_config_cli=$(python3 -c "
|
|
32
|
+
import json, sys
|
|
33
|
+
try:
|
|
34
|
+
with open('.prizmkit/config.json') as f:
|
|
35
|
+
d = json.load(f)
|
|
36
|
+
v = d.get('ai_cli', '')
|
|
37
|
+
if v: print(v)
|
|
38
|
+
except: pass
|
|
39
|
+
" 2>/dev/null || true)
|
|
40
|
+
CLI_CMD="${_config_cli:-}"
|
|
41
|
+
if [[ -z "$CLI_CMD" ]]; then
|
|
42
|
+
if [[ -n "${CODEBUDDY_CLI:-}" ]]; then CLI_CMD="$CODEBUDDY_CLI"
|
|
43
|
+
elif command -v cbc &>/dev/null; then CLI_CMD="cbc"
|
|
44
|
+
elif command -v claude &>/dev/null; then CLI_CMD="claude"
|
|
45
|
+
else echo "ERROR: No AI CLI found. Set AI_CLI or configure .prizmkit/config.json" >&2; exit 1
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
30
48
|
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
31
49
|
CLI_CMD="$CODEBUDDY_CLI"
|
|
32
50
|
elif command -v cbc &>/dev/null; then
|
|
@@ -34,7 +52,7 @@ elif command -v cbc &>/dev/null; then
|
|
|
34
52
|
elif command -v claude &>/dev/null; then
|
|
35
53
|
CLI_CMD="claude"
|
|
36
54
|
else
|
|
37
|
-
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
55
|
+
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude), or set AI_CLI." >&2
|
|
38
56
|
exit 1
|
|
39
57
|
fi
|
|
40
58
|
|
|
@@ -24,9 +24,27 @@ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
|
24
24
|
SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
|
|
25
25
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
26
26
|
|
|
27
|
-
# AI CLI detection: AI_CLI > CODEBUDDY_CLI > auto-detect
|
|
27
|
+
# AI CLI detection: AI_CLI env > .prizmkit/config.json > CODEBUDDY_CLI > auto-detect
|
|
28
28
|
if [[ -n "${AI_CLI:-}" ]]; then
|
|
29
29
|
CLI_CMD="$AI_CLI"
|
|
30
|
+
elif [[ -f ".prizmkit/config.json" ]]; then
|
|
31
|
+
_config_cli=$(python3 -c "
|
|
32
|
+
import json, sys
|
|
33
|
+
try:
|
|
34
|
+
with open('.prizmkit/config.json') as f:
|
|
35
|
+
d = json.load(f)
|
|
36
|
+
v = d.get('ai_cli', '')
|
|
37
|
+
if v: print(v)
|
|
38
|
+
except: pass
|
|
39
|
+
" 2>/dev/null || true)
|
|
40
|
+
CLI_CMD="${_config_cli:-}"
|
|
41
|
+
if [[ -z "$CLI_CMD" ]]; then
|
|
42
|
+
if [[ -n "${CODEBUDDY_CLI:-}" ]]; then CLI_CMD="$CODEBUDDY_CLI"
|
|
43
|
+
elif command -v cbc &>/dev/null; then CLI_CMD="cbc"
|
|
44
|
+
elif command -v claude &>/dev/null; then CLI_CMD="claude"
|
|
45
|
+
else echo "ERROR: No AI CLI found. Set AI_CLI or configure .prizmkit/config.json" >&2; exit 1
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
30
48
|
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
31
49
|
CLI_CMD="$CODEBUDDY_CLI"
|
|
32
50
|
elif command -v cbc &>/dev/null; then
|
|
@@ -34,7 +52,7 @@ elif command -v cbc &>/dev/null; then
|
|
|
34
52
|
elif command -v claude &>/dev/null; then
|
|
35
53
|
CLI_CMD="claude"
|
|
36
54
|
else
|
|
37
|
-
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
55
|
+
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude), or set AI_CLI." >&2
|
|
38
56
|
exit 1
|
|
39
57
|
fi
|
|
40
58
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "PrizmKit
|
|
2
|
+
description: "PrizmKit .prizm-docs maintenance rules — when/how to update structured AI documentation"
|
|
3
3
|
globs:
|
|
4
4
|
- "**/*.ts"
|
|
5
5
|
- "**/*.tsx"
|
|
@@ -11,9 +11,70 @@ globs:
|
|
|
11
11
|
- "**/*.java"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
WHEN TO UPDATE .prizm-docs/:
|
|
15
|
+
- Feature development (new interface, new module, new behavior) → UPDATE .prizm-docs/
|
|
16
|
+
- Bug fix (fixing broken logic, no structural change) → SKIP, do NOT update
|
|
17
|
+
- Rationale: bugs are incomplete features. Recording bug details causes doc bloat with no AI value.
|
|
18
|
+
- Before modifying any source file, read `.prizm-docs/root.prizm` if it exists to understand project structure.
|
|
19
|
+
|
|
20
|
+
FORMAT RULES (enforced by pre-commit hook — violations block commit):
|
|
21
|
+
- ALL CAPS section headers: MODULE:, FILES:, RESPONSIBILITY:, UPDATED:, etc.
|
|
22
|
+
- KEY: value pairs and dash-prefixed lists only
|
|
23
|
+
- PROHIBITED: prose paragraphs, markdown headers (##/###), code blocks (```), emoji, ASCII art
|
|
24
|
+
- This format is designed for AI token efficiency, not human readability. Do not add human-friendly formatting.
|
|
25
|
+
|
|
26
|
+
SIZE LIMITS (hard — pre-commit hook blocks commits exceeding these):
|
|
27
|
+
- L0 root.prizm: 4KB max
|
|
28
|
+
- L1 module.prizm: 3KB max
|
|
29
|
+
- L2 detail.prizm: 5KB max
|
|
30
|
+
|
|
31
|
+
SIZE OVERFLOW HANDLING:
|
|
32
|
+
- L0 approaching 4KB: consolidate MODULE_INDEX, keep only top-5 RULES, remove PATTERNS detail
|
|
33
|
+
- L1 approaching 3KB: move implementation details to L2, keep only INTERFACES signatures
|
|
34
|
+
- L2 approaching 5KB: archive CHANGELOG entries older than 90 days to changelog-archive.prizm
|
|
35
|
+
- NEVER exceed hard limits — pre-commit hook will block the commit
|
|
36
|
+
|
|
37
|
+
REQUIRED FIELDS PER LEVEL:
|
|
38
|
+
|
|
39
|
+
L0 root.prizm:
|
|
40
|
+
- PRIZM_VERSION
|
|
41
|
+
- PROJECT
|
|
42
|
+
- LANG
|
|
43
|
+
- MODULE_INDEX (with -> pointers to L1 files)
|
|
44
|
+
- RULES (top-level project rules)
|
|
45
|
+
- UPDATED
|
|
46
|
+
|
|
47
|
+
L1 module.prizm:
|
|
48
|
+
- MODULE
|
|
49
|
+
- FILES
|
|
50
|
+
- RESPONSIBILITY
|
|
51
|
+
- INTERFACES (public/exported only)
|
|
52
|
+
- DEPENDENCIES
|
|
53
|
+
- UPDATED
|
|
54
|
+
|
|
55
|
+
L2 detail.prizm:
|
|
56
|
+
- MODULE
|
|
57
|
+
- FILES
|
|
58
|
+
- KEY_FILES
|
|
59
|
+
- DEPENDENCIES
|
|
60
|
+
- TRAPS
|
|
61
|
+
- CHANGELOG
|
|
62
|
+
- UPDATED
|
|
63
|
+
|
|
64
|
+
L2 GENERATION TEMPLATE (use when AI first touches a sub-module with no L2 doc):
|
|
65
|
+
|
|
66
|
+
MODULE: <path>
|
|
67
|
+
FILES: <comma-separated file list>
|
|
68
|
+
RESPONSIBILITY: <one-line>
|
|
69
|
+
KEY_FILES:
|
|
70
|
+
- <file>: <role, line count, complexity note>
|
|
71
|
+
DEPENDENCIES:
|
|
72
|
+
- uses: <lib>: <why>
|
|
73
|
+
- imports: <module>: <what>
|
|
74
|
+
TRAPS:
|
|
75
|
+
- <gotcha, race condition, or non-obvious coupling>
|
|
76
|
+
CHANGELOG:
|
|
77
|
+
- <YYYY-MM-DD> | add: initial L2 documentation
|
|
78
|
+
UPDATED: <YYYY-MM-DD>
|
|
79
|
+
|
|
80
|
+
TRAPS is critical — always record gotchas, race conditions, non-obvious behavior, and surprising coupling between modules.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
2
|
+
"version": "1.0.15",
|
|
3
3
|
"skills": {
|
|
4
4
|
"prizm-kit": {
|
|
5
5
|
"description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
|
|
@@ -293,22 +293,45 @@
|
|
|
293
293
|
"frontend": {
|
|
294
294
|
"label": "前端项目 (React/Vue/Angular)",
|
|
295
295
|
"base": "core",
|
|
296
|
-
"include": [
|
|
296
|
+
"include": [
|
|
297
|
+
"prizmkit-tool-perf-profiler",
|
|
298
|
+
"prizmkit-tool-security-audit",
|
|
299
|
+
"prizmkit-tool-ci-cd-generator"
|
|
300
|
+
]
|
|
297
301
|
},
|
|
298
302
|
"backend": {
|
|
299
303
|
"label": "后端服务 (API/微服务)",
|
|
300
304
|
"base": "core",
|
|
301
|
-
"include": [
|
|
305
|
+
"include": [
|
|
306
|
+
"prizmkit-tool-db-migration",
|
|
307
|
+
"prizmkit-tool-api-doc-generator",
|
|
308
|
+
"prizmkit-tool-monitoring-setup",
|
|
309
|
+
"prizmkit-tool-deployment-strategy",
|
|
310
|
+
"prizmkit-tool-security-audit",
|
|
311
|
+
"prizmkit-tool-error-triage",
|
|
312
|
+
"prizmkit-tool-log-analyzer"
|
|
313
|
+
]
|
|
302
314
|
},
|
|
303
315
|
"fullstack": {
|
|
304
316
|
"label": "全栈项目",
|
|
305
317
|
"base": "core",
|
|
306
|
-
"include": [
|
|
318
|
+
"include": [
|
|
319
|
+
"prizmkit-tool-db-migration",
|
|
320
|
+
"prizmkit-tool-api-doc-generator",
|
|
321
|
+
"prizmkit-tool-ci-cd-generator",
|
|
322
|
+
"prizmkit-tool-security-audit",
|
|
323
|
+
"prizmkit-tool-deployment-strategy",
|
|
324
|
+
"prizmkit-tool-perf-profiler"
|
|
325
|
+
]
|
|
307
326
|
},
|
|
308
327
|
"library": {
|
|
309
328
|
"label": "工具库/SDK",
|
|
310
329
|
"base": "core",
|
|
311
|
-
"include": [
|
|
330
|
+
"include": [
|
|
331
|
+
"prizmkit-tool-api-doc-generator",
|
|
332
|
+
"prizmkit-tool-ci-cd-generator",
|
|
333
|
+
"prizmkit-tool-dependency-health"
|
|
334
|
+
]
|
|
312
335
|
}
|
|
313
336
|
},
|
|
314
337
|
"external_skills": {
|
|
@@ -325,21 +348,30 @@
|
|
|
325
348
|
"description": "Intelligent file and code search skill",
|
|
326
349
|
"source": "https://skills.sh/skills/find-skill/SKILL.md",
|
|
327
350
|
"registry": "skills.sh",
|
|
328
|
-
"tags": [
|
|
351
|
+
"tags": [
|
|
352
|
+
"search",
|
|
353
|
+
"utility"
|
|
354
|
+
]
|
|
329
355
|
},
|
|
330
356
|
{
|
|
331
357
|
"name": "uiuxpromax",
|
|
332
358
|
"description": "UI/UX design review and suggestions",
|
|
333
359
|
"source": "https://skills.sh/skills/uiuxpromax/SKILL.md",
|
|
334
360
|
"registry": "skills.sh",
|
|
335
|
-
"tags": [
|
|
361
|
+
"tags": [
|
|
362
|
+
"design",
|
|
363
|
+
"frontend"
|
|
364
|
+
]
|
|
336
365
|
},
|
|
337
366
|
{
|
|
338
367
|
"name": "thinkthought",
|
|
339
368
|
"description": "Structured reasoning and problem decomposition",
|
|
340
369
|
"source": "https://skills.sh/skills/thinkthought/SKILL.md",
|
|
341
370
|
"registry": "skills.sh",
|
|
342
|
-
"tags": [
|
|
371
|
+
"tags": [
|
|
372
|
+
"reasoning",
|
|
373
|
+
"universal"
|
|
374
|
+
]
|
|
343
375
|
}
|
|
344
376
|
]
|
|
345
377
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "
|
|
9
|
+
"command": "SRC=$(git diff --cached --name-only 2>/dev/null | grep -cE '\\.(ts|tsx|js|jsx|py|go|rs|java)$'); if [ \"$SRC\" -gt 0 ]; then PRIZM=$(git diff --cached --name-only 2>/dev/null | grep -c '^\\.prizm-docs/'); if [ \"$PRIZM\" -gt 0 ]; then echo \"PRIZMKIT_DOC_STATUS: $SRC source file(s) staged | .prizm-docs/ updated ($PRIZM file(s))\"; else echo \"PRIZMKIT_DOC_STATUS: $SRC source file(s) staged | .prizm-docs/ NOT UPDATED — update if this is a feature change\"; fi; fi"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "
|
|
9
|
+
"command": "SRC=$(git diff --cached --name-only 2>/dev/null | grep -cE '\\.(ts|tsx|js|jsx|py|go|rs|java)$'); if [ \"$SRC\" -gt 0 ]; then PRIZM=$(git diff --cached --name-only 2>/dev/null | grep -c '^\\.prizm-docs/'); if [ \"$PRIZM\" -gt 0 ]; then echo \"PRIZMKIT_DOC_STATUS: $SRC source file(s) staged | .prizm-docs/ updated ($PRIZM file(s))\"; else echo \"PRIZMKIT_DOC_STATUS: $SRC source file(s) staged | .prizm-docs/ NOT UPDATED — update if this is a feature change\"; fi; fi"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# prizm-pre-commit.sh — git pre-commit hook for .prizm-docs validation
|
|
3
|
+
|
|
4
|
+
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
5
|
+
|
|
6
|
+
# Only run in prizm projects
|
|
7
|
+
[ -f "$PROJECT_ROOT/.prizm-docs/root.prizm" ] || exit 0
|
|
8
|
+
|
|
9
|
+
VALIDATE_SCRIPT="$PROJECT_ROOT/dev-pipeline/scripts/validate-prizm-docs.sh"
|
|
10
|
+
|
|
11
|
+
if [ -f "$VALIDATE_SCRIPT" ]; then
|
|
12
|
+
sh "$VALIDATE_SCRIPT" --staged
|
|
13
|
+
exit $?
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Inline fallback: core checks for staged .prizm files
|
|
17
|
+
FILES=$(git diff --cached --name-only 2>/dev/null | grep '\.prizm$')
|
|
18
|
+
[ -z "$FILES" ] && exit 0
|
|
19
|
+
|
|
20
|
+
ERRORS=0
|
|
21
|
+
for FILE in $FILES; do
|
|
22
|
+
[ -f "$FILE" ] || continue
|
|
23
|
+
|
|
24
|
+
if grep -qE '^#{1,6} ' "$FILE"; then
|
|
25
|
+
echo "ERROR: $FILE contains markdown headers (##). Use KEY: value format." >&2
|
|
26
|
+
ERRORS=$((ERRORS + 1))
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if grep -q '^```' "$FILE"; then
|
|
30
|
+
echo "ERROR: $FILE contains code blocks. Use file_path:line_number reference." >&2
|
|
31
|
+
ERRORS=$((ERRORS + 1))
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
SIZE=$(wc -c < "$FILE" | tr -d ' ')
|
|
35
|
+
case "$FILE" in
|
|
36
|
+
*root.prizm) LIMIT=4096 ;;
|
|
37
|
+
*)
|
|
38
|
+
DIR=$(dirname "$FILE")
|
|
39
|
+
if [ "$DIR" = ".prizm-docs" ]; then
|
|
40
|
+
LIMIT=3072
|
|
41
|
+
else
|
|
42
|
+
LIMIT=5120
|
|
43
|
+
fi
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
46
|
+
|
|
47
|
+
if [ "$SIZE" -gt "$LIMIT" ]; then
|
|
48
|
+
echo "ERROR: $FILE exceeds size limit (${SIZE}B > ${LIMIT}B)." >&2
|
|
49
|
+
ERRORS=$((ERRORS + 1))
|
|
50
|
+
fi
|
|
51
|
+
done
|
|
52
|
+
|
|
53
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
54
|
+
echo "PrizmKit: $ERRORS format error(s) in .prizm files. Fix before committing." >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exit 0
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# validate-prizm-docs.sh — validate .prizm file format and size limits
|
|
3
|
+
# Usage: validate-prizm-docs.sh [--all | --staged]
|
|
4
|
+
|
|
5
|
+
MODE="${1:---staged}"
|
|
6
|
+
ERRORS=0
|
|
7
|
+
|
|
8
|
+
# Not a prizm project — exit silently
|
|
9
|
+
[ -f ".prizm-docs/root.prizm" ] || exit 0
|
|
10
|
+
|
|
11
|
+
# Collect files to check
|
|
12
|
+
if [ "$MODE" = "--all" ]; then
|
|
13
|
+
FILES=$(find .prizm-docs -name '*.prizm' 2>/dev/null)
|
|
14
|
+
elif [ "$MODE" = "--staged" ]; then
|
|
15
|
+
FILES=$(git diff --cached --name-only 2>/dev/null | grep '\.prizm$')
|
|
16
|
+
else
|
|
17
|
+
echo "Usage: validate-prizm-docs.sh [--all | --staged]" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Nothing to check — exit silently
|
|
22
|
+
[ -z "$FILES" ] && exit 0
|
|
23
|
+
|
|
24
|
+
for FILE in $FILES; do
|
|
25
|
+
[ -f "$FILE" ] || continue
|
|
26
|
+
|
|
27
|
+
# Check markdown headers
|
|
28
|
+
if grep -qE '^#{1,6} ' "$FILE"; then
|
|
29
|
+
echo "ERROR: $FILE contains markdown headers (##). Use KEY: value format." >&2
|
|
30
|
+
ERRORS=$((ERRORS + 1))
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Check code blocks
|
|
34
|
+
if grep -q '^```' "$FILE"; then
|
|
35
|
+
echo "ERROR: $FILE contains code blocks. Use file_path:line_number reference." >&2
|
|
36
|
+
ERRORS=$((ERRORS + 1))
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Size limits — determine level by path depth
|
|
40
|
+
SIZE=$(wc -c < "$FILE" | tr -d ' ')
|
|
41
|
+
case "$FILE" in
|
|
42
|
+
*root.prizm)
|
|
43
|
+
LIMIT=4096; LEVEL="L0"
|
|
44
|
+
HINT="Consolidate MODULE_INDEX, keep top-5 RULES."
|
|
45
|
+
;;
|
|
46
|
+
*)
|
|
47
|
+
# L1 = direct child of .prizm-docs/, L2 = nested deeper
|
|
48
|
+
DIR=$(dirname "$FILE")
|
|
49
|
+
if [ "$DIR" = ".prizm-docs" ]; then
|
|
50
|
+
LIMIT=3072; LEVEL="L1"
|
|
51
|
+
HINT="Move implementation details to L2."
|
|
52
|
+
else
|
|
53
|
+
LIMIT=5120; LEVEL="L2"
|
|
54
|
+
HINT="Archive CHANGELOG entries older than 90 days."
|
|
55
|
+
fi
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
if [ "$SIZE" -gt "$LIMIT" ]; then
|
|
60
|
+
echo "ERROR: $FILE exceeds $LEVEL limit (${SIZE}B > ${LIMIT}B). $HINT" >&2
|
|
61
|
+
ERRORS=$((ERRORS + 1))
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Required fields in root.prizm
|
|
65
|
+
case "$FILE" in
|
|
66
|
+
*root.prizm)
|
|
67
|
+
if ! grep -q 'PRIZM_VERSION:' "$FILE"; then
|
|
68
|
+
echo "ERROR: $FILE missing required field PRIZM_VERSION:" >&2
|
|
69
|
+
ERRORS=$((ERRORS + 1))
|
|
70
|
+
fi
|
|
71
|
+
if ! grep -q 'MODULE_INDEX:' "$FILE"; then
|
|
72
|
+
echo "ERROR: $FILE missing required field MODULE_INDEX:" >&2
|
|
73
|
+
ERRORS=$((ERRORS + 1))
|
|
74
|
+
fi
|
|
75
|
+
;;
|
|
76
|
+
esac
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
80
|
+
echo "PrizmKit: $ERRORS format error(s) in .prizm files. Fix before committing." >&2
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
exit 0
|
package/package.json
CHANGED
package/src/detect-platform.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
import { execFileSync } from 'child_process';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 允许检测的命令白名单(仅用于 which 检测)
|
|
10
10
|
*/
|
|
11
|
-
const ALLOWED_COMMANDS = ['cbc', 'claude'];
|
|
11
|
+
const ALLOWED_COMMANDS = ['cbc', 'claude', 'claude-internal'];
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* 检查命令是否存在于 PATH 中
|
|
@@ -26,21 +26,33 @@ function commandExists(cmd) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* 检测已安装的 AI CLI
|
|
30
|
-
*
|
|
29
|
+
* 检测已安装的 AI CLI 工具,并返回推荐的默认平台和 CLI 命令。
|
|
30
|
+
*
|
|
31
|
+
* 注意:platform 控制目录结构(.codebuddy/ 或 .claude/),
|
|
32
|
+
* suggestedCli 是实际运行的可执行命令,二者相互独立。
|
|
33
|
+
*
|
|
34
|
+
* @returns {{ cbc: boolean, claude: boolean, claudeInternal: boolean, suggested: string, suggestedCli: string }}
|
|
31
35
|
*/
|
|
32
36
|
export function detectPlatform() {
|
|
33
37
|
const cbc = commandExists('cbc');
|
|
34
38
|
const claude = commandExists('claude');
|
|
39
|
+
const claudeInternal = commandExists('claude-internal');
|
|
35
40
|
|
|
41
|
+
// platform 建议:基于目录结构,优先 both
|
|
36
42
|
let suggested = 'codebuddy';
|
|
37
|
-
if (cbc && claude) {
|
|
43
|
+
if ((cbc) && (claude || claudeInternal)) {
|
|
38
44
|
suggested = 'both';
|
|
39
|
-
} else if (claude && !cbc) {
|
|
45
|
+
} else if ((claude || claudeInternal) && !cbc) {
|
|
40
46
|
suggested = 'claude';
|
|
41
|
-
} else if (cbc && !claude) {
|
|
47
|
+
} else if (cbc && !claude && !claudeInternal) {
|
|
42
48
|
suggested = 'codebuddy';
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
// CLI 命令建议:优先 cbc,其次 claude-internal,再次 claude
|
|
52
|
+
let suggestedCli = '';
|
|
53
|
+
if (cbc) suggestedCli = 'cbc';
|
|
54
|
+
else if (claudeInternal) suggestedCli = 'claude-internal';
|
|
55
|
+
else if (claude) suggestedCli = 'claude';
|
|
56
|
+
|
|
57
|
+
return { cbc, claude, claudeInternal, suggested, suggestedCli };
|
|
46
58
|
}
|
package/src/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export async function runScaffold(directory, options) {
|
|
|
33
33
|
const cliStatus = [
|
|
34
34
|
detected.cbc ? chalk.green('cbc ✓') : chalk.gray('cbc ✗'),
|
|
35
35
|
detected.claude ? chalk.green('claude ✓') : chalk.gray('claude ✗'),
|
|
36
|
+
detected.claudeInternal ? chalk.green('claude-internal ✓') : chalk.gray('claude-internal ✗'),
|
|
36
37
|
].join(' ');
|
|
37
38
|
console.log(` 检测到的 CLI 工具: ${cliStatus}`);
|
|
38
39
|
console.log(` 目标目录: ${projectRoot}`);
|
|
@@ -64,16 +65,16 @@ export async function runScaffold(directory, options) {
|
|
|
64
65
|
|
|
65
66
|
// === 交互式模式 ===
|
|
66
67
|
try {
|
|
67
|
-
// 1.
|
|
68
|
+
// 1. 选择平台(控制安装的目录结构)
|
|
68
69
|
const platform = await select({
|
|
69
|
-
message: '
|
|
70
|
+
message: '选择目标平台 (决定安装的目录结构):',
|
|
70
71
|
choices: [
|
|
71
72
|
{
|
|
72
|
-
name: `CodeBuddy
|
|
73
|
+
name: `CodeBuddy (.codebuddy/)${detected.cbc ? chalk.green(' ← 已检测到 cbc') : ''}`,
|
|
73
74
|
value: 'codebuddy',
|
|
74
75
|
},
|
|
75
76
|
{
|
|
76
|
-
name: `Claude Code
|
|
77
|
+
name: `Claude Code (.claude/)${(detected.claude || detected.claudeInternal) ? chalk.green(' ← 已检测到') : ''}`,
|
|
77
78
|
value: 'claude',
|
|
78
79
|
},
|
|
79
80
|
{
|
|
@@ -84,11 +85,10 @@ export async function runScaffold(directory, options) {
|
|
|
84
85
|
default: detected.suggested,
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
// 1.5. AI CLI
|
|
88
|
-
const defaultCli = detected.cbc ? 'cbc' : detected.claude ? 'claude' : '';
|
|
88
|
+
// 1.5. AI CLI 命令配置(独立于平台,决定实际运行的可执行命令)
|
|
89
89
|
const aiCli = await input({
|
|
90
|
-
message: '底层 AI CLI 可执行命令 (
|
|
91
|
-
default:
|
|
90
|
+
message: '底层 AI CLI 可执行命令 (可自定义,如 claude-internal --dangerously-skip-permissions):',
|
|
91
|
+
default: detected.suggestedCli,
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
// 2. 选择技能套件
|
package/src/scaffold.js
CHANGED
|
@@ -439,6 +439,60 @@ async function installSettings(platform, projectRoot, options, dryRun) {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
+
/**
|
|
443
|
+
* 安装 git pre-commit hook(prizm 格式校验)
|
|
444
|
+
*/
|
|
445
|
+
async function installGitHook(projectRoot, dryRun) {
|
|
446
|
+
const gitDir = path.join(projectRoot, '.git');
|
|
447
|
+
|
|
448
|
+
if (dryRun) {
|
|
449
|
+
console.log(chalk.gray(' [dry-run] .git/hooks/pre-commit (prizm format check)'));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!fs.existsSync(gitDir)) return;
|
|
454
|
+
|
|
455
|
+
const templatePath = path.join(getTemplatesDir(), 'hooks', 'prizm-pre-commit.sh');
|
|
456
|
+
const hooksDir = path.join(gitDir, 'hooks');
|
|
457
|
+
const preCommitPath = path.join(hooksDir, 'pre-commit');
|
|
458
|
+
|
|
459
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
460
|
+
|
|
461
|
+
if (fs.existsSync(preCommitPath)) {
|
|
462
|
+
const existing = fs.readFileSync(preCommitPath, 'utf8');
|
|
463
|
+
if (existing.includes('PrizmKit')) return;
|
|
464
|
+
const hookContent = fs.readFileSync(templatePath, 'utf8');
|
|
465
|
+
fs.writeFileSync(preCommitPath, existing + '\n\n' + hookContent);
|
|
466
|
+
} else {
|
|
467
|
+
await fs.copy(templatePath, preCommitPath);
|
|
468
|
+
fs.chmodSync(preCommitPath, 0o755);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
console.log(chalk.green(' ✓ .git/hooks/pre-commit (prizm format check)'));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 安装 validate-prizm-docs.sh 到 dev-pipeline/scripts/
|
|
476
|
+
*/
|
|
477
|
+
async function installValidateScript(projectRoot, dryRun) {
|
|
478
|
+
const sourcePath = path.join(getTemplatesDir(), 'hooks', 'validate-prizm-docs.sh');
|
|
479
|
+
if (!fs.existsSync(sourcePath)) return;
|
|
480
|
+
|
|
481
|
+
const targetDir = path.join(projectRoot, 'dev-pipeline', 'scripts');
|
|
482
|
+
const targetPath = path.join(targetDir, 'validate-prizm-docs.sh');
|
|
483
|
+
|
|
484
|
+
if (dryRun) {
|
|
485
|
+
console.log(chalk.gray(' [dry-run] dev-pipeline/scripts/validate-prizm-docs.sh'));
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
await fs.ensureDir(targetDir);
|
|
490
|
+
await fs.copy(sourcePath, targetPath);
|
|
491
|
+
fs.chmodSync(targetPath, 0o755);
|
|
492
|
+
|
|
493
|
+
console.log(chalk.green(' ✓ dev-pipeline/scripts/validate-prizm-docs.sh'));
|
|
494
|
+
}
|
|
495
|
+
|
|
442
496
|
/**
|
|
443
497
|
* 安装项目记忆文件(CODEBUDDY.md / CLAUDE.md)
|
|
444
498
|
*/
|
|
@@ -653,6 +707,16 @@ export async function scaffold(config) {
|
|
|
653
707
|
}
|
|
654
708
|
}
|
|
655
709
|
|
|
710
|
+
// 10. Git pre-commit hook
|
|
711
|
+
console.log(chalk.blue(' Git Hook:'));
|
|
712
|
+
await installGitHook(projectRoot, dryRun);
|
|
713
|
+
|
|
714
|
+
// 11. Validate script (only if pipeline enabled)
|
|
715
|
+
if (pipeline) {
|
|
716
|
+
console.log(chalk.blue(' 验证脚本:'));
|
|
717
|
+
await installValidateScript(projectRoot, dryRun);
|
|
718
|
+
}
|
|
719
|
+
|
|
656
720
|
// === 完成 ===
|
|
657
721
|
console.log('');
|
|
658
722
|
console.log(chalk.bold(' ════════════════════════════════════════════════'));
|
|
@@ -667,7 +731,8 @@ export async function scaffold(config) {
|
|
|
667
731
|
if (!dryRun) {
|
|
668
732
|
// 打印下一步提示
|
|
669
733
|
const mainPlatform = platforms.includes('claude') ? 'claude' : 'codebuddy';
|
|
670
|
-
const
|
|
734
|
+
const defaultCli = mainPlatform === 'claude' ? 'claude' : 'cbc';
|
|
735
|
+
const cli = aiCli || defaultCli;
|
|
671
736
|
|
|
672
737
|
console.log('');
|
|
673
738
|
console.log(' 下一步:');
|