@haposoft/cafekit 0.16.7 → 0.18.0

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 (193) hide show
  1. package/README.md +14 -22
  2. package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
  3. package/bin/__tests__/codex-hooks.test.js +0 -36
  4. package/bin/__tests__/codex-native.test.js +46 -88
  5. package/bin/__tests__/develop-contract.test.js +26 -57
  6. package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
  7. package/bin/__tests__/grok-envelope.test.js +0 -56
  8. package/bin/__tests__/hook-payload.test.js +4 -6
  9. package/bin/__tests__/legacy-authority-decoupled.test.js +159 -0
  10. package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
  11. package/bin/__tests__/omp-bridge.test.js +20 -17
  12. package/bin/__tests__/omp-hooks.test.js +0 -19
  13. package/bin/__tests__/optional-skill-inventory.test.js +112 -4
  14. package/bin/__tests__/orca-session.test.js +0 -2
  15. package/bin/__tests__/package-inventory.test.js +100 -541
  16. package/bin/__tests__/runtime-dir.test.js +0 -3
  17. package/bin/__tests__/runtime-schema.test.js +3 -2
  18. package/bin/__tests__/skill-routing-source.test.js +27 -15
  19. package/bin/__tests__/spec-narrowing.test.js +0 -27
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +4 -69
  21. package/bin/__tests__/ui-ux-skill.test.js +101 -0
  22. package/bin/__tests__/usage-hook-retired.test.js +114 -0
  23. package/bin/__tests__/validator-grounding.test.js +9 -28
  24. package/bin/phases/codex-runtime.js +24 -0
  25. package/bin/phases/omp-runtime.js +22 -0
  26. package/package.json +1 -1
  27. package/src/claude/CLAUDE.md +3 -10
  28. package/src/claude/agents/brainstormer.md +7 -3
  29. package/src/claude/agents/code-auditor.md +35 -50
  30. package/src/claude/agents/debugger.md +5 -79
  31. package/src/claude/agents/deployer.md +3 -5
  32. package/src/claude/agents/docs-keeper.md +2 -7
  33. package/src/claude/agents/implementer.md +6 -9
  34. package/src/claude/agents/inspector.md +2 -3
  35. package/src/claude/agents/project-manager.md +7 -15
  36. package/src/claude/agents/researcher.md +9 -3
  37. package/src/claude/agents/spec-maker.md +11 -11
  38. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
  39. package/src/claude/hooks/__tests__/spec-gate.test.js +74 -3
  40. package/src/claude/hooks/__tests__/spec-state-touched.test.js +134 -0
  41. package/src/claude/hooks/__tests__/state.test.js +68 -0
  42. package/src/claude/hooks/__tests__/statusline.test.js +140 -11
  43. package/src/claude/hooks/agent.cjs +2 -1
  44. package/src/claude/hooks/lib/context.cjs +0 -89
  45. package/src/claude/hooks/lib/hook-payload.cjs +2 -2
  46. package/src/claude/hooks/rules.cjs +2 -1
  47. package/src/claude/hooks/session.cjs +0 -1
  48. package/src/claude/hooks/spec-gate.cjs +17 -28
  49. package/src/claude/hooks/spec-state.cjs +95 -1
  50. package/src/claude/hooks/state.cjs +21 -2
  51. package/src/claude/migration-manifest.json +30 -15
  52. package/src/claude/rules/ai-dev-rules.md +1 -1
  53. package/src/claude/rules/hook-protocols.md +8 -15
  54. package/src/claude/rules/manage-docs.md +9 -23
  55. package/src/claude/rules/orchestrator.md +3 -4
  56. package/src/claude/rules/review-audit-self-decision.md +3 -3
  57. package/src/claude/rules/skill-domain-routing.md +1 -3
  58. package/src/claude/rules/skill-workflow-routing.md +4 -5
  59. package/src/claude/rules/state-sync.md +3 -12
  60. package/src/claude/rules/workflow.md +10 -18
  61. package/src/claude/runtime.schema.json +5 -3
  62. package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
  63. package/src/claude/scripts/provenance.cjs +23 -5
  64. package/src/claude/scripts/spec-receipt.cjs +24 -7
  65. package/src/claude/scripts/spec-resolver.cjs +1 -2
  66. package/src/claude/scripts/validate-spec-output.cjs +5 -32
  67. package/src/claude/settings/settings.json +1 -32
  68. package/src/claude/skills/{question → ask}/SKILL.md +9 -75
  69. package/src/claude/skills/brainstorm/SKILL.md +43 -131
  70. package/src/claude/skills/code-review/SKILL.md +48 -17
  71. package/src/claude/skills/code-review/references/spec-compliance-review.md +2 -3
  72. package/src/claude/skills/code-review/references/verification-gate.md +22 -27
  73. package/src/claude/skills/debug/SKILL.md +29 -149
  74. package/src/claude/skills/develop/SKILL.md +27 -36
  75. package/src/claude/skills/develop/references/parallel-waves.md +0 -8
  76. package/src/claude/skills/develop/references/quality-gate.md +15 -13
  77. package/src/claude/skills/docs/SKILL.md +2 -2
  78. package/src/claude/skills/docs/references/update-workflow.md +1 -1
  79. package/src/claude/skills/fix/SKILL.md +153 -0
  80. package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
  81. package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
  82. package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
  83. package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
  84. package/src/claude/skills/git/SKILL.md +48 -9
  85. package/src/claude/skills/git/references/commit-protocols.md +35 -16
  86. package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
  87. package/src/claude/skills/orca/SKILL.md +9 -8
  88. package/src/claude/skills/research/SKILL.md +2 -0
  89. package/src/claude/skills/scout/SKILL.md +61 -0
  90. package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
  91. package/src/claude/skills/specs/SKILL.md +22 -29
  92. package/src/claude/skills/specs/references/review.md +8 -6
  93. package/src/claude/skills/specs/references/templates.md +34 -41
  94. package/src/claude/skills/sync/SKILL.md +8 -15
  95. package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
  96. package/src/claude/skills/sync/references/sync-protocols.md +4 -13
  97. package/src/claude/skills/test/SKILL.md +22 -3
  98. package/src/claude/skills/test/references/execution-strategy.md +83 -0
  99. package/src/claude/skills/test/references/failure-triage.md +1 -1
  100. package/src/claude/skills/ui-ux/LICENSE +21 -0
  101. package/src/claude/skills/ui-ux/SKILL.md +497 -0
  102. package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
  103. package/src/claude/skills/ui-ux/references/budgets.md +113 -0
  104. package/src/claude/skills/ui-ux/references/checklist.md +271 -0
  105. package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
  106. package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
  107. package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
  108. package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
  109. package/src/claude/skills/ui-ux/references/components/button.md +142 -0
  110. package/src/claude/skills/ui-ux/references/components/card.md +113 -0
  111. package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
  112. package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
  113. package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
  114. package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
  115. package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
  116. package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
  117. package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
  118. package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
  119. package/src/claude/skills/ui-ux/references/components/input.md +161 -0
  120. package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
  121. package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
  122. package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
  123. package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
  124. package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
  125. package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
  126. package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
  127. package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
  128. package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
  129. package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
  130. package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
  131. package/src/claude/skills/ui-ux/references/design-process.md +650 -0
  132. package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
  133. package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
  134. package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
  135. package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
  136. package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
  137. package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
  138. package/src/claude/skills/ui-ux/references/principles.md +408 -0
  139. package/src/claude/skills/ui-ux/references/refactor.md +245 -0
  140. package/src/claude/skills/ui-ux/references/responsive.md +195 -0
  141. package/src/claude/skills/ui-ux/references/review.md +528 -0
  142. package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
  143. package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
  144. package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
  145. package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
  146. package/src/claude/skills/ui-ux/references/styles.md +311 -0
  147. package/src/claude/skills/ui-ux/references/system.md +219 -0
  148. package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
  149. package/src/claude/skills/ui-ux/references/tokens.css +362 -0
  150. package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
  151. package/src/claude/skills/ui-ux/scripts/package.json +9 -0
  152. package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
  153. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  154. package/src/claude/status.cjs +149 -144
  155. package/src/codex/AGENTS.md +8 -13
  156. package/src/codex/hooks/agent.cjs +2 -1
  157. package/src/codex/hooks/lib/spec-receipt.cjs +2 -2
  158. package/src/codex/hooks/lib/spec-utils.cjs +0 -7
  159. package/src/codex/hooks/rules.cjs +2 -1
  160. package/src/codex/hooks/session.cjs +0 -2
  161. package/src/codex/hooks/spec-gate.cjs +7 -18
  162. package/src/codex/hooks.json +0 -20
  163. package/src/codex/rules/state-sync.md +3 -12
  164. package/src/common/AGENTS.md +2 -2
  165. package/src/omp/extensions/cafekit-bridge.mjs +6 -6
  166. package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
  167. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
  168. package/src/claude/hooks/completion-authority-check.cjs +0 -56
  169. package/src/claude/hooks/completion-authority-state.cjs +0 -527
  170. package/src/claude/hooks/completion-authority.cjs +0 -163
  171. package/src/claude/hooks/semantic-review-authority.cjs +0 -154
  172. package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
  173. package/src/claude/hooks/usage.cjs +0 -197
  174. package/src/claude/skills/delegate/SKILL.md +0 -124
  175. package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
  176. package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
  177. package/src/claude/skills/hotfix/SKILL.md +0 -289
  178. package/src/claude/skills/inspect/SKILL.md +0 -142
  179. package/src/claude/skills/loop/SKILL.md +0 -114
  180. package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
  181. package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
  182. package/src/claude/skills/route/SKILL.md +0 -94
  183. package/src/claude/skills/route/references/agent-timing.md +0 -46
  184. package/src/claude/skills/route/references/chaining-patterns.md +0 -53
  185. package/src/claude/skills/route/references/task-taxonomy.md +0 -48
  186. package/src/codex/hooks/completion-authority-check.cjs +0 -67
  187. package/src/codex/hooks/completion-authority-state.cjs +0 -527
  188. package/src/codex/hooks/completion-authority.cjs +0 -109
  189. package/src/codex/hooks/semantic-review-authority.cjs +0 -130
  190. package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
  191. /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
  192. /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
  193. /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ // Soát câu chữ của skill trước khi giao: bắt những câu nói màu, mức nặng của nút mà không trỏ về
3
+ // luật gốc, và những lý lẽ chủ dự án đã bác (references/locked-rules.md) quay lại trong spec.
4
+ // Máy chỉ bắt được chữ, không hiểu nghĩa: không báo gì chưa chắc là đúng, báo thì phải đọc lại.
5
+ //
6
+ // node skills/ui-ux/scripts/lint-skill.mjs chỉ các dòng mới thêm/sửa so với HEAD
7
+ // node skills/ui-ux/scripts/lint-skill.mjs --all toàn bộ file .md của skill (để rà lại)
8
+ // node skills/ui-ux/scripts/lint-skill.mjs <file.md>… toàn bộ các file chỉ định
9
+ //
10
+ // Chạy từ gốc repo evondevKit. Có lỗi thì thoát mã 1.
11
+
12
+ import { execFileSync } from "node:child_process";
13
+ import { readdirSync, readFileSync, statSync } from "node:fs";
14
+ import { join, relative } from "node:path";
15
+
16
+ const skillDir = "skills/ui-ux";
17
+
18
+ // Lý lẽ từng dẫn tới bản sai (locked-rules.md, dòng 1 và 3).
19
+ const rejectedArgumentPattern =
20
+ /không mất (dữ liệu|gì)|lấy lại được|nối lại được|dùng (được )?tới hết kỳ|nhiều app (để|làm)|số đông (để|làm) (nó )?trung tính/i;
21
+ // Câu đang hạ một việc xuống trung tính.
22
+ const downgradePattern = /không đỏ|trung tính|không `?rose|icon xám|nút (xác nhận )?`?primary`? đen/i;
23
+ // Câu đang ghi lại chính lý lẽ đó để bác nó, không phải dùng nó.
24
+ const rebuttalPattern = /bác|đã dính|chưa đủ|không làm .{0,40}hết nguy hiểm|vẫn đỏ|đừng dùng lại|không lật/i;
25
+
26
+ // Câu quyết màu nguy hiểm cho một nút hay một hộp.
27
+ const dangerDecisionPattern =
28
+ /không đỏ|nút nguy hiểm|isDestructive|nền `?rose-500\/1[05]|chữ `?rose-700|đỏ như hộp xoá|không `?rose/i;
29
+ // Câu chọn nút nền đặc.
30
+ // Chỉ câu gán vai nút chính ("là `primary`", "một nút `primary`", nút `primary` "Nhãn"), không câu mô tả
31
+ // ("đứng cạnh nút `primary`", "nút `primary` đổi sang `primary-hover`": báo nhầm 27/09/2026).
32
+ const primaryDecisionPattern = /(là|thành|sang|dùng) (nút )?`primary`(?!-)|(một|duy nhất) nút `primary`|nút `primary` (duy nhất|["“])|`primary` "/;
33
+ // "Không đỏ" của một thứ không phải hành động (tiêu đề lỗi, chấm chưa đọc, câu "đã dừng") dựa vào luật màu
34
+ // mang nghĩa (M4, M7, M30), không phải I4: gắn I4 vào đó là sai nghĩa.
35
+ const dangerRulePattern = /\bI4\b|\bM(4|7|30)\b|locked-rules/;
36
+ const primaryRulePattern = /\bI[23]\b|ngoại lệ có tên|locked-rules/;
37
+
38
+ function listSkillMarkdown(directory) {
39
+ return readdirSync(directory).flatMap((name) => {
40
+ const path = join(directory, name);
41
+
42
+ if (statSync(path).isDirectory()) return listSkillMarkdown(path);
43
+
44
+ return path.endsWith(".md") ? [path] : [];
45
+ });
46
+ }
47
+
48
+ // Số dòng (tính từ 1) đã thêm hoặc sửa trong mỗi file so với HEAD, kể cả file chưa track.
49
+ function collectChangedLines() {
50
+ const changedLines = new Map();
51
+ const diff = execFileSync("git", ["diff", "HEAD", "--unified=0", "--no-color", "--", skillDir], {
52
+ encoding: "utf8",
53
+ });
54
+ let currentFile = "";
55
+
56
+ for (const line of diff.split("\n")) {
57
+ if (line.startsWith("+++ ")) {
58
+ currentFile = line.slice(4).replace(/^b\//, "");
59
+ continue;
60
+ }
61
+
62
+ const hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
63
+
64
+ if (hunk && currentFile.endsWith(".md")) {
65
+ const start = Number(hunk[1]);
66
+ const count = hunk[2] === undefined ? 1 : Number(hunk[2]);
67
+ const lines = changedLines.get(currentFile) ?? new Set();
68
+
69
+ for (let offset = 0; offset < count; offset++) lines.add(start + offset);
70
+ changedLines.set(currentFile, lines);
71
+ }
72
+ }
73
+
74
+ const untracked = execFileSync("git", ["ls-files", "--others", "--exclude-standard", "--", skillDir], {
75
+ encoding: "utf8",
76
+ })
77
+ .split("\n")
78
+ .filter((path) => path.endsWith(".md"));
79
+
80
+ for (const path of untracked) changedLines.set(path, "all");
81
+
82
+ return changedLines;
83
+ }
84
+
85
+ // Chia file thành khối: một gạch đầu dòng (kể cả các dòng thụt nối theo), một dòng bảng, hoặc
86
+ // một đoạn văn. Luật và lý do thường nằm cùng khối, không cùng dòng. Mỗi khối mang thêm chữ của
87
+ // các khối cha (đoạn dẫn ngay trên danh sách, gạch đầu dòng thụt ít hơn): gạch con dưới dòng
88
+ // "Lý lẽ đã bị bác:" hay dưới một gạch đã ghi `I4` thì đọc cả phần cha.
89
+ function splitBlocks(text) {
90
+ const blocks = [];
91
+ let current = null;
92
+ let parents = [];
93
+ // Luật hay mục đang đứng trong (dòng "**I4. …**" hoặc tiêu đề #): bảng và đoạn nằm trong
94
+ // chính luật I4 thì đã ở đúng chỗ, không cần ghi lại mã.
95
+ let sectionText = "";
96
+ let isInCodeFence = false;
97
+
98
+ text.split("\n").forEach((line, index) => {
99
+ const lineNumber = index + 1;
100
+
101
+ if (/^\s*```/.test(line)) {
102
+ isInCodeFence = !isInCodeFence;
103
+ current = null;
104
+ parents = [];
105
+ return;
106
+ }
107
+
108
+ if (isInCodeFence) return;
109
+
110
+ if (!line.trim()) {
111
+ current = null;
112
+ parents = [];
113
+ return;
114
+ }
115
+
116
+ if (/^\*\*[A-Z]\d+[a-z]?\.|^#/.test(line)) sectionText = line;
117
+
118
+ const isBullet = /^\s*([-*]|\d+\.)\s/.test(line);
119
+ const isBlockStart = isBullet || /^\s*\||^#/.test(line);
120
+
121
+ if (isBlockStart || !current) {
122
+ // Đoạn văn đứng trước mọi gạch đầu dòng, nên thụt -1.
123
+ const indent = isBullet ? line.match(/^\s*/)[0].length : -1;
124
+
125
+ parents = parents.filter((parent) => parent.indent < indent);
126
+ current = {
127
+ startLine: lineNumber,
128
+ endLine: lineNumber,
129
+ text: line,
130
+ indent,
131
+ contextText: [sectionText, ...parents.map((parent) => parent.text)].join("\n"),
132
+ };
133
+ blocks.push(current);
134
+ parents.push(current);
135
+ return;
136
+ }
137
+
138
+ current.endLine = lineNumber;
139
+ current.text += `\n${line}`;
140
+ });
141
+
142
+ return blocks;
143
+ }
144
+
145
+ function lintBlock(block) {
146
+ const problems = [];
147
+ const { text } = block;
148
+ const fullText = `${block.contextText}\n${text}`;
149
+ // "Không dùng khuôn … nút `primary` đen" là câu bác, không phải câu chọn primary.
150
+ const textWithoutNegatedPrimary = text.replace(/không[^.;:]{0,60}`primary`/gi, "");
151
+
152
+ if (rejectedArgumentPattern.test(text) && downgradePattern.test(text) && !rebuttalPattern.test(fullText)) {
153
+ problems.push(
154
+ 'Dùng lý lẽ đã bị bác để hạ một việc xuống trung tính ("không mất dữ liệu", "lấy lại được", "tới hết kỳ"…). Xét lại ba câu của I4, xem locked-rules.md.',
155
+ );
156
+ }
157
+
158
+ if (dangerDecisionPattern.test(text) && !dangerRulePattern.test(fullText)) {
159
+ problems.push("Quyết màu nguy hiểm (đỏ hay không đỏ) mà không ghi mã I4 trong khối này hay khối cha.");
160
+ }
161
+
162
+ if (primaryDecisionPattern.test(textWithoutNegatedPrimary) && !primaryRulePattern.test(fullText)) {
163
+ problems.push('Chọn nút `primary` mà không ghi I2/I3 (hay "ngoại lệ có tên") trong khối này hay khối cha.');
164
+ }
165
+
166
+ return problems;
167
+ }
168
+
169
+ function lintFile(path, changedLines) {
170
+ const blocks = splitBlocks(readFileSync(path, "utf8"));
171
+ const findings = [];
172
+
173
+ for (const block of blocks) {
174
+ if (changedLines !== "all") {
175
+ let isTouched = false;
176
+
177
+ for (let line = block.startLine; line <= block.endLine; line++) {
178
+ if (changedLines.has(line)) isTouched = true;
179
+ }
180
+
181
+ if (!isTouched) continue;
182
+ }
183
+
184
+ for (const problem of lintBlock(block)) findings.push({ path, line: block.startLine, problem, text: block.text });
185
+ }
186
+
187
+ return findings;
188
+ }
189
+
190
+ const args = process.argv.slice(2);
191
+ const isAll = args.includes("--all");
192
+ const explicitFiles = args.filter((arg) => !arg.startsWith("--"));
193
+ let targets;
194
+
195
+ if (explicitFiles.length) targets = new Map(explicitFiles.map((path) => [path, "all"]));
196
+ else if (isAll) targets = new Map(listSkillMarkdown(skillDir).map((path) => [path, "all"]));
197
+ else targets = collectChangedLines();
198
+
199
+ // locked-rules.md là chỗ ghi các lý lẽ đã bác, chính nó không phải spec.
200
+ const findings = [...targets]
201
+ .filter(([path]) => !path.endsWith("locked-rules.md"))
202
+ .flatMap(([path, changedLines]) => lintFile(path, changedLines));
203
+
204
+ if (!findings.length) {
205
+ console.log(`lint-skill: không bắt được câu nào (${targets.size} file). Máy chỉ đọc chữ, vẫn phải tự đọc lại.`);
206
+ process.exit(0);
207
+ }
208
+
209
+ for (const finding of findings) {
210
+ const excerpt = finding.text.replace(/\s+/g, " ").slice(0, 220);
211
+
212
+ console.log(`${relative(process.cwd(), finding.path)}:${finding.line}\n ${finding.problem}\n > ${excerpt}\n`);
213
+ }
214
+
215
+ console.log(`lint-skill: ${findings.length} chỗ cần đọc lại.`);
216
+ process.exit(1);
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "cafekit-skill-ui-ux",
3
+ "version": "0.3.13",
4
+ "private": true,
5
+ "description": "Node dependencies for the CafeKit ui-ux skill (probe.mjs).",
6
+ "dependencies": {
7
+ "playwright": "^1.40.0"
8
+ }
9
+ }