prizmkit 1.1.60 → 1.1.61

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.
Files changed (39) hide show
  1. package/bin/create-prizmkit.js +2 -2
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/agents/prizm-dev-team-dev.md +6 -5
  4. package/bundled/rules/prizm/prizm-documentation.md +2 -4
  5. package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
  6. package/bundled/skills/_metadata.json +1 -1
  7. package/bundled/skills/app-planner/SKILL.md +1 -1
  8. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
  9. package/bundled/skills/feature-planner/SKILL.md +1 -1
  10. package/bundled/skills/prizmkit-implement/SKILL.md +1 -0
  11. package/bundled/skills/prizmkit-init/SKILL.md +1 -1
  12. package/bundled/skills/prizmkit-init/references/config-schema.md +3 -1
  13. package/bundled/skills/prizmkit-plan/SKILL.md +2 -0
  14. package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +6 -1
  15. package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +2 -2
  16. package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +2 -1
  17. package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +2 -2
  18. package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -0
  19. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +2 -0
  20. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +2 -0
  21. package/bundled/skills-windows/app-planner/SKILL.md +1 -1
  22. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +1 -1
  23. package/bundled/skills-windows/feature-planner/SKILL.md +1 -1
  24. package/bundled/skills-windows/prizmkit-init/SKILL.md +1 -1
  25. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +3 -1
  26. package/bundled/templates/hooks/diff-prizm-docs.sh +1 -1
  27. package/bundled/templates/hooks/prizm-pre-commit.sh +53 -4
  28. package/bundled/templates/hooks/validate-prizm-docs.sh +62 -7
  29. package/bundled/templates/project-memory-template.md +2 -1
  30. package/package.json +1 -1
  31. package/src/config.js +9 -1
  32. package/src/detect-platform.js +0 -2
  33. package/src/external-skills.js +2 -2
  34. package/src/index.js +0 -4
  35. package/src/manifest.js +1 -1
  36. package/src/platforms.js +6 -4
  37. package/src/prompts.js +23 -7
  38. package/src/scaffold.js +145 -16
  39. package/src/upgrade.js +9 -6
@@ -31,7 +31,7 @@ program
31
31
  program
32
32
  .command('install [directory]')
33
33
  .description('Install PrizmKit into a project directory')
34
- .option('--platform <platform>', 'Target platform: codebuddy, claude, codex, both, or all')
34
+ .option('--platform <platform>', 'Target platform: codebuddy, claude, codex, or all')
35
35
  .option('--runtime <runtime>', 'Runtime assets: unix, windows, or auto')
36
36
  .option('--skills <suite>', 'Skill suite: core, minimal, or recommended:<type> (frontend/backend/fullstack/library)', 'core')
37
37
  .option('--team', 'Enable multi-agent team mode (default: true)')
@@ -90,7 +90,7 @@ program
90
90
  program
91
91
  .command('config [directory]')
92
92
  .description('Reconfigure existing PrizmKit installation (change platform, skills, rules, options)')
93
- .option('--platform <platform>', 'Target platform: codebuddy, claude, codex, both, or all')
93
+ .option('--platform <platform>', 'Target platform: codebuddy, claude, codex, or all')
94
94
  .option('--runtime <runtime>', 'Runtime assets: unix, windows, or auto')
95
95
  .option('--skills <suite>', 'Skill suite: core, minimal, or recommended:<type>')
96
96
  .option('--rules <preset>', 'Rules preset: recommended, minimal, or none')
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.1.60",
3
- "bundledAt": "2026-06-08T12:49:43.025Z",
4
- "bundledFrom": "62b5c3d"
2
+ "frameworkVersion": "1.1.61",
3
+ "bundledAt": "2026-06-08T16:20:29.783Z",
4
+ "bundledFrom": "819ffc3"
5
5
  }
@@ -48,8 +48,8 @@ If the snapshot does not exist:
48
48
  8. Checkpoint tasks must verify that build and tests pass before proceeding to the next phase
49
49
  9. Execute sequential tasks in order; stop on failure. Parallel `[P]` tasks may continue
50
50
  10. When creating a new sub-module, generate the corresponding `.prizmkit/prizm-docs/` L2 document. **Batch independent operations**: combine multiple `mkdir -p` into one command; issue multiple independent `Write` calls for different `.prizmkit/prizm-docs/` files in a single message turn (they have no dependencies between them).
51
- 11. **`.prizmkit/prizm-docs/` write safety**: Before writing ANY `.prizmkit/prizm-docs/` file, check if it already exists (`ls <path>`). If it **exists**: only append or update structural fields (KEY_FILES, INTERFACES, DEPENDENCIES, file counts, UPDATED date) — **never overwrite the full file**. DECISIONS and CHANGELOG sections are **append-only** never delete or replace existing entries. If it does **not** exist: create it only for sub-modules you are actively creating in this session. Do NOT write `.prizmkit/prizm-docs/` files for modules you are not directly creating.
52
- 11. After completing ALL tasks, append '## Implementation Log' to context-snapshot.md: files changed/created, key decisions, notable discoveries
51
+ 11. **`.prizmkit/prizm-docs/` write safety**: Before writing ANY `.prizmkit/prizm-docs/` file, check if it already exists (`ls <path>`). If it **exists**: update only durable structural/knowledge fields (KEY_FILES, INTERFACES, DEPENDENCIES, file counts, RULES, TRAPS, DECISIONS) — **never overwrite the full file**. Do not add CHANGELOG, UPDATED/date fields, Bug IDs, feature IDs, refactor IDs, task IDs, session IDs, run IDs, pipeline IDs, workflow IDs, branch names, absolute worktree paths, pipeline artifact paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths. If a durable fact changes, update the existing section in place. If the file does **not** exist: create it only for sub-modules you are actively creating in this session. Do NOT write `.prizmkit/prizm-docs/` files for modules you are not directly creating.
52
+ 12. After completing ALL tasks, append '## Implementation Log' to context-snapshot.md: files changed/created, key decisions, notable discoveries
53
53
 
54
54
  ### Never Do (NEVER)
55
55
 
@@ -59,7 +59,8 @@ If the snapshot does not exist:
59
59
  - **Do not execute any git operations** (git commit / git add / git reset / git push are all prohibited — the Orchestrator handles commits via /prizmkit-committer)
60
60
  - Do not modify any files in `.prizmkit/specs/` except `plan.md` (marking Tasks [x]) and `context-snapshot.md` (appending Implementation Log)
61
61
  - Do not use TaskCreate/TaskUpdate to create or modify Orchestrator-level tasks (Task tools are for internal progress tracking only, and task IDs are not shared across agent sub-sessions)
62
- - **Do not overwrite existing `.prizmkit/prizm-docs/` files in full** — if a doc already exists, only update structural fields; never replace the entire file. Do NOT write `.prizmkit/prizm-docs/` entries for modules you are not actively creating in this session.
62
+ - **Do not overwrite existing `.prizmkit/prizm-docs/` files in full** — if a doc already exists, only update the affected durable fields; never replace the entire file. Do NOT write `.prizmkit/prizm-docs/` entries for modules you are not actively creating in this session.
63
+ - **Do not leak internal PrizmKit metadata into product surfaces or memory docs** — feature IDs (`F-001`), bug/refactor IDs, task IDs, session IDs, run IDs, pipeline IDs, workflow IDs, branch names, absolute worktree paths, pipeline artifact paths, and `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths must not appear in `.prizmkit/prizm-docs/`, user-visible UI copy, API responses, emails, notifications, or tests that assert visible product text.
63
64
 
64
65
  ### Behavioral Rules
65
66
 
@@ -83,7 +84,8 @@ DEV-15: After ALL tasks, append '## Implementation Log' to context-snapshot.md (
83
84
  DEV-16: Without context-snapshot: read .prizmkit/prizm-docs/ → read source files directly
84
85
  DEV-17: DO NOT re-read source files already listed in context-snapshot.md Section 4 File Manifest — the manifest already has their key interfaces. Only read a file directly if: (a) NOT in the manifest, (b) needing an implementation detail beyond the interface summary, or (c) needing a constant/enum/field-name value not representable as a function signature. Unnecessary re-reads waste significant context budget.
85
86
  DEV-18: When tests fail, run `$TEST_CMD 2>&1 | tee /tmp/test-out.txt` ONCE, then grep `/tmp/test-out.txt` for failure details. Never re-run the full test suite just to apply a different grep filter to its output.
86
- DEV-19: Before writing any `.prizmkit/prizm-docs/` file, check if it exists. If it exists: only update structural fields (KEY_FILES, INTERFACES, DEPENDENCIES, file counts, UPDATED) — never overwrite the full file. DECISIONS/CHANGELOG are append-only. Only create new L2 docs for sub-modules you are actively creating in this session.
87
+ DEV-19: Before writing any `.prizmkit/prizm-docs/` file, check if it exists. If it exists: only update durable fields (KEY_FILES, INTERFACES, DEPENDENCIES, file counts, RULES, TRAPS, DECISIONS) — never overwrite the full file. Never add CHANGELOG, UPDATED/date fields, or workflow metadata. Only create new L2 docs for sub-modules you are actively creating in this session.
88
+ DEV-20: Internal tracking IDs are not product copy. Before writing UI text or UI-copy assertions, translate references like `F-003 guard` into product-language behavior such as `the high-risk guard`. Add regression coverage when a feature touches user-visible text.
87
89
  ```
88
90
 
89
91
  ### Workflow
@@ -121,4 +123,3 @@ Direct communication between Agents is allowed, but key messages and conclusions
121
123
  - Send COMPLETION_SIGNAL to indicate all tasks are complete
122
124
  - Send ESCALATION to report interface ambiguities or task blockers
123
125
  - Receive TASK_ASSIGNMENT to get assigned work
124
-
@@ -22,6 +22,7 @@ FORMAT RULES (enforced by pre-commit hook — violations block commit):
22
22
  - KEY: value pairs and dash-prefixed lists only
23
23
  - PROHIBITED: prose paragraphs, markdown headers (##/###), code blocks (```), emoji, ASCII art
24
24
  - No UPDATED timestamps — git is the authoritative source for temporal information
25
+ - PROHIBITED: CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, and `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths
25
26
  - This format is designed for AI token efficiency, not human readability. Do not add human-friendly formatting.
26
27
 
27
28
  SIZE LIMITS (hard — pre-commit hook blocks commits exceeding these):
@@ -32,7 +33,7 @@ SIZE LIMITS (hard — pre-commit hook blocks commits exceeding these):
32
33
  SIZE OVERFLOW HANDLING:
33
34
  - L0 approaching 4KB: if MODULE_INDEX has > 15 entries, convert to MODULE_GROUPS format (group by domain). Otherwise consolidate descriptions, keep only top-5 RULES, remove PATTERNS detail.
34
35
  - L1 approaching 4KB: trim KEY_FILES descriptions, ensure RULES <= 3 entries, move detail to L2
35
- - L2 approaching 5KB: archive CHANGELOG entries older than 90 days to changelog-archive.prizm
36
+ - L2 approaching 5KB: remove stale or trivially derivable entries; never create changelog-archive.prizm
36
37
  - NEVER exceed hard limits — pre-commit hook will block the commit
37
38
 
38
39
  REQUIRED FIELDS PER LEVEL:
@@ -58,7 +59,6 @@ L2 detail.prizm:
58
59
  - DEPENDENCIES
59
60
  - INTERFACES
60
61
  - TRAPS (with severity prefix: [CRITICAL], [HIGH], or [LOW])
61
- - CHANGELOG
62
62
 
63
63
  L2 GENERATION TEMPLATE (use when AI first touches a sub-module with no L2 doc):
64
64
 
@@ -74,7 +74,5 @@ INTERFACES:
74
74
  - <exported function/class>: <signature and purpose>
75
75
  TRAPS:
76
76
  - [LOW] <gotcha, race condition, or non-obvious coupling> | FIX: <approach>
77
- CHANGELOG:
78
- - root | add: initial L2 documentation
79
77
 
80
78
  TRAPS is critical — always record gotchas, race conditions, non-obvious behavior, and surprising coupling between modules. Every TRAP must have a severity prefix ([CRITICAL], [HIGH], or [LOW]).
@@ -9,4 +9,5 @@ This project uses PrizmKit's progressive loading protocol:
9
9
  - ON FILE EDIT: Read L2 (`.prizmkit/prizm-docs/<module>/<submodule>.prizm`) before modifying
10
10
  - NEVER load all .prizm docs at once
11
11
  - Arrow notation (->) in .prizm files indicates load pointers
12
- - DECISIONS and CHANGELOG in .prizm files are append-only
12
+ - .prizm files do not contain CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths
13
+ - Update stale durable knowledge in place; git history is the change log
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.60",
2
+ "version": "1.1.61",
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.",
@@ -45,7 +45,7 @@ If you believe the task is better suited for a different workflow, you MUST:
45
45
  - Codex → `AGENTS.md`
46
46
  - Claude Code → `CLAUDE.md`
47
47
  - CodeBuddy → `CODEBUDDY.md`
48
- - `both` → `CLAUDE.md` and `CODEBUDDY.md`; `all` → all three files.
48
+ - `all` → all three files.
49
49
  - Only when the manifest is missing, fall back to installed PrizmKit-owned platform files such as `.codex/agents/*.toml`, `.claude/commands/prizm-kit.md`, or `.codebuddy/skills/prizm-kit/SKILL.md`. Do not treat a generic `.agents/` directory as Codex.
50
50
 
51
51
  **After planning is complete**, you MUST:
@@ -29,7 +29,7 @@ After Phase 2 (Confirm constraints and tech assumptions), before Phase 3 (Captur
29
29
  - `codex` → append to `AGENTS.md`
30
30
  - `claude` → append to `CLAUDE.md`
31
31
  - `codebuddy` → append to `CODEBUDDY.md`
32
- - `both` → append to `CLAUDE.md` and `CODEBUDDY.md`; `all` append to all three files.
32
+ - `all` → append to all three files. Legacy manifests may contain `both`; treat it as read-only compatibility and append to `CLAUDE.md` and `CODEBUDDY.md` only when encountered.
33
33
  - Only when the manifest is missing, fall back to PrizmKit-owned install artifacts: `.codex/agents/*.toml`, `.claude/commands/prizm-kit.md`, `.codebuddy/skills/prizm-kit/SKILL.md`.
34
34
  - Do not treat a generic `.agents/` directory as Codex; it may contain unrelated third-party skills.
35
35
  - If no platform can be determined, skip (no project instruction file).
@@ -111,7 +111,7 @@ Before questions, check optional context files (never block if absent):
111
111
  - `.prizmkit/config.json` (existing stack preferences and detected tech stack)
112
112
  - `.prizmkit/plans/project-brief.md` (project context from app-planner, if available)
113
113
  - existing `.prizmkit/plans/feature-list.json` (required for incremental mode)
114
- - Platform instruction file: use the `platform` field in `.prizmkit/manifest.json` as source of truth when present (`codex` → `AGENTS.md`, `claude` → `CLAUDE.md`, `codebuddy` → `CODEBUDDY.md`; `both`/`all` → read every matching file)
114
+ - Platform instruction file: use the `platform` field in `.prizmkit/manifest.json` as source of truth when present (`codex` → `AGENTS.md`, `claude` → `CLAUDE.md`, `codebuddy` → `CODEBUDDY.md`, `all` → read every matching file). Legacy manifests may contain `both`; treat it as read-only compatibility and read `CLAUDE.md` plus `CODEBUDDY.md` only when encountered.
115
115
  - If `.prizmkit/prizm-docs/root.prizm` is absent and the project has existing source code, scan the directory structure to understand the codebase layout:
116
116
  ```bash
117
117
  find . -maxdepth 2 -type d \
@@ -41,6 +41,7 @@ For each unchecked task in plan.md, in order:
41
41
 
42
42
  1. Read L1/L2 doc for the target file's module — check TRAPS and DECISIONS before modifying files
43
43
  2. Apply TDD where applicable: write a failing test first, then implement until it passes. For UI components or configuration changes where unit tests don't apply, skip the test-first step.
44
+ - **Internal ID hygiene**: Do not place PrizmKit feature/bug/refactor IDs (`F-001`, `B-001`, `R-001`), task IDs, session IDs, run IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths in `.prizmkit/prizm-docs/`, user-visible UI text, API responses, emails, notifications, or tests that assert visible product copy. Translate internal references into durable product/domain language before writing memory docs, tests, or UI copy.
44
45
  - **Cover three paths for each function**: happy path (valid inputs producing expected behavior), edge cases (boundary values specific to the parameter type and domain — e.g., zero/min/max for numeric, empty collection, boundary index), and error conditions (inputs that should trigger error handling). Determine edge cases from the function's parameter types and domain logic, not from a fixed checklist. If a function has no edge or error paths, don't force them.
45
46
  - **No redundant tests**: Check if a test for this behavior already exists before writing. Each test must verify a uniquely different code path — don't write multiple tests that exercise the same logic.
46
47
  - **Test your own code only**: Don't test framework behavior, third-party library internals, or language built-ins. For library calls, test the integration point (correct parameters passed, return value correctly handled), not the library itself.
@@ -183,7 +183,7 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
183
183
  - For each module entry in MODULE_INDEX/MODULE_GROUPS, include keyword tags extracted from the module's source files — scan for: exported symbols, imported packages, domain terms in file/directory names. Format: `- module-name [tag1, tag2, tag3]: ...`. Tags help AI match user intent to relevant modules.
184
184
  - Generate L1 docs for top-level modules at `.prizmkit/prizm-docs/<M>.prizm` and for sub-modules at `.prizmkit/prizm-docs/<M>/<S>.prizm`
185
185
  - Skip L2 (lazy generation) — L2 is generated on first file modification, saving tokens upfront
186
- - Do not create auxiliary `changelog.prizm` or date/time metadata fields; git history is the source of change history
186
+ - Do not create auxiliary `changelog.prizm`, CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths; git history is the source of change history
187
187
 
188
188
  **Phase 6: Workspace Initialization**
189
189
  6a. Create `.prizmkit/` directory structure (if missing):
@@ -19,11 +19,13 @@ Handles re-init without losing user edits:
19
19
  | Field | Type | Description |
20
20
  |-------|------|-------------|
21
21
  | `adoption_mode` | string | `"passive"` \| `"advisory"` \| `"active"` |
22
- | `platform` | string | `"codebuddy"` \| `"claude"` \| `"both"` |
22
+ | `platform` | string | `"codebuddy"` \| `"claude"` \| `"codex"` \| `"all"` |
23
23
  | `tech_stack` | object | Detected or user-provided tech stack |
24
24
  | `tech_stack._auto_detected` | boolean | `true` if auto-detected, `false` if user-provided |
25
25
  | `detected_layers` | string[] | Development layers detected in the project. Written by prizmkit-init Phase 4.5. Used to determine available rule configuration options. Values: `frontend` / `backend` / `database` / `mobile`. Empty array when no layers detected or user skipped rules. Always updated on every init run based on fresh code detection — not gated by `_auto_detected` (see Merge Strategy above). |
26
26
 
27
+ Legacy manifests may still contain `both` for read-only migration compatibility. New config writes must use `codebuddy`, `claude`, `codex`, or `all`.
28
+
27
29
  ## Examples
28
30
 
29
31
  Fullstack project:
@@ -49,6 +49,8 @@ A universal spec + plan generator. Takes a natural-language description of ANY d
49
49
 
50
50
  **Writing principles**: Focus on WHAT and WHY, never HOW. Every goal needs acceptance criteria. Scope boundaries must be explicit. Mark all genuine ambiguities — the clarification phase resolves them.
51
51
 
52
+ **Internal ID hygiene**: PrizmKit IDs (`F-001`, `B-001`, `R-001`), task IDs (`T-100`), session IDs, run IDs, branch names, absolute worktree paths, and `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths are internal tracking metadata. They may appear in specs, plans, commit messages, and non-memory pipeline artifacts, but must never be written to `.prizmkit/prizm-docs/`, user-visible product copy, UI text, API responses, or expected UI strings in tests. If a behavior is scoped to the current feature, describe the product behavior without the ID.
53
+
52
54
  ### Phase 1: Design (spec.md → plan.md)
53
55
 
54
56
  **Precondition**: `spec.md` exists in the artifact directory.
@@ -37,6 +37,7 @@ CORE_PRINCIPLES:
37
37
  - Self-updating (docs stay fresh via commit-time hooks)
38
38
  - Universal (language and framework agnostic)
39
39
  - Durable project knowledge over auxiliary history (decisions, traps, interfaces, dependencies)
40
+ - Memory hygiene over traceability noise (no CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths)
40
41
  - Size-enforced (hard limits per level prevent bloat)
41
42
  - Lazy L2 generation (detail docs created on first modification or deep read, not during init)
42
43
  - Rules hierarchy (root.prizm RULES are authoritative, module RULES supplement only)
@@ -294,7 +295,7 @@ CONSTRAINTS:
294
295
  - DATA_FLOW: describes how data moves through the module (moved here from L1 in V4)
295
296
  - RULES: full module-specific rules list (L1 only has a 1-3 item summary)
296
297
  - DOMAIN-SPECIFIC SECTIONS are flexible, not prescribed
297
- - DECISIONS is append-only (never delete, archive if >20 entries)
298
+ - DECISIONS records durable rationale only; update or remove stale entries in place when code reality changes
298
299
  - TRAPS section is CRITICAL for preventing AI from making known mistakes
299
300
  - TRAPS entries MUST have severity prefix ([CRITICAL], [HIGH], or [LOW]). [REVIEW] may precede severity as a temporary staleness marker.
300
301
  - TRAPS optional fields: append `| REF: <7-char-hash>` for traceability, `| STALE_IF: <glob>` for auto-expiry detection
@@ -307,6 +308,7 @@ CONSTRAINTS:
307
308
 
308
309
  TEMPORAL_INFO: Git history is the authoritative source for change timing and edit history.
309
310
  AUXILIARY_FIELDS: Do not generate CHANGELOG or UPDATED fields in .prizm files.
311
+ WORKFLOW_METADATA: Do not write feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths into .prizm files.
310
312
  RATIONALE: Keep project memory focused on durable architecture, interfaces, dependencies, traps, rules, and decisions.
311
313
 
312
314
  ---
@@ -429,6 +431,7 @@ DETAILED_STEPS: → ${SKILL_DIR}/references/op-update.md
429
431
 
430
432
  NEVER: Add CHANGELOG sections or changelog.prizm during doc sync.
431
433
  NEVER: Add UPDATED/date/time fields to .prizm files.
434
+ NEVER: Add workflow metadata such as feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths.
432
435
  RATIONALE: Git already provides history; .prizm files should store only durable project memory.
433
436
 
434
437
  ---
@@ -446,6 +449,8 @@ NEVER: Stale information (update or delete, never leave outdated entries)
446
449
  NEVER: Full file contents or large code blocks (summarize purpose and interfaces)
447
450
  NEVER: TODO items or future plans (those belong in issue trackers)
448
451
  NEVER: Session-specific context or conversation history (docs are session-independent)
452
+ NEVER: Workflow metadata such as feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths
453
+ NEVER: CHANGELOG sections, changelog.prizm, or update-history sections
449
454
  NEVER: Flowcharts, diagrams, mermaid blocks, or ASCII art (wastes tokens, AI cannot parse visually)
450
455
  NEVER: Markdown headers (## / ###) inside .prizm files (use ALL CAPS KEY: format instead)
451
456
  NEVER: Rewrite entire .prizm files on update (modify only affected sections)
@@ -13,7 +13,7 @@ STEPS:
13
13
  - HIERARCHY RULE: directory X inside top-level module M maps to .prizmkit/prizm-docs/<M>/<X>.prizm, never to .prizmkit/prizm-docs/<X>.prizm.
14
14
  - Exclude .git/, node_modules/, vendor/, build/, dist/, __pycache__/, target/, bin/, .claude/, .codebuddy/, .prizmkit/, .prizmkit/prizm-docs/, dev-pipeline/. If total module count > 30, ask user for include/exclude patterns.
15
15
  3. Create .prizmkit/prizm-docs/ directory structure mirroring the source tree exactly. For each top-level module M that has sub-modules, create the subdirectory .prizmkit/prizm-docs/<M>/.
16
- 4. Generate root.prizm (L0) with PROJECT, LANG, FRAMEWORK, BUILD, TEST, ENTRY, MODULE_INDEX with multi-level entries as needed for efficient navigation (constrained by 4KB limit), RULES extracted from CODEBUDDY.md/CLAUDE.md/README/linter configs, PATTERNS, and CROSS_CUTTING (cross-module concerns spanning 2+ modules). Set PRIZM_VERSION: 4. Max 4KB. No date/time metadata fields — git tracks modification times.
16
+ 4. Generate root.prizm (L0) with PROJECT, LANG, FRAMEWORK, BUILD, TEST, ENTRY, MODULE_INDEX with multi-level entries as needed for efficient navigation (constrained by 4KB limit), RULES extracted from CODEBUDDY.md/CLAUDE.md/README/linter configs, PATTERNS, and CROSS_CUTTING (cross-module concerns spanning 2+ modules). Set PRIZM_VERSION: 4. Max 4KB. No CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths — git tracks history.
17
17
  - If `.prizmkit/plans/project-brief.md` exists: add `PROJECT_BRIEF: .prizmkit/plans/project-brief.md` to root.prizm (generated by prizmkit-init). If not present, skip — prizmkit-init Phase 7 will add it later.
18
18
  - If module count > 15: use MODULE_GROUPS format instead of MODULE_INDEX — group modules by functional domain (3-8 domains, inferred from directory structure and module responsibilities). See prizm-docs-format.md for MODULE_GROUPS format.
19
19
  - For each module entry, auto-generate 3-6 keyword tags by scanning the module's key source files for: exported function/class names, imported library names, domain-specific terms in file/directory names. Add tags in square brackets after the module name (e.g., `- auth [login, session, jwt]: ...`). Tags are optional but recommended for projects with 10+ modules.
@@ -23,7 +23,7 @@ STEPS:
23
23
  Each L1 includes MODULE (full relative path), FILES count, RESPONSIBILITY, SUBDIRS with pointers, KEY_FILES (5-10 most important), DEPENDENCIES (imports, imported-by, external), RULES (1-3 most critical only). L1 does NOT include INTERFACES, DATA_FLOW, TRAPS, or DECISIONS (those are L2, generated lazily). Max 4KB each.
24
24
  6. Skip L2 docs during init — L2 is created lazily on first file modification or when AI needs deep understanding (ON_DEEP_READ trigger). L2 contains behavioral detail: INTERFACES, DATA_FLOW, TRAPS, DECISIONS, full RULES, domain-specific sections.
25
25
  7. Configure UserPromptSubmit hook in platform settings per ${SKILL_DIR}/assets/prizm-docs-format.md.md Section 11.
26
- 8. Validate all generated docs: size limits (L0 <= 4KB, L1 <= 4KB), pointer resolution (every -> reference resolves), no circular dependencies, KEY: value format compliance, no anti-patterns (prose, code blocks, markdown headers), L1 does not contain INTERFACES/DATA_FLOW/TRAPS/DECISIONS, no date/time metadata fields (git is authority).
26
+ 8. Validate all generated docs: size limits (L0 <= 4KB, L1 <= 4KB), pointer resolution (every -> reference resolves), no circular dependencies, KEY: value format compliance, no anti-patterns (prose, code blocks, markdown headers), L1 does not contain INTERFACES/DATA_FLOW/TRAPS/DECISIONS, no CHANGELOG sections/files, no UPDATED/date metadata, no feature/bug/refactor/task/session/run/pipeline/workflow IDs, no branch names, no absolute worktree paths, and no `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths.
27
27
  9. Report summary: modules discovered, L1 docs generated, files excluded, warnings.
28
28
 
29
29
  OUTPUT: List of generated files, module count, and validation results.
@@ -8,11 +8,12 @@ STEPS:
8
8
  1. Get changed files via `git diff --cached --name-status`. If nothing staged, use `git diff --name-status`. If no git changes at all, do full rescan comparing code against existing docs — this includes checking for modules that have source files but no L2 doc.
9
9
  2. Map changed files to modules by matching against MODULE_INDEX or MODULE_GROUPS in root.prizm. Group changes by module.
10
10
  3. Classify each change: A (added) -> new KEY_FILES entries. D (deleted) -> remove entries, update counts. M (modified) -> check dependency changes. R (renamed) -> update all path references.
11
- 4. Update affected docs: L2 first (KEY_FILES, INTERFACES, DATA_FLOW, DEPENDENCIES, TRAPS, DECISIONS), then L1 (FILES count, KEY_FILES, DEPENDENCIES — L1 does NOT contain INTERFACES/DATA_FLOW/TRAPS/DECISIONS), then L0 (MODULE_INDEX or MODULE_GROUPS counts, CROSS_CUTTING) only if structural change. No date/time metadata fields — git tracks modification times. **Preserve** any `PROJECT_BRIEF:` line in root.prizm — it is managed by prizmkit-init, not by this skill.
11
+ 4. Update affected docs: L2 first (KEY_FILES, INTERFACES, DATA_FLOW, DEPENDENCIES, TRAPS, DECISIONS), then L1 (FILES count, KEY_FILES, DEPENDENCIES — L1 does NOT contain INTERFACES/DATA_FLOW/TRAPS/DECISIONS), then L0 (MODULE_INDEX or MODULE_GROUPS counts, CROSS_CUTTING) only if structural change. Do not write CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths — git tracks history. **Preserve** any `PROJECT_BRIEF:` line in root.prizm — it is managed by prizmkit-init, not by this skill.
12
12
  5. Skip updates if: only internal implementation changed (no interface/dependency change), only comments/whitespace/formatting, only .prizm files changed. DO NOT skip test file changes or bug fixes — they may reveal TRAPS worth capturing in L2.
13
13
  6. If new directory qualifies as a module (per MODULE_DISCOVERY_CRITERIA) and matches no existing module: create L1 immediately, add to MODULE_INDEX. If the current diff includes Added or Modified source files in this module → also create L2 immediately with sections: MODULE, FILES, RESPONSIBILITY, INTERFACES, DATA_FLOW, KEY_FILES, DEPENDENCIES, RULES, TRAPS, DECISIONS. Otherwise defer L2.
14
14
  6a. **L2 gap check** (runs during full rescan mode only — when no git changes detected): For each existing module in MODULE_INDEX, check if L2 doc exists. If L2 is missing and the module has source files with meaningful logic (not trivial config/wrapper) → create L2 with sections: MODULE, FILES, RESPONSIBILITY, INTERFACES, DATA_FLOW, KEY_FILES, DEPENDENCIES, RULES, TRAPS, DECISIONS. This ensures Update fills documentation gaps left by previous sessions.
15
15
  7. Enforce size limits: L0 > 4KB -> consolidate. L1 > 4KB -> trim KEY_FILES descriptions, ensure RULES <= 3 entries. L2 > 5KB -> trim non-essential derived detail or split oversized cross-cutting detail.
16
+ 7a. Validate memory hygiene: no CHANGELOG/UPDATED fields, no workflow metadata, no L1 behavioral sections.
16
17
  8. Stage updated .prizm files via `git add .prizmkit/prizm-docs/`
17
18
 
18
19
  OUTPUT: List of updated/created/skipped docs with reasons.
@@ -5,12 +5,12 @@ Check format compliance and consistency of all .prizm docs.
5
5
  PRECONDITION: .prizmkit/prizm-docs/ exists.
6
6
 
7
7
  STEPS:
8
- 1. FORMAT CHECK: Verify all .prizm files use KEY: value format. Flag any prose paragraphs, code blocks (```), markdown headers (##), emoji, ASCII art, or horizontal rules. Flag TRAPS entries missing severity prefix ([CRITICAL], [HIGH], or [LOW]). Flag CHANGELOG/UPDATED/date-time metadata fields as auxiliary noise. Note: [REVIEW] preceding severity (e.g., `[REVIEW][HIGH]`) is a valid temporary staleness marker, not a format violation.
8
+ 1. FORMAT CHECK: Verify all .prizm files use KEY: value format. Flag any prose paragraphs, code blocks (```), markdown headers (##), emoji, ASCII art, or horizontal rules. Flag TRAPS entries missing severity prefix ([CRITICAL], [HIGH], or [LOW]). Flag CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, and `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths as auxiliary noise. Note: [REVIEW] preceding severity (e.g., `[REVIEW][HIGH]`) is a valid temporary staleness marker, not a format violation.
9
9
  2. SIZE CHECK: Verify size limits: L0 <= 4KB, L1 <= 4KB, L2 <= 5KB. Report files exceeding limits with current size.
10
10
  3. POINTER CHECK: Verify all arrow (->) references resolve to existing .prizm files. Report broken pointers.
11
11
  4. STALENESS CHECK: Compare git modification time of each .prizm file against source directory. Flag docs where source was modified more recently.
12
12
  5. COMPLETENESS CHECK: Verify root.prizm has all required fields (PRIZM_VERSION, PROJECT, LANG, MODULE_INDEX or MODULE_GROUPS). Verify L1 docs have MODULE, FILES, RESPONSIBILITY, DEPENDENCIES (no INTERFACES/TRAPS/DECISIONS). Verify L2 docs have MODULE, FILES, KEY_FILES, DEPENDENCIES, INTERFACES, TRAPS.
13
- 6. ANTI-PATTERN CHECK: Flag duplicate information across levels, implementation details in L0/L1, TODO items, session-specific context.
13
+ 6. ANTI-PATTERN CHECK: Flag duplicate information across levels, implementation details in L0/L1, TODO items, session-specific context, changelog/history sections, and workflow metadata.
14
14
  7. RULES HIERARCHY CHECK: Verify L1/L2 RULES do not contradict root.prizm RULES. L1/L2 may only supplement with module-specific exceptions.
15
15
  8. TRAPS STALENESS CHECK: For each L2 doc where TRAPS section has more than 8 entries, verify that TRAPS include staleness metadata (`STALE_IF:` or `REF:` fields). Flag TRAPS without any staleness metadata as `NEEDS_METADATA` — these are not auto-removed, but flagged for the next `/prizmkit-retrospective` to enrich with `STALE_IF:` globs or `REF:` hashes.
16
16
 
@@ -43,6 +43,8 @@ Synchronize `.prizmkit/prizm-docs/` structure with actual codebase changes from
43
43
 
44
44
  **Key outputs**: Synced L1 file counts, L2 INTERFACES/DATA_FLOW, DEPENDENCIES, and stale TRAPS cleanup.
45
45
 
46
+ **Memory hygiene**: `.prizmkit/prizm-docs/` must not contain CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths. Convert artifact-scoped wording into durable product/domain language before writing.
47
+
46
48
  ---
47
49
 
48
50
  ### Job 2: Knowledge Injection (conditional)
@@ -40,6 +40,8 @@ When writing TRAPS:
40
40
 
41
41
  **QUALITY GATE**: Every item must answer: "If a new AI session reads only `.prizmkit/prizm-docs/` and this entry, does it gain actionable understanding?" If not, discard. Do not record trivially observable code patterns — the AI can read the code directly.
42
42
 
43
+ **MEMORY HYGIENE GATE**: Before writing any `.prizmkit/prizm-docs/` entry, remove or translate workflow metadata. Never write CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths. If source artifacts say "fixed in B-001" or "implemented in F-003", write only the durable product/domain fact.
44
+
43
45
  **2c.** Inject into the correct `.prizmkit/prizm-docs/` file:
44
46
  - Module-level TRAPS/RULES/DECISIONS → the affected **L2** `.prizm` file. If the target L2 does not exist, create it first using the L2 GENERATION TEMPLATE before injecting knowledge. (TRAPS/DECISIONS/RULES belong in L2, not L1.)
45
47
  - Project-level RULES/PATTERNS → `root.prizm` (respect the current format — MODULE_INDEX or MODULE_GROUPS — do not convert between them during injection)
@@ -19,6 +19,8 @@ git diff HEAD --name-status
19
19
  - **L1**: Update FILES count, KEY_FILES (if major files added/removed), DEPENDENCIES (if module-level deps changed). **L1 does NOT contain INTERFACES, DATA_FLOW, TRAPS, or DECISIONS** — those belong in L2 only.
20
20
  - **L0 root.prizm**: Update MODULE_INDEX file counts only if counts changed. Update CROSS_CUTTING if cross-module concerns changed. Update only if structural change (module added/removed). **Preserve** any `PROJECT_BRIEF:` line — it is managed by prizmkit-init.
21
21
 
22
+ **Memory hygiene**: During L0/L1/L2 updates, do not write CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths. Update durable sections in place; git history is the change log.
23
+
22
24
  **1e.** If new directory qualifies as a module and matches no existing module:
23
25
  - A directory qualifies as a module if any of: contains source files forming a logical unit, contains entry/config/interface files, contains qualifying sub-modules, or is referenced by multiple modules as dependency.
24
26
  - Create L1 doc immediately, add to MODULE_INDEX.
@@ -45,7 +45,7 @@ If you believe the task is better suited for a different workflow, you MUST:
45
45
  - Codex → `AGENTS.md`
46
46
  - Claude Code → `CLAUDE.md`
47
47
  - CodeBuddy → `CODEBUDDY.md`
48
- - `both` → `CLAUDE.md` and `CODEBUDDY.md`; `all` all three files.
48
+ - `all` → all three files. Legacy manifests may contain `both`; treat it as read-only compatibility and update `CLAUDE.md` and `CODEBUDDY.md` only when encountered.
49
49
  - Only when the manifest is missing, fall back to installed PrizmKit-owned platform files such as `.codex/agents/*.toml`, `.claude/commands/prizm-kit.md`, or `.codebuddy/skills/prizm-kit/SKILL.md`. Do not treat a generic `.agents/` directory as Codex.
50
50
 
51
51
  **After planning is complete**, you MUST:
@@ -29,7 +29,7 @@ After Phase 2 (Confirm constraints and tech assumptions), before Phase 3 (Captur
29
29
  - `codex` → append to `AGENTS.md`
30
30
  - `claude` → append to `CLAUDE.md`
31
31
  - `codebuddy` → append to `CODEBUDDY.md`
32
- - `both` → append to `CLAUDE.md` and `CODEBUDDY.md`; `all` append to all three files.
32
+ - `all` → append to all three files. Legacy manifests may contain `both`; treat it as read-only compatibility and append to `CLAUDE.md` and `CODEBUDDY.md` only when encountered.
33
33
  - Only when the manifest is missing, fall back to PrizmKit-owned install artifacts: `.codex/agents/*.toml`, `.claude/commands/prizm-kit.md`, `.codebuddy/skills/prizm-kit/SKILL.md`.
34
34
  - Do not treat a generic `.agents/` directory as Codex; it may contain unrelated third-party skills.
35
35
  - If no platform can be determined, skip (no project instruction file).
@@ -135,7 +135,7 @@ Before questions, check optional context files (never block if absent):
135
135
  - `.prizmkit/config.json` (existing stack preferences and detected tech stack)
136
136
  - `.prizmkit/plans/project-brief.md` (project context from app-planner, if available)
137
137
  - existing `.prizmkit/plans/feature-list.json` (required for incremental mode)
138
- - Platform instruction file: use the `platform` field in `.prizmkit/manifest.json` as source of truth when present (`codex` → `AGENTS.md`, `claude` → `CLAUDE.md`, `codebuddy` → `CODEBUDDY.md`; `both`/`all` → read every matching file)
138
+ - Platform instruction file: use the `platform` field in `.prizmkit/manifest.json` as source of truth when present (`codex` → `AGENTS.md`, `claude` → `CLAUDE.md`, `codebuddy` → `CODEBUDDY.md`, `all` → read every matching file). Legacy manifests may contain `both`; treat it as read-only compatibility and read `CLAUDE.md` plus `CODEBUDDY.md` only when encountered.
139
139
  - If `.prizmkit/prizm-docs/root.prizm` is absent and the project has existing source code, scan the directory structure to understand the codebase layout:
140
140
  ```powershell
141
141
  Get-ChildItem -Path . -Directory -Recurse -Depth 2 -ErrorAction SilentlyContinue |
@@ -184,7 +184,7 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
184
184
  - For each module entry in MODULE_INDEX/MODULE_GROUPS, include keyword tags extracted from the module's source files — scan for: exported symbols, imported packages, domain terms in file/directory names. Format: `- module-name [tag1, tag2, tag3]: ...`. Tags help AI match user intent to relevant modules.
185
185
  - Generate L1 docs for top-level modules at `.prizmkit/prizm-docs/<M>.prizm` and for sub-modules at `.prizmkit/prizm-docs/<M>/<S>.prizm`
186
186
  - Skip L2 (lazy generation) — L2 is generated on first file modification, saving tokens upfront
187
- - Do not create auxiliary `changelog.prizm` or date/time metadata fields; git history is the source of change history
187
+ - Do not create auxiliary `changelog.prizm`, CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths; git history is the source of change history
188
188
 
189
189
  **Phase 6: Workspace Initialization**
190
190
  6a. Create `.prizmkit/` directory structure (if missing):
@@ -19,11 +19,13 @@ Handles re-init without losing user edits:
19
19
  | Field | Type | Description |
20
20
  |-------|------|-------------|
21
21
  | `adoption_mode` | string | `"passive"` \| `"advisory"` \| `"active"` |
22
- | `platform` | string | `"codebuddy"` \| `"claude"` \| `"both"` |
22
+ | `platform` | string | `"codebuddy"` \| `"claude"` \| `"codex"` \| `"all"` |
23
23
  | `tech_stack` | object | Detected or user-provided tech stack |
24
24
  | `tech_stack._auto_detected` | boolean | `true` if auto-detected, `false` if user-provided |
25
25
  | `detected_layers` | string[] | Development layers detected in the project. Written by prizmkit-init Phase 4.5. Used to determine available rule configuration options. Values: `frontend` / `backend` / `database` / `mobile`. Empty array when no layers detected or user skipped rules. Always updated on every init run based on fresh code detection — not gated by `_auto_detected` (see Merge Strategy above). |
26
26
 
27
+ Legacy manifests may still contain `both` for read-only migration compatibility. New config writes must use `codebuddy`, `claude`, `codex`, or `all`.
28
+
27
29
  ## Examples
28
30
 
29
31
  Fullstack project:
@@ -107,7 +107,7 @@ done
107
107
  find .prizmkit/prizm-docs -name '*.prizm' 2>/dev/null | while IFS= read -r prizm_file; do
108
108
  _basename=$(basename "$prizm_file")
109
109
  case "$_basename" in
110
- root.prizm|changelog.prizm|changelog-archive.prizm)
110
+ root.prizm)
111
111
  continue
112
112
  ;;
113
113
  esac
@@ -19,20 +19,69 @@ FILES=$(git diff --cached --name-only 2>/dev/null | grep '\.prizm$')
19
19
  [ -z "$FILES" ] && exit 0
20
20
 
21
21
  ERRORS=0
22
+ TEMP_FILES=""
23
+
24
+ cleanup() {
25
+ # shellcheck disable=SC2086
26
+ [ -z "$TEMP_FILES" ] || rm -f $TEMP_FILES
27
+ }
28
+ trap cleanup EXIT HUP INT TERM
29
+
22
30
  for FILE in $FILES; do
23
- [ -f "$FILE" ] || continue
31
+ git cat-file -e ":$FILE" 2>/dev/null || continue
32
+ CHECK_FILE=$(mktemp "${TMPDIR:-/tmp}/prizm-doc.XXXXXX") || exit 1
33
+ TEMP_FILES="$TEMP_FILES $CHECK_FILE"
34
+ git show ":$FILE" > "$CHECK_FILE" 2>/dev/null || continue
24
35
 
25
- if grep -qE '^#{1,6} ' "$FILE"; then
36
+ if grep -qE '^#{1,6} ' "$CHECK_FILE"; then
26
37
  echo "ERROR: $FILE contains markdown headers (##). Use KEY: value format." >&2
27
38
  ERRORS=$((ERRORS + 1))
28
39
  fi
29
40
 
30
- if grep -q '^```' "$FILE"; then
41
+ if grep -q '^```' "$CHECK_FILE"; then
31
42
  echo "ERROR: $FILE contains code blocks. Use file_path:line_number reference." >&2
32
43
  ERRORS=$((ERRORS + 1))
33
44
  fi
34
45
 
35
- SIZE=$(wc -c < "$FILE" | tr -d ' ')
46
+ BASE_NAME=$(basename "$FILE" | tr '[:upper:]' '[:lower:]')
47
+ case "$BASE_NAME" in
48
+ changelog.prizm|changelog-archive.prizm|*changelog*.prizm)
49
+ echo "ERROR: $FILE is auxiliary history. Use git history instead of changelog .prizm files." >&2
50
+ ERRORS=$((ERRORS + 1))
51
+ ;;
52
+ esac
53
+ if grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(CHANGELOG|CREATED([ _-]?(AT|ON|DATE))?|UPDATED([ _-]?(AT|ON|DATE))?|MODIFIED([ _-]?(AT|ON|DATE))?|LAST[ _-]?(UPDATED|MODIFIED)([ _-]?(AT|ON|DATE))?|DATE|TIMESTAMP):' "$CHECK_FILE"; then
54
+ echo "ERROR: $FILE contains auxiliary history metadata. Use durable KEY: value sections only; git tracks history." >&2
55
+ ERRORS=$((ERRORS + 1))
56
+ fi
57
+
58
+ if grep -qiE '(^|[^[:alnum:]_])([FBR]-[0-9]{3}|T-[0-9]{3})(-[A-Z])?([^[:alnum:]_]|$)' "$CHECK_FILE" \
59
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*((bug|feature|refactor|task)[ _-]?id|(session|run|pipeline|workflow)([ _-]?id)?)([[:space:]]*(:|=)|[[:space:]]+)' "$CHECK_FILE" \
60
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(bug|feature|refactor|task|session|run|pipeline|workflow)([ _-](bug|feature|refactor|task|session|run|pipeline|workflow|id))+([[:space:]]*(:|=)|[[:space:]]+)' "$CHECK_FILE"; then
61
+ echo "ERROR: $FILE contains workflow metadata. Translate IDs, branch names, worktree paths, and pipeline artifacts into durable product/domain language." >&2
62
+ ERRORS=$((ERRORS + 1))
63
+ fi
64
+
65
+ if sed 's#\\#/#g' "$CHECK_FILE" | grep -qE '(^|[^[:alnum:]_.-])\.prizmkit/(specs|dev-pipeline)($|/|[^[:alnum:]_.-])'; then
66
+ echo "ERROR: $FILE contains PrizmKit artifact paths. Translate specs/dev-pipeline references into durable product/domain language." >&2
67
+ ERRORS=$((ERRORS + 1))
68
+ fi
69
+
70
+ if sed 's#\\#/#g' "$CHECK_FILE" | grep -qE '(^|[^[:alnum:]_./:-])(/Users/|/home/|/tmp/|/private/tmp/|/var/folders/|/workspace/|/workspaces/|/opt/|/srv/|/root/|/var/tmp/|/Volumes/|/mnt/[A-Za-z]/|[A-Za-z]:/)'; then
71
+ echo "ERROR: $FILE contains absolute worktree paths. Use repository-relative source paths only." >&2
72
+ ERRORS=$((ERRORS + 1))
73
+ fi
74
+
75
+ if grep -qE '^[[:space:]]*[-*]?[[:space:]]*(BRANCH|BRANCH_NAME|CURRENT_BRANCH|SOURCE_BRANCH|BASE_BRANCH|HEAD_BRANCH|GIT_BRANCH|TARGET_BRANCH)[[:space:]_-]*(:|=|[[:space:]])' "$CHECK_FILE" \
76
+ || grep -qE '^[[:space:]]*[-*]?[[:space:]]*[A-Z][A-Z0-9_]*_BRANCH[[:space:]_-]*(:|=|[[:space:]])[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE" \
77
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(branch[ _-]*name|current[ _-]*branch|source[ _-]*branch|base[ _-]*branch|head[ _-]*branch|git[ _-]*branch|target[ _-]*branch)[[:space:]_-]*(:|=|[[:space:]])' "$CHECK_FILE" \
78
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*([[:alnum:]]+[ _-]+)+branch([[:space:]]*(:|=)|[[:space:]]+)[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE" \
79
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*branch[[:space:]_-]*(:|=|[[:space:]])[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE"; then
80
+ echo "ERROR: $FILE contains branch names. Describe durable product/domain context instead." >&2
81
+ ERRORS=$((ERRORS + 1))
82
+ fi
83
+
84
+ SIZE=$(wc -c < "$CHECK_FILE" | tr -d ' ')
36
85
  case "$FILE" in
37
86
  *root.prizm) LIMIT=4096 ;;
38
87
  *)
@@ -4,6 +4,13 @@
4
4
 
5
5
  MODE="${1:---staged}"
6
6
  ERRORS=0
7
+ TEMP_FILES=""
8
+
9
+ cleanup() {
10
+ # shellcheck disable=SC2086
11
+ [ -z "$TEMP_FILES" ] || rm -f $TEMP_FILES
12
+ }
13
+ trap cleanup EXIT HUP INT TERM
7
14
 
8
15
  # Not a prizm project — exit silently
9
16
  [ -f ".prizmkit/prizm-docs/root.prizm" ] || exit 0
@@ -22,22 +29,70 @@ fi
22
29
  [ -z "$FILES" ] && exit 0
23
30
 
24
31
  for FILE in $FILES; do
25
- [ -f "$FILE" ] || continue
32
+ CHECK_FILE="$FILE"
33
+ if [ "$MODE" = "--staged" ]; then
34
+ git cat-file -e ":$FILE" 2>/dev/null || continue
35
+ CHECK_FILE=$(mktemp "${TMPDIR:-/tmp}/prizm-doc.XXXXXX") || exit 1
36
+ TEMP_FILES="$TEMP_FILES $CHECK_FILE"
37
+ git show ":$FILE" > "$CHECK_FILE" 2>/dev/null || continue
38
+ else
39
+ [ -f "$FILE" ] || continue
40
+ fi
26
41
 
27
42
  # Check markdown headers
28
- if grep -qE '^#{1,6} ' "$FILE"; then
43
+ if grep -qE '^#{1,6} ' "$CHECK_FILE"; then
29
44
  echo "ERROR: $FILE contains markdown headers (##). Use KEY: value format." >&2
30
45
  ERRORS=$((ERRORS + 1))
31
46
  fi
32
47
 
33
48
  # Check code blocks
34
- if grep -q '^```' "$FILE"; then
49
+ if grep -q '^```' "$CHECK_FILE"; then
35
50
  echo "ERROR: $FILE contains code blocks. Use file_path:line_number reference." >&2
36
51
  ERRORS=$((ERRORS + 1))
37
52
  fi
38
53
 
54
+ # Check auxiliary history fields and files
55
+ BASE_NAME=$(basename "$FILE" | tr '[:upper:]' '[:lower:]')
56
+ case "$BASE_NAME" in
57
+ changelog.prizm|changelog-archive.prizm|*changelog*.prizm)
58
+ echo "ERROR: $FILE is auxiliary history. Use git history instead of changelog .prizm files." >&2
59
+ ERRORS=$((ERRORS + 1))
60
+ ;;
61
+ esac
62
+ if grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(CHANGELOG|CREATED([ _-]?(AT|ON|DATE))?|UPDATED([ _-]?(AT|ON|DATE))?|MODIFIED([ _-]?(AT|ON|DATE))?|LAST[ _-]?(UPDATED|MODIFIED)([ _-]?(AT|ON|DATE))?|DATE|TIMESTAMP):' "$CHECK_FILE"; then
63
+ echo "ERROR: $FILE contains auxiliary history metadata. Use durable KEY: value sections only; git tracks history." >&2
64
+ ERRORS=$((ERRORS + 1))
65
+ fi
66
+
67
+ # Check workflow metadata leakage
68
+ if grep -qiE '(^|[^[:alnum:]_])([FBR]-[0-9]{3}|T-[0-9]{3})(-[A-Z])?([^[:alnum:]_]|$)' "$CHECK_FILE" \
69
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*((bug|feature|refactor|task)[ _-]?id|(session|run|pipeline|workflow)([ _-]?id)?)([[:space:]]*(:|=)|[[:space:]]+)' "$CHECK_FILE" \
70
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(bug|feature|refactor|task|session|run|pipeline|workflow)([ _-](bug|feature|refactor|task|session|run|pipeline|workflow|id))+([[:space:]]*(:|=)|[[:space:]]+)' "$CHECK_FILE"; then
71
+ echo "ERROR: $FILE contains workflow metadata. Translate IDs, branch names, worktree paths, and pipeline artifacts into durable product/domain language." >&2
72
+ ERRORS=$((ERRORS + 1))
73
+ fi
74
+
75
+ if sed 's#\\#/#g' "$CHECK_FILE" | grep -qE '(^|[^[:alnum:]_.-])\.prizmkit/(specs|dev-pipeline)($|/|[^[:alnum:]_.-])'; then
76
+ echo "ERROR: $FILE contains PrizmKit artifact paths. Translate specs/dev-pipeline references into durable product/domain language." >&2
77
+ ERRORS=$((ERRORS + 1))
78
+ fi
79
+
80
+ if sed 's#\\#/#g' "$CHECK_FILE" | grep -qE '(^|[^[:alnum:]_./:-])(/Users/|/home/|/tmp/|/private/tmp/|/var/folders/|/workspace/|/workspaces/|/opt/|/srv/|/root/|/var/tmp/|/Volumes/|/mnt/[A-Za-z]/|[A-Za-z]:/)'; then
81
+ echo "ERROR: $FILE contains absolute worktree paths. Use repository-relative source paths only." >&2
82
+ ERRORS=$((ERRORS + 1))
83
+ fi
84
+
85
+ if grep -qE '^[[:space:]]*[-*]?[[:space:]]*(BRANCH|BRANCH_NAME|CURRENT_BRANCH|SOURCE_BRANCH|BASE_BRANCH|HEAD_BRANCH|GIT_BRANCH|TARGET_BRANCH)[[:space:]_-]*(:|=|[[:space:]])' "$CHECK_FILE" \
86
+ || grep -qE '^[[:space:]]*[-*]?[[:space:]]*[A-Z][A-Z0-9_]*_BRANCH[[:space:]_-]*(:|=|[[:space:]])[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE" \
87
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*(branch[ _-]*name|current[ _-]*branch|source[ _-]*branch|base[ _-]*branch|head[ _-]*branch|git[ _-]*branch|target[ _-]*branch)[[:space:]_-]*(:|=|[[:space:]])' "$CHECK_FILE" \
88
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*([[:alnum:]]+[ _-]+)+branch([[:space:]]*(:|=)|[[:space:]]+)[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE" \
89
+ || grep -qiE '^[[:space:]]*[-*]?[[:space:]]*branch[[:space:]_-]*(:|=|[[:space:]])[[:space:]]*(main|master|develop|development|trunk|[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+|(feature|fix|bugfix|hotfix|release|chore|refactor|task|dev)[/_-][A-Za-z0-9._/-]+)[[:space:]]*(#.*)?$' "$CHECK_FILE"; then
90
+ echo "ERROR: $FILE contains branch names. Describe durable product/domain context instead." >&2
91
+ ERRORS=$((ERRORS + 1))
92
+ fi
93
+
39
94
  # Size limits — determine level by path depth
40
- SIZE=$(wc -c < "$FILE" | tr -d ' ')
95
+ SIZE=$(wc -c < "$CHECK_FILE" | tr -d ' ')
41
96
  case "$FILE" in
42
97
  *root.prizm)
43
98
  LIMIT=4096; LEVEL="L0"
@@ -51,7 +106,7 @@ for FILE in $FILES; do
51
106
  HINT="Move implementation details to L2."
52
107
  else
53
108
  LIMIT=5120; LEVEL="L2"
54
- HINT="Archive CHANGELOG entries older than 90 days."
109
+ HINT="Remove stale or trivially derivable entries."
55
110
  fi
56
111
  ;;
57
112
  esac
@@ -64,11 +119,11 @@ for FILE in $FILES; do
64
119
  # Required fields in root.prizm
65
120
  case "$FILE" in
66
121
  *root.prizm)
67
- if ! grep -q 'PRIZM_VERSION:' "$FILE"; then
122
+ if ! grep -q 'PRIZM_VERSION:' "$CHECK_FILE"; then
68
123
  echo "ERROR: $FILE missing required field PRIZM_VERSION:" >&2
69
124
  ERRORS=$((ERRORS + 1))
70
125
  fi
71
- if ! grep -q 'MODULE_INDEX:' "$FILE"; then
126
+ if ! grep -q 'MODULE_INDEX:' "$CHECK_FILE"; then
72
127
  echo "ERROR: $FILE missing required field MODULE_INDEX:" >&2
73
128
  ERRORS=$((ERRORS + 1))
74
129
  fi
@@ -19,7 +19,8 @@ This project uses PrizmKit with the Prizm documentation system for AI-optimized
19
19
  - All `.prizm` files use KEY: value format, not prose
20
20
  - Size limits: L0 = 4KB, L1 = 4KB, L2 = 5KB
21
21
  - Arrow notation (->) indicates load pointers to other .prizm docs
22
- - DECISIONS and CHANGELOG are append-only (never delete entries)
22
+ - Memory files must not contain CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths
23
+ - Update durable sections in place; git history is the change log
23
24
  - No date/time fields — git is the authoritative source for temporal info
24
25
 
25
26
  ### Creating New L2 Docs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.60",
3
+ "version": "1.1.61",
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": {
package/src/config.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  confirmTeamMode,
54
54
  confirmPipeline,
55
55
  } from './prompts.js';
56
- import { expandPlatforms, platformLabel, projectMemoryFile } from './platforms.js';
56
+ import { expandPlatforms, isKnownPlatform, platformLabel, projectMemoryFile } from './platforms.js';
57
57
  import { normalizeRuntime, runtimeLabel } from './runtimes.js';
58
58
 
59
59
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -362,6 +362,10 @@ export async function runConfig(directory, options = {}) {
362
362
  aiCli: userConfig.ai_cli || oldManifest.options?.aiCli || '',
363
363
  };
364
364
 
365
+ if (options.platform !== undefined && !isKnownPlatform(options.platform)) {
366
+ throw new Error(`Unknown platform "${options.platform}". Expected codebuddy, claude, codex, or all.`);
367
+ }
368
+
365
369
  // Display current config
366
370
  console.log(chalk.bold(' 当前配置:'));
367
371
  console.log(` 平台: ${chalk.cyan(platformLabel(currentConfig.platform))}`);
@@ -431,6 +435,10 @@ export async function runConfig(directory, options = {}) {
431
435
  }
432
436
  }
433
437
 
438
+ if (!isKnownPlatform(newConfig.platform)) {
439
+ throw new Error(`Unknown platform "${newConfig.platform}". Expected codebuddy, claude, codex, or all.`);
440
+ }
441
+
434
442
  // ── Phase 3: COMPUTE DIFF ────────────────────────────────────────────────
435
443
 
436
444
  const changes = [];
@@ -44,8 +44,6 @@ export function detectPlatform() {
44
44
  let suggested = 'codebuddy';
45
45
  if (cbc && claude && codex) {
46
46
  suggested = 'all';
47
- } else if ((cbc) && (claude)) {
48
- suggested = 'both';
49
47
  } else if (codex && !cbc && !claude) {
50
48
  suggested = 'codex';
51
49
  } else if ((claude) && !cbc) {
@@ -21,7 +21,7 @@ import { expandPlatforms } from './platforms.js';
21
21
  * skill.repo {string} - GitHub short ref (e.g. "pbakaus/impeccable") or full URL
22
22
  * skill.name {string} - Skill name (used for display / single-skill mode)
23
23
  * skill.installAll {boolean} - If true, install all skills from the repo
24
- * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'both' | 'all'
24
+ * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
25
25
  * @param {string} projectRoot - Target project root
26
26
  * @param {boolean} dryRun
27
27
  */
@@ -105,7 +105,7 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
105
105
  * Call this once after all external skills have been installed.
106
106
  *
107
107
  * @param {string} projectRoot
108
- * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'both' | 'all'
108
+ * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
109
109
  */
110
110
  export async function cleanExternalSkillArtifacts(projectRoot, platform) {
111
111
  const keepDirs = new Set(
package/src/index.js CHANGED
@@ -106,10 +106,6 @@ export async function runScaffold(directory, options) {
106
106
  name: `Codex (AGENTS.md + .agents/skills + .codex/)${detected.codex ? chalk.green(' ← 已检测到 codex') : ''}`,
107
107
  value: 'codex',
108
108
  },
109
- {
110
- name: '同时安装 CodeBuddy + Claude Code',
111
- value: 'both',
112
- },
113
109
  {
114
110
  name: '同时安装全部平台',
115
111
  value: 'all',
package/src/manifest.js CHANGED
@@ -47,7 +47,7 @@ export async function writeManifest(projectRoot, data) {
47
47
  * Build a manifest object from installation config.
48
48
  * @param {Object} params
49
49
  * @param {string} params.version - PrizmKit version
50
- * @param {string} params.platform - 'codebuddy' | 'claude' | 'codex' | 'both' | 'all'
50
+ * @param {string} params.platform - 'codebuddy' | 'claude' | 'codex' | 'all' ('both' supported only for legacy manifests)
51
51
  * @param {string} [params.runtime] - 'unix' | 'windows'
52
52
  * @param {string} params.suite - skill suite name
53
53
  * @param {string[]} params.skills - resolved skill name list
package/src/platforms.js CHANGED
@@ -1,11 +1,13 @@
1
1
  export const PLATFORM_IDS = ['codebuddy', 'claude', 'codex'];
2
2
  export const PLATFORM_GROUPS = {
3
- both: ['codebuddy', 'claude'],
4
3
  all: PLATFORM_IDS,
5
4
  };
5
+ const LEGACY_PLATFORM_GROUPS = {
6
+ both: ['codebuddy', 'claude'],
7
+ };
6
8
 
7
9
  export function expandPlatforms(platform) {
8
- return PLATFORM_GROUPS[platform] || [platform];
10
+ return PLATFORM_GROUPS[platform] || LEGACY_PLATFORM_GROUPS[platform] || [platform];
9
11
  }
10
12
 
11
13
  export function isKnownPlatform(platform) {
@@ -13,8 +15,8 @@ export function isKnownPlatform(platform) {
13
15
  }
14
16
 
15
17
  export function platformLabel(platform) {
16
- if (platform === 'both') return 'CodeBuddy + Claude Code';
17
- if (platform === 'all') return 'CodeBuddy + Claude Code + Codex';
18
+ if (platform === 'both') return 'legacy codebuddy/claude group';
19
+ if (platform === 'all') return 'All platforms (CodeBuddy, Claude Code, Codex)';
18
20
  if (platform === 'codebuddy') return 'CodeBuddy';
19
21
  if (platform === 'claude') return 'Claude Code';
20
22
  if (platform === 'codex') return 'Codex';
package/src/prompts.js CHANGED
@@ -8,6 +8,16 @@ import chalk from 'chalk';
8
8
  import { platformLabel } from './platforms.js';
9
9
  import { normalizeRuntime } from './runtimes.js';
10
10
 
11
+ let selectPlatformPrompt = select;
12
+
13
+ export function __setSelectPlatformPromptForTests(promptFn) {
14
+ selectPlatformPrompt = promptFn;
15
+ }
16
+
17
+ export function __resetSelectPlatformPromptForTests() {
18
+ selectPlatformPrompt = select;
19
+ }
20
+
11
21
  export async function selectRuntime(currentRuntime) {
12
22
  return select({
13
23
  message: '选择运行系统 (决定安装 macOS/Linux 或 Windows 版本的流水线与编排技能):',
@@ -20,13 +30,23 @@ export async function selectRuntime(currentRuntime) {
20
30
  }
21
31
 
22
32
  /**
23
- * Select target platform (codebuddy, claude, codex, both, or all).
33
+ * Select target platform (codebuddy, claude, codex, or all).
24
34
  * @param {string} currentPlatform - current platform value
25
35
  * @param {Object} detected - detected environment info (e.g., { cbc: true, claude: false })
26
36
  * @returns {Promise<string>}
27
37
  */
28
38
  export async function selectPlatform(currentPlatform, detected = {}) {
29
- const platform = await select({
39
+ const selectablePlatforms = ['codebuddy', 'claude', 'codex', 'all'];
40
+ const detectedDefault = selectablePlatforms.includes(detected.suggested)
41
+ ? detected.suggested
42
+ : 'claude';
43
+ let defaultPlatform = detectedDefault;
44
+ if (currentPlatform === 'both') {
45
+ defaultPlatform = 'all';
46
+ } else if (selectablePlatforms.includes(currentPlatform)) {
47
+ defaultPlatform = currentPlatform;
48
+ }
49
+ const platform = await selectPlatformPrompt({
30
50
  message: '选择目标平台 (决定安装的目录结构):',
31
51
  choices: [
32
52
  {
@@ -41,16 +61,12 @@ export async function selectPlatform(currentPlatform, detected = {}) {
41
61
  name: `Codex (AGENTS.md + .agents/skills + .codex/)${detected.codex ? chalk.green(' ← 已检测到 codex') : ''}`,
42
62
  value: 'codex',
43
63
  },
44
- {
45
- name: '同时安装 CodeBuddy + Claude Code',
46
- value: 'both',
47
- },
48
64
  {
49
65
  name: `同时安装全部平台 (${platformLabel('all')})`,
50
66
  value: 'all',
51
67
  },
52
68
  ],
53
- default: currentPlatform || detected.suggested || 'claude',
69
+ default: defaultPlatform,
54
70
  });
55
71
  return platform;
56
72
  }
package/src/scaffold.js CHANGED
@@ -574,6 +574,135 @@ max_depth = 1
574
574
  /**
575
575
  * 安装 git pre-commit hook(prizm 格式校验)
576
576
  */
577
+ const PRIZMKIT_HOOK_MARKER_START = '# PRIZMKIT_PRE_COMMIT_HOOK_START';
578
+ const PRIZMKIT_HOOK_MARKER_END = '# PRIZMKIT_PRE_COMMIT_HOOK_END';
579
+
580
+ function escapeRegExp(value) {
581
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
582
+ }
583
+
584
+ function ensureTrailingNewline(value) {
585
+ return value.endsWith('\n') ? value : `${value}\n`;
586
+ }
587
+
588
+ function buildStandalonePrizmHook(templateContent) {
589
+ const content = templateContent.trimEnd();
590
+ const firstNewline = content.indexOf('\n');
591
+
592
+ if (content.startsWith('#!') && firstNewline !== -1) {
593
+ return [
594
+ content.slice(0, firstNewline),
595
+ PRIZMKIT_HOOK_MARKER_START,
596
+ content.slice(firstNewline + 1),
597
+ PRIZMKIT_HOOK_MARKER_END,
598
+ '',
599
+ ].join('\n');
600
+ }
601
+
602
+ return [
603
+ PRIZMKIT_HOOK_MARKER_START,
604
+ content,
605
+ PRIZMKIT_HOOK_MARKER_END,
606
+ '',
607
+ ].join('\n');
608
+ }
609
+
610
+ function buildAppendedPrizmHook(templateContent) {
611
+ return [
612
+ PRIZMKIT_HOOK_MARKER_START,
613
+ templateContent.trimEnd(),
614
+ PRIZMKIT_HOOK_MARKER_END,
615
+ '',
616
+ ].join('\n');
617
+ }
618
+
619
+ function insertPrizmHookIntoUserHook(userHookContent, prizmHookBlock) {
620
+ const content = userHookContent.trimEnd();
621
+ const terminalSuccessExitPattern = /(^|\n)([ \t]*exit[ \t]+0[ \t]*(?:#.*)?)[ \t]*$/;
622
+ const match = content.match(terminalSuccessExitPattern);
623
+
624
+ if (!match) {
625
+ return `${content}\n\n${prizmHookBlock}`;
626
+ }
627
+
628
+ const exitLineStart = match.index + match[1].length;
629
+ const beforeExit = content.slice(0, exitLineStart).trimEnd();
630
+ const exitLine = match[2];
631
+
632
+ return [
633
+ beforeExit,
634
+ prizmHookBlock.trimEnd(),
635
+ exitLine,
636
+ '',
637
+ ].filter((part, index) => index > 0 || part.length > 0).join('\n\n');
638
+ }
639
+
640
+ function findLegacyPrizmHookEnd(content, markerIndex) {
641
+ const afterMarker = content.slice(markerIndex);
642
+ const exitMatch = afterMarker.match(/(^|\n)[ \t]*exit[ \t]+0[ \t]*(\r?\n|$)/);
643
+ if (exitMatch) {
644
+ return markerIndex + exitMatch.index + exitMatch[0].length;
645
+ }
646
+
647
+ const markerLineEnd = content.indexOf('\n', markerIndex);
648
+ const searchStart = markerLineEnd === -1 ? content.length : markerLineEnd + 1;
649
+ const blankLineIndex = content.slice(searchStart).search(/\r?\n[ \t]*(\r?\n|$)/);
650
+ if (blankLineIndex !== -1) {
651
+ return searchStart + blankLineIndex + 1;
652
+ }
653
+
654
+ return content.length;
655
+ }
656
+
657
+ function stripLegacyPrizmHookBlocks(existing) {
658
+ const legacyMarker = '# prizm-pre-commit.sh';
659
+ let cleaned = '';
660
+ let cursor = 0;
661
+
662
+ while (cursor < existing.length) {
663
+ const markerIndex = existing.indexOf(legacyMarker, cursor);
664
+ if (markerIndex === -1) {
665
+ cleaned += existing.slice(cursor);
666
+ break;
667
+ }
668
+
669
+ const markerLineStart = existing.lastIndexOf('\n', markerIndex) + 1;
670
+ let blockStart = markerLineStart;
671
+ if (markerLineStart > 0) {
672
+ const previousLineEnd = markerLineStart - 1;
673
+ const previousLineStart = existing.lastIndexOf('\n', previousLineEnd - 1) + 1;
674
+ const previousLine = existing.slice(previousLineStart, previousLineEnd).replace(/\r$/, '');
675
+ if (previousLine.startsWith('#!')) {
676
+ blockStart = previousLineStart;
677
+ }
678
+ }
679
+
680
+ cleaned += existing.slice(cursor, blockStart);
681
+ cursor = findLegacyPrizmHookEnd(existing, markerIndex);
682
+ }
683
+
684
+ return cleaned;
685
+ }
686
+
687
+ function stripManagedPrizmHook(existing) {
688
+ const markedBlockPattern = new RegExp(
689
+ `(^|\\n)${escapeRegExp(PRIZMKIT_HOOK_MARKER_START)}[\\s\\S]*?${escapeRegExp(PRIZMKIT_HOOK_MARKER_END)}\\n?`,
690
+ 'g',
691
+ );
692
+
693
+ let cleaned = existing.replace(markedBlockPattern, '\n');
694
+ cleaned = stripLegacyPrizmHookBlocks(cleaned);
695
+
696
+ return cleaned.replace(/\n{3,}/g, '\n\n').trimEnd();
697
+ }
698
+
699
+ function hasUserHookContent(content) {
700
+ return content
701
+ .replace(/^#!.*(\r?\n|$)/, '')
702
+ .trim()
703
+ .length > 0;
704
+ }
705
+
577
706
  export async function installGitHook(projectRoot, dryRun, runtime = 'unix') {
578
707
  const gitDir = path.join(projectRoot, '.git');
579
708
  const normalizedRuntime = normalizeRuntime(runtime);
@@ -591,21 +720,17 @@ export async function installGitHook(projectRoot, dryRun, runtime = 'unix') {
591
720
  const templatePath = path.join(getTemplatesDir(), 'hooks', 'prizm-pre-commit.sh');
592
721
  const hooksDir = path.join(gitDir, 'hooks');
593
722
  const preCommitPath = path.join(hooksDir, 'pre-commit');
723
+ const hookContent = fs.readFileSync(templatePath, 'utf8');
594
724
 
595
725
  if (normalizedRuntime === RUNTIME_WINDOWS) {
596
- if (fs.existsSync(preCommitPath) && fs.existsSync(templatePath)) {
726
+ if (fs.existsSync(preCommitPath)) {
597
727
  const existing = fs.readFileSync(preCommitPath, 'utf8');
598
- const shellTemplate = fs.readFileSync(templatePath, 'utf8');
599
- if (existing === shellTemplate) {
728
+ const cleaned = stripManagedPrizmHook(existing);
729
+ if (!hasUserHookContent(cleaned)) {
600
730
  await fs.remove(preCommitPath);
601
731
  console.log(chalk.gray(' • .git/hooks/pre-commit shell hook removed for Windows runtime'));
602
- } else if (existing.includes(shellTemplate)) {
603
- const cleaned = existing.replace(shellTemplate, '').replace(/\n{3,}/g, '\n\n').trimEnd();
604
- if (cleaned) {
605
- fs.writeFileSync(preCommitPath, `${cleaned}\n`);
606
- } else {
607
- await fs.remove(preCommitPath);
608
- }
732
+ } else if (cleaned !== existing.trimEnd()) {
733
+ fs.writeFileSync(preCommitPath, ensureTrailingNewline(cleaned));
609
734
  console.log(chalk.gray(' • PrizmKit shell hook block removed for Windows runtime'));
610
735
  }
611
736
  }
@@ -617,11 +742,15 @@ export async function installGitHook(projectRoot, dryRun, runtime = 'unix') {
617
742
 
618
743
  if (fs.existsSync(preCommitPath)) {
619
744
  const existing = fs.readFileSync(preCommitPath, 'utf8');
620
- if (existing.includes('PrizmKit')) return;
621
- const hookContent = fs.readFileSync(templatePath, 'utf8');
622
- fs.writeFileSync(preCommitPath, existing + '\n\n' + hookContent);
745
+ const cleaned = stripManagedPrizmHook(existing);
746
+ if (hasUserHookContent(cleaned)) {
747
+ fs.writeFileSync(preCommitPath, insertPrizmHookIntoUserHook(cleaned, buildAppendedPrizmHook(hookContent)));
748
+ } else {
749
+ fs.writeFileSync(preCommitPath, buildStandalonePrizmHook(hookContent));
750
+ }
751
+ fs.chmodSync(preCommitPath, 0o755);
623
752
  } else {
624
- await fs.copy(templatePath, preCommitPath);
753
+ fs.writeFileSync(preCommitPath, buildStandalonePrizmHook(hookContent));
625
754
  fs.chmodSync(preCommitPath, 0o755);
626
755
  }
627
756
 
@@ -1153,7 +1282,7 @@ export const EXTRAS_REGISTRY = {
1153
1282
  /**
1154
1283
  * 执行纯净安装
1155
1284
  * @param {Object} config
1156
- * @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'both' | 'all'
1285
+ * @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'all'
1157
1286
  * @param {string} [config.runtime] - 'unix' | 'windows'
1158
1287
  * @param {string} config.skills - 'core' | 'minimal' | 'recommended:<type>'
1159
1288
  * @param {boolean} config.team - 是否启用团队模式
@@ -1171,7 +1300,7 @@ export async function scaffold(config) {
1171
1300
  const { platform, skills, team, pipeline, rules, aiCli, externalSkills, playwrightCli, openCli, openCliAutoDownload, projectRoot, dryRun } = config;
1172
1301
  const runtime = normalizeRuntime(config.runtime);
1173
1302
  if (!isKnownPlatform(platform)) {
1174
- throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, both, or all.`);
1303
+ throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, or all.`);
1175
1304
  }
1176
1305
  const platforms = expandPlatforms(platform);
1177
1306
 
package/src/upgrade.js CHANGED
@@ -229,7 +229,7 @@ export async function runUpgrade(directory, options = {}) {
229
229
  // (e.g. legacy installs before manifest tracked platform).
230
230
  let platform;
231
231
  if (oldManifest?.platform) {
232
- platform = oldManifest.platform;
232
+ platform = oldManifest.platform === 'both' ? 'all' : oldManifest.platform;
233
233
  } else {
234
234
  // Fallback: detect from filesystem for legacy manifests without platform field
235
235
  const hasPlatformFiles = async (dir) => {
@@ -247,11 +247,14 @@ export async function runUpgrade(directory, options = {}) {
247
247
  const hasCodex = await hasPlatformFiles(path.join(projectRoot, '.agents', 'skills'))
248
248
  || await hasPlatformFiles(path.join(projectRoot, '.codex', 'agents'));
249
249
 
250
- if (hasClaude && hasCodeBuddy && hasCodex) platform = 'all';
251
- else if (hasClaude && hasCodeBuddy) platform = 'both';
252
- else if (hasCodex) platform = 'codex';
253
- else if (hasCodeBuddy) platform = 'codebuddy';
254
- else if (hasClaude) platform = 'claude';
250
+ const detectedPlatforms = [
251
+ hasCodeBuddy && 'codebuddy',
252
+ hasClaude && 'claude',
253
+ hasCodex && 'codex',
254
+ ].filter(Boolean);
255
+
256
+ if (detectedPlatforms.length > 1) platform = 'all';
257
+ else if (detectedPlatforms.length === 1) platform = detectedPlatforms[0];
255
258
  else platform = 'claude'; // ultimate fallback
256
259
  }
257
260