@qijenchen/design-system 0.1.0-beta.10 → 0.1.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +201 -0
- package/README.md +7 -15
- package/cli-init.mjs +90 -0
- package/ds-canonical/commands/README.md +26 -0
- package/ds-canonical/commands/gov-status.md +79 -0
- package/ds-canonical/hooks/README.md +145 -0
- package/ds-canonical/hooks/_log-fire.sh +44 -0
- package/ds-canonical/hooks/block_prototype_imports.py +111 -0
- package/ds-canonical/hooks/check_app_shell_primary_header_consistency.sh +68 -0
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +88 -0
- package/ds-canonical/hooks/check_audit_sample_escape.sh +73 -0
- package/ds-canonical/hooks/check_benchmark_citation.sh +106 -0
- package/ds-canonical/hooks/check_canonical_propagation.sh +189 -0
- package/ds-canonical/hooks/check_chrome_header_handcraft.sh +70 -0
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +83 -0
- package/ds-canonical/hooks/check_codex_collab_5step.sh +108 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +117 -0
- package/ds-canonical/hooks/check_dim_count_drift.sh +72 -0
- package/ds-canonical/hooks/check_field_controls_contracts.sh +110 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +205 -0
- package/ds-canonical/hooks/check_file_size_budget.sh +60 -0
- package/ds-canonical/hooks/check_header_with_tabs_border.sh +87 -0
- package/ds-canonical/hooks/check_main_branch_workbench.sh +93 -0
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +165 -0
- package/ds-canonical/hooks/check_opacity_token_usage.sh +149 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +194 -0
- package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +56 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +53 -0
- package/ds-canonical/hooks/check_propose_plain_chinese.sh +74 -0
- package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +70 -0
- package/ds-canonical/hooks/check_select_all_canonical.sh +58 -0
- package/ds-canonical/hooks/check_solo_workflow.sh +258 -0
- package/ds-canonical/hooks/check_spec_class_drift.sh +88 -0
- package/ds-canonical/hooks/check_story_invariants.sh +612 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +105 -0
- package/ds-canonical/hooks/check_tab_lg_chrome_header_equal.sh +66 -0
- package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +104 -0
- package/ds-canonical/hooks/enforce_home_charter.sh +44 -0
- package/ds-canonical/hooks/inject_pending_self_audit.sh +204 -0
- package/ds-canonical/hooks/lib/_approval_re.sh +33 -0
- package/ds-canonical/hooks/lib/_code_quality.sh +73 -0
- package/ds-canonical/hooks/lib/_cva_default_sync.sh +69 -0
- package/ds-canonical/hooks/lib/_governance_coverage_check.sh +49 -0
- package/ds-canonical/hooks/lib/_hardcoded_strings.sh +163 -0
- package/ds-canonical/hooks/lib/_layout_space_canonical.sh +56 -0
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +141 -0
- package/ds-canonical/hooks/lib/_person_data_richness.sh +42 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +48 -0
- package/ds-canonical/hooks/lib/_token_hygiene.sh +95 -0
- package/ds-canonical/hooks/log_governance_fires.sh +50 -0
- package/ds-canonical/hooks/log_skill_invokes.sh +41 -0
- package/ds-canonical/hooks/post_edit_dispatcher.sh +62 -0
- package/ds-canonical/hooks/retired/check_anatomy_section_numbering.sh +106 -0
- package/ds-canonical/hooks/retired/check_avatar_hovercard.sh +90 -0
- package/ds-canonical/hooks/retired/check_button_icon_literal.sh.retired-2026-04-28 +38 -0
- package/ds-canonical/hooks/retired/check_container_breathing.sh +142 -0
- package/ds-canonical/hooks/retired/check_governance_compliance.sh +61 -0
- package/ds-canonical/hooks/retired/check_icon_only_padding_formula.sh +104 -0
- package/ds-canonical/hooks/retired/check_item_content_primitive.sh +150 -0
- package/ds-canonical/hooks/retired/check_item_list_gap.sh +153 -0
- package/ds-canonical/hooks/retired/check_sideoffset_canonical.sh +65 -0
- package/ds-canonical/hooks/retired/check_spec_iteration_tag.sh +87 -0
- package/ds-canonical/hooks/retired/check_ssot_consultation.sh +88 -0
- package/ds-canonical/hooks/retired/check_sync_update.sh +20 -0
- package/ds-canonical/hooks/retired/check_third_party_dom_verified.sh +95 -0
- package/ds-canonical/hooks/retired/enforce_home_charter.sh +125 -0
- package/ds-canonical/hooks/retired/post_edit_canonical_interrogate.sh +109 -0
- package/ds-canonical/hooks/retired/pre_edit_spec_check.sh +68 -0
- package/ds-canonical/hooks/retired/pre_new_component_spec.sh +39 -0
- package/ds-canonical/hooks/retired/pre_write_subsumption_check.sh +112 -0
- package/ds-canonical/hooks/retired/stop_meta_self_audit.sh.retired-2026-05-13 +76 -0
- package/ds-canonical/hooks/retired/tests/test_check_anatomy_section_numbering.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_avatar_hovercard.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_container_breathing.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_governance_compliance.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_icon_only_padding_formula.sh +79 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_content_primitive.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_list_gap.sh +163 -0
- package/ds-canonical/hooks/retired/tests/test_check_sideoffset_canonical.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_spec_iteration_tag.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_ssot_consultation.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_sync_update.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_third_party_dom_verified.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_enforce_home_charter.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_edit_spec_check.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_new_component_spec.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_write_subsumption_check.sh +63 -0
- package/ds-canonical/hooks/session_start_governance_check.sh +263 -0
- package/ds-canonical/hooks/stop_passive_logging.sh +322 -0
- package/ds-canonical/hooks/stop_self_audit.sh +450 -0
- package/ds-canonical/hooks/tests/KNOWN-BROKEN.md +15 -0
- package/ds-canonical/hooks/tests/run-all.sh +76 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +143 -0
- package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +140 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +93 -0
- package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +133 -0
- package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +123 -0
- package/ds-canonical/hooks/tests/test_check_code_quality.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +96 -0
- package/ds-canonical/hooks/tests/test_check_cva_default_sync.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +122 -0
- package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +98 -0
- package/ds-canonical/hooks/tests/test_check_field_controls_contracts.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +194 -0
- package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +32 -0
- package/ds-canonical/hooks/tests/test_check_hardcoded_strings.sh +14 -0
- package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_layout_space_canonical.sh +73 -0
- package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +147 -0
- package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +136 -0
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +148 -0
- package/ds-canonical/hooks/tests/test_check_peoplepicker_ssot_drift.sh +108 -0
- package/ds-canonical/hooks/tests/test_check_person_data_richness.sh +58 -0
- package/ds-canonical/hooks/tests/test_check_pixel_quantified_audit.sh +142 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +117 -0
- package/ds-canonical/hooks/tests/test_check_select_all_canonical.sh +125 -0
- package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +201 -0
- package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +135 -0
- package/ds-canonical/hooks/tests/test_check_story_anatomy.sh.broken +197 -0
- package/ds-canonical/hooks/tests/test_check_story_category.sh.broken +187 -0
- package/ds-canonical/hooks/tests/test_check_story_compile_drift.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +209 -0
- package/ds-canonical/hooks/tests/test_check_story_name_jargon.sh.broken +53 -0
- package/ds-canonical/hooks/tests/test_check_story_slot_split.sh +156 -0
- package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +176 -0
- package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +138 -0
- package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +21 -0
- package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +169 -0
- package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +77 -0
- package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +125 -0
- package/ds-canonical/hooks/tests/test_log_governance_fires.sh +10 -0
- package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +7 -0
- package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +108 -0
- package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +143 -0
- package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +95 -0
- package/ds-canonical/hooks/tests/test_stop_governance_drift_check.sh.broken +125 -0
- package/ds-canonical/hooks/tests/test_stop_harvest_corrections.sh +10 -0
- package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +100 -0
- package/ds-canonical/hooks/tests/test_stop_self_audit.sh +76 -0
- package/ds-canonical/hooks/tests/test_stop_tsc_sanity.sh +10 -0
- package/ds-canonical/references/README.md +43 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +74 -0
- package/ds-canonical/references/build-ui-canonicals.md +69 -0
- package/ds-canonical/references/cva-patterns.md +41 -0
- package/ds-canonical/references/drag-canonical.md +331 -0
- package/ds-canonical/references/item-anatomy-recipe.md +225 -0
- package/ds-canonical/references/naming-conventions.md +56 -0
- package/ds-canonical/references/principle-dim-map.json +515 -0
- package/ds-canonical/references/props-naming.md +45 -0
- package/ds-canonical/references/spec-rules.md +58 -0
- package/ds-canonical/references/ssot-consultation.md +63 -0
- package/ds-canonical/references/ssot-index.md +40 -0
- package/ds-canonical/references/story-baseline-registry.json +79 -0
- package/ds-canonical/references/structural-token-retention.md +42 -0
- package/ds-canonical/references/tailwind-gotchas.md +87 -0
- package/ds-canonical/references/ui-dev-rules.md +60 -0
- package/ds-canonical/rules/README.md +34 -0
- package/ds-canonical/rules/meta-patterns.md +87 -0
- package/ds-canonical/rules/self-verify.md +53 -0
- package/ds-canonical/rules/spec-rules.md +25 -0
- package/ds-canonical/rules/story-rules.md +56 -0
- package/ds-canonical/rules/ui-development.md +87 -0
- package/ds-canonical/skills/README.md +88 -0
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +181 -0
- package/ds-canonical/skills/code-quality-audit/SKILL.md +63 -0
- package/ds-canonical/skills/codex-collab/SKILL.md +249 -0
- package/ds-canonical/skills/codex-collab/references/brief-template.md +48 -0
- package/ds-canonical/skills/codex-collab/references/transport.md +58 -0
- package/ds-canonical/skills/codify-corrections/SKILL.md +184 -0
- package/ds-canonical/skills/codify-principle/SKILL.md +151 -0
- package/ds-canonical/skills/component-quality-gate/SKILL.md +102 -0
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +79 -0
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +247 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +123 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +165 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +91 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +229 -0
- package/ds-canonical/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +343 -0
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1260 -0
- package/ds-canonical/skills/design-system-audit/references/checkpoints.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +364 -0
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +175 -0
- package/ds-canonical/skills/design-system-audit/references/spec-template.md +66 -0
- package/ds-canonical/skills/ensure-canonical/SKILL.md +196 -0
- package/ds-canonical/skills/governance-health/SKILL.md +146 -0
- package/ds-canonical/skills/knowledge-prune/SKILL.md +303 -0
- package/ds-canonical/skills/new-component/SKILL.md +170 -0
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +85 -0
- package/ds-canonical/skills/performance-audit/SKILL.md +107 -0
- package/ds-canonical/skills/product-ui-audit/SKILL.md +230 -0
- package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +246 -0
- package/ds-canonical/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/skills/product-ui-audit/references/report-template.md +159 -0
- package/ds-canonical/skills/propose-options/SKILL.md +177 -0
- package/ds-canonical/skills/prototype/SKILL.md +244 -0
- package/ds-canonical/skills/prototype/references/audit-checks.md +37 -0
- package/ds-canonical/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/skills/prototype/references/checkpoints.md +191 -0
- package/ds-canonical/skills/prototype/references/evaluation-matrix.md +141 -0
- package/ds-canonical/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/skills/prototype/references/proposal-template.md +229 -0
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +198 -0
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +159 -0
- package/ds-canonical/skills/story-writing/SKILL.md +122 -0
- package/ds-canonical/skills/story-writing/references/anatomy-standard.md +217 -0
- package/ds-canonical/skills/story-writing/references/category-templates.md +174 -0
- package/ds-canonical/skills/story-writing/references/example-selection.md +70 -0
- package/ds-canonical/skills/story-writing/references/self-check.md +20 -0
- package/ds-canonical/skills/ux-audit/SKILL.md +130 -0
- package/ds-canonical/skills/visual-audit/SKILL.md +245 -0
- package/ds-canonical/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/skills/visual-audit/references/audit-architecture.md +100 -0
- package/ds-canonical/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/package.json +9 -5
- package/src/components/Accordion/accordion.spec.md +114 -0
- package/src/components/Alert/alert.spec.md +197 -0
- package/src/components/AppShell/app-shell.spec.md +331 -0
- package/src/components/AspectRatio/aspect-ratio.spec.md +134 -0
- package/src/components/Avatar/avatar.spec.md +329 -0
- package/src/components/Badge/badge.spec.md +380 -0
- package/src/components/Breadcrumb/breadcrumb.spec.md +257 -0
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +210 -0
- package/src/components/Button/button.spec.md +460 -0
- package/src/components/Calendar/calendar.spec.md +242 -0
- package/src/components/Carousel/carousel.spec.md +253 -0
- package/src/components/Chart/chart.spec.md +155 -0
- package/src/components/Checkbox/checkbox.spec.md +344 -0
- package/src/components/Chip/chip.spec.md +237 -0
- package/src/components/CircularProgress/circular-progress.spec.md +268 -0
- package/src/components/Coachmark/coachmark.spec.md +230 -0
- package/src/components/Combobox/combobox.spec.md +180 -0
- package/src/components/Command/command.spec.md +171 -0
- package/src/components/DataTable/data-table.spec.md +525 -0
- package/src/components/DateGrid/date-grid.spec.md +215 -0
- package/src/components/DatePicker/date-picker.spec.md +334 -0
- package/src/components/DescriptionList/description-list.spec.md +214 -0
- package/src/components/Dialog/dialog.spec.md +202 -0
- package/src/components/DropdownMenu/dropdown-menu.spec.md +250 -0
- package/src/components/Empty/empty.spec.md +214 -0
- package/src/components/Field/field-controls.spec.md +338 -0
- package/src/components/Field/field.spec.md +438 -0
- package/src/components/Field/form-validation.spec.md +152 -0
- package/src/components/FieldControlGroup/field-control-group.spec.md +176 -0
- package/src/components/FileItem/file-item.spec.md +467 -0
- package/src/components/FileUpload/file-upload.spec.md +123 -0
- package/src/components/FileViewer/file-viewer.spec.md +373 -0
- package/src/components/HoverCard/hover-card.spec.md +157 -0
- package/src/components/Input/input.spec.md +193 -0
- package/src/components/LinkInput/link-input.spec.md +130 -0
- package/src/components/Menu/menu-item.spec.md +290 -0
- package/src/components/NameCard/name-card.spec.md +171 -0
- package/src/components/Notice/notice.spec.md +149 -0
- package/src/components/NumberInput/number-input.spec.md +126 -0
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +120 -0
- package/src/components/PeoplePicker/people-picker.spec.md +263 -0
- package/src/components/Popover/popover.spec.md +198 -0
- package/src/components/ProgressBar/progress-bar.spec.md +232 -0
- package/src/components/RadioGroup/radio-group.spec.md +141 -0
- package/src/components/Rating/rating.spec.md +208 -0
- package/src/components/ScrollArea/scroll-area.spec.md +145 -0
- package/src/components/SegmentedControl/segmented-control.spec.md +295 -0
- package/src/components/Select/select.spec.md +299 -0
- package/src/components/SelectMenu/select-menu.spec.md +220 -0
- package/src/components/SelectionControl/selection-item.spec.md +128 -0
- package/src/components/Separator/separator.spec.md +109 -0
- package/src/components/Sheet/sheet.spec.md +148 -0
- package/src/components/Sidebar/sidebar.spec.md +713 -0
- package/src/components/Skeleton/skeleton.spec.md +104 -0
- package/src/components/Slider/slider.spec.md +353 -0
- package/src/components/Steps/steps.spec.md +465 -0
- package/src/components/Switch/switch.spec.md +215 -0
- package/src/components/Tabs/tabs.spec.md +314 -0
- package/src/components/Tag/tag.spec.md +282 -0
- package/src/components/Textarea/textarea.spec.md +151 -0
- package/src/components/TimePicker/time-picker.spec.md +279 -0
- package/src/components/Toast/toast.spec.md +177 -0
- package/src/components/Tooltip/tooltip.spec.md +139 -0
- package/src/components/TreeView/tree-view.spec.md +374 -0
- package/src/patterns/action-bar/action-bar.spec.md +458 -0
- package/src/patterns/element-anatomy/element-anatomy.spec.md +215 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +315 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +1042 -0
- package/src/patterns/header-canonical/header-canonical.spec.md +285 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +191 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +428 -0
- package/src/patterns/resize-handle/resize-handle.spec.md +109 -0
- package/src/tokens/color/color.spec.md +804 -0
- package/src/tokens/density/density.spec.md +127 -0
- package/src/tokens/elevation/elevation.spec.md +81 -0
- package/src/tokens/layoutSpace/layoutSpace.spec.md +314 -0
- package/src/tokens/motion/motion.spec.md +97 -0
- package/src/tokens/opacity/opacity.spec.md +78 -0
- package/src/tokens/orphan-tokens.spec.md +117 -0
- package/src/tokens/radius/radius.spec.md +123 -0
- package/src/tokens/typography/typography.spec.md +202 -0
- package/src/tokens/uiSize/uiSize.spec.md +438 -0
- package/src/styles/preset.css +0 -31
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check_ssot_consultation.sh
|
|
3
|
+
#
|
|
4
|
+
# Purpose: When writing a NEW .tsx file to packages/design-system/src/components/ or
|
|
5
|
+
# src/explorations/, warn if the file lacks the canonical SSOT Consultation
|
|
6
|
+
# comment block. This enforces CLAUDE.md `# SSOT 消費 canonical` — the
|
|
7
|
+
# mechanical guardrail for Mindset #2 ("不憑直覺發明 / 優先消費既有").
|
|
8
|
+
#
|
|
9
|
+
# Input: $CLAUDE_TOOL_INPUT contains JSON with file_path + content for Write tool.
|
|
10
|
+
# Output: stderr warning if missing; exit 0 (non-blocking by design — AI decides
|
|
11
|
+
# whether to re-edit and add the block).
|
|
12
|
+
|
|
13
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
14
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# Read stdin JSON payload (Claude Code hook input)
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
|
|
21
|
+
# Only fire on Write tool, not Edit (Edit implies existing file, checklist should already be there)
|
|
22
|
+
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; d = json.load(sys.stdin); print(d.get('tool_name', ''))" 2>/dev/null || echo "")
|
|
23
|
+
if [[ "$TOOL_NAME" != "Write" ]]; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Extract file_path + content
|
|
28
|
+
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; d = json.load(sys.stdin); print(d.get('tool_input', {}).get('file_path', ''))" 2>/dev/null || echo "")
|
|
29
|
+
CONTENT=$(echo "$INPUT" | python3 -c "import sys, json; d = json.load(sys.stdin); print(d.get('tool_input', {}).get('content', ''))" 2>/dev/null || echo "")
|
|
30
|
+
|
|
31
|
+
# Only .tsx files in design-system components or explorations
|
|
32
|
+
case "$FILE_PATH" in
|
|
33
|
+
*/packages/design-system/src/components/*/*.tsx) ;;
|
|
34
|
+
*/src/explorations/*/*.tsx) ;;
|
|
35
|
+
*) exit 0 ;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
# Skip .stories.tsx, .anatomy.stories.tsx, .principles.stories.tsx (stories have own anatomy checks)
|
|
39
|
+
case "$FILE_PATH" in
|
|
40
|
+
*.stories.tsx) exit 0 ;;
|
|
41
|
+
*.test.tsx) exit 0 ;;
|
|
42
|
+
esac
|
|
43
|
+
|
|
44
|
+
# Check if content contains the SSOT Consultation marker.
|
|
45
|
+
# Canonical markers (any of):
|
|
46
|
+
# - `── 消費的 SSOT ──` (zh canonical block)
|
|
47
|
+
# - `── 實作基礎 ──` (loose form, existing elements in code)
|
|
48
|
+
# - `## Consumes SSOT` (future English form)
|
|
49
|
+
if echo "$CONTENT" | grep -qE "(消費的 SSOT|實作基礎|Consumes SSOT)"; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Missing — emit warning to stderr (non-blocking)
|
|
54
|
+
cat >&2 <<EOF
|
|
55
|
+
⚠️ SSOT Consultation 檢查:新 tsx 檔缺少消費宣告
|
|
56
|
+
|
|
57
|
+
路徑: $FILE_PATH
|
|
58
|
+
|
|
59
|
+
CLAUDE.md \`# SSOT 消費 canonical\` 要求新元件 / 新 feature 的 tsx 開頭必含
|
|
60
|
+
消費宣告 comment block。這是 Mindset #2「不憑直覺發明」+ Meta-Pattern M1 的
|
|
61
|
+
機械化 guardrail。
|
|
62
|
+
|
|
63
|
+
建議模板(加到 file top 的 JSDoc):
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* {Component} — {定位一句話}
|
|
67
|
+
*
|
|
68
|
+
* ── 定位 ──
|
|
69
|
+
* {one-liner purpose}
|
|
70
|
+
*
|
|
71
|
+
* ── 實作基礎 ──
|
|
72
|
+
* 消費: [Button, Input, ItemInlineAction, ...] // DS components used
|
|
73
|
+
* 對應 pattern: [item-anatomy, action-bar]
|
|
74
|
+
*
|
|
75
|
+
* ── 消費的 SSOT ──
|
|
76
|
+
* - components: [...]
|
|
77
|
+
* - patterns: [patterns/xxx/spec.md]
|
|
78
|
+
* - tokens: [--layout-space-loose, --chrome-header-height]
|
|
79
|
+
* - spec refs: {nearest kin specs}
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
若檔案不是新增元件(例:test / helper / type-only),可忽略本警告。
|
|
83
|
+
若是新元件或重大 feature,請補齊再 commit。
|
|
84
|
+
|
|
85
|
+
(本 hook 非 blocking,只是提醒。)
|
|
86
|
+
EOF
|
|
87
|
+
|
|
88
|
+
exit 0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook: remind to check sync when editing design-system files
|
|
3
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
4
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
5
|
+
|
|
6
|
+
FILE_PATH=$(jq -r '.tool_input.file_path // empty')
|
|
7
|
+
|
|
8
|
+
# Component .tsx edited → check spec + stories
|
|
9
|
+
if echo "$FILE_PATH" | grep -q 'packages/design-system/src/components/.*\.tsx$' && \
|
|
10
|
+
! echo "$FILE_PATH" | grep -q '\.stories\.tsx$'; then
|
|
11
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Sync check: you just edited a design-system component .tsx file. Check if the corresponding .spec.md and .stories.tsx need updates (see CLAUDE.md sync rules)."}}'
|
|
12
|
+
|
|
13
|
+
# Spec .md edited → check stories
|
|
14
|
+
elif echo "$FILE_PATH" | grep -q 'packages/design-system/src/.*\.spec\.md$'; then
|
|
15
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Sync check: you just edited a spec.md. Check if the corresponding .principles.stories.tsx and .anatomy.stories.tsx need updates to reflect the new rules."}}'
|
|
16
|
+
|
|
17
|
+
# Pattern spec/code edited → check all consumers
|
|
18
|
+
elif echo "$FILE_PATH" | grep -q 'packages/design-system/src/patterns/.*\.\(tsx\|md\)$'; then
|
|
19
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Sync check: you just edited a pattern file. Check ALL consumer components listed in the pattern spec to ensure they still comply."}}'
|
|
20
|
+
fi
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check_third_party_dom_verified.sh
|
|
3
|
+
#
|
|
4
|
+
# Purpose: Warn when .tsx uses `[&[data-xxx]]:` attribute selector targeting
|
|
5
|
+
# a 3rd-party library's internal DOM state. History: react-day-picker v9
|
|
6
|
+
# DateGrid used `[&[data-range-middle]]:bg-...` assuming `data-range-middle`
|
|
7
|
+
# existed, but v9 only emits `data-selected`/`data-disabled` etc. Attribute
|
|
8
|
+
# selector silently failed.
|
|
9
|
+
#
|
|
10
|
+
# This is the CLAUDE.md Meta-Pattern M2 mechanical guardrail:
|
|
11
|
+
# "消費 3rd-party lib 必驗 rendered DOM".
|
|
12
|
+
#
|
|
13
|
+
# Exit 0 always (non-blocking warning).
|
|
14
|
+
|
|
15
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
16
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
|
|
22
|
+
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; d = json.load(sys.stdin); print(d.get('tool_name', ''))" 2>/dev/null || echo "")
|
|
23
|
+
case "$TOOL_NAME" in
|
|
24
|
+
Write|Edit|MultiEdit) ;;
|
|
25
|
+
*) exit 0 ;;
|
|
26
|
+
esac
|
|
27
|
+
|
|
28
|
+
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; d = json.load(sys.stdin); print(d.get('tool_input', {}).get('file_path', ''))" 2>/dev/null || echo "")
|
|
29
|
+
|
|
30
|
+
case "$FILE_PATH" in
|
|
31
|
+
*.tsx) ;;
|
|
32
|
+
*.ts) ;;
|
|
33
|
+
*) exit 0 ;;
|
|
34
|
+
esac
|
|
35
|
+
|
|
36
|
+
# Extract new content from tool input (content for Write, new_string for Edit)
|
|
37
|
+
NEW_CONTENT=$(echo "$INPUT" | python3 -c "
|
|
38
|
+
import sys, json
|
|
39
|
+
d = json.load(sys.stdin)
|
|
40
|
+
ti = d.get('tool_input', {})
|
|
41
|
+
# Write: content; Edit: new_string; MultiEdit: edits[].new_string joined
|
|
42
|
+
if 'content' in ti:
|
|
43
|
+
print(ti.get('content', ''))
|
|
44
|
+
elif 'new_string' in ti:
|
|
45
|
+
print(ti.get('new_string', ''))
|
|
46
|
+
elif 'edits' in ti:
|
|
47
|
+
print('\n'.join([e.get('new_string', '') for e in ti.get('edits', [])]))
|
|
48
|
+
else:
|
|
49
|
+
print('')
|
|
50
|
+
" 2>/dev/null || echo "")
|
|
51
|
+
|
|
52
|
+
# Detect pattern: attribute selector targeting data-* that could be 3rd-party
|
|
53
|
+
# Matches: [&[data-xxx]] or [&[data-xxx=yyy]] inside className or cva strings
|
|
54
|
+
if ! echo "$NEW_CONTENT" | grep -qE '\[&\[data-[a-z-]+'; then
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Only warn if the file imports a known 3rd-party UI library
|
|
59
|
+
THIRD_PARTY_LIBS="react-day-picker|@radix-ui|cmdk|sonner|embla-carousel|react-zoom-pan-pinch|@tanstack/react-virtual|@tanstack/react-table|recharts|react-aria"
|
|
60
|
+
|
|
61
|
+
if ! echo "$NEW_CONTENT" | grep -qE "from ['\"]($THIRD_PARTY_LIBS)"; then
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Emit warning
|
|
66
|
+
cat >&2 <<'EOF'
|
|
67
|
+
⚠️ 3rd-party DOM 驗證檢查:attribute selector 針對第三方 lib 內部 DOM
|
|
68
|
+
|
|
69
|
+
偵測到 `[&[data-xxx]]:` 類 attribute selector,且檔案 import 了第三方 UI lib。
|
|
70
|
+
|
|
71
|
+
Meta-Pattern M2 canonical(CLAUDE.md):**消費 3rd-party lib 必驗 rendered DOM
|
|
72
|
+
(不信 docs)**。第三方 lib 的內部 DOM attribute 常跟 docs 不符,或在不同
|
|
73
|
+
version 行為漂移。
|
|
74
|
+
|
|
75
|
+
歷史 bug anchor:
|
|
76
|
+
react-day-picker v9 的 Day CELL 只有 data-selected / data-disabled /
|
|
77
|
+
data-today / data-outside / data-focused;**data-range-start / data-range-middle
|
|
78
|
+
/ data-range-end 不存在**。用 `[&[data-range-middle]]:bg-...` 是靜默失效。
|
|
79
|
+
|
|
80
|
+
驗證步驟(寫這類 selector 之前)──
|
|
81
|
+
|
|
82
|
+
1. 開 Storybook 或 dev server
|
|
83
|
+
2. Inspect element 看實際 rendered DOM 有哪些 data-* attribute
|
|
84
|
+
3. 或查 lib 源碼 `node_modules/{lib}/dist/` 找 emit 的 attribute 清單
|
|
85
|
+
4. 確認 attribute 存在 + 在當前 version 穩定 + document 過,再寫 selector
|
|
86
|
+
|
|
87
|
+
替代做法(若 attribute 不存在):
|
|
88
|
+
- 透過 lib 官方 API(classNames / components props)傳 class
|
|
89
|
+
- 例:react-day-picker 用 `classNames={{ range_middle: 'bg-...' }}`,
|
|
90
|
+
lib 內部 `getClassNamesForModifiers` 會附加到 cell
|
|
91
|
+
|
|
92
|
+
(本 hook 非 blocking,只是提醒。寫下 selector 前,請確認已驗過 DOM。)
|
|
93
|
+
EOF
|
|
94
|
+
|
|
95
|
+
exit 0
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook for Write:
|
|
3
|
+
# When creating a NEW file in a classification-sensitive dir (patterns / skills / hooks /
|
|
4
|
+
# components / tokens), inject the home's README.md charter as additionalContext so AI
|
|
5
|
+
# cannot skip seeing it before creating new classification.
|
|
6
|
+
#
|
|
7
|
+
# Design choice — non-blocking context injection (not exit 2 block):
|
|
8
|
+
# - Correct placements must not get blocked → zero friction when AI classifies well
|
|
9
|
+
# - Incorrect placements: AI sees charter, the「這裡不收」table tells AI where to go instead
|
|
10
|
+
# - The injection happens on EVERY Write to sensitive dirs, so AI cannot「forget」the charter
|
|
11
|
+
#
|
|
12
|
+
# Exit codes (Claude Code hook protocol):
|
|
13
|
+
# 0 + stdout JSON — inject additionalContext (non-blocking)
|
|
14
|
+
|
|
15
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
16
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
22
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
23
|
+
|
|
24
|
+
[ "$TOOL" = "Write" ] || exit 0
|
|
25
|
+
[ -n "$FILE_PATH" ] || exit 0
|
|
26
|
+
|
|
27
|
+
# Skip if file already exists (overwrite, not classification event).
|
|
28
|
+
[ -e "$FILE_PATH" ] && exit 0
|
|
29
|
+
|
|
30
|
+
# README.md itself — the charter file, no self-reference needed.
|
|
31
|
+
case "$(basename "$FILE_PATH")" in
|
|
32
|
+
README.md) exit 0 ;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
36
|
+
|
|
37
|
+
# Longest prefix first for nested matches.
|
|
38
|
+
SENSITIVE_DIRS=(
|
|
39
|
+
"$PROJECT_ROOT/packages/design-system/src/patterns"
|
|
40
|
+
"$PROJECT_ROOT/packages/design-system/src/components"
|
|
41
|
+
"$PROJECT_ROOT/packages/design-system/src/tokens"
|
|
42
|
+
"$PROJECT_ROOT/packages/design-system/src"
|
|
43
|
+
"$PROJECT_ROOT/.claude/skills"
|
|
44
|
+
"$PROJECT_ROOT/.claude/hooks"
|
|
45
|
+
"$PROJECT_ROOT/.claude/commands"
|
|
46
|
+
"$PROJECT_ROOT/.claude/agents"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
MATCHED_DIR=""
|
|
50
|
+
for dir in "${SENSITIVE_DIRS[@]}"; do
|
|
51
|
+
if [[ "$FILE_PATH" == "$dir/"* ]]; then
|
|
52
|
+
MATCHED_DIR="$dir"
|
|
53
|
+
break
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
[ -n "$MATCHED_DIR" ] || exit 0
|
|
58
|
+
|
|
59
|
+
README_PATH="$MATCHED_DIR/README.md"
|
|
60
|
+
[ -f "$README_PATH" ] || exit 0
|
|
61
|
+
|
|
62
|
+
REL_IN_HOME="${FILE_PATH#$MATCHED_DIR/}"
|
|
63
|
+
FIRST_SEGMENT="${REL_IN_HOME%%/*}"
|
|
64
|
+
NEW_SUBDIR_PATH="$MATCHED_DIR/$FIRST_SEGMENT"
|
|
65
|
+
|
|
66
|
+
IS_FLAT_FILE="false"
|
|
67
|
+
[ "$REL_IN_HOME" = "$FIRST_SEGMENT" ] && IS_FLAT_FILE="true"
|
|
68
|
+
|
|
69
|
+
# Flat files are CONVENTION for these dirs (per Claude Code native format):
|
|
70
|
+
# .claude/hooks/ — individual hook scripts
|
|
71
|
+
# .claude/commands/ — individual command .md files
|
|
72
|
+
# .claude/agents/ — individual agent .md files
|
|
73
|
+
# For these, flat file is not a classification anomaly; exit silently.
|
|
74
|
+
case "$MATCHED_DIR" in
|
|
75
|
+
*/.claude/hooks|*/.claude/commands|*/.claude/agents)
|
|
76
|
+
if [ "$IS_FLAT_FILE" = "true" ]; then
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
|
|
82
|
+
IS_NEW_SUBDIR="false"
|
|
83
|
+
if [ "$IS_FLAT_FILE" = "false" ] && [ ! -d "$NEW_SUBDIR_PATH" ]; then
|
|
84
|
+
IS_NEW_SUBDIR="true"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Only fire on classification events:
|
|
88
|
+
# - flat file directly under sensitive dir root (usually wrong — breaks convention)
|
|
89
|
+
# - new sub-folder creation (first time this classification exists)
|
|
90
|
+
# Nested writes into existing sub-folders are not classification events; stay silent.
|
|
91
|
+
if [ "$IS_FLAT_FILE" = "false" ] && [ "$IS_NEW_SUBDIR" = "false" ]; then
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
HOME_NAME="${MATCHED_DIR#$PROJECT_ROOT/}"
|
|
96
|
+
CHARTER_CONTENT=$(cat "$README_PATH")
|
|
97
|
+
|
|
98
|
+
if [ "$IS_FLAT_FILE" = "true" ]; then
|
|
99
|
+
HEADER="⚠️ FLAT FILE at $HOME_NAME/ root — convention requires sub-folder structure. Verify this is not a misplacement before proceeding."
|
|
100
|
+
else
|
|
101
|
+
HEADER="🚪 NEW SUB-FOLDER being created in $HOME_NAME/ (classification event). Verify against the charter below BEFORE the Write proceeds."
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
cat <<EOJSON
|
|
105
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":$(jq -Rs . <<<"HOME CHARTER GATE — $HOME_NAME/
|
|
106
|
+
|
|
107
|
+
$HEADER
|
|
108
|
+
|
|
109
|
+
Path being written: $FILE_PATH
|
|
110
|
+
|
|
111
|
+
=== CHARTER ($README_PATH) ===
|
|
112
|
+
$CHARTER_CONTENT
|
|
113
|
+
=== END CHARTER ===
|
|
114
|
+
|
|
115
|
+
Three-question verification BEFORE proceeding with Write:
|
|
116
|
+
1. Does your content match 「這裡只收」?
|
|
117
|
+
2. Does it match any row of 「這裡不收」 (i.e. belongs elsewhere)?
|
|
118
|
+
3. Does it pass all 「新增 criteria」?
|
|
119
|
+
|
|
120
|
+
If misplaced → STOP, cancel this Write, redirect to correct home.
|
|
121
|
+
If charter is outdated → update README.md first.
|
|
122
|
+
If placement is correct → proceed.")}}
|
|
123
|
+
EOJSON
|
|
124
|
+
|
|
125
|
+
exit 0
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse Edit|Write|MultiEdit: 對 canonical 文件的「新增 rule/section」
|
|
3
|
+
# 注入 3 題短 interrogation,補 M8 benchmark / Rule-of-3 / M10 下游吸收的
|
|
4
|
+
# runtime adherence(不是 infra,是 model 自律)。
|
|
5
|
+
#
|
|
6
|
+
# Scope(極窄,避免噪音):
|
|
7
|
+
# - CLAUDE.md Edit/Write
|
|
8
|
+
# - packages/design-system/src/**/*.spec.md Edit/Write
|
|
9
|
+
# - .claude/skills/*/SKILL.md Edit/Write
|
|
10
|
+
#
|
|
11
|
+
# 觸發條件(只在真正「加新 rule」時 fire):
|
|
12
|
+
# - Write(新檔)- 一律 fire
|
|
13
|
+
# - Edit:new_string 淨增 > 200 chars OR 含新 `##` heading OR 含「**M\d+**」
|
|
14
|
+
# (M-row 由 pre_write_subsumption_check 處理,本 hook skip 以免 double fire)
|
|
15
|
+
#
|
|
16
|
+
# Non-blocking(PostToolUse 僅注 context,AI 讀到 3 題自律回答)。
|
|
17
|
+
#
|
|
18
|
+
# Per-hook fire logging
|
|
19
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
INPUT=$(cat)
|
|
24
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
25
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
26
|
+
|
|
27
|
+
case "$TOOL" in
|
|
28
|
+
Edit|Write|MultiEdit) ;;
|
|
29
|
+
*) exit 0 ;;
|
|
30
|
+
esac
|
|
31
|
+
|
|
32
|
+
# Scope gate
|
|
33
|
+
IN_SCOPE=0
|
|
34
|
+
case "$FILE_PATH" in
|
|
35
|
+
*/CLAUDE.md) IN_SCOPE=1 ;;
|
|
36
|
+
*/packages/design-system/src/*/*.spec.md) IN_SCOPE=1 ;;
|
|
37
|
+
*/.claude/skills/*/SKILL.md) IN_SCOPE=1 ;;
|
|
38
|
+
esac
|
|
39
|
+
[ "$IN_SCOPE" = "0" ] && exit 0
|
|
40
|
+
|
|
41
|
+
# Extract new content
|
|
42
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '
|
|
43
|
+
(.tool_input.content // "") + "\n" +
|
|
44
|
+
(.tool_input.new_string // "") + "\n" +
|
|
45
|
+
([.tool_input.edits[]? | .new_string] | join("\n"))
|
|
46
|
+
' 2>/dev/null || echo "")
|
|
47
|
+
OLD_CONTENT=$(echo "$INPUT" | jq -r '
|
|
48
|
+
(.tool_input.old_string // "") + "\n" +
|
|
49
|
+
([.tool_input.edits[]? | .old_string] | join("\n"))
|
|
50
|
+
' 2>/dev/null || echo "")
|
|
51
|
+
|
|
52
|
+
# Skip if M-row addition(pre_write_subsumption_check 已處理)
|
|
53
|
+
if echo "$NEW_CONTENT" | grep -qE '\|\s*\*\*M[0-9]+\*\*\s*\|'; then
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Trigger heuristics
|
|
58
|
+
TRIGGER=0
|
|
59
|
+
TRIGGER_REASON=""
|
|
60
|
+
|
|
61
|
+
# (a) Write(新檔)
|
|
62
|
+
if [ "$TOOL" = "Write" ]; then
|
|
63
|
+
TRIGGER=1
|
|
64
|
+
TRIGGER_REASON="new file"
|
|
65
|
+
else
|
|
66
|
+
# (b) Edit 淨增 > 200 chars
|
|
67
|
+
NEW_LEN=$(printf '%s' "$NEW_CONTENT" | wc -c | tr -d ' ')
|
|
68
|
+
OLD_LEN=$(printf '%s' "$OLD_CONTENT" | wc -c | tr -d ' ')
|
|
69
|
+
NET_ADD=$(( NEW_LEN - OLD_LEN ))
|
|
70
|
+
if [ "$NET_ADD" -gt 200 ]; then
|
|
71
|
+
TRIGGER=1
|
|
72
|
+
TRIGGER_REASON="net +${NET_ADD} chars"
|
|
73
|
+
fi
|
|
74
|
+
# (c) new_string 含新 `##` heading 而 old_string 不含
|
|
75
|
+
if echo "$NEW_CONTENT" | grep -qE '^## '; then
|
|
76
|
+
if ! echo "$OLD_CONTENT" | grep -qE '^## '; then
|
|
77
|
+
TRIGGER=1
|
|
78
|
+
TRIGGER_REASON="new ## section"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
[ "$TRIGGER" = "0" ] && exit 0
|
|
84
|
+
|
|
85
|
+
# Tailor prompt by file type
|
|
86
|
+
case "$FILE_PATH" in
|
|
87
|
+
*/CLAUDE.md)
|
|
88
|
+
HOME_LABEL="CLAUDE.md"
|
|
89
|
+
SCOPE_NOTE="canonical 最高層;任何新規則必 benchmark + 下游吸收"
|
|
90
|
+
;;
|
|
91
|
+
*/packages/design-system/src/*/*.spec.md)
|
|
92
|
+
HOME_LABEL="spec.md"
|
|
93
|
+
SCOPE_NOTE="spec SSOT;新規則必對齊 7 維 + 近親 spec cross-check"
|
|
94
|
+
;;
|
|
95
|
+
*/.claude/skills/*/SKILL.md)
|
|
96
|
+
HOME_LABEL="SKILL.md"
|
|
97
|
+
SCOPE_NOTE="skill workflow;新步驟 / checkpoint 必 world-class 對照 + skill 重複 check"
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
|
|
101
|
+
MSG="🧭 Canonical interrogation(${HOME_LABEL} ${TRIGGER_REASON})— ${SCOPE_NOTE}。3 題短 self-check:\n\n"
|
|
102
|
+
MSG="${MSG}1. **World-class benchmark**(M8):新 rule / pattern 有 ≥ 3 家 DS 對照嗎?列具體實作名或「無」(無 = rule 未成熟,考慮 ship 前先調研)。\n\n"
|
|
103
|
+
MSG="${MSG}2. **Rule-of-3**(資訊治理):本概念已在別處出現?若 ≥ 3 處 → 選 SSOT owner,其他 pointer only。確認是 SSOT 新寫還是應該 pointer?\n\n"
|
|
104
|
+
MSG="${MSG}3. **M10 下游吸收**:新 rule 是否讓既有某條 rule / memory / bug case 變冗餘?明寫「可刪 X」或「無下游變動」。空白不算。\n\n"
|
|
105
|
+
MSG="${MSG}⚠️ 回答上述 3 題(even 簡短)再繼續,避免 silent append-only 膨脹。"
|
|
106
|
+
|
|
107
|
+
ESCAPED=$(printf '%b' "$MSG" | jq -Rs .)
|
|
108
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":%s}}\n' "$ESCAPED"
|
|
109
|
+
exit 0
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook: before editing a design-system component .tsx,
|
|
3
|
+
# remind AI to read the spec first.
|
|
4
|
+
#
|
|
5
|
+
# Diff-aware: skip if the edit is purely import cleanup / type-only / typo —
|
|
6
|
+
# spec reading not required for those. Only fire if edit touches render body,
|
|
7
|
+
# cva, variants, tokens, or other design-meaningful regions.
|
|
8
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
9
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
10
|
+
|
|
11
|
+
INPUT=$(cat)
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
13
|
+
|
|
14
|
+
# Only trigger for design-system component .tsx files (not stories, not specs)
|
|
15
|
+
if ! echo "$FILE_PATH" | grep -q 'packages/design-system/src/components/.*\.tsx$'; then
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
echo "$FILE_PATH" | grep -q '\.stories\.tsx$' && exit 0
|
|
19
|
+
|
|
20
|
+
# Diff-aware: extract old+new content. Skip if it's purely import / export-list
|
|
21
|
+
# manipulation or type-alias tweaks (not design-meaningful).
|
|
22
|
+
DIFF_TEXT=$(echo "$INPUT" | jq -r '
|
|
23
|
+
(.tool_input.old_string // "") + "\n---\n" +
|
|
24
|
+
(.tool_input.new_string // "") + "\n---\n" +
|
|
25
|
+
(.tool_input.content // "") + "\n---\n" +
|
|
26
|
+
([.tool_input.edits[]? | (.old_string + "\n---\n" + .new_string + "\n---\n")] | join(""))
|
|
27
|
+
' 2>/dev/null || echo "")
|
|
28
|
+
|
|
29
|
+
# If diff is non-empty and consists ONLY of import lines / export lines /
|
|
30
|
+
# pure type annotations — skip.
|
|
31
|
+
if [ -n "$DIFF_TEXT" ]; then
|
|
32
|
+
# Strip lines that are imports / exports / type declarations / whitespace.
|
|
33
|
+
MEANINGFUL=$(echo "$DIFF_TEXT" | grep -vE '^[[:space:]]*(import |export (type )?\{|export type |type [A-Z]|interface [A-Z]|//|---|[[:space:]]*$|\}|\{)' | head -5)
|
|
34
|
+
# If nothing substantive remains, skip the nag.
|
|
35
|
+
[ -z "$MEANINGFUL" ] && exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
COMP_DIR=$(echo "$FILE_PATH" | sed -n 's|.*packages/design-system/src/components/\([^/]*\)/.*|\1|p')
|
|
39
|
+
[ -z "$COMP_DIR" ] && exit 0
|
|
40
|
+
|
|
41
|
+
SPEC_BASENAME=$(echo "$COMP_DIR" | sed 's/\([a-z]\)\([A-Z]\)/\1-\2/g; s/\([A-Z]\)\([A-Z][a-z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
|
|
42
|
+
SPEC_PATH="${CLAUDE_PROJECT_DIR}/packages/design-system/src/components/${COMP_DIR}/${SPEC_BASENAME}.spec.md"
|
|
43
|
+
|
|
44
|
+
# Extract 禁止事項 section(如 spec 存在且有該 section)— inject 到 additionalContext
|
|
45
|
+
# 讓 AI 改 tsx 前機械上讀到 forbid list,不靠記憶。2026-04-24 FileUpload `files` prop
|
|
46
|
+
# 事件根因:spec 禁止事項寫「不在 FileUpload 自己畫 list」AI 未讀就加 prop。
|
|
47
|
+
FORBID_SECTION=""
|
|
48
|
+
if [ -f "$SPEC_PATH" ]; then
|
|
49
|
+
# awk 抓從「## 禁止事項」到下個 H2 前的內容(最多 40 行 cap,避免爆量)
|
|
50
|
+
FORBID_SECTION=$(awk '
|
|
51
|
+
/^## (禁止事項|Forbidden|❌)/ { in_section=1; print; next }
|
|
52
|
+
in_section && /^## / { exit }
|
|
53
|
+
in_section { print }
|
|
54
|
+
' "$SPEC_PATH" 2>/dev/null | head -40)
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
CONTEXT="⚠️ Editing ${COMP_DIR} (design-meaningful region). READ ${SPEC_BASENAME}.spec.md full 禁止事項 + Props 行為 sections before proceeding."
|
|
58
|
+
if [ -n "$FORBID_SECTION" ]; then
|
|
59
|
+
# 用 jq 安全 escape multiline content into JSON string
|
|
60
|
+
ESCAPED=$(printf '%s\n\n---\n\n%s' "$CONTEXT" "$FORBID_SECTION" | jq -Rs .)
|
|
61
|
+
cat <<EOJSON
|
|
62
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":${ESCAPED}}}
|
|
63
|
+
EOJSON
|
|
64
|
+
else
|
|
65
|
+
cat <<EOJSON
|
|
66
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"${CONTEXT}"}}
|
|
67
|
+
EOJSON
|
|
68
|
+
fi
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook for Write:
|
|
3
|
+
# When creating a NEW {name}.spec.md under packages/design-system/src/components/{Name}/,
|
|
4
|
+
# inject a Layout Family declaration reminder. Prevents silent drift where
|
|
5
|
+
# a new component spec is created without Family 1/2/3/4 or exception declared —
|
|
6
|
+
# which bypasses the 4-Family Model taxonomy and accumulates classification debt.
|
|
7
|
+
#
|
|
8
|
+
# Exit: 0 + stdout JSON additionalContext (non-blocking)
|
|
9
|
+
|
|
10
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
11
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
INPUT=$(cat)
|
|
16
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
17
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
18
|
+
|
|
19
|
+
[ "$TOOL" = "Write" ] || exit 0
|
|
20
|
+
[ -n "$FILE_PATH" ] || exit 0
|
|
21
|
+
[ -e "$FILE_PATH" ] && exit 0 # only fire on NEW file
|
|
22
|
+
|
|
23
|
+
# Match: packages/design-system/src/components/{Name}/{name}.spec.md (exclude nested sub-specs)
|
|
24
|
+
if ! echo "$FILE_PATH" | grep -qE 'packages/design-system/src/components/[^/]+/[^/]+\.spec\.md$'; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Exclude Field sub-specs (field-controls.spec.md, form-validation.spec.md — these are
|
|
29
|
+
# Field-system shared specs, not single-component specs — they inherit from field.spec.md)
|
|
30
|
+
BASE_NAME=$(basename "$FILE_PATH" .spec.md)
|
|
31
|
+
case "$BASE_NAME" in
|
|
32
|
+
field-controls|form-validation|field-types) exit 0 ;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
cat <<EOJSON
|
|
36
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"🏛️ NEW COMPONENT SPEC — Layout Family declaration REQUIRED in first paragraph.\n\nPath: $FILE_PATH\n\nMANDATORY first-paragraph declaration (see CLAUDE.md '# 系統內部 Layout — 4-Family Model' + patterns/element-anatomy/item-anatomy.spec.md):\n\n **Layout Family**: <one of>\n - Family 1 (Menu item layout, scanning mode)\n - Family 2 (List item layout, reading mode)\n - Family 3 (Pill layout, action trigger OR data indicator sub-profile)\n - Family 4 (Field control layout)\n - Non-family (self-contained primitive OR composite multi-section) + rationale\n\nIf Family 1/2 → consume <MenuItem> or compose item-anatomy slot primitives (<ItemIcon>, <ItemAvatar>, <ItemLabel>, <ItemSuffix>, <ItemInlineAction>), don't reinvent.\nIf Family 3 → follow Button Pill Layout.\nIf Family 4 → follow field-controls.spec.md.\nIf Non-family → spec MUST justify why (self-contained vs composite).\n\nForgetting this declaration accumulates taxonomy debt. Also consider invoking /component-quality-gate before merging this component into DS."}}
|
|
37
|
+
EOJSON
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse Write / Edit: before adding NEW governance entry, check if existing
|
|
3
|
+
# homes already cover the topic. Prevents CLAUDE.md / Meta-Pattern / spec drift
|
|
4
|
+
# from append-only accumulation(CLAUDE.md 資訊治理 canonical「上游加 = 下游減」+
|
|
5
|
+
# mindset #6 + M10 exhaustive scan)。
|
|
6
|
+
#
|
|
7
|
+
# Scope A — New file(Write only):
|
|
8
|
+
# - new memory_*.md → grep MEMORY.md for similar topic
|
|
9
|
+
# - new hook → grep existing hooks for similar pattern
|
|
10
|
+
# - new skill → grep .claude/skills/ for similar purpose
|
|
11
|
+
# - new spec.md → grep existing specs for same component name
|
|
12
|
+
#
|
|
13
|
+
# Scope B — Meta-Pattern M-row addition(Edit CLAUDE.md,2026-04-25 G4):
|
|
14
|
+
# 當 Edit CLAUDE.md new_string 包含新增 M-row pattern(`| **M\d+** |`),prompt:
|
|
15
|
+
# - 列出被吸收可刪的下游 M-row / specific bug / memory entry
|
|
16
|
+
# - world-class benchmark(≥ 3 家,M8 強制)
|
|
17
|
+
# - Rule-of-3 check:本 pattern 已在 3+ 處 → 需挑 SSOT owner
|
|
18
|
+
#
|
|
19
|
+
# Output: additionalContext warning — Claude 必 acknowledge + answer 3 題才能繼續。
|
|
20
|
+
# Non-blocking mechanical exit(0);enforcement 靠 Claude 讀 additionalContext 自律。
|
|
21
|
+
|
|
22
|
+
# Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
|
|
23
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
INPUT=$(cat)
|
|
28
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
29
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
30
|
+
|
|
31
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
32
|
+
|
|
33
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
34
|
+
cd "$PROJECT_DIR" || exit 0
|
|
35
|
+
|
|
36
|
+
# ── Scope B: Meta-Pattern addition(Edit CLAUDE.md)── EARLY FIRE before new-file check
|
|
37
|
+
case "$FILE_PATH" in
|
|
38
|
+
*/CLAUDE.md)
|
|
39
|
+
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "MultiEdit" ] || [ "$TOOL_NAME" = "Write" ]; then
|
|
40
|
+
# Pull new content(Write: tool_input.content / Edit: new_string / MultiEdit: edits[].new_string)
|
|
41
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '
|
|
42
|
+
(.tool_input.content // "") + "\n" +
|
|
43
|
+
(.tool_input.new_string // "") + "\n" +
|
|
44
|
+
([.tool_input.edits[]? | .new_string] | join("\n"))
|
|
45
|
+
' 2>/dev/null || echo "")
|
|
46
|
+
|
|
47
|
+
# Detect new M-row pattern:CLAUDE.md Meta-Pattern 表格的 row 格式
|
|
48
|
+
# `| **M\d+** |` 或 `| \*\*M\d+\*\* |`(markdown bold escape 不同呈現)
|
|
49
|
+
if echo "$NEW_CONTENT" | grep -qE '\|\s*\*\*M[0-9]+\*\*\s*\|'; then
|
|
50
|
+
M_ROW=$(echo "$NEW_CONTENT" | grep -oE '\*\*M[0-9]+\*\*' | head -1 | tr -d '*')
|
|
51
|
+
MSG="🧭 Meta-Pattern 新增偵測(${M_ROW})— CLAUDE.md 資訊治理 canonical「上游加 = 下游減」+ M8(world-class benchmark)+ M10(exhaustive scan)觸發 3 題強制 self-check:\n\n"
|
|
52
|
+
MSG="${MSG}1. **World-class benchmark**(M8 強制):≥ 3 家 world-class DS(Polaris / Material / Atlassian / Ant / Carbon / Apple HIG / VS Code / Figma / Slack / Notion)的對照?列具體實作名或 API。\n\n"
|
|
53
|
+
MSG="${MSG}2. **上游加 = 下游減**(M10 + 資訊治理 retire pipeline):新 M-row 吸收了哪些既有內容?具體列:\n"
|
|
54
|
+
MSG="${MSG} - 被吸收的下游 M-row(如 M3 加新章後,M5 某條變冗餘 → 可刪)\n"
|
|
55
|
+
MSG="${MSG} - 被吸收的 specific bug(historical-bugs.md 內記的事件)\n"
|
|
56
|
+
MSG="${MSG} - 被吸收的 memory entry(feedback_*.md / project_*.md)\n"
|
|
57
|
+
MSG="${MSG} - 「無」也要明寫(空白不算回答)\n\n"
|
|
58
|
+
MSG="${MSG}3. **Rule-of-3**(資訊治理 canonical):本 pattern 概念在幾處出現?若已 ≥ 3 → 選 SSOT owner,其他 pointer only。新 M-row 是 SSOT 本身還是 pointer?\n\n"
|
|
59
|
+
MSG="${MSG}⚠️ 未在本輪對話明確回答 3 題前,禁止 commit 這個 edit。回答後繼續 OK。"
|
|
60
|
+
ESCAPED=$(printf '%b' "$MSG" | jq -Rs .)
|
|
61
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":%s}}\n' "$ESCAPED"
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
|
|
68
|
+
# ── Scope A: New file detection ──
|
|
69
|
+
# Only fire for NEW files (Write creates; Edit modifies existing)
|
|
70
|
+
[ -f "$FILE_PATH" ] && exit 0
|
|
71
|
+
|
|
72
|
+
BASENAME=$(basename "$FILE_PATH" .md)
|
|
73
|
+
BASENAME=${BASENAME%.sh}
|
|
74
|
+
BASENAME=${BASENAME%.py}
|
|
75
|
+
|
|
76
|
+
MATCHES=""
|
|
77
|
+
|
|
78
|
+
case "$FILE_PATH" in
|
|
79
|
+
*/memory/*.md)
|
|
80
|
+
# Extract keywords from filename (e.g. feedback_xxx_yyy → xxx yyy)
|
|
81
|
+
KEYWORDS=$(echo "$BASENAME" | tr '_' ' ' | awk '{for(i=2;i<=NF;i++) printf "%s ",$i}')
|
|
82
|
+
MEMORY_DIR=$(dirname "$FILE_PATH")
|
|
83
|
+
[ -d "$MEMORY_DIR" ] && MATCHES=$(ls "$MEMORY_DIR" 2>/dev/null | grep -iE "$(echo "$KEYWORDS" | tr ' ' '|')" | head -3 || true)
|
|
84
|
+
HOME_LABEL="memory"
|
|
85
|
+
;;
|
|
86
|
+
*/.claude/hooks/*)
|
|
87
|
+
# New hook — check for similar name
|
|
88
|
+
KEYWORDS=$(echo "$BASENAME" | tr '_' ' ' | awk '{for(i=2;i<=NF;i++) printf "%s ",$i}')
|
|
89
|
+
[ -z "$KEYWORDS" ] && KEYWORDS="$BASENAME"
|
|
90
|
+
MATCHES=$(ls .claude/hooks/ 2>/dev/null | grep -iE "$(echo "$KEYWORDS" | tr ' ' '|')" | head -3 || true)
|
|
91
|
+
HOME_LABEL="hook"
|
|
92
|
+
;;
|
|
93
|
+
*/.claude/skills/*/SKILL.md)
|
|
94
|
+
# New skill — grep existing skill names
|
|
95
|
+
MATCHES=$(ls .claude/skills/ 2>/dev/null | grep -iE "$BASENAME" | head -3 || true)
|
|
96
|
+
HOME_LABEL="skill"
|
|
97
|
+
;;
|
|
98
|
+
*.spec.md)
|
|
99
|
+
# New spec — grep existing specs for same component
|
|
100
|
+
MATCHES=$(find packages/design-system/src -name "${BASENAME}*" 2>/dev/null | head -3 || true)
|
|
101
|
+
HOME_LABEL="spec"
|
|
102
|
+
;;
|
|
103
|
+
*) exit 0 ;;
|
|
104
|
+
esac
|
|
105
|
+
|
|
106
|
+
[ -z "$MATCHES" ] && exit 0
|
|
107
|
+
|
|
108
|
+
MSG="📋 Creating new ${HOME_LABEL} file: ${FILE_PATH}\nPossibly-related existing files:\n$(echo "$MATCHES" | sed 's/^/ /')\nBefore writing, verify: (a) is this a duplicate? (b) can we extend an existing file instead? (c) CLAUDE.md Rule-of-3 SSOT — if concept exists in 3+ places, pick one owner, others point."
|
|
109
|
+
|
|
110
|
+
ESCAPED=$(printf '%s' "$MSG" | jq -Rs .)
|
|
111
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":%s}}\n' "$ESCAPED"
|
|
112
|
+
exit 0
|