@planu/cli 0.71.0 → 0.73.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 (283) hide show
  1. package/README.md +1 -1
  2. package/dist/config/license-plans.json +9 -2
  3. package/dist/config/tool-groups.json +5 -1
  4. package/dist/engine/actuals/git-analyzer.js +1 -1
  5. package/dist/engine/actuals/git-analyzer.js.map +1 -1
  6. package/dist/engine/agent-prompt-generator.d.ts +12 -0
  7. package/dist/engine/agent-prompt-generator.d.ts.map +1 -0
  8. package/dist/engine/agent-prompt-generator.js +171 -0
  9. package/dist/engine/agent-prompt-generator.js.map +1 -0
  10. package/dist/engine/ai-tool-detector/index.d.ts +6 -0
  11. package/dist/engine/ai-tool-detector/index.d.ts.map +1 -0
  12. package/dist/engine/ai-tool-detector/index.js +95 -0
  13. package/dist/engine/ai-tool-detector/index.js.map +1 -0
  14. package/dist/engine/ci-generator/planu-steps.d.ts.map +1 -1
  15. package/dist/engine/ci-generator/planu-steps.js +10 -11
  16. package/dist/engine/ci-generator/planu-steps.js.map +1 -1
  17. package/dist/engine/convention-scanner/codebase-scanner.d.ts +7 -0
  18. package/dist/engine/convention-scanner/codebase-scanner.d.ts.map +1 -0
  19. package/dist/engine/convention-scanner/codebase-scanner.js +258 -0
  20. package/dist/engine/convention-scanner/codebase-scanner.js.map +1 -0
  21. package/dist/engine/convention-scanner/convention-parser.d.ts +7 -0
  22. package/dist/engine/convention-scanner/convention-parser.d.ts.map +1 -0
  23. package/dist/engine/convention-scanner/convention-parser.js +195 -0
  24. package/dist/engine/convention-scanner/convention-parser.js.map +1 -0
  25. package/dist/engine/convention-scanner/index.d.ts +3 -0
  26. package/dist/engine/convention-scanner/index.d.ts.map +1 -0
  27. package/dist/engine/convention-scanner/index.js +5 -0
  28. package/dist/engine/convention-scanner/index.js.map +1 -0
  29. package/dist/engine/convention-sync.d.ts +9 -0
  30. package/dist/engine/convention-sync.d.ts.map +1 -0
  31. package/dist/engine/convention-sync.js +156 -0
  32. package/dist/engine/convention-sync.js.map +1 -0
  33. package/dist/engine/doc-generator/portal/portal-regenerator.d.ts +2 -1
  34. package/dist/engine/doc-generator/portal/portal-regenerator.d.ts.map +1 -1
  35. package/dist/engine/doc-generator/portal/portal-regenerator.js +23 -15
  36. package/dist/engine/doc-generator/portal/portal-regenerator.js.map +1 -1
  37. package/dist/engine/feedback/triage.d.ts +16 -0
  38. package/dist/engine/feedback/triage.d.ts.map +1 -0
  39. package/dist/engine/feedback/triage.js +165 -0
  40. package/dist/engine/feedback/triage.js.map +1 -0
  41. package/dist/engine/hooks-reconciler.d.ts +7 -0
  42. package/dist/engine/hooks-reconciler.d.ts.map +1 -0
  43. package/dist/engine/hooks-reconciler.js +214 -0
  44. package/dist/engine/hooks-reconciler.js.map +1 -0
  45. package/dist/engine/platform-registry.d.ts +17 -0
  46. package/dist/engine/platform-registry.d.ts.map +1 -0
  47. package/dist/engine/platform-registry.js +78 -0
  48. package/dist/engine/platform-registry.js.map +1 -0
  49. package/dist/engine/project-health-checker.d.ts +4 -2
  50. package/dist/engine/project-health-checker.d.ts.map +1 -1
  51. package/dist/engine/project-health-checker.js +78 -5
  52. package/dist/engine/project-health-checker.js.map +1 -1
  53. package/dist/engine/rules-generator/index.d.ts +14 -0
  54. package/dist/engine/rules-generator/index.d.ts.map +1 -0
  55. package/dist/engine/rules-generator/index.js +313 -0
  56. package/dist/engine/rules-generator/index.js.map +1 -0
  57. package/dist/engine/rules-reconciler.d.ts +13 -0
  58. package/dist/engine/rules-reconciler.d.ts.map +1 -0
  59. package/dist/engine/rules-reconciler.js +193 -0
  60. package/dist/engine/rules-reconciler.js.map +1 -0
  61. package/dist/engine/skill-generator/detect.d.ts +1 -0
  62. package/dist/engine/skill-generator/detect.d.ts.map +1 -1
  63. package/dist/engine/skill-generator/detect.js +13 -5
  64. package/dist/engine/skill-generator/detect.js.map +1 -1
  65. package/dist/engine/skills/skills-fetcher.d.ts +4 -0
  66. package/dist/engine/skills/skills-fetcher.d.ts.map +1 -0
  67. package/dist/engine/skills/skills-fetcher.js +102 -0
  68. package/dist/engine/skills/skills-fetcher.js.map +1 -0
  69. package/dist/engine/skills-reconciler.d.ts +15 -0
  70. package/dist/engine/skills-reconciler.d.ts.map +1 -0
  71. package/dist/engine/skills-reconciler.js +98 -0
  72. package/dist/engine/skills-reconciler.js.map +1 -0
  73. package/dist/engine/spec-template-cleaner.d.ts +12 -0
  74. package/dist/engine/spec-template-cleaner.d.ts.map +1 -0
  75. package/dist/engine/spec-template-cleaner.js +119 -0
  76. package/dist/engine/spec-template-cleaner.js.map +1 -0
  77. package/dist/engine/validator/analyzer.d.ts +4 -2
  78. package/dist/engine/validator/analyzer.d.ts.map +1 -1
  79. package/dist/engine/validator/analyzer.js +14 -3
  80. package/dist/engine/validator/analyzer.js.map +1 -1
  81. package/dist/engine/validator/criterion-enricher.d.ts +42 -0
  82. package/dist/engine/validator/criterion-enricher.d.ts.map +1 -0
  83. package/dist/engine/validator/criterion-enricher.js +186 -0
  84. package/dist/engine/validator/criterion-enricher.js.map +1 -0
  85. package/dist/engine/validator/deep-code-checker.d.ts +41 -0
  86. package/dist/engine/validator/deep-code-checker.d.ts.map +1 -0
  87. package/dist/engine/validator/deep-code-checker.js +363 -0
  88. package/dist/engine/validator/deep-code-checker.js.map +1 -0
  89. package/dist/engine/validator/extractors.d.ts +8 -1
  90. package/dist/engine/validator/extractors.d.ts.map +1 -1
  91. package/dist/engine/validator/extractors.js +33 -0
  92. package/dist/engine/validator/extractors.js.map +1 -1
  93. package/dist/engine/validator/index.d.ts +7 -0
  94. package/dist/engine/validator/index.d.ts.map +1 -0
  95. package/dist/engine/validator/index.js +8 -0
  96. package/dist/engine/validator/index.js.map +1 -0
  97. package/dist/engine/validator.d.ts.map +1 -1
  98. package/dist/engine/validator.js +13 -2
  99. package/dist/engine/validator.js.map +1 -1
  100. package/dist/engine/workers/schema.d.ts +58 -0
  101. package/dist/engine/workers/schema.d.ts.map +1 -0
  102. package/dist/engine/workers/schema.js +41 -0
  103. package/dist/engine/workers/schema.js.map +1 -0
  104. package/dist/engine/workers/worker-engine.js +1 -1
  105. package/dist/engine/workers/worker-engine.js.map +1 -1
  106. package/dist/index.js +10 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/storage/convention-baseline.d.ts +20 -0
  109. package/dist/storage/convention-baseline.d.ts.map +1 -0
  110. package/dist/storage/convention-baseline.js +57 -0
  111. package/dist/storage/convention-baseline.js.map +1 -0
  112. package/dist/storage/feedback-remote.d.ts +8 -0
  113. package/dist/storage/feedback-remote.d.ts.map +1 -0
  114. package/dist/storage/feedback-remote.js +27 -0
  115. package/dist/storage/feedback-remote.js.map +1 -0
  116. package/dist/storage/feedback-store.d.ts +27 -0
  117. package/dist/storage/feedback-store.d.ts.map +1 -0
  118. package/dist/storage/feedback-store.js +272 -0
  119. package/dist/storage/feedback-store.js.map +1 -0
  120. package/dist/storage/index.d.ts +1 -0
  121. package/dist/storage/index.d.ts.map +1 -1
  122. package/dist/storage/index.js +1 -0
  123. package/dist/storage/index.js.map +1 -1
  124. package/dist/tools/convention-sync.d.ts +8 -0
  125. package/dist/tools/convention-sync.d.ts.map +1 -0
  126. package/dist/tools/convention-sync.js +77 -0
  127. package/dist/tools/convention-sync.js.map +1 -0
  128. package/dist/tools/create-spec-hu/ac-adapters/convention-adapter.d.ts +9 -0
  129. package/dist/tools/create-spec-hu/ac-adapters/convention-adapter.d.ts.map +1 -0
  130. package/dist/tools/create-spec-hu/ac-adapters/convention-adapter.js +68 -0
  131. package/dist/tools/create-spec-hu/ac-adapters/convention-adapter.js.map +1 -0
  132. package/dist/tools/create-spec-hu/ac-adapters/custom-template-adapter.d.ts +14 -0
  133. package/dist/tools/create-spec-hu/ac-adapters/custom-template-adapter.d.ts.map +1 -0
  134. package/dist/tools/create-spec-hu/ac-adapters/custom-template-adapter.js +60 -0
  135. package/dist/tools/create-spec-hu/ac-adapters/custom-template-adapter.js.map +1 -0
  136. package/dist/tools/create-spec-hu/ac-adapters/index.d.ts +4 -0
  137. package/dist/tools/create-spec-hu/ac-adapters/index.d.ts.map +1 -1
  138. package/dist/tools/create-spec-hu/ac-adapters/index.js +9 -1
  139. package/dist/tools/create-spec-hu/ac-adapters/index.js.map +1 -1
  140. package/dist/tools/create-spec-hu/hu-body-generators.d.ts.map +1 -1
  141. package/dist/tools/create-spec-hu/hu-body-generators.js +15 -2
  142. package/dist/tools/create-spec-hu/hu-body-generators.js.map +1 -1
  143. package/dist/tools/create-spec.d.ts.map +1 -1
  144. package/dist/tools/create-spec.js +3 -2
  145. package/dist/tools/create-spec.js.map +1 -1
  146. package/dist/tools/delete-decision.d.ts.map +1 -1
  147. package/dist/tools/delete-decision.js +2 -0
  148. package/dist/tools/delete-decision.js.map +1 -1
  149. package/dist/tools/delete-project.d.ts.map +1 -1
  150. package/dist/tools/delete-project.js +11 -14
  151. package/dist/tools/delete-project.js.map +1 -1
  152. package/dist/tools/delete-spec.d.ts.map +1 -1
  153. package/dist/tools/delete-spec.js +11 -9
  154. package/dist/tools/delete-spec.js.map +1 -1
  155. package/dist/tools/estimate.d.ts.map +1 -1
  156. package/dist/tools/estimate.js +6 -5
  157. package/dist/tools/estimate.js.map +1 -1
  158. package/dist/tools/export-session.d.ts.map +1 -1
  159. package/dist/tools/export-session.js +7 -9
  160. package/dist/tools/export-session.js.map +1 -1
  161. package/dist/tools/feedback-handler.d.ts +5 -0
  162. package/dist/tools/feedback-handler.d.ts.map +1 -0
  163. package/dist/tools/feedback-handler.js +106 -0
  164. package/dist/tools/feedback-handler.js.map +1 -0
  165. package/dist/tools/generate-teammate-prompt.d.ts +8 -0
  166. package/dist/tools/generate-teammate-prompt.d.ts.map +1 -1
  167. package/dist/tools/generate-teammate-prompt.js +33 -0
  168. package/dist/tools/generate-teammate-prompt.js.map +1 -1
  169. package/dist/tools/init-project/handler.d.ts.map +1 -1
  170. package/dist/tools/init-project/handler.js +73 -15
  171. package/dist/tools/init-project/handler.js.map +1 -1
  172. package/dist/tools/init-project/helpers.d.ts.map +1 -1
  173. package/dist/tools/init-project/helpers.js +1 -2
  174. package/dist/tools/init-project/helpers.js.map +1 -1
  175. package/dist/tools/init-project/result-builder.d.ts.map +1 -1
  176. package/dist/tools/init-project/result-builder.js +13 -1
  177. package/dist/tools/init-project/result-builder.js.map +1 -1
  178. package/dist/tools/init-project/skill-auto-installer.d.ts +10 -0
  179. package/dist/tools/init-project/skill-auto-installer.d.ts.map +1 -0
  180. package/dist/tools/init-project/skill-auto-installer.js +28 -0
  181. package/dist/tools/init-project/skill-auto-installer.js.map +1 -0
  182. package/dist/tools/log-decision.d.ts.map +1 -1
  183. package/dist/tools/log-decision.js +15 -4
  184. package/dist/tools/log-decision.js.map +1 -1
  185. package/dist/tools/manage-trash.d.ts.map +1 -1
  186. package/dist/tools/manage-trash.js +13 -27
  187. package/dist/tools/manage-trash.js.map +1 -1
  188. package/dist/tools/reconcile-hooks.d.ts +7 -0
  189. package/dist/tools/reconcile-hooks.d.ts.map +1 -0
  190. package/dist/tools/reconcile-hooks.js +78 -0
  191. package/dist/tools/reconcile-hooks.js.map +1 -0
  192. package/dist/tools/reconcile-rules.d.ts +7 -0
  193. package/dist/tools/reconcile-rules.d.ts.map +1 -0
  194. package/dist/tools/reconcile-rules.js +86 -0
  195. package/dist/tools/reconcile-rules.js.map +1 -0
  196. package/dist/tools/reconcile-skills.d.ts +8 -0
  197. package/dist/tools/reconcile-skills.d.ts.map +1 -0
  198. package/dist/tools/reconcile-skills.js +70 -0
  199. package/dist/tools/reconcile-skills.js.map +1 -0
  200. package/dist/tools/register-feedback-tools.d.ts +3 -0
  201. package/dist/tools/register-feedback-tools.d.ts.map +1 -0
  202. package/dist/tools/register-feedback-tools.js +65 -0
  203. package/dist/tools/register-feedback-tools.js.map +1 -0
  204. package/dist/tools/register-spec-tools/core-spec-tools.d.ts.map +1 -1
  205. package/dist/tools/register-spec-tools/core-spec-tools.js +14 -4
  206. package/dist/tools/register-spec-tools/core-spec-tools.js.map +1 -1
  207. package/dist/tools/register-team-tools.d.ts.map +1 -1
  208. package/dist/tools/register-team-tools.js +35 -8
  209. package/dist/tools/register-team-tools.js.map +1 -1
  210. package/dist/tools/safe-handler.d.ts.map +1 -1
  211. package/dist/tools/safe-handler.js +20 -2
  212. package/dist/tools/safe-handler.js.map +1 -1
  213. package/dist/tools/schemas/index.d.ts +1 -0
  214. package/dist/tools/schemas/index.d.ts.map +1 -1
  215. package/dist/tools/schemas/index.js +1 -0
  216. package/dist/tools/schemas/index.js.map +1 -1
  217. package/dist/tools/schemas/reconcile-skills-schemas.d.ts +7 -0
  218. package/dist/tools/schemas/reconcile-skills-schemas.d.ts.map +1 -0
  219. package/dist/tools/schemas/reconcile-skills-schemas.js +18 -0
  220. package/dist/tools/schemas/reconcile-skills-schemas.js.map +1 -0
  221. package/dist/tools/schemas/workers-schema.d.ts +1 -56
  222. package/dist/tools/schemas/workers-schema.d.ts.map +1 -1
  223. package/dist/tools/schemas/workers-schema.js +6 -42
  224. package/dist/tools/schemas/workers-schema.js.map +1 -1
  225. package/dist/tools/skill-registry/search.d.ts +2 -1
  226. package/dist/tools/skill-registry/search.d.ts.map +1 -1
  227. package/dist/tools/skill-registry/search.js +81 -8
  228. package/dist/tools/skill-registry/search.js.map +1 -1
  229. package/dist/tools/suggest-tooling/skills-fetcher.d.ts +1 -3
  230. package/dist/tools/suggest-tooling/skills-fetcher.d.ts.map +1 -1
  231. package/dist/tools/suggest-tooling/skills-fetcher.js +3 -101
  232. package/dist/tools/suggest-tooling/skills-fetcher.js.map +1 -1
  233. package/dist/tools/token-recording.d.ts +16 -0
  234. package/dist/tools/token-recording.d.ts.map +1 -0
  235. package/dist/tools/token-recording.js +47 -0
  236. package/dist/tools/token-recording.js.map +1 -0
  237. package/dist/tools/update-status-convention-gate.d.ts +12 -0
  238. package/dist/tools/update-status-convention-gate.d.ts.map +1 -0
  239. package/dist/tools/update-status-convention-gate.js +159 -0
  240. package/dist/tools/update-status-convention-gate.js.map +1 -0
  241. package/dist/tools/update-status-reconcile.d.ts +7 -0
  242. package/dist/tools/update-status-reconcile.d.ts.map +1 -0
  243. package/dist/tools/update-status-reconcile.js +47 -0
  244. package/dist/tools/update-status-reconcile.js.map +1 -0
  245. package/dist/tools/update-status.d.ts.map +1 -1
  246. package/dist/tools/update-status.js +113 -67
  247. package/dist/tools/update-status.js.map +1 -1
  248. package/dist/tools/validate.d.ts.map +1 -1
  249. package/dist/tools/validate.js +92 -2
  250. package/dist/tools/validate.js.map +1 -1
  251. package/dist/types/ai-tool-rules.d.ts +42 -0
  252. package/dist/types/ai-tool-rules.d.ts.map +1 -0
  253. package/dist/types/ai-tool-rules.js +3 -0
  254. package/dist/types/ai-tool-rules.js.map +1 -0
  255. package/dist/types/analysis.d.ts +16 -0
  256. package/dist/types/analysis.d.ts.map +1 -1
  257. package/dist/types/conventions.d.ts +155 -0
  258. package/dist/types/conventions.d.ts.map +1 -0
  259. package/dist/types/conventions.js +3 -0
  260. package/dist/types/conventions.js.map +1 -0
  261. package/dist/types/feedback.d.ts +76 -0
  262. package/dist/types/feedback.d.ts.map +1 -0
  263. package/dist/types/feedback.js +3 -0
  264. package/dist/types/feedback.js.map +1 -0
  265. package/dist/types/index.d.ts +4 -0
  266. package/dist/types/index.d.ts.map +1 -1
  267. package/dist/types/index.js +1 -0
  268. package/dist/types/index.js.map +1 -1
  269. package/dist/types/mcp.d.ts +2 -0
  270. package/dist/types/mcp.d.ts.map +1 -1
  271. package/dist/types/project/inputs.d.ts +2 -0
  272. package/dist/types/project/inputs.d.ts.map +1 -1
  273. package/dist/types/project/planu-config.d.ts +7 -1
  274. package/dist/types/project/planu-config.d.ts.map +1 -1
  275. package/dist/types/skill-registry.d.ts +12 -0
  276. package/dist/types/skill-registry.d.ts.map +1 -1
  277. package/dist/types/verifiable-criteria.d.ts +50 -0
  278. package/dist/types/verifiable-criteria.d.ts.map +1 -0
  279. package/dist/types/verifiable-criteria.js +3 -0
  280. package/dist/types/verifiable-criteria.js.map +1 -0
  281. package/package.json +1 -1
  282. package/src/config/license-plans.json +9 -2
  283. package/src/config/tool-groups.json +5 -1
@@ -0,0 +1,186 @@
1
+ // engine/validator/criterion-enricher.ts — Enrich acceptance criteria with verify blocks (SPEC-186)
2
+ //
3
+ // Adds machine-verifiable `verify` blocks to acceptance criteria text.
4
+ // Verification metadata is stored as HTML comments in spec.md output —
5
+ // invisible in rendered markdown but fully parseable by tooling.
6
+ // ---------------------------------------------------------------------------
7
+ // Built-in templates — priority-ordered (first match wins)
8
+ // ---------------------------------------------------------------------------
9
+ /** Exported for extensibility: callers may add domain-specific templates. */
10
+ export const VERIFY_TEMPLATES = [
11
+ // i18n / hardcoded strings
12
+ {
13
+ keywords: ['hardcoded string', 'hardcoded text', 'raw string', 'literal string'],
14
+ type: 'grep_absent',
15
+ patternTemplate: '[áéíóúñ¿¡]|"[A-Z][a-z]+ [a-z]+"',
16
+ scopeTemplate: 'src/**/*.{tsx,jsx,vue}',
17
+ description: 'No hardcoded strings in components',
18
+ },
19
+ // Type safety
20
+ {
21
+ keywords: ['no any type', 'strict typing', 'no typescript any', 'no-any', 'no any'],
22
+ type: 'grep_absent',
23
+ patternTemplate: ':\\s*any[\\s,;>)]',
24
+ scopeTemplate: 'src/**/*.ts',
25
+ description: 'No any types in source',
26
+ },
27
+ // Security — no secrets
28
+ {
29
+ keywords: ['no secrets', 'no credentials', 'no api key', 'no password', 'no hardcoded secret'],
30
+ type: 'grep_absent',
31
+ patternTemplate: 'password\\s*=|api_key\\s*=|secret\\s*=',
32
+ scopeTemplate: 'src/**/*.ts',
33
+ description: 'No hardcoded secrets',
34
+ },
35
+ // Test coverage / test existence
36
+ {
37
+ keywords: ['test for every', 'test coverage', 'tests exist for', 'test file'],
38
+ type: 'file_exists',
39
+ scopeTemplate: 'tests/**/*.test.ts',
40
+ description: 'Test files exist for source files',
41
+ },
42
+ // File / line limits
43
+ {
44
+ keywords: ['no file exceeds', 'max lines', 'maximum lines', 'line limit', 'lines per file'],
45
+ type: 'metric_max',
46
+ expected: 300,
47
+ description: 'No file exceeds line limit',
48
+ },
49
+ // Error handling
50
+ {
51
+ keywords: ['error handling', 'handles errors', 'try catch', 'try/catch', 'error boundary'],
52
+ type: 'grep_present',
53
+ patternTemplate: 'try\\s*\\{|catch\\s*\\(',
54
+ description: 'Error handling present',
55
+ },
56
+ // Imports / dependency injection / architecture pattern
57
+ {
58
+ keywords: ['import from', 'uses dependency injection', 'follows pattern', 'injects'],
59
+ type: 'grep_present',
60
+ description: 'Pattern usage detected',
61
+ },
62
+ ];
63
+ // ---------------------------------------------------------------------------
64
+ // Internal helpers
65
+ // ---------------------------------------------------------------------------
66
+ /** Extract the first integer found in a string (e.g. "200 lines" → 200). */
67
+ function extractNumber(text) {
68
+ const match = /\b(\d+)\b/.exec(text);
69
+ return match?.[1] !== undefined ? parseInt(match[1], 10) : undefined;
70
+ }
71
+ /**
72
+ * Extract an explicit scope path from criterion text.
73
+ * Only matches directory paths that contain at least one slash (e.g. "src/components/").
74
+ * This prevents plain words like "source" or "target" from being treated as paths.
75
+ */
76
+ function extractScope(text) {
77
+ // Require at least one slash in the captured path to distinguish paths from words
78
+ const match = /\bin\s+([\w.*{},-]+(?:\/[\w.*{},-]*)+(?:\/\*\*)?)|under\s+([\w.*{},-]+(?:\/[\w.*{},-]*)+(?:\/\*\*)?)/i.exec(text);
79
+ const raw = match?.[1] ?? match?.[2];
80
+ if (!raw) {
81
+ return undefined;
82
+ }
83
+ // Normalize trailing slash before appending glob
84
+ const normalized = raw.replace(/\/+$/, '');
85
+ return normalized.endsWith('/**') || normalized.includes('*') ? normalized : `${normalized}/**`;
86
+ }
87
+ // ---------------------------------------------------------------------------
88
+ // Public API
89
+ // ---------------------------------------------------------------------------
90
+ /**
91
+ * Match a criterion text against the built-in (and optionally injected) templates.
92
+ * Returns the first matching template or null if none match.
93
+ */
94
+ export function matchTemplate(criterion) {
95
+ const lower = criterion.toLowerCase();
96
+ for (const template of VERIFY_TEMPLATES) {
97
+ if (template.keywords.some((kw) => lower.includes(kw))) {
98
+ return template;
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+ /**
104
+ * Build a VerifyBlock from a matched template, extracting numeric/scope hints
105
+ * from the criterion text when possible.
106
+ */
107
+ export function buildVerifyBlock(criterion, template) {
108
+ const block = {
109
+ type: template.type,
110
+ description: template.description,
111
+ };
112
+ if (template.patternTemplate !== undefined) {
113
+ block.pattern = template.patternTemplate;
114
+ }
115
+ // Prefer explicit scope from criterion text; fall back to template default
116
+ const scopeFromText = extractScope(criterion);
117
+ if (scopeFromText !== undefined) {
118
+ block.scope = scopeFromText;
119
+ }
120
+ else if (template.scopeTemplate !== undefined) {
121
+ block.scope = template.scopeTemplate;
122
+ }
123
+ // For metric checks, try to extract an explicit number from the criterion
124
+ if (template.type === 'metric_max' || template.type === 'metric_min') {
125
+ const extracted = extractNumber(criterion);
126
+ block.expected = extracted ?? template.expected;
127
+ }
128
+ else if (template.expected !== undefined) {
129
+ block.expected = template.expected;
130
+ }
131
+ return block;
132
+ }
133
+ /**
134
+ * Enrich a single criterion text with a VerifyBlock.
135
+ * Falls back to `manual` when no template matches.
136
+ */
137
+ export function enrichCriterion(criterion) {
138
+ const template = matchTemplate(criterion);
139
+ if (template !== null) {
140
+ return { text: criterion, verify: buildVerifyBlock(criterion, template) };
141
+ }
142
+ return {
143
+ text: criterion,
144
+ verify: { type: 'manual', description: 'Requires manual verification' },
145
+ };
146
+ }
147
+ /**
148
+ * Enrich a batch of criterion texts.
149
+ * Empty or whitespace-only entries are silently skipped.
150
+ */
151
+ export function enrichCriteria(criteria) {
152
+ return criteria.filter((c) => c.trim().length > 0).map((c) => enrichCriterion(c));
153
+ }
154
+ /**
155
+ * Parse raw markdown AC lines (e.g. "- [ ] Criterion text") into plain strings.
156
+ * Strips list prefixes so enrichers receive clean criterion text.
157
+ */
158
+ export function parseCriteriaText(lines) {
159
+ return lines
160
+ .map((line) => line.replace(/^\s*[-*+]\s+\[?\s*[xX ]?\s*\]?\s*/, '').trim())
161
+ .filter((line) => line.length > 0);
162
+ }
163
+ /**
164
+ * Format enriched criteria as markdown list items with embedded verify blocks.
165
+ *
166
+ * Output format:
167
+ * ```
168
+ * - [ ] Criterion text
169
+ * <!-- verify: {"type":"grep_absent","pattern":"...","scope":"..."} -->
170
+ * ```
171
+ *
172
+ * The `<!-- verify: ... -->` HTML comment is invisible in rendered markdown
173
+ * but fully parseable by automated tooling.
174
+ */
175
+ export function formatVerifiableCriteria(criteria) {
176
+ if (criteria.length === 0) {
177
+ return '';
178
+ }
179
+ return criteria
180
+ .map((c) => {
181
+ const verifyJson = JSON.stringify(c.verify);
182
+ return `- [ ] ${c.text}\n <!-- verify: ${verifyJson} -->`;
183
+ })
184
+ .join('\n');
185
+ }
186
+ //# sourceMappingURL=criterion-enricher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criterion-enricher.js","sourceRoot":"","sources":["../../../src/engine/validator/criterion-enricher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { DeepCheckResult, DeepCheckConfig, DeepCheckType, VerifyBlock } from '../../types/index.js';
2
+ export type { DeepCheckResult, DeepCheckConfig, DeepCheckType };
3
+ /**
4
+ * Classify criterion text into a check type using keyword heuristics.
5
+ * Returns the type and optional config derived from the criterion.
6
+ */
7
+ export declare function classifyCriterion(text: string): {
8
+ type: DeepCheckType;
9
+ config?: DeepCheckConfig;
10
+ };
11
+ /**
12
+ * Search for a regex pattern in files matching the scope glob.
13
+ * For grep_absent: passed when matchCount === 0.
14
+ * For grep_present: passed when matchCount >= expected.
15
+ */
16
+ export declare function grepCheck(config: DeepCheckConfig, projectPath: string, checkType: 'grep_absent' | 'grep_present'): DeepCheckResult;
17
+ /**
18
+ * Check that no file in scope exceeds the line threshold encoded in config.expected.
19
+ */
20
+ export declare function metricCheck(config: DeepCheckConfig, projectPath: string): DeepCheckResult;
21
+ /**
22
+ * Check that each source file has a corresponding test file.
23
+ * sourceGlob: e.g. "src/**\/*.ts"
24
+ * testGlob: e.g. "tests/**\/*.test.ts"
25
+ */
26
+ export declare function coverageCheck(sourceGlob: string, testGlob: string, projectPath: string): DeepCheckResult;
27
+ /**
28
+ * Verify a specific file exists.
29
+ */
30
+ export declare function fileExistsCheck(config: DeepCheckConfig, projectPath: string): DeepCheckResult;
31
+ /**
32
+ * Classify a criterion and dispatch to the appropriate checker.
33
+ * When `explicitVerify` is provided the keyword-based classifier is bypassed
34
+ * and the verify block is used directly for the check.
35
+ */
36
+ export declare function deepCheckCriterion(criterionText: string, projectPath: string, explicitVerify?: VerifyBlock): DeepCheckResult;
37
+ /**
38
+ * Run deep checks on all criteria. Returns one result per criterion.
39
+ */
40
+ export declare function runDeepChecks(criteria: string[], projectPath: string): DeepCheckResult[];
41
+ //# sourceMappingURL=deep-code-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-code-checker.d.ts","sourceRoot":"","sources":["../../../src/engine/validator/deep-code-checker.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,aAAa,EACb,WAAW,EACZ,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC;AAMhE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC/C,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,CA4CA;AAkED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,eAAe,EACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,GAAG,cAAc,GACxC,eAAe,CAuCjB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe,CA2BzF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,eAAe,CA6CjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe,CAoB7F;AAiBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,WAAW,GAC3B,eAAe,CAsEjB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe,EAAE,CAExF"}
@@ -0,0 +1,363 @@
1
+ // Planu — Validator: deep code checker
2
+ // Performs real filesystem/grep analysis for spec criteria that go beyond file-name matching.
3
+ import { readFileSync, statSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { globSync } from 'glob';
6
+ const DEFAULT_IGNORE = ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'coverage/**'];
7
+ // ─── Keyword classification ──────────────────────────────────────────────────
8
+ /**
9
+ * Classify criterion text into a check type using keyword heuristics.
10
+ * Returns the type and optional config derived from the criterion.
11
+ */
12
+ export function classifyCriterion(text) {
13
+ const lower = text.toLowerCase();
14
+ // grep_absent: look for patterns that MUST NOT exist
15
+ if (lower.includes('no hardcoded') ||
16
+ lower.includes('no literal') ||
17
+ lower.includes('no magic') ||
18
+ lower.includes('no raw') ||
19
+ lower.includes('remove all')) {
20
+ return { type: 'grep_absent', config: buildAbsentConfig(text) };
21
+ }
22
+ // metric_max: file-size / line-count thresholds
23
+ if (lower.includes('max lines') ||
24
+ lower.includes('no file exceeds') ||
25
+ lower.includes('under ') ||
26
+ /\d+\s+lines/.exec(lower)) {
27
+ return { type: 'metric_max', config: buildMetricConfig(text) };
28
+ }
29
+ // file_exists: a specific file must be present
30
+ if (lower.includes('file exists') || lower.includes('must have file')) {
31
+ return { type: 'file_exists', config: buildFileExistsConfig(text) };
32
+ }
33
+ // coverage: every source file must have a companion test
34
+ if (lower.includes('test coverage') || /all .+ have tests/.exec(lower)) {
35
+ return { type: 'coverage', config: buildCoverageConfig(text) };
36
+ }
37
+ // grep_present: a pattern MUST exist somewhere
38
+ if (/all .+ have/.exec(lower) ||
39
+ /every .+ includes?/.exec(lower) ||
40
+ lower.includes('must contain')) {
41
+ return { type: 'grep_present', config: buildPresentConfig(text) };
42
+ }
43
+ return { type: 'manual' };
44
+ }
45
+ // ─── Config builders ──────────────────────────────────────────────────────────
46
+ function buildAbsentConfig(text) {
47
+ // Detect known patterns by keyword
48
+ const lower = text.toLowerCase();
49
+ if (lower.includes('spanish') || lower.includes('español') || lower.includes('i18n')) {
50
+ return { pattern: '[áéíóúñ¿¡]', scope: 'src/**/*.{ts,tsx}', expected: 0 };
51
+ }
52
+ if (lower.includes('console.log')) {
53
+ return { pattern: 'console\\.(log|debug|info)\\(', scope: 'src/**/*.{ts,tsx}', expected: 0 };
54
+ }
55
+ if (lower.includes('magic number') || lower.includes('magic string')) {
56
+ return {
57
+ pattern: '(?<![a-zA-Z])\\d{3,}(?![a-zA-Z0-9])',
58
+ scope: 'src/**/*.{ts,tsx}',
59
+ expected: 0,
60
+ };
61
+ }
62
+ // Generic: try to extract a quoted term or word after "no"
63
+ const quotedMatch = /"([^"]+)"|'([^']+)'/.exec(text);
64
+ if (quotedMatch) {
65
+ const term = quotedMatch[1] ?? quotedMatch[2] ?? '';
66
+ return { pattern: term, scope: 'src/**/*.{ts,tsx,js,jsx}', expected: 0 };
67
+ }
68
+ const noMatch = /no\s+(\w+)/i.exec(text);
69
+ const term = noMatch ? noMatch[1] : 'TODO';
70
+ return { pattern: term ?? 'TODO', scope: 'src/**/*.{ts,tsx,js,jsx}', expected: 0 };
71
+ }
72
+ function buildPresentConfig(text) {
73
+ const lower = text.toLowerCase();
74
+ if (lower.includes('error handling') || lower.includes('try') || lower.includes('catch')) {
75
+ return { pattern: 'try\\s*\\{|catch\\s*\\(', scope: 'src/**/*.{ts,tsx}', expected: 1 };
76
+ }
77
+ if (lower.includes('test') || lower.includes('spec')) {
78
+ return { pattern: 'describe\\(|it\\(|test\\(', scope: 'tests/**/*.test.ts', expected: 1 };
79
+ }
80
+ // Generic: extract quoted term
81
+ const quotedMatch = /"([^"]+)"|'([^']+)'/.exec(text);
82
+ const term = quotedMatch ? (quotedMatch[1] ?? quotedMatch[2] ?? 'TODO') : 'export';
83
+ return { pattern: term, scope: 'src/**/*.{ts,tsx,js,jsx}', expected: 1 };
84
+ }
85
+ function buildMetricConfig(text) {
86
+ const numMatch = /(\d+)\s+lines?/i.exec(text);
87
+ const threshold = numMatch ? parseInt(numMatch[1] ?? '400', 10) : 400;
88
+ return { pattern: String(threshold), scope: 'src/**/*.{ts,tsx}', expected: threshold };
89
+ }
90
+ function buildFileExistsConfig(text) {
91
+ const pathMatch = /["']([^"']+\.\w+)["']/.exec(text);
92
+ const filePath = pathMatch ? (pathMatch[1] ?? '') : '';
93
+ return { pattern: filePath, scope: filePath, expected: 1 };
94
+ }
95
+ function buildCoverageConfig(_text) {
96
+ return { pattern: 'src/**/*.ts', scope: 'tests/**/*.test.ts', expected: 1 };
97
+ }
98
+ // ─── Checkers ─────────────────────────────────────────────────────────────────
99
+ /**
100
+ * Search for a regex pattern in files matching the scope glob.
101
+ * For grep_absent: passed when matchCount === 0.
102
+ * For grep_present: passed when matchCount >= expected.
103
+ */
104
+ export function grepCheck(config, projectPath, checkType) {
105
+ const files = resolveFiles(config.scope, projectPath, config.exclude);
106
+ const regex = buildRegex(config.pattern);
107
+ const evidence = [];
108
+ if (regex) {
109
+ for (const file of files) {
110
+ const fullPath = join(projectPath, file);
111
+ try {
112
+ const content = readFileSync(fullPath, 'utf-8');
113
+ const lines = content.split('\n');
114
+ for (let i = 0; i < lines.length; i++) {
115
+ const line = lines[i];
116
+ if (line !== undefined && regex.test(line)) {
117
+ evidence.push(`${file}:${i + 1}`);
118
+ regex.lastIndex = 0; // reset for global flag
119
+ }
120
+ }
121
+ }
122
+ catch {
123
+ // Skip unreadable files
124
+ }
125
+ }
126
+ }
127
+ const matchCount = evidence.length;
128
+ const passed = checkType === 'grep_absent' ? matchCount === 0 : matchCount >= config.expected;
129
+ return {
130
+ criterionText: config.pattern,
131
+ checkType,
132
+ passed,
133
+ evidence,
134
+ matchCount,
135
+ expected: config.expected,
136
+ description: checkType === 'grep_absent'
137
+ ? `Pattern "${config.pattern}" must not appear in ${config.scope}`
138
+ : `Pattern "${config.pattern}" must appear at least ${config.expected} time(s) in ${config.scope}`,
139
+ };
140
+ }
141
+ /**
142
+ * Check that no file in scope exceeds the line threshold encoded in config.expected.
143
+ */
144
+ export function metricCheck(config, projectPath) {
145
+ const threshold = config.expected;
146
+ const files = resolveFiles(config.scope, projectPath, config.exclude);
147
+ const evidence = [];
148
+ for (const file of files) {
149
+ const fullPath = join(projectPath, file);
150
+ try {
151
+ const content = readFileSync(fullPath, 'utf-8');
152
+ const lineCount = content.split('\n').length;
153
+ if (lineCount > threshold) {
154
+ evidence.push(`${file}:${lineCount} lines (max ${threshold})`);
155
+ }
156
+ }
157
+ catch {
158
+ // Skip
159
+ }
160
+ }
161
+ return {
162
+ criterionText: `No file exceeds ${threshold} lines`,
163
+ checkType: 'metric_max',
164
+ passed: evidence.length === 0,
165
+ evidence,
166
+ matchCount: evidence.length,
167
+ expected: 0,
168
+ description: `All files in ${config.scope} must have <= ${threshold} lines`,
169
+ };
170
+ }
171
+ /**
172
+ * Check that each source file has a corresponding test file.
173
+ * sourceGlob: e.g. "src/**\/*.ts"
174
+ * testGlob: e.g. "tests/**\/*.test.ts"
175
+ */
176
+ export function coverageCheck(sourceGlob, testGlob, projectPath) {
177
+ const sourceFiles = resolveFiles(sourceGlob, projectPath);
178
+ // Cannot verify coverage with no source files — treat as non-authoritative
179
+ if (sourceFiles.length === 0) {
180
+ return {
181
+ criterionText: `All ${sourceGlob} files have tests`,
182
+ checkType: 'coverage',
183
+ passed: false,
184
+ evidence: [],
185
+ matchCount: 0,
186
+ expected: 0,
187
+ description: `No source files found in ${sourceGlob} — coverage cannot be verified`,
188
+ };
189
+ }
190
+ const testFiles = resolveFiles(testGlob, projectPath);
191
+ // Build a set of base names from test files (strip .test.ts / .spec.ts)
192
+ const testBaseNames = new Set(testFiles.map((f) => {
193
+ const base = f
194
+ .replace(/\.test\.(ts|tsx|js|jsx)$/, '')
195
+ .replace(/\.spec\.(ts|tsx|js|jsx)$/, '');
196
+ return base.split('/').pop() ?? base;
197
+ }));
198
+ const uncovered = [];
199
+ for (const src of sourceFiles) {
200
+ const name = (src.split('/').pop() ?? src).replace(/\.(ts|tsx|js|jsx)$/, '');
201
+ if (!testBaseNames.has(name)) {
202
+ uncovered.push(src);
203
+ }
204
+ }
205
+ return {
206
+ criterionText: `All ${sourceGlob} files have tests`,
207
+ checkType: 'coverage',
208
+ passed: uncovered.length === 0,
209
+ evidence: uncovered,
210
+ matchCount: uncovered.length,
211
+ expected: 0,
212
+ description: `Each source file in ${sourceGlob} must have a corresponding test file`,
213
+ };
214
+ }
215
+ /**
216
+ * Verify a specific file exists.
217
+ */
218
+ export function fileExistsCheck(config, projectPath) {
219
+ const filePath = config.scope || config.pattern;
220
+ const fullPath = join(projectPath, filePath);
221
+ let exists = false;
222
+ try {
223
+ statSync(fullPath);
224
+ exists = true;
225
+ }
226
+ catch {
227
+ // file does not exist — exists remains false
228
+ }
229
+ return {
230
+ criterionText: `File exists: ${filePath}`,
231
+ checkType: 'file_exists',
232
+ passed: exists,
233
+ evidence: exists ? [filePath] : [],
234
+ matchCount: exists ? 1 : 0,
235
+ expected: 1,
236
+ description: `File ${filePath} must exist in the project`,
237
+ };
238
+ }
239
+ // ─── Orchestrators ────────────────────────────────────────────────────────────
240
+ /**
241
+ * Build a DeepCheckConfig from a VerifyBlock.
242
+ * Maps the VerifyBlock fields to the internal DeepCheckConfig shape.
243
+ */
244
+ function configFromVerifyBlock(block) {
245
+ return {
246
+ pattern: block.pattern ?? '',
247
+ scope: block.scope ?? 'src/**/*.{ts,tsx,js,jsx}',
248
+ exclude: block.exclude,
249
+ expected: block.expected ?? (block.type === 'grep_absent' ? 0 : 1),
250
+ };
251
+ }
252
+ /**
253
+ * Classify a criterion and dispatch to the appropriate checker.
254
+ * When `explicitVerify` is provided the keyword-based classifier is bypassed
255
+ * and the verify block is used directly for the check.
256
+ */
257
+ export function deepCheckCriterion(criterionText, projectPath, explicitVerify) {
258
+ // Use explicit verify block when available — skip keyword inference.
259
+ // VerifyType and DeepCheckType overlap but are not identical, so we compare
260
+ // the type as a string to avoid exhaustiveness errors on union mismatches.
261
+ if (explicitVerify) {
262
+ const config = configFromVerifyBlock(explicitVerify);
263
+ const vtype = explicitVerify.type;
264
+ if (vtype === 'grep_absent') {
265
+ return { ...grepCheck(config, projectPath, 'grep_absent'), criterionText };
266
+ }
267
+ if (vtype === 'grep_present') {
268
+ return { ...grepCheck(config, projectPath, 'grep_present'), criterionText };
269
+ }
270
+ if (vtype === 'metric_max') {
271
+ return { ...metricCheck(config, projectPath), criterionText };
272
+ }
273
+ if (vtype === 'file_exists') {
274
+ return { ...fileExistsCheck(config, projectPath), criterionText };
275
+ }
276
+ // manual, metric_min, command, and any unknown types fall through to manual result
277
+ return {
278
+ criterionText,
279
+ checkType: 'manual',
280
+ passed: true,
281
+ evidence: [],
282
+ matchCount: 0,
283
+ expected: 0,
284
+ description: explicitVerify.description,
285
+ };
286
+ }
287
+ const { type, config } = classifyCriterion(criterionText);
288
+ if (type === 'manual' || !config) {
289
+ return {
290
+ criterionText,
291
+ checkType: 'manual',
292
+ passed: true, // cannot verify automatically
293
+ evidence: [],
294
+ matchCount: 0,
295
+ expected: 0,
296
+ description: 'Criterion requires manual review — no automated check available',
297
+ };
298
+ }
299
+ switch (type) {
300
+ case 'grep_absent':
301
+ return { ...grepCheck(config, projectPath, 'grep_absent'), criterionText };
302
+ case 'grep_present':
303
+ return { ...grepCheck(config, projectPath, 'grep_present'), criterionText };
304
+ case 'metric_max':
305
+ return { ...metricCheck(config, projectPath), criterionText };
306
+ case 'file_exists':
307
+ return { ...fileExistsCheck(config, projectPath), criterionText };
308
+ case 'coverage': {
309
+ const result = coverageCheck('src/**/*.ts', 'tests/**/*.test.ts', projectPath);
310
+ return { ...result, criterionText };
311
+ }
312
+ default:
313
+ return {
314
+ criterionText,
315
+ checkType: 'manual',
316
+ passed: true,
317
+ evidence: [],
318
+ matchCount: 0,
319
+ expected: 0,
320
+ description: 'Criterion requires manual review',
321
+ };
322
+ }
323
+ }
324
+ /**
325
+ * Run deep checks on all criteria. Returns one result per criterion.
326
+ */
327
+ export function runDeepChecks(criteria, projectPath) {
328
+ return criteria.map((criterion) => deepCheckCriterion(criterion, projectPath));
329
+ }
330
+ // ─── Utilities ────────────────────────────────────────────────────────────────
331
+ function resolveFiles(scopeGlob, projectPath, exclude) {
332
+ const ignore = [...DEFAULT_IGNORE];
333
+ if (exclude) {
334
+ ignore.push(exclude);
335
+ }
336
+ try {
337
+ return globSync(scopeGlob, {
338
+ cwd: projectPath,
339
+ nodir: true,
340
+ ignore,
341
+ maxDepth: 8,
342
+ });
343
+ }
344
+ catch {
345
+ return [];
346
+ }
347
+ }
348
+ function buildRegex(pattern) {
349
+ try {
350
+ return new RegExp(pattern, 'g');
351
+ }
352
+ catch {
353
+ // If pattern is not a valid regex, escape it
354
+ try {
355
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
356
+ return new RegExp(escaped, 'g');
357
+ }
358
+ catch {
359
+ return null;
360
+ }
361
+ }
362
+ }
363
+ //# sourceMappingURL=deep-code-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-code-checker.js","sourceRoot":"","sources":["../../../src/engine/validator/deep-code-checker.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,15 @@
1
- import type { Spec } from '../../types/index.js';
1
+ import type { Spec, VerifyBlock } from '../../types/index.js';
2
2
  /**
3
3
  * Extract all acceptance criteria from a spec's source files.
4
4
  */
5
5
  export declare function extractCriteria(spec: Spec): Promise<string[]>;
6
+ /**
7
+ * Parse verify blocks from HTML comments following acceptance criteria checkboxes.
8
+ * Format: `- [ ] criterion text` followed by `<!-- verify: {...} -->`
9
+ * Returns a Map of criterion text -> VerifyBlock.
10
+ * Invalid JSON blocks are silently skipped (forgiving parser).
11
+ */
12
+ export declare function extractVerifyBlocks(content: string): Map<string, VerifyBlock>;
6
13
  /**
7
14
  * Extract the text content under a markdown heading that matches any of the given keywords.
8
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"extractors.d.ts","sourceRoot":"","sources":["../../../src/engine/validator/extractors.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4DnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CA8B3F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAyB1D"}
1
+ {"version":3,"file":"extractors.d.ts","sourceRoot":"","sources":["../../../src/engine/validator/extractors.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAG9D;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4DnE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CA6B7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CA8B3F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAyB1D"}