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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.14",
3
- "bundledAt": "2026-03-15T11:21:10.029Z",
4
- "bundledFrom": "94ac498"
2
+ "frameworkVersion": "1.0.16",
3
+ "bundledAt": "2026-03-15T13:35:03.135Z",
4
+ "bundledFrom": "d609245"
5
5
  }
@@ -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 documentation rules"
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
- When modifying source files in this project:
15
- 1. Check if `.prizm-docs/root.prizm` exists
16
- 2. If it does, read it before making changes to understand project structure
17
- 3. After making changes, update affected `.prizm-docs/` files
18
- 4. Follow the Prizm doc format (KEY: value, not prose)
19
- 5. Size limits: L0 = 4KB, L1 = 3KB, L2 = 5KB
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.14",
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": ["prizmkit-tool-perf-profiler", "prizmkit-tool-security-audit", "prizmkit-tool-ci-cd-generator"]
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": ["prizmkit-tool-db-migration", "prizmkit-tool-api-doc-generator", "prizmkit-tool-monitoring-setup", "prizmkit-tool-deployment-strategy", "prizmkit-tool-security-audit", "prizmkit-tool-error-triage", "prizmkit-tool-log-analyzer"]
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": ["prizmkit-tool-db-migration", "prizmkit-tool-api-doc-generator", "prizmkit-tool-ci-cd-generator", "prizmkit-tool-security-audit", "prizmkit-tool-deployment-strategy", "prizmkit-tool-perf-profiler"]
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": ["prizmkit-tool-api-doc-generator", "prizmkit-tool-ci-cd-generator", "prizmkit-tool-dependency-health"]
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": ["search", "utility"]
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": ["design", "frontend"]
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": ["reasoning", "universal"]
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: State Assessment**
43
- 2a. Run dependency analysis:
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
- 3b. If project has existing `docs/AI_CONTEXT/`: suggest running `prizmkit.doc.migrate`
50
+ 2b. If project has existing `docs/AI_CONTEXT/`: suggest running `prizmkit.doc.migrate`
63
51
 
64
- **Step 4: PrizmKit Workspace Initialization**
65
- 4a. Create `.prizmkit/` directory:
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 5: Hook & Settings Configuration (Platform-Specific)**
57
+ **Step 4: Hook & Settings Configuration (Platform-Specific)**
70
58
 
71
59
  **If platform is CodeBuddy (or both):**
72
- 5a-cb. Read or create `.codebuddy/settings.json`
73
- 5b-cb. Add UserPromptSubmit hook from `${SKILL_DIR}/../../../assets/hooks/prizm-commit-hook.json`
74
- 5c-cb. Preserve any existing hooks
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
- 5a-cl. Read or create `.claude/settings.json`
78
- 5b-cl. Add `permissions` and `allowedTools` entries if needed
79
- 5c-cl. Create `.claude/rules/prizm-documentation.md` with glob-scoped rules:
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
- 5d-cl. Create `.claude/rules/prizm-commit-workflow.md` with commit-scoped rules:
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
- 5e-cl. Preserve any existing Claude settings and rules
96
+ 4e-cl. Preserve any existing Claude settings and rules
109
97
 
110
- **Step 6: Project Memory Update (Platform-Specific)**
98
+ **Step 5: Project Memory Update (Platform-Specific)**
111
99
 
112
100
  **If platform is CodeBuddy (or both):**
113
- 6a-cb. Read existing `CODEBUDDY.md` (or create if missing)
114
- 6b-cb. Append PrizmKit section from `${SKILL_DIR}/../../../assets/codebuddy-md-template.md`
115
- 6c-cb. Do not duplicate if already present
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
- 6a-cl. Read existing `CLAUDE.md` (or create if missing)
119
- 6b-cl. Append PrizmKit section from `${SKILL_DIR}/../../../assets/claude-md-template.md`
120
- 6c-cl. Adjust command references to use `/command-name` format (not `prizmkit.xxx`)
121
- 6d-cl. Do not duplicate if already present
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 7: Report**
124
- Output summary: platform detected, tech stack detected, modules discovered, L1 docs generated, assessment highlights, platform-specific configuration applied, next recommended steps.
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 Steps 1-2 (no code to scan)
132
- - Step 3: Create minimal `.prizm-docs/` with just `root.prizm` skeleton
133
- - Steps 4-6: Same as brownfield
134
- - Step 7: Recommend starting with specify for first feature (platform-appropriate command format)
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": "echo 'PRIZMKIT_DOC_UPDATE_REQUIRED: Before committing, you MUST update .prizm-docs/ per Prizm auto-update protocol. Steps: 1) Run git diff --cached --name-status. 2) Map changed files to modules via root.prizm MODULE_INDEX. 3) Read and update affected .prizm files (only changed sections). 4) Append to changelog.prizm. 5) Stage .prizm files with git add .prizm-docs/. 6) Then proceed with commit using prizmkit-committer workflow. RULES: Never rewrite entire .prizm files. Never add prose. Only update affected sections.'"
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": "echo 'PRIZMKIT_DOC_UPDATE_REQUIRED: Before committing, you MUST update .prizm-docs/ per Prizm auto-update protocol. Steps: 1) Run git diff --cached --name-status. 2) Map changed files to modules via root.prizm MODULE_INDEX. 3) Read and update affected .prizm files (only changed sections). 4) Append to changelog.prizm. 5) Stage .prizm files with git add .prizm-docs/. 6) Then proceed with commit using prizmkit-committer workflow. RULES: Never rewrite entire .prizm files. Never add prose. Only update affected sections.'"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- * @returns {{ cbc: boolean, claude: boolean, suggested: string }}
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
- return { cbc, claude, suggested };
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 CLI (cbc)${detected.cbc ? chalk.green(' ← 已检测到') : ''}`,
73
+ name: `CodeBuddy (.codebuddy/)${detected.cbc ? chalk.green(' ← 已检测到 cbc') : ''}`,
73
74
  value: 'codebuddy',
74
75
  },
75
76
  {
76
- name: `Claude Code CLI (claude)${detected.claude ? chalk.green(' ← 已检测到') : ''}`,
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: defaultCli,
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 cli = mainPlatform === 'claude' ? 'claude' : 'cbc';
741
+ const defaultCli = mainPlatform === 'claude' ? 'claude' : 'cbc';
742
+ const cli = aiCli || defaultCli;
671
743
 
672
744
  console.log('');
673
745
  console.log(' 下一步:');