@dewtech/dare-cli 3.11.0 → 3.12.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 (308) hide show
  1. package/README.md +2 -0
  2. package/dist/__tests__/ensure-skills.test.js +5 -0
  3. package/dist/__tests__/ensure-skills.test.js.map +1 -1
  4. package/dist/__tests__/ide-command-parity.test.js +1 -0
  5. package/dist/__tests__/ide-command-parity.test.js.map +1 -1
  6. package/dist/__tests__/project-generator.test.js +17 -0
  7. package/dist/__tests__/project-generator.test.js.map +1 -1
  8. package/dist/__tests__/reverse-facts.test.js +1 -0
  9. package/dist/__tests__/reverse-facts.test.js.map +1 -1
  10. package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
  11. package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
  12. package/dist/__tests__/terminal-parity-regression.test.js +116 -0
  13. package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
  14. package/dist/__tests__/terminal-parity.test.d.ts +2 -0
  15. package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
  16. package/dist/__tests__/terminal-parity.test.js +81 -0
  17. package/dist/__tests__/terminal-parity.test.js.map +1 -0
  18. package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
  19. package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
  20. package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
  21. package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
  22. package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
  23. package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
  24. package/dist/agent/__tests__/codex-driver.test.js +68 -0
  25. package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
  26. package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
  27. package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
  28. package/dist/agent/__tests__/cursor-driver.test.js +52 -0
  29. package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
  30. package/dist/agent/driver.d.ts +1 -1
  31. package/dist/agent/driver.d.ts.map +1 -1
  32. package/dist/agent/drivers/antigravity.d.ts +8 -0
  33. package/dist/agent/drivers/antigravity.d.ts.map +1 -0
  34. package/dist/agent/drivers/antigravity.js +99 -0
  35. package/dist/agent/drivers/antigravity.js.map +1 -0
  36. package/dist/agent/drivers/codex.d.ts +12 -0
  37. package/dist/agent/drivers/codex.d.ts.map +1 -0
  38. package/dist/agent/drivers/codex.js +137 -0
  39. package/dist/agent/drivers/codex.js.map +1 -0
  40. package/dist/agent/drivers/cursor.d.ts +8 -0
  41. package/dist/agent/drivers/cursor.d.ts.map +1 -0
  42. package/dist/agent/drivers/cursor.js +99 -0
  43. package/dist/agent/drivers/cursor.js.map +1 -0
  44. package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
  45. package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
  46. package/dist/ai/__tests__/ai-core.test.js +41 -0
  47. package/dist/ai/__tests__/ai-core.test.js.map +1 -0
  48. package/dist/ai/__tests__/parity.test.d.ts +2 -0
  49. package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
  50. package/dist/ai/__tests__/parity.test.js +36 -0
  51. package/dist/ai/__tests__/parity.test.js.map +1 -0
  52. package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
  53. package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
  54. package/dist/ai/__tests__/pipeline.test.js +147 -0
  55. package/dist/ai/__tests__/pipeline.test.js.map +1 -0
  56. package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
  57. package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
  58. package/dist/ai/__tests__/refine-bridge.test.js +17 -0
  59. package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
  60. package/dist/ai/__tests__/resolve.test.d.ts +2 -0
  61. package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
  62. package/dist/ai/__tests__/resolve.test.js +42 -0
  63. package/dist/ai/__tests__/resolve.test.js.map +1 -0
  64. package/dist/ai/capabilities.d.ts +3 -0
  65. package/dist/ai/capabilities.d.ts.map +1 -0
  66. package/dist/ai/capabilities.js +11 -0
  67. package/dist/ai/capabilities.js.map +1 -0
  68. package/dist/ai/command-options.d.ts +10 -0
  69. package/dist/ai/command-options.d.ts.map +1 -0
  70. package/dist/ai/command-options.js +15 -0
  71. package/dist/ai/command-options.js.map +1 -0
  72. package/dist/ai/config.d.ts +27 -0
  73. package/dist/ai/config.d.ts.map +1 -0
  74. package/dist/ai/config.js +89 -0
  75. package/dist/ai/config.js.map +1 -0
  76. package/dist/ai/parity.d.ts +13 -0
  77. package/dist/ai/parity.d.ts.map +1 -0
  78. package/dist/ai/parity.js +87 -0
  79. package/dist/ai/parity.js.map +1 -0
  80. package/dist/ai/parse-json-output.d.ts +5 -0
  81. package/dist/ai/parse-json-output.d.ts.map +1 -0
  82. package/dist/ai/parse-json-output.js +25 -0
  83. package/dist/ai/parse-json-output.js.map +1 -0
  84. package/dist/ai/pipeline.d.ts +20 -0
  85. package/dist/ai/pipeline.d.ts.map +1 -0
  86. package/dist/ai/pipeline.js +303 -0
  87. package/dist/ai/pipeline.js.map +1 -0
  88. package/dist/ai/prompts.d.ts +6 -0
  89. package/dist/ai/prompts.d.ts.map +1 -0
  90. package/dist/ai/prompts.js +49 -0
  91. package/dist/ai/prompts.js.map +1 -0
  92. package/dist/ai/providers.d.ts +63 -0
  93. package/dist/ai/providers.d.ts.map +1 -0
  94. package/dist/ai/providers.js +297 -0
  95. package/dist/ai/providers.js.map +1 -0
  96. package/dist/ai/refine-bridge.d.ts +5 -0
  97. package/dist/ai/refine-bridge.d.ts.map +1 -0
  98. package/dist/ai/refine-bridge.js +14 -0
  99. package/dist/ai/refine-bridge.js.map +1 -0
  100. package/dist/ai/registry.d.ts +12 -0
  101. package/dist/ai/registry.d.ts.map +1 -0
  102. package/dist/ai/registry.js +43 -0
  103. package/dist/ai/registry.js.map +1 -0
  104. package/dist/ai/resolve.d.ts +28 -0
  105. package/dist/ai/resolve.d.ts.map +1 -0
  106. package/dist/ai/resolve.js +83 -0
  107. package/dist/ai/resolve.js.map +1 -0
  108. package/dist/ai/schemas.d.ts +175 -0
  109. package/dist/ai/schemas.d.ts.map +1 -0
  110. package/dist/ai/schemas.js +199 -0
  111. package/dist/ai/schemas.js.map +1 -0
  112. package/dist/ai/types.d.ts +52 -0
  113. package/dist/ai/types.d.ts.map +1 -0
  114. package/dist/ai/types.js +8 -0
  115. package/dist/ai/types.js.map +1 -0
  116. package/dist/bin/dare.js +2 -0
  117. package/dist/bin/dare.js.map +1 -1
  118. package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
  119. package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
  120. package/dist/commands/__tests__/ai-command.test.js +68 -0
  121. package/dist/commands/__tests__/ai-command.test.js.map +1 -0
  122. package/dist/commands/__tests__/execute-agent.test.js +82 -0
  123. package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
  124. package/dist/commands/ai.d.ts +3 -0
  125. package/dist/commands/ai.d.ts.map +1 -0
  126. package/dist/commands/ai.js +141 -0
  127. package/dist/commands/ai.js.map +1 -0
  128. package/dist/commands/blueprint.d.ts.map +1 -1
  129. package/dist/commands/blueprint.js +17 -3
  130. package/dist/commands/blueprint.js.map +1 -1
  131. package/dist/commands/design.d.ts.map +1 -1
  132. package/dist/commands/design.js +21 -2
  133. package/dist/commands/design.js.map +1 -1
  134. package/dist/commands/discover.d.ts.map +1 -1
  135. package/dist/commands/discover.js +9 -1
  136. package/dist/commands/discover.js.map +1 -1
  137. package/dist/commands/dna.d.ts.map +1 -1
  138. package/dist/commands/dna.js +23 -3
  139. package/dist/commands/dna.js.map +1 -1
  140. package/dist/commands/execute.d.ts +11 -0
  141. package/dist/commands/execute.d.ts.map +1 -1
  142. package/dist/commands/execute.js +111 -4
  143. package/dist/commands/execute.js.map +1 -1
  144. package/dist/commands/init.d.ts.map +1 -1
  145. package/dist/commands/init.js +1 -0
  146. package/dist/commands/init.js.map +1 -1
  147. package/dist/commands/migrate.d.ts.map +1 -1
  148. package/dist/commands/migrate.js +14 -2
  149. package/dist/commands/migrate.js.map +1 -1
  150. package/dist/commands/patterns.d.ts.map +1 -1
  151. package/dist/commands/patterns.js +14 -2
  152. package/dist/commands/patterns.js.map +1 -1
  153. package/dist/commands/refine.d.ts.map +1 -1
  154. package/dist/commands/refine.js +23 -2
  155. package/dist/commands/refine.js.map +1 -1
  156. package/dist/commands/reverse.d.ts.map +1 -1
  157. package/dist/commands/reverse.js +28 -3
  158. package/dist/commands/reverse.js.map +1 -1
  159. package/dist/commands/review.d.ts.map +1 -1
  160. package/dist/commands/review.js +25 -3
  161. package/dist/commands/review.js.map +1 -1
  162. package/dist/core/types/project.d.ts +1 -1
  163. package/dist/core/types/project.d.ts.map +1 -1
  164. package/dist/dag-runner/run_dag.d.ts +1 -1
  165. package/dist/dag-runner/run_dag.d.ts.map +1 -1
  166. package/dist/exec/safe-spawn.d.ts.map +1 -1
  167. package/dist/exec/safe-spawn.js +6 -1
  168. package/dist/exec/safe-spawn.js.map +1 -1
  169. package/dist/skills/bundled.d.ts +5 -0
  170. package/dist/skills/bundled.d.ts.map +1 -0
  171. package/dist/skills/bundled.js +34 -0
  172. package/dist/skills/bundled.js.map +1 -0
  173. package/dist/skills/commands/add.d.ts +1 -3
  174. package/dist/skills/commands/add.d.ts.map +1 -1
  175. package/dist/skills/commands/add.js +20 -3
  176. package/dist/skills/commands/add.js.map +1 -1
  177. package/dist/skills/tests/bundled.spec.d.ts +2 -0
  178. package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
  179. package/dist/skills/tests/bundled.spec.js +24 -0
  180. package/dist/skills/tests/bundled.spec.js.map +1 -0
  181. package/dist/types/UpdateManifest.types.d.ts +1 -1
  182. package/dist/types/UpdateManifest.types.d.ts.map +1 -1
  183. package/dist/utils/dag-converter.js +1 -1
  184. package/dist/utils/dag-converter.js.map +1 -1
  185. package/dist/utils/project-detector.d.ts +1 -0
  186. package/dist/utils/project-detector.d.ts.map +1 -1
  187. package/dist/utils/project-detector.js +8 -0
  188. package/dist/utils/project-detector.js.map +1 -1
  189. package/dist/utils/project-generator.d.ts +1 -1
  190. package/dist/utils/project-generator.d.ts.map +1 -1
  191. package/dist/utils/project-generator.js +23 -2
  192. package/dist/utils/project-generator.js.map +1 -1
  193. package/dist/utils/templates.d.ts +2 -0
  194. package/dist/utils/templates.d.ts.map +1 -1
  195. package/dist/utils/templates.js +74 -0
  196. package/dist/utils/templates.js.map +1 -1
  197. package/dist/verification/__tests__/safe-spawn.test.js +12 -0
  198. package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
  199. package/package.json +2 -1
  200. package/skills/dare-ax/generator.ts +325 -0
  201. package/skills/dare-ax/index.ts +19 -0
  202. package/skills/dare-ax/metrics.ts +352 -0
  203. package/skills/dare-ax/package-lock.json +1855 -0
  204. package/skills/dare-ax/package.json +50 -0
  205. package/skills/dare-ax/secret-detector.ts +123 -0
  206. package/skills/dare-ax/skill.yml +19 -0
  207. package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
  208. package/skills/dare-ax/tests/generator.spec.ts +193 -0
  209. package/skills/dare-ax/tests/metrics.spec.ts +394 -0
  210. package/skills/dare-ax/tests/validator.spec.ts +298 -0
  211. package/skills/dare-ax/tsconfig.json +18 -0
  212. package/skills/dare-ax/types.ts +79 -0
  213. package/skills/dare-ax/validator.ts +238 -0
  214. package/skills/dare-frontend-design/generator.ts +616 -0
  215. package/skills/dare-frontend-design/index.ts +25 -0
  216. package/skills/dare-frontend-design/linter.ts +227 -0
  217. package/skills/dare-frontend-design/metrics.ts +82 -0
  218. package/skills/dare-frontend-design/package-lock.json +1855 -0
  219. package/skills/dare-frontend-design/package.json +43 -0
  220. package/skills/dare-frontend-design/skill.yml +20 -0
  221. package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
  222. package/skills/dare-frontend-design/tsconfig.json +18 -0
  223. package/skills/dare-frontend-design/types.ts +62 -0
  224. package/skills/dare-layered-design/generator.ts +740 -0
  225. package/skills/dare-layered-design/index.ts +17 -0
  226. package/skills/dare-layered-design/linter.ts +462 -0
  227. package/skills/dare-layered-design/metrics.ts +409 -0
  228. package/skills/dare-layered-design/package-lock.json +1855 -0
  229. package/skills/dare-layered-design/package.json +50 -0
  230. package/skills/dare-layered-design/skill.yml +35 -0
  231. package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
  232. package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
  233. package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
  234. package/skills/dare-layered-design/tsconfig.json +18 -0
  235. package/skills/dare-layered-design/types.ts +48 -0
  236. package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
  237. package/skills/dare-llm-integration/index.ts +49 -0
  238. package/skills/dare-llm-integration/metrics.ts +107 -0
  239. package/skills/dare-llm-integration/package-lock.json +1855 -0
  240. package/skills/dare-llm-integration/package.json +49 -0
  241. package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
  242. package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
  243. package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
  244. package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
  245. package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
  246. package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
  247. package/skills/dare-llm-integration/skill.yml +23 -0
  248. package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
  249. package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
  250. package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
  251. package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
  252. package/skills/dare-llm-integration/tsconfig.json +23 -0
  253. package/skills/dare-llm-integration/types.ts +91 -0
  254. package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
  255. package/skills/dare-quality-telemetry/collect.ts +134 -0
  256. package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
  257. package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
  258. package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
  259. package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
  260. package/skills/dare-quality-telemetry/index.ts +18 -0
  261. package/skills/dare-quality-telemetry/metrics.ts +137 -0
  262. package/skills/dare-quality-telemetry/package-lock.json +1855 -0
  263. package/skills/dare-quality-telemetry/package.json +48 -0
  264. package/skills/dare-quality-telemetry/regression.ts +60 -0
  265. package/skills/dare-quality-telemetry/reporter.ts +132 -0
  266. package/skills/dare-quality-telemetry/skill.yml +18 -0
  267. package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
  268. package/skills/dare-quality-telemetry/tsconfig.json +19 -0
  269. package/skills/dare-quality-telemetry/types.ts +41 -0
  270. package/skills/dare-realtime/event_registry.ts +101 -0
  271. package/skills/dare-realtime/index.ts +30 -0
  272. package/skills/dare-realtime/metrics.ts +84 -0
  273. package/skills/dare-realtime/package-lock.json +1855 -0
  274. package/skills/dare-realtime/package.json +43 -0
  275. package/skills/dare-realtime/reconnect_strategy.ts +85 -0
  276. package/skills/dare-realtime/schema_validator.ts +80 -0
  277. package/skills/dare-realtime/skill.yml +21 -0
  278. package/skills/dare-realtime/subscription_manager.ts +106 -0
  279. package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
  280. package/skills/dare-realtime/tsconfig.json +18 -0
  281. package/skills/dare-realtime/types.ts +51 -0
  282. package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
  283. package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
  284. package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
  285. package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
  286. package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
  287. package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
  288. package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
  289. package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
  290. package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
  291. package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
  292. package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
  293. package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
  294. package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
  295. package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
  296. package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
  297. package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
  298. package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
  299. package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
  300. package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
  301. package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
  302. package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
  303. package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
  304. package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
  305. package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
  306. package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
  307. package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
  308. package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
@@ -0,0 +1,227 @@
1
+ /**
2
+ * dare-frontend-design — FrontendLinter
3
+ * Detects DARE frontend antipatterns in .tsx and .vue files.
4
+ *
5
+ * Rules:
6
+ * component-too-large — component file > 300 lines
7
+ * fetch-in-jsx — fetch() or axios. used directly in JSX/template
8
+ * (outside custom hooks/composables)
9
+ *
10
+ * License: MIT
11
+ */
12
+
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import type { LinterViolation, LinterResult } from './types.js';
16
+
17
+ const COMPONENT_MAX_LINES = 300;
18
+
19
+ /** File extensions treated as frontend component files */
20
+ const COMPONENT_EXTENSIONS = new Set(['.tsx', '.vue']);
21
+
22
+ /**
23
+ * Patterns that indicate a fetch/axios call is inside a hook or composable
24
+ * (e.g. the function name starts with "use").
25
+ * We detect this via function scope heuristic.
26
+ */
27
+ const HOOK_PATTERN = /function\s+use[A-Z]/;
28
+ const COMPOSABLE_PATTERN = /(?:export\s+(?:default\s+)?)?(?:const|function)\s+use[A-Z]/;
29
+
30
+ export class FrontendLinter {
31
+ /**
32
+ * Lint a single file and return any violations found.
33
+ */
34
+ lintFile(filePath: string): LinterViolation[] {
35
+ if (!fs.existsSync(filePath)) {
36
+ return [];
37
+ }
38
+
39
+ const content = fs.readFileSync(filePath, 'utf-8');
40
+ const lines = content.split('\n');
41
+ const violations: LinterViolation[] = [];
42
+
43
+ const ext = path.extname(filePath);
44
+ if (!COMPONENT_EXTENSIONS.has(ext)) {
45
+ return [];
46
+ }
47
+
48
+ // Rule 1: component-too-large
49
+ if (lines.length > COMPONENT_MAX_LINES) {
50
+ violations.push({
51
+ file: filePath,
52
+ line: 1,
53
+ rule: 'component-too-large',
54
+ message: `Component has ${lines.length} lines (max ${COMPONENT_MAX_LINES}). Split into smaller components.`,
55
+ severity: 'error',
56
+ });
57
+ }
58
+
59
+ // Rule 2: fetch-in-jsx — detect fetch() or axios. outside hooks/composables
60
+ const fetchViolations = detectInlineFetch(filePath, lines);
61
+ violations.push(...fetchViolations);
62
+
63
+ return violations;
64
+ }
65
+
66
+ /**
67
+ * Lint all .tsx and .vue files under a directory (recursively).
68
+ */
69
+ lintDirectory(dirPath: string): LinterResult {
70
+ if (!fs.existsSync(dirPath)) {
71
+ return { violations: [], filesChecked: 0, pass: true };
72
+ }
73
+
74
+ const files = collectComponentFiles(dirPath);
75
+ const violations: LinterViolation[] = [];
76
+
77
+ for (const file of files) {
78
+ violations.push(...this.lintFile(file));
79
+ }
80
+
81
+ return {
82
+ violations,
83
+ filesChecked: files.length,
84
+ pass: violations.filter((v) => v.severity === 'error').length === 0,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Lint a list of specific files.
90
+ */
91
+ lintFiles(filePaths: string[]): LinterResult {
92
+ const violations: LinterViolation[] = [];
93
+ let filesChecked = 0;
94
+
95
+ for (const filePath of filePaths) {
96
+ const ext = path.extname(filePath);
97
+ if (!COMPONENT_EXTENSIONS.has(ext)) continue;
98
+ filesChecked++;
99
+ violations.push(...this.lintFile(filePath));
100
+ }
101
+
102
+ return {
103
+ violations,
104
+ filesChecked,
105
+ pass: violations.filter((v) => v.severity === 'error').length === 0,
106
+ };
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Collect all .tsx and .vue files in a directory tree.
112
+ */
113
+ function collectComponentFiles(dirPath: string): string[] {
114
+ const results: string[] = [];
115
+
116
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
117
+ for (const entry of entries) {
118
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git') continue;
119
+
120
+ const fullPath = path.join(dirPath, entry.name);
121
+ if (entry.isDirectory()) {
122
+ results.push(...collectComponentFiles(fullPath));
123
+ } else if (COMPONENT_EXTENSIONS.has(path.extname(entry.name))) {
124
+ results.push(fullPath);
125
+ }
126
+ }
127
+
128
+ return results;
129
+ }
130
+
131
+ /**
132
+ * Detect fetch() or axios. calls that appear directly in JSX/template context
133
+ * (i.e., NOT inside a function named useXxx).
134
+ */
135
+ function detectInlineFetch(filePath: string, lines: string[]): LinterViolation[] {
136
+ const violations: LinterViolation[] = [];
137
+ const ext = path.extname(filePath);
138
+
139
+ // Inline fetch pattern: fetch( or axios. appearing in JSX/return context
140
+ const FETCH_REGEX = /\bfetch\s*\(|axios\./;
141
+
142
+ // Track scope: are we inside a hook/composable function?
143
+ // Simple heuristic: scan for useXxx function declarations above each match.
144
+ const content = lines.join('\n');
145
+ const inHookOrComposable = HOOK_PATTERN.test(content) || COMPOSABLE_PATTERN.test(content);
146
+
147
+ // For Vue files, the <script setup> with composable is considered safe if
148
+ // the file itself is a composable (useXxx naming).
149
+ const filename = path.basename(filePath, path.extname(filePath));
150
+ const isHookFile = /^use[A-Z]/.test(filename);
151
+
152
+ if (inHookOrComposable || isHookFile) {
153
+ // The file itself is a hook/composable — fetch calls are expected
154
+ return [];
155
+ }
156
+
157
+ // Scan each line for direct fetch/axios usage outside of function blocks
158
+ for (let i = 0; i < lines.length; i++) {
159
+ const line = lines[i];
160
+
161
+ // Skip comments
162
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
163
+
164
+ if (FETCH_REGEX.test(line)) {
165
+ // Check if this line is inside a local function named useXxx
166
+ // by scanning backwards for the enclosing function signature
167
+ const context = lines.slice(Math.max(0, i - 20), i + 1).join('\n');
168
+ const inLocalHook = HOOK_PATTERN.test(context) || COMPOSABLE_PATTERN.test(context);
169
+
170
+ if (!inLocalHook) {
171
+ // Additional check: is this in a .tsx return block or template?
172
+ const isInReturn = isInJSXOrTemplate(lines, i, ext);
173
+ if (isInReturn) {
174
+ violations.push({
175
+ file: filePath,
176
+ line: i + 1,
177
+ rule: 'fetch-in-jsx',
178
+ message: `Direct fetch() or axios. call found outside custom hook/composable at line ${i + 1}. Move to useXxx hook or composable.`,
179
+ severity: 'error',
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ return violations;
187
+ }
188
+
189
+ /**
190
+ * Heuristic: is line i inside a JSX/template context (not in a hook)?
191
+ * We check if:
192
+ * - For .tsx: the line is inside a return() block of a component function (not a useXxx)
193
+ * - For .vue: the line is in <template> or <script setup> (non-composable)
194
+ */
195
+ function isInJSXOrTemplate(lines: string[], lineIdx: number, ext: string): boolean {
196
+ const line = lines[lineIdx];
197
+
198
+ if (ext === '.vue') {
199
+ // Check if we are in <template> section
200
+ let inTemplate = false;
201
+ for (let i = 0; i <= lineIdx; i++) {
202
+ const l = lines[i].trim();
203
+ if (l === '<template>' || l.startsWith('<template')) inTemplate = true;
204
+ if (l === '</template>') inTemplate = false;
205
+ }
206
+ if (inTemplate) return true;
207
+
208
+ // In <script setup> directly (not in composable)
209
+ let inScript = false;
210
+ for (let i = 0; i <= lineIdx; i++) {
211
+ const l = lines[i].trim();
212
+ if (l.startsWith('<script')) inScript = true;
213
+ if (l === '</script>') inScript = false;
214
+ }
215
+ return inScript;
216
+ }
217
+
218
+ // .tsx: check if the fetch is in any non-hook function
219
+ // Simple: if any nearby context has JSX (<, />, return (
220
+ const context = lines.slice(Math.max(0, lineIdx - 5), lineIdx + 5).join('\n');
221
+ const hasJSX = /<[A-Z]|<\/|jsx|return\s*\(/.test(context);
222
+
223
+ // Also flag if fetch appears as a direct statement (not in arrow fn named useXxx)
224
+ const isDirectStatement = /^\s*(const|let|var)?\s*(await\s+)?fetch\s*\(|^\s*(const|let|var)\s+\w+\s*=\s*(await\s+)?fetch\s*\(/.test(line);
225
+
226
+ return hasJSX || isDirectStatement;
227
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * dare-frontend-design — Metrics collector
3
+ * Evaluates M-01 to M-04 for the dare-frontend-design skill.
4
+ * License: MIT
5
+ */
6
+
7
+ import type { MetricResult, FrontendMetricsInput } from './types.js';
8
+
9
+ export function collectFrontendMetrics(input: FrontendMetricsInput): MetricResult[] {
10
+ return [
11
+ checkM01(input),
12
+ checkM02(input),
13
+ checkM03(input),
14
+ checkM04(input),
15
+ ];
16
+ }
17
+
18
+ /**
19
+ * M-01: 0 components > 300 lines.
20
+ */
21
+ function checkM01(input: FrontendMetricsInput): MetricResult {
22
+ const pass = input.largeComponentCount === 0;
23
+
24
+ return {
25
+ id: 'M-01',
26
+ pass,
27
+ description: '100% of components < 300 lines',
28
+ detail: pass
29
+ ? `All ${input.totalComponentsChecked} component(s) within size limit`
30
+ : `${input.largeComponentCount}/${input.totalComponentsChecked} component(s) exceed 300 lines — split into smaller components`,
31
+ };
32
+ }
33
+
34
+ /**
35
+ * M-02: 0 fetch() calls inline in JSX/template.
36
+ */
37
+ function checkM02(input: FrontendMetricsInput): MetricResult {
38
+ const pass = input.inlineFetchCount === 0;
39
+
40
+ return {
41
+ id: 'M-02',
42
+ pass,
43
+ description: '0 fetch() calls inline in JSX/template',
44
+ detail: pass
45
+ ? 'All API calls are in custom hooks/composables'
46
+ : `${input.inlineFetchCount} inline fetch() call(s) detected — move to useXxx hook or composable`,
47
+ };
48
+ }
49
+
50
+ /**
51
+ * M-03: 100% of pages have error boundaries.
52
+ */
53
+ function checkM03(input: FrontendMetricsInput): MetricResult {
54
+ const pass = input.totalPages === 0 || input.pagesWithErrorBoundary === input.totalPages;
55
+
56
+ return {
57
+ id: 'M-03',
58
+ pass,
59
+ description: '100% of pages have error boundary',
60
+ detail: input.totalPages === 0
61
+ ? 'No pages found'
62
+ : pass
63
+ ? `All ${input.totalPages} page(s) have error boundaries`
64
+ : `${input.totalPages - input.pagesWithErrorBoundary} page(s) missing error boundary`,
65
+ };
66
+ }
67
+
68
+ /**
69
+ * M-04: Bundle config (vite.config.ts / webpack.config.js / etc.) exists.
70
+ */
71
+ function checkM04(input: FrontendMetricsInput): MetricResult {
72
+ const pass = input.bundleConfigExists;
73
+
74
+ return {
75
+ id: 'M-04',
76
+ pass,
77
+ description: 'Bundle config exists (for size monitoring)',
78
+ detail: pass
79
+ ? 'Bundle configuration found'
80
+ : 'No bundle config detected — add vite.config.ts or webpack.config.js to monitor bundle size',
81
+ };
82
+ }