prizmkit 1.0.14 → 1.0.16
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/skills/prizmkit-init/SKILL.md +28 -40
- package/bundled/templates/hooks/commit-intent-claude.json +12 -1
- package/bundled/templates/hooks/commit-intent-codebuddy.json +12 -1
- package/bundled/templates/hooks/diff-prizm-docs.sh +158 -0
- package/bundled/templates/hooks/prizm-pre-commit.sh +59 -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 +73 -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.16",
|
|
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
|
}
|
|
@@ -39,44 +39,32 @@ BROWNFIELD WORKFLOW (existing project):
|
|
|
39
39
|
4. Catalog dependencies (external packages)
|
|
40
40
|
5. Count source files per directory
|
|
41
41
|
|
|
42
|
-
**Step 2:
|
|
43
|
-
2a.
|
|
44
|
-
- Count outdated dependencies (if lockfile exists)
|
|
45
|
-
- Note any known vulnerability patterns
|
|
46
|
-
|
|
47
|
-
2b. Scan for technical debt indicators:
|
|
48
|
-
- Count TODO/FIXME/HACK/XXX comments
|
|
49
|
-
- Identify large files (>500 lines)
|
|
50
|
-
- Check for test directories and coverage config
|
|
51
|
-
|
|
52
|
-
2c. Generate `ASSESSMENT.md` in project root with findings
|
|
53
|
-
|
|
54
|
-
**Step 3: Prizm Documentation Generation**
|
|
55
|
-
3a. Invoke prizmkit-prizm-docs `prizmkit.doc.init` algorithm:
|
|
42
|
+
**Step 2: Prizm Documentation Generation**
|
|
43
|
+
2a. Invoke prizmkit-prizm-docs `prizmkit.doc.init` algorithm:
|
|
56
44
|
- Create `.prizm-docs/` directory structure
|
|
57
45
|
- Generate `root.prizm` (L0) with project meta and module index
|
|
58
46
|
- Generate L1 docs for all discovered modules
|
|
59
47
|
- Create `changelog.prizm`
|
|
60
48
|
- Skip L2 (lazy generation)
|
|
61
49
|
|
|
62
|
-
|
|
50
|
+
2b. If project has existing `docs/AI_CONTEXT/`: suggest running `prizmkit.doc.migrate`
|
|
63
51
|
|
|
64
|
-
**Step
|
|
65
|
-
|
|
52
|
+
**Step 3: PrizmKit Workspace Initialization**
|
|
53
|
+
3a. Create `.prizmkit/` directory:
|
|
66
54
|
- `.prizmkit/config.json` (adoption_mode, speckit_hooks_enabled, platform)
|
|
67
55
|
- `.prizmkit/specs/` (empty)
|
|
68
56
|
|
|
69
|
-
**Step
|
|
57
|
+
**Step 4: Hook & Settings Configuration (Platform-Specific)**
|
|
70
58
|
|
|
71
59
|
**If platform is CodeBuddy (or both):**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
4a-cb. Read or create `.codebuddy/settings.json`
|
|
61
|
+
4b-cb. Add UserPromptSubmit hook from `${SKILL_DIR}/../../../assets/hooks/prizm-commit-hook.json`
|
|
62
|
+
4c-cb. Preserve any existing hooks
|
|
75
63
|
|
|
76
64
|
**If platform is Claude Code (or both):**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
4a-cl. Read or create `.claude/settings.json`
|
|
66
|
+
4b-cl. Add `permissions` and `allowedTools` entries if needed
|
|
67
|
+
4c-cl. Create `.claude/rules/prizm-documentation.md` with glob-scoped rules:
|
|
80
68
|
```yaml
|
|
81
69
|
---
|
|
82
70
|
description: PrizmKit documentation rules
|
|
@@ -91,7 +79,7 @@ BROWNFIELD WORKFLOW (existing project):
|
|
|
91
79
|
2. After changes, update affected `.prizm-docs/` files
|
|
92
80
|
3. Follow Prizm doc format (KEY: value, not prose)
|
|
93
81
|
```
|
|
94
|
-
|
|
82
|
+
4d-cl. Create `.claude/rules/prizm-commit-workflow.md` with commit-scoped rules:
|
|
95
83
|
```yaml
|
|
96
84
|
---
|
|
97
85
|
description: PrizmKit commit workflow enforcement
|
|
@@ -105,33 +93,33 @@ BROWNFIELD WORKFLOW (existing project):
|
|
|
105
93
|
4. Stage .prizm-docs/ changes
|
|
106
94
|
5. Use /prizmkit-committer for the complete workflow
|
|
107
95
|
```
|
|
108
|
-
|
|
96
|
+
4e-cl. Preserve any existing Claude settings and rules
|
|
109
97
|
|
|
110
|
-
**Step
|
|
98
|
+
**Step 5: Project Memory Update (Platform-Specific)**
|
|
111
99
|
|
|
112
100
|
**If platform is CodeBuddy (or both):**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
5a-cb. Read existing `CODEBUDDY.md` (or create if missing)
|
|
102
|
+
5b-cb. Append PrizmKit section from `${SKILL_DIR}/../../../assets/codebuddy-md-template.md`
|
|
103
|
+
5c-cb. Do not duplicate if already present
|
|
116
104
|
|
|
117
105
|
**If platform is Claude Code (or both):**
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
5a-cl. Read existing `CLAUDE.md` (or create if missing)
|
|
107
|
+
5b-cl. Append PrizmKit section from `${SKILL_DIR}/../../../assets/claude-md-template.md`
|
|
108
|
+
5c-cl. Adjust command references to use `/command-name` format (not `prizmkit.xxx`)
|
|
109
|
+
5d-cl. Do not duplicate if already present
|
|
122
110
|
|
|
123
|
-
**Step
|
|
124
|
-
Output summary: platform detected, tech stack detected, modules discovered, L1 docs generated,
|
|
111
|
+
**Step 6: Report**
|
|
112
|
+
Output summary: platform detected, tech stack detected, modules discovered, L1 docs generated, platform-specific configuration applied, next recommended steps.
|
|
125
113
|
|
|
126
114
|
Include platform-specific guidance:
|
|
127
115
|
- CodeBuddy: "Use `prizmkit.specify` to start your first feature"
|
|
128
116
|
- Claude Code: "Use `/prizmkit-specify` to start your first feature"
|
|
129
117
|
|
|
130
118
|
GREENFIELD WORKFLOW (new project):
|
|
131
|
-
- Skip
|
|
132
|
-
- Step
|
|
133
|
-
- Steps
|
|
134
|
-
- Step
|
|
119
|
+
- Skip Step 1 (no code to scan)
|
|
120
|
+
- Step 2: Create minimal `.prizm-docs/` with just `root.prizm` skeleton
|
|
121
|
+
- Steps 3-5: Same as brownfield
|
|
122
|
+
- Step 6: Recommend starting with specify for first feature (platform-appropriate command format)
|
|
135
123
|
|
|
136
124
|
### Gradual Adoption Path
|
|
137
125
|
After init, PrizmKit operates in phases:
|
|
@@ -6,7 +6,18 @@
|
|
|
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
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "Bash",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "if echo \"$CLAUDE_TOOL_INPUT\" 2>/dev/null | grep -qE 'git (commit|merge|rebase)' 2>/dev/null; then DRIFT=$(sh .prizmkit/scripts/diff-prizm-docs.sh 2>/dev/null); if [ -n \"$DRIFT\" ]; then echo \"PRIZMKIT_DRIFT_DETECTED: prizm-docs structural differences found after git operation:\"; echo \"$DRIFT\"; echo \"Please update affected .prizm-docs/ files using /prizmkit-prizm-docs.\"; fi; fi"
|
|
10
21
|
}
|
|
11
22
|
]
|
|
12
23
|
}
|
|
@@ -6,7 +6,18 @@
|
|
|
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
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "Bash",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "TOOL_INPUT=\"${CLAUDE_TOOL_INPUT:-${CODEBUDDY_TOOL_INPUT:-}}\"; if echo \"$TOOL_INPUT\" | grep -qE 'git (commit|merge|rebase)' 2>/dev/null; then DRIFT=$(sh .prizmkit/scripts/diff-prizm-docs.sh 2>/dev/null); if [ -n \"$DRIFT\" ]; then echo \"PRIZMKIT_DRIFT_DETECTED: prizm-docs structural differences found:\"; echo \"$DRIFT\"; echo \"Please update affected .prizm-docs/ files.\"; fi; fi"
|
|
10
21
|
}
|
|
11
22
|
]
|
|
12
23
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# PrizmKit: diff-prizm-docs.sh
|
|
3
|
+
# Outputs structural differences between .prizm-docs/ and source code.
|
|
4
|
+
# Silent (exit 0) if no differences or if .prizm-docs doesn't exist.
|
|
5
|
+
# POSIX sh only — no arrays, no bashisms.
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
ROOT_PRIZM=".prizm-docs/root.prizm"
|
|
10
|
+
|
|
11
|
+
# 1. Exit silently if no root.prizm
|
|
12
|
+
[ -f "$ROOT_PRIZM" ] || exit 0
|
|
13
|
+
|
|
14
|
+
# 2. Detect language from LANG: field
|
|
15
|
+
LANG_VAL=$(grep '^LANG:' "$ROOT_PRIZM" | head -1 | sed 's/^LANG:[[:space:]]*//' | tr '[:upper:]' '[:lower:]')
|
|
16
|
+
|
|
17
|
+
case "$LANG_VAL" in
|
|
18
|
+
go)
|
|
19
|
+
FILE_PATTERN="*.go"
|
|
20
|
+
;;
|
|
21
|
+
javascript|typescript|javascript/typescript|typescript/javascript)
|
|
22
|
+
FILE_PATTERN="*.ts *.tsx *.js *.jsx"
|
|
23
|
+
;;
|
|
24
|
+
python)
|
|
25
|
+
FILE_PATTERN="*.py"
|
|
26
|
+
;;
|
|
27
|
+
rust)
|
|
28
|
+
FILE_PATTERN="*.rs"
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
FILE_PATTERN=""
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
# Helper: count source files in a directory (maxdepth 1) by language
|
|
36
|
+
count_source_files() {
|
|
37
|
+
_dir="$1"
|
|
38
|
+
[ -d "$_dir" ] || { echo 0; return; }
|
|
39
|
+
if [ -z "$FILE_PATTERN" ]; then
|
|
40
|
+
# All non-hidden files
|
|
41
|
+
find "$_dir" -maxdepth 1 -type f ! -name '.*' 2>/dev/null | wc -l | tr -d ' '
|
|
42
|
+
else
|
|
43
|
+
_count=0
|
|
44
|
+
for _pat in $FILE_PATTERN; do
|
|
45
|
+
_c=$(find "$_dir" -maxdepth 1 -type f -name "$_pat" 2>/dev/null | wc -l | tr -d ' ')
|
|
46
|
+
_count=$((_count + _c))
|
|
47
|
+
done
|
|
48
|
+
echo "$_count"
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# 3. Parse MODULE_INDEX from root.prizm
|
|
53
|
+
# Extract lines between MODULE_INDEX: and next ALL-CAPS header or EOF
|
|
54
|
+
# Each module line: "- src/auth: ..." -> source path is text before first ":" after "- "
|
|
55
|
+
REGISTERED_PATHS=""
|
|
56
|
+
_in_index=0
|
|
57
|
+
while IFS= read -r line; do
|
|
58
|
+
case "$line" in
|
|
59
|
+
MODULE_INDEX:*)
|
|
60
|
+
_in_index=1
|
|
61
|
+
continue
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
if [ "$_in_index" -eq 1 ]; then
|
|
65
|
+
# Check for next ALL-CAPS section header (word followed by colon, all uppercase letters)
|
|
66
|
+
_header=$(echo "$line" | grep '^[A-Z_][A-Z_]*:' || true)
|
|
67
|
+
if [ -n "$_header" ]; then
|
|
68
|
+
break
|
|
69
|
+
fi
|
|
70
|
+
# Extract source path from "- src/auth: ..."
|
|
71
|
+
case "$line" in
|
|
72
|
+
"- "*)
|
|
73
|
+
_path=$(echo "$line" | sed 's/^- //' | sed 's/:.*//')
|
|
74
|
+
if [ -n "$_path" ]; then
|
|
75
|
+
REGISTERED_PATHS="${REGISTERED_PATHS}${_path}
|
|
76
|
+
"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
fi
|
|
81
|
+
done < "$ROOT_PRIZM"
|
|
82
|
+
|
|
83
|
+
# 4. Check each registered module for drift
|
|
84
|
+
echo "$REGISTERED_PATHS" | while IFS= read -r source_path; do
|
|
85
|
+
[ -z "$source_path" ] && continue
|
|
86
|
+
|
|
87
|
+
# Derive L1 prizm file path
|
|
88
|
+
l1_file=".prizm-docs/${source_path}.prizm"
|
|
89
|
+
|
|
90
|
+
if [ ! -f "$l1_file" ]; then
|
|
91
|
+
continue
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Read declared FILES: count
|
|
95
|
+
declared=$(grep '^FILES:' "$l1_file" | head -1 | awk '{print $2}')
|
|
96
|
+
[ -z "$declared" ] && continue
|
|
97
|
+
|
|
98
|
+
# Count actual source files
|
|
99
|
+
actual=$(count_source_files "$source_path")
|
|
100
|
+
|
|
101
|
+
if [ "$actual" -gt 0 ] && [ "$declared" != "$actual" ]; then
|
|
102
|
+
echo "MODULE_DRIFT: $source_path | declared FILES: $declared | actual: $actual"
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
|
|
106
|
+
# 5. Find orphan docs
|
|
107
|
+
find .prizm-docs -name '*.prizm' 2>/dev/null | while IFS= read -r prizm_file; do
|
|
108
|
+
_basename=$(basename "$prizm_file")
|
|
109
|
+
case "$_basename" in
|
|
110
|
+
root.prizm|changelog.prizm|changelog-archive.prizm)
|
|
111
|
+
continue
|
|
112
|
+
;;
|
|
113
|
+
esac
|
|
114
|
+
|
|
115
|
+
# Derive source path: remove .prizm-docs/ prefix and .prizm suffix
|
|
116
|
+
source_path=$(echo "$prizm_file" | sed 's|^\.prizm-docs/||' | sed 's|\.prizm$||')
|
|
117
|
+
|
|
118
|
+
if [ ! -d "$source_path" ]; then
|
|
119
|
+
echo "ORPHAN_DOC: $prizm_file | source dir missing"
|
|
120
|
+
fi
|
|
121
|
+
done
|
|
122
|
+
|
|
123
|
+
# 6. Find unregistered directories
|
|
124
|
+
find . -mindepth 1 -maxdepth 3 -type d \
|
|
125
|
+
! -path '*/.*' \
|
|
126
|
+
! -path '*/node_modules/*' ! -path './node_modules' \
|
|
127
|
+
! -path '*/vendor/*' ! -path './vendor' \
|
|
128
|
+
! -path '*/dist/*' ! -path './dist' \
|
|
129
|
+
! -path '*/build/*' ! -path './build' \
|
|
130
|
+
! -path '*/__pycache__/*' ! -path './__pycache__' \
|
|
131
|
+
! -path '*/target/*' ! -path './target' \
|
|
132
|
+
! -path '*/tests/*' ! -path './tests' \
|
|
133
|
+
! -path '*/__tests__/*' ! -path './__tests__' \
|
|
134
|
+
! -path '*/.prizm-docs/*' ! -path './.prizm-docs' \
|
|
135
|
+
! -path '*/.claude/*' ! -path './.claude' \
|
|
136
|
+
! -path '*/.codebuddy/*' ! -path './.codebuddy' \
|
|
137
|
+
! -path '*/dev-pipeline/*' ! -path './dev-pipeline' \
|
|
138
|
+
! -path '*/coverage/*' ! -path './coverage' \
|
|
139
|
+
! -path '*/.nyc_output/*' ! -path './.nyc_output' \
|
|
140
|
+
! -path '*/out/*' ! -path './out' \
|
|
141
|
+
! -path '*/tmp/*' ! -path './tmp' \
|
|
142
|
+
2>/dev/null | while IFS= read -r candidate; do
|
|
143
|
+
# Normalize: remove leading ./
|
|
144
|
+
candidate_clean=$(echo "$candidate" | sed 's|^\./||')
|
|
145
|
+
|
|
146
|
+
# Count source files
|
|
147
|
+
file_count=$(count_source_files "$candidate_clean")
|
|
148
|
+
|
|
149
|
+
if [ "$file_count" -ge 3 ]; then
|
|
150
|
+
# Check if registered in MODULE_INDEX
|
|
151
|
+
_match=$(echo "$REGISTERED_PATHS" | grep -Fx "$candidate_clean" || true)
|
|
152
|
+
if [ -z "$_match" ]; then
|
|
153
|
+
echo "UNREGISTERED: $candidate_clean | $file_count source files | not in MODULE_INDEX"
|
|
154
|
+
fi
|
|
155
|
+
fi
|
|
156
|
+
done
|
|
157
|
+
|
|
158
|
+
exit 0
|
|
@@ -0,0 +1,59 @@
|
|
|
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/.prizmkit/scripts/validate-prizm-docs.sh"
|
|
10
|
+
[ -f "$VALIDATE_SCRIPT" ] || VALIDATE_SCRIPT="$PROJECT_ROOT/dev-pipeline/scripts/validate-prizm-docs.sh"
|
|
11
|
+
|
|
12
|
+
if [ -f "$VALIDATE_SCRIPT" ]; then
|
|
13
|
+
sh "$VALIDATE_SCRIPT" --staged
|
|
14
|
+
exit $?
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Inline fallback: core checks for staged .prizm files
|
|
18
|
+
FILES=$(git diff --cached --name-only 2>/dev/null | grep '\.prizm$')
|
|
19
|
+
[ -z "$FILES" ] && exit 0
|
|
20
|
+
|
|
21
|
+
ERRORS=0
|
|
22
|
+
for FILE in $FILES; do
|
|
23
|
+
[ -f "$FILE" ] || continue
|
|
24
|
+
|
|
25
|
+
if grep -qE '^#{1,6} ' "$FILE"; then
|
|
26
|
+
echo "ERROR: $FILE contains markdown headers (##). Use KEY: value format." >&2
|
|
27
|
+
ERRORS=$((ERRORS + 1))
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if grep -q '^```' "$FILE"; then
|
|
31
|
+
echo "ERROR: $FILE contains code blocks. Use file_path:line_number reference." >&2
|
|
32
|
+
ERRORS=$((ERRORS + 1))
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
SIZE=$(wc -c < "$FILE" | tr -d ' ')
|
|
36
|
+
case "$FILE" in
|
|
37
|
+
*root.prizm) LIMIT=4096 ;;
|
|
38
|
+
*)
|
|
39
|
+
DIR=$(dirname "$FILE")
|
|
40
|
+
if [ "$DIR" = ".prizm-docs" ]; then
|
|
41
|
+
LIMIT=3072
|
|
42
|
+
else
|
|
43
|
+
LIMIT=5120
|
|
44
|
+
fi
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
if [ "$SIZE" -gt "$LIMIT" ]; then
|
|
49
|
+
echo "ERROR: $FILE exceeds size limit (${SIZE}B > ${LIMIT}B)." >&2
|
|
50
|
+
ERRORS=$((ERRORS + 1))
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
55
|
+
echo "PrizmKit: $ERRORS format error(s) in .prizm files. Fix before committing." >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
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,69 @@ 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
|
+
* 安装 PrizmKit 脚本到 .prizmkit/scripts/(始终安装)
|
|
476
|
+
*/
|
|
477
|
+
async function installPrizmkitScripts(projectRoot, dryRun) {
|
|
478
|
+
const scriptsDir = path.join(projectRoot, '.prizmkit', 'scripts');
|
|
479
|
+
const templateHooksDir = path.join(getTemplatesDir(), 'hooks');
|
|
480
|
+
|
|
481
|
+
const scripts = [
|
|
482
|
+
'validate-prizm-docs.sh',
|
|
483
|
+
'diff-prizm-docs.sh',
|
|
484
|
+
];
|
|
485
|
+
|
|
486
|
+
if (dryRun) {
|
|
487
|
+
for (const s of scripts) {
|
|
488
|
+
console.log(chalk.gray(` [dry-run] .prizmkit/scripts/${s}`));
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
await fs.ensureDir(scriptsDir);
|
|
494
|
+
|
|
495
|
+
for (const s of scripts) {
|
|
496
|
+
const src = path.join(templateHooksDir, s);
|
|
497
|
+
if (!fs.existsSync(src)) continue;
|
|
498
|
+
const tgt = path.join(scriptsDir, s);
|
|
499
|
+
await fs.copy(src, tgt);
|
|
500
|
+
fs.chmodSync(tgt, 0o755);
|
|
501
|
+
console.log(chalk.green(` ✓ .prizmkit/scripts/${s}`));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
442
505
|
/**
|
|
443
506
|
* 安装项目记忆文件(CODEBUDDY.md / CLAUDE.md)
|
|
444
507
|
*/
|
|
@@ -653,6 +716,14 @@ export async function scaffold(config) {
|
|
|
653
716
|
}
|
|
654
717
|
}
|
|
655
718
|
|
|
719
|
+
// 10. Git pre-commit hook
|
|
720
|
+
console.log(chalk.blue(' Git Hook:'));
|
|
721
|
+
await installGitHook(projectRoot, dryRun);
|
|
722
|
+
|
|
723
|
+
// 11. PrizmKit scripts (always installed)
|
|
724
|
+
console.log(chalk.blue(' PrizmKit 脚本:'));
|
|
725
|
+
await installPrizmkitScripts(projectRoot, dryRun);
|
|
726
|
+
|
|
656
727
|
// === 完成 ===
|
|
657
728
|
console.log('');
|
|
658
729
|
console.log(chalk.bold(' ════════════════════════════════════════════════'));
|
|
@@ -667,7 +738,8 @@ export async function scaffold(config) {
|
|
|
667
738
|
if (!dryRun) {
|
|
668
739
|
// 打印下一步提示
|
|
669
740
|
const mainPlatform = platforms.includes('claude') ? 'claude' : 'codebuddy';
|
|
670
|
-
const
|
|
741
|
+
const defaultCli = mainPlatform === 'claude' ? 'claude' : 'cbc';
|
|
742
|
+
const cli = aiCli || defaultCli;
|
|
671
743
|
|
|
672
744
|
console.log('');
|
|
673
745
|
console.log(' 下一步:');
|