@popoverai/dotrequirements 0.22.0 → 0.24.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 (230) hide show
  1. package/README.md +173 -23
  2. package/dist/cli.js +121 -61
  3. package/dist/codebase-to-spec/budget.d.ts +53 -0
  4. package/dist/codebase-to-spec/budget.js +80 -0
  5. package/dist/codebase-to-spec/cache.d.ts +49 -0
  6. package/dist/codebase-to-spec/cache.js +54 -0
  7. package/dist/codebase-to-spec/claude.d.ts +69 -0
  8. package/dist/codebase-to-spec/claude.js +126 -0
  9. package/dist/codebase-to-spec/compose.d.ts +49 -0
  10. package/dist/codebase-to-spec/compose.js +124 -0
  11. package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
  12. package/dist/codebase-to-spec/edit-loop.js +195 -0
  13. package/dist/codebase-to-spec/editor.d.ts +54 -0
  14. package/dist/codebase-to-spec/editor.js +74 -0
  15. package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
  16. package/dist/codebase-to-spec/exit-codes.js +58 -0
  17. package/dist/codebase-to-spec/fan-out.d.ts +63 -0
  18. package/dist/codebase-to-spec/fan-out.js +215 -0
  19. package/dist/codebase-to-spec/interactive.d.ts +30 -0
  20. package/dist/codebase-to-spec/interactive.js +48 -0
  21. package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
  22. package/dist/codebase-to-spec/outline-review-loop.js +187 -0
  23. package/dist/codebase-to-spec/pack.d.ts +51 -0
  24. package/dist/codebase-to-spec/pack.js +127 -0
  25. package/dist/codebase-to-spec/planner.d.ts +41 -0
  26. package/dist/codebase-to-spec/planner.js +76 -0
  27. package/dist/codebase-to-spec/present.d.ts +94 -0
  28. package/dist/codebase-to-spec/present.js +288 -0
  29. package/dist/codebase-to-spec/progress.d.ts +33 -0
  30. package/dist/codebase-to-spec/progress.js +28 -0
  31. package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
  32. package/dist/codebase-to-spec/prompts/editor.js +57 -0
  33. package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
  34. package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
  35. package/dist/codebase-to-spec/prompts/planner-apply.d.ts +12 -0
  36. package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
  37. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
  38. package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
  39. package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
  40. package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
  41. package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
  42. package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
  43. package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
  44. package/dist/codebase-to-spec/prompts/specifier.js +100 -0
  45. package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
  46. package/dist/codebase-to-spec/prompts/style-check.js +78 -0
  47. package/dist/codebase-to-spec/schemas.d.ts +257 -0
  48. package/dist/codebase-to-spec/schemas.js +183 -0
  49. package/dist/codebase-to-spec/skill-install.d.ts +57 -0
  50. package/dist/codebase-to-spec/skill-install.js +79 -0
  51. package/dist/codebase-to-spec/slice.d.ts +49 -0
  52. package/dist/codebase-to-spec/slice.js +111 -0
  53. package/dist/codebase-to-spec/specifier.d.ts +60 -0
  54. package/dist/codebase-to-spec/specifier.js +79 -0
  55. package/dist/codebase-to-spec/style-check.d.ts +29 -0
  56. package/dist/codebase-to-spec/style-check.js +33 -0
  57. package/dist/codebase-to-spec/summary.d.ts +51 -0
  58. package/dist/codebase-to-spec/summary.js +183 -0
  59. package/dist/codebase-to-spec/validate.d.ts +46 -0
  60. package/dist/codebase-to-spec/validate.js +130 -0
  61. package/dist/commands/acceptance-test.d.ts +6 -0
  62. package/dist/commands/acceptance-test.js +212 -0
  63. package/dist/commands/ai-setup.d.ts +5 -0
  64. package/dist/commands/ai-setup.js +441 -0
  65. package/dist/commands/browsertest.d.ts +0 -1
  66. package/dist/commands/browsertest.js +51 -26
  67. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  68. package/dist/commands/codebase-to-spec/compose.js +57 -0
  69. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  70. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  71. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  72. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  73. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  74. package/dist/commands/codebase-to-spec/index.js +135 -0
  75. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  76. package/dist/commands/codebase-to-spec/pack.js +76 -0
  77. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  78. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  79. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  80. package/dist/commands/codebase-to-spec/present.js +92 -0
  81. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  82. package/dist/commands/codebase-to-spec/run.js +85 -0
  83. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  84. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  85. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  86. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  87. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  88. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  89. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  90. package/dist/commands/codebase-to-spec/validate.js +38 -0
  91. package/dist/commands/create-requirement-document.d.ts +2 -0
  92. package/dist/commands/create-requirement-document.js +41 -0
  93. package/dist/commands/finalize.js +7 -7
  94. package/dist/commands/get.d.ts +2 -0
  95. package/dist/commands/get.js +55 -0
  96. package/dist/commands/init.js +132 -117
  97. package/dist/commands/link.js +27 -27
  98. package/dist/commands/list.d.ts +6 -0
  99. package/dist/commands/list.js +43 -0
  100. package/dist/commands/mcp-setup.js +159 -149
  101. package/dist/commands/mcp.js +1 -1
  102. package/dist/commands/prepare.js +4 -4
  103. package/dist/commands/pull.js +116 -121
  104. package/dist/commands/push.js +106 -112
  105. package/dist/commands/report.d.ts +6 -2
  106. package/dist/commands/report.js +177 -122
  107. package/dist/commands/requirements-for.d.ts +2 -0
  108. package/dist/commands/requirements-for.js +29 -0
  109. package/dist/commands/review-test.d.ts +2 -0
  110. package/dist/commands/review-test.js +75 -0
  111. package/dist/commands/search.d.ts +6 -0
  112. package/dist/commands/search.js +39 -0
  113. package/dist/commands/style-check.d.ts +7 -0
  114. package/dist/commands/style-check.js +75 -0
  115. package/dist/commands/test.js +53 -59
  116. package/dist/commands/tests-for.d.ts +2 -0
  117. package/dist/commands/tests-for.js +80 -0
  118. package/dist/commands/validate.d.ts +6 -0
  119. package/dist/commands/validate.js +72 -0
  120. package/dist/config.js +1 -1
  121. package/dist/convex.d.ts +34 -22
  122. package/dist/convex.js +38 -22
  123. package/dist/harness/cache.d.ts +1 -5
  124. package/dist/harness/cache.js +49 -59
  125. package/dist/harness/convexReporting.d.ts +1 -1
  126. package/dist/harness/convexReporting.js +9 -7
  127. package/dist/harness/coverageCache.js +3 -3
  128. package/dist/harness/finalize.js +59 -46
  129. package/dist/harness/index.d.ts +6 -7
  130. package/dist/harness/index.js +9 -10
  131. package/dist/harness/prepare.js +6 -5
  132. package/dist/harness/requirementsLoader.d.ts +2 -2
  133. package/dist/harness/requirementsLoader.js +13 -35
  134. package/dist/harness/tracking.js +18 -18
  135. package/dist/harness/types.d.ts +1 -1
  136. package/dist/mcp/convexClient.d.ts +0 -39
  137. package/dist/mcp/convexClient.js +2 -107
  138. package/dist/mcp/grep.d.ts +1 -1
  139. package/dist/mcp/grep.js +87 -42
  140. package/dist/mcp/handlers/authoring.d.ts +1 -1
  141. package/dist/mcp/handlers/authoring.js +30 -234
  142. package/dist/mcp/handlers/coverage.d.ts +1 -1
  143. package/dist/mcp/handlers/coverage.js +13 -15
  144. package/dist/mcp/handlers/debug.d.ts +2 -3
  145. package/dist/mcp/handlers/debug.js +10 -10
  146. package/dist/mcp/handlers/get.d.ts +1 -1
  147. package/dist/mcp/handlers/get.js +11 -10
  148. package/dist/mcp/handlers/index.d.ts +20 -20
  149. package/dist/mcp/handlers/index.js +10 -10
  150. package/dist/mcp/handlers/list.d.ts +4 -33
  151. package/dist/mcp/handlers/list.js +16 -38
  152. package/dist/mcp/handlers/push.d.ts +1 -1
  153. package/dist/mcp/handlers/push.js +28 -18
  154. package/dist/mcp/handlers/report.d.ts +16 -0
  155. package/dist/mcp/handlers/report.js +134 -0
  156. package/dist/mcp/handlers/review.d.ts +1 -1
  157. package/dist/mcp/handlers/review.js +40 -59
  158. package/dist/mcp/handlers/search.d.ts +1 -1
  159. package/dist/mcp/handlers/search.js +7 -9
  160. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  161. package/dist/mcp/handlers/test-mapping.js +14 -14
  162. package/dist/mcp/handlers/types.d.ts +3 -3
  163. package/dist/mcp/handlers/types.js +2 -2
  164. package/dist/mcp/index.d.ts +1 -1
  165. package/dist/mcp/index.js +156 -178
  166. package/dist/mcp/requirements.d.ts +2 -2
  167. package/dist/mcp/requirements.js +30 -30
  168. package/dist/mcp/testCodeExtractor.js +24 -26
  169. package/dist/mcp/types.d.ts +1 -1
  170. package/dist/push/core.d.ts +2 -2
  171. package/dist/push/core.js +20 -20
  172. package/dist/push/index.d.ts +1 -1
  173. package/dist/push/index.js +2 -2
  174. package/dist/requirements/cloud-ai.d.ts +57 -0
  175. package/dist/requirements/cloud-ai.js +104 -0
  176. package/dist/requirements/cloud-coverage.d.ts +41 -0
  177. package/dist/requirements/cloud-coverage.js +60 -0
  178. package/dist/requirements/coverage.d.ts +45 -0
  179. package/dist/requirements/coverage.js +114 -0
  180. package/dist/requirements/grep.d.ts +33 -0
  181. package/dist/requirements/grep.js +306 -0
  182. package/dist/requirements/index.d.ts +73 -0
  183. package/dist/requirements/index.js +174 -0
  184. package/dist/requirements/style-guide.d.ts +67 -0
  185. package/dist/requirements/style-guide.js +299 -0
  186. package/dist/requirements/testCodeExtractor.d.ts +22 -0
  187. package/dist/requirements/testCodeExtractor.js +150 -0
  188. package/dist/schema/browser.d.ts +8 -6
  189. package/dist/schema/browser.js +14 -14
  190. package/dist/schema/builder.d.ts +1 -1
  191. package/dist/schema/builder.js +13 -44
  192. package/dist/schema/conversions.d.ts +2 -2
  193. package/dist/schema/conversions.js +11 -11
  194. package/dist/schema/index.d.ts +9 -7
  195. package/dist/schema/index.js +15 -13
  196. package/dist/schema/parser-core.d.ts +1 -1
  197. package/dist/schema/parser-core.js +23 -22
  198. package/dist/schema/parser.d.ts +3 -3
  199. package/dist/schema/parser.js +27 -31
  200. package/dist/schema/resolver.d.ts +1 -1
  201. package/dist/schema/resolver.js +9 -9
  202. package/dist/schema/scenario.d.ts +91 -0
  203. package/dist/schema/scenario.js +82 -0
  204. package/dist/schema/schemas.d.ts +3 -3
  205. package/dist/schema/schemas.js +41 -28
  206. package/dist/schema/test-schema.js +27 -27
  207. package/dist/templates/context-file-section.md +3 -2
  208. package/dist/templates/example-requirements.js +1 -1
  209. package/dist/templates/example-requirements.ts +3 -1
  210. package/dist/templates/requirements-readme.js +1 -1
  211. package/dist/templates/requirements-readme.ts +1 -1
  212. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  213. package/dist/utils/brand.js +3 -3
  214. package/dist/utils/browser-launch.js +4 -4
  215. package/dist/utils/context-file.d.ts +1 -1
  216. package/dist/utils/context-file.js +26 -26
  217. package/dist/utils/env.js +7 -7
  218. package/dist/utils/gitignore.js +7 -7
  219. package/dist/utils/oauth-callback-server.d.ts +1 -1
  220. package/dist/utils/oauth-callback-server.js +27 -25
  221. package/dist/utils/oauth-flow.js +32 -29
  222. package/dist/utils/project-discovery.d.ts +3 -3
  223. package/dist/utils/project-discovery.js +18 -17
  224. package/dist/utils/project-name.js +8 -8
  225. package/dist/utils/project-selector.d.ts +1 -1
  226. package/dist/utils/project-selector.js +24 -21
  227. package/dist/utils/project-settings.d.ts +5 -4
  228. package/dist/utils/project-settings.js +28 -20
  229. package/dist/utils/templates.js +6 -6
  230. package/package.json +2 -1
@@ -2,8 +2,8 @@
2
2
  * Zod schemas for Markdown requirements format.
3
3
  * TypeScript types are inferred from these schemas using z.infer<>.
4
4
  */
5
- import { z } from 'zod';
6
- import { fromZodError } from 'zod-validation-error';
5
+ import { z } from "zod";
6
+ import { fromZodError } from "zod-validation-error";
7
7
  // =============================================================================
8
8
  // Prefix and Key Validation
9
9
  // =============================================================================
@@ -24,21 +24,21 @@ export const REQUIREMENT_KEY_PATTERN = /^[A-Z][A-Z-]*-[0-9]+$/;
24
24
  */
25
25
  export const RequirementPrefixSchema = z
26
26
  .string()
27
- .min(1, 'Prefix must be at least 1 character')
28
- .max(30, 'Prefix must be at most 30 characters')
27
+ .min(1, "Prefix must be at least 1 character")
28
+ .max(30, "Prefix must be at most 30 characters")
29
29
  .transform((val) => val.toUpperCase())
30
- .refine((val) => REQUIREMENT_PREFIX_PATTERN.test(val), 'Prefix must start with a letter and contain only uppercase letters and hyphens');
30
+ .refine((val) => REQUIREMENT_PREFIX_PATTERN.test(val), "Prefix must start with a letter and contain only uppercase letters and hyphens");
31
31
  /**
32
32
  * Zod schema for requirement key (PREFIX-number).
33
33
  * Normalizes to uppercase and strips leading zeros from numbers.
34
34
  */
35
35
  export const RequirementKeySchema = z
36
36
  .string()
37
- .refine((val) => REQUIREMENT_KEY_PATTERN.test(val.toUpperCase()), 'Key must be in format PREFIX-number (e.g., LOGIN-1, AUTH-42)')
37
+ .refine((val) => REQUIREMENT_KEY_PATTERN.test(val.toUpperCase()), "Key must be in format PREFIX-number (e.g., LOGIN-1, AUTH-42)")
38
38
  .transform((val) => {
39
39
  const upper = val.toUpperCase();
40
40
  // Normalize leading zeros: LOGIN-01 -> LOGIN-1, LOGIN-007 -> LOGIN-7
41
- return upper.replace(/-0+(\d+)$/, '-$1');
41
+ return upper.replace(/-0+(\d+)$/, "-$1");
42
42
  });
43
43
  /**
44
44
  * Normalize a prefix to uppercase.
@@ -54,8 +54,9 @@ export function normalizePrefix(prefix) {
54
54
  export function validatePrefix(prefix) {
55
55
  const result = RequirementPrefixSchema.safeParse(prefix);
56
56
  if (!result.success) {
57
+ // biome-ignore lint/suspicious/noExplicitAny: zod-validation-error and our zod v3 ZodError have a typing mismatch across versions; the runtime shape is identical.
57
58
  const validationError = fromZodError(result.error);
58
- throw new ValidationError(`Invalid prefix "${prefix}": ${validationError.message}`, 'prefix', result.error);
59
+ throw new ValidationError(`Invalid prefix "${prefix}": ${validationError.message}`, "prefix", result.error);
59
60
  }
60
61
  return result.data;
61
62
  }
@@ -66,8 +67,9 @@ export function validatePrefix(prefix) {
66
67
  export function validateKey(key) {
67
68
  const result = RequirementKeySchema.safeParse(key);
68
69
  if (!result.success) {
70
+ // biome-ignore lint/suspicious/noExplicitAny: zod-validation-error and our zod v3 ZodError have a typing mismatch across versions; the runtime shape is identical.
69
71
  const validationError = fromZodError(result.error);
70
- throw new ValidationError(`Invalid key "${key}": ${validationError.message}`, 'key', result.error);
72
+ throw new ValidationError(`Invalid key "${key}": ${validationError.message}`, "key", result.error);
71
73
  }
72
74
  return result.data;
73
75
  }
@@ -93,10 +95,10 @@ export function parseRequirementKey(key) {
93
95
  export function buildRequirementKey(prefix, number) {
94
96
  const normalizedPrefix = prefix.toUpperCase();
95
97
  if (!REQUIREMENT_PREFIX_PATTERN.test(normalizedPrefix)) {
96
- throw new ValidationError(`Invalid prefix "${prefix}": must match pattern ${REQUIREMENT_PREFIX_PATTERN}`, 'prefix');
98
+ throw new ValidationError(`Invalid prefix "${prefix}": must match pattern ${REQUIREMENT_PREFIX_PATTERN}`, "prefix");
97
99
  }
98
100
  if (number < 0 || !Number.isInteger(number)) {
99
- throw new ValidationError(`Invalid number "${number}": must be a non-negative integer`, 'number');
101
+ throw new ValidationError(`Invalid number "${number}": must be a non-negative integer`, "number");
100
102
  }
101
103
  return `${normalizedPrefix}-${number}`;
102
104
  }
@@ -110,11 +112,13 @@ export function buildRequirementKey(prefix, number) {
110
112
  export const MetadataSchema = z.object({
111
113
  version: z.number().optional(), // Informational - incremented on push
112
114
  pulledAt: z.string().optional(), // ISO 8601 timestamp - used for conflict detection
113
- document: z.object({
115
+ document: z
116
+ .object({
114
117
  id: z.string().optional(), // Cloud document link - filled by push when creating
115
118
  title: z.string(), // Required for push
116
119
  defaultPrefix: z.string().optional(), // Editor hint for new requirement keys
117
- }).optional(),
120
+ })
121
+ .optional(),
118
122
  });
119
123
  // =============================================================================
120
124
  // Push Readiness Validation
@@ -135,31 +139,31 @@ export function validateForPush(metadata) {
135
139
  if (!metadata.document) {
136
140
  return {
137
141
  valid: false,
138
- reason: 'Missing document section. Add a document section with a title to push this file.'
142
+ reason: "Missing document section. Add a document section with a title to push this file.",
139
143
  };
140
144
  }
141
145
  // Must have title
142
- if (!metadata.document.title || metadata.document.title.trim() === '') {
146
+ if (!metadata.document.title || metadata.document.title.trim() === "") {
143
147
  return {
144
148
  valid: false,
145
- reason: 'Document section missing required title.'
149
+ reason: "Document section missing required title.",
146
150
  };
147
151
  }
148
152
  // Check document.id
149
153
  if (!metadata.document.id) {
150
154
  // No ID = create new document
151
- return { valid: true, action: 'create' };
155
+ return { valid: true, action: "create" };
152
156
  }
153
157
  // Has ID - check if it's a valid Convex ID
154
158
  if (CONVEX_ID_PATTERN.test(metadata.document.id)) {
155
159
  // Valid Convex ID = update existing document
156
- return { valid: true, action: 'update' };
160
+ return { valid: true, action: "update" };
157
161
  }
158
162
  // Invalid ID format - will create new document and overwrite
159
163
  return {
160
164
  valid: true,
161
- action: 'create',
162
- warning: `Invalid document ID '${metadata.document.id}' will be replaced with a new ID.`
165
+ action: "create",
166
+ warning: `Invalid document ID '${metadata.document.id}' will be replaced with a new ID.`,
163
167
  };
164
168
  }
165
169
  /**
@@ -172,31 +176,37 @@ export const RequirementNodeSchema = z.lazy(() => z.object({
172
176
  content: z.string(), // The actual requirement text
173
177
  children: z.array(RequirementNodeSchema).default([]),
174
178
  // Optional metadata preserved from Convex
175
- metadata: z.object({
179
+ metadata: z
180
+ .object({
176
181
  convexId: z.string().optional(), // Convex _id
177
182
  rootId: z.string().optional(), // Convex rootId
178
183
  position: z.string().optional(), // Convex position path
179
184
  updatedAt: z.number().optional(),
180
- externalLinks: z.object({
185
+ externalLinks: z
186
+ .object({
181
187
  jira: z.string().optional(),
182
188
  notion: z.string().optional(),
183
- }).optional(),
189
+ })
190
+ .optional(),
184
191
  // New schema fields for bidirectional sync
185
192
  prefix: z.string().optional(), // e.g., "REQ"
186
193
  index: z.number().optional(), // e.g., 123
187
194
  documentId: z.string().optional(), // Convex document ID
188
- }).optional(),
195
+ })
196
+ .optional(),
189
197
  }));
190
198
  /**
191
199
  * Complete requirements file structure.
192
200
  * Top-level keys are either "_meta" or requirement IDs.
193
201
  */
194
- export const RequirementsFileSchema = z.object({
202
+ export const RequirementsFileSchema = z
203
+ .object({
195
204
  _meta: MetadataSchema,
196
205
  // All other keys are requirement roots
197
206
  // We'll validate the structure separately since Zod doesn't handle
198
207
  // arbitrary keys with specific structure well
199
- }).passthrough(); // Allow additional keys
208
+ })
209
+ .passthrough(); // Allow additional keys
200
210
  /**
201
211
  * Parsed criterion from Markdown (position. Label: content format)
202
212
  * Example: "0. Given: user has valid credentials"
@@ -216,7 +226,7 @@ export class ValidationError extends Error {
216
226
  super(message);
217
227
  this.path = path;
218
228
  this.cause = cause;
219
- this.name = 'ValidationError';
229
+ this.name = "ValidationError";
220
230
  }
221
231
  }
222
232
  /**
@@ -225,8 +235,9 @@ export class ValidationError extends Error {
225
235
  export function validateMetadata(data) {
226
236
  const result = MetadataSchema.safeParse(data);
227
237
  if (!result.success) {
238
+ // biome-ignore lint/suspicious/noExplicitAny: zod-validation-error and our zod v3 ZodError have a typing mismatch across versions; the runtime shape is identical.
228
239
  const validationError = fromZodError(result.error);
229
- throw new ValidationError(`Invalid metadata block:\n${validationError.message}`, '_meta', result.error);
240
+ throw new ValidationError(`Invalid metadata block:\n${validationError.message}`, "_meta", result.error);
230
241
  }
231
242
  return result.data;
232
243
  }
@@ -236,6 +247,7 @@ export function validateMetadata(data) {
236
247
  export function validateRequirementNode(data, path) {
237
248
  const result = RequirementNodeSchema.safeParse(data);
238
249
  if (!result.success) {
250
+ // biome-ignore lint/suspicious/noExplicitAny: zod-validation-error and our zod v3 ZodError have a typing mismatch across versions; the runtime shape is identical.
239
251
  const validationError = fromZodError(result.error);
240
252
  throw new ValidationError(`Invalid requirement node structure at ${path}:\n${validationError.message}`, path, result.error);
241
253
  }
@@ -247,6 +259,7 @@ export function validateRequirementNode(data, path) {
247
259
  export function validateRequirementsFile(data) {
248
260
  const result = RequirementsFileSchema.safeParse(data);
249
261
  if (!result.success) {
262
+ // biome-ignore lint/suspicious/noExplicitAny: zod-validation-error and our zod v3 ZodError have a typing mismatch across versions; the runtime shape is identical.
250
263
  const validationError = fromZodError(result.error);
251
264
  throw new ValidationError(`Invalid requirements file structure:\n${validationError.message}`, undefined, result.error);
252
265
  }
@@ -1,59 +1,59 @@
1
1
  /**
2
2
  * Quick test to verify the new YAML schema is valid and round-trips correctly.
3
3
  */
4
- import { buildRequirementsMarkdown, parseRequirementsFile, buildMetadata } from './index.js';
4
+ import { buildMetadata, buildRequirementsMarkdown, parseRequirementsFile, } from "./index.js";
5
5
  // Create test data
6
6
  const metadata = buildMetadata(1);
7
7
  const testRequirements = [
8
8
  {
9
- id: 'REQ123',
10
- label: 'requirement',
11
- content: 'User can authenticate',
9
+ id: "REQ123",
10
+ label: "requirement",
11
+ content: "User can authenticate",
12
12
  children: [
13
13
  {
14
- id: 'REQ123.0',
15
- label: 'given',
16
- content: 'user has valid credentials',
14
+ id: "REQ123.0",
15
+ label: "given",
16
+ content: "user has valid credentials",
17
17
  children: [],
18
18
  },
19
19
  {
20
- id: 'REQ123.1',
21
- label: 'when',
22
- content: 'user submits login form',
20
+ id: "REQ123.1",
21
+ label: "when",
22
+ content: "user submits login form",
23
23
  children: [
24
24
  {
25
- id: 'REQ123.1.0',
26
- label: 'then',
27
- content: 'system validates credentials',
25
+ id: "REQ123.1.0",
26
+ label: "then",
27
+ content: "system validates credentials",
28
28
  children: [],
29
29
  },
30
30
  {
31
- id: 'REQ123.1.1',
32
- label: 'then',
33
- content: 'system creates session',
31
+ id: "REQ123.1.1",
32
+ label: "then",
33
+ content: "system creates session",
34
34
  children: [],
35
35
  },
36
36
  ],
37
37
  },
38
38
  {
39
- id: 'REQ123.2',
40
- label: 'then',
41
- content: 'user is redirected to dashboard',
39
+ id: "REQ123.2",
40
+ label: "then",
41
+ content: "user is redirected to dashboard",
42
42
  children: [],
43
43
  },
44
44
  ],
45
45
  },
46
46
  ];
47
- console.log('=== Building Markdown ===\n');
47
+ console.log("=== Building Markdown ===\n");
48
48
  const markdown = buildRequirementsMarkdown(metadata, testRequirements);
49
49
  console.log(markdown);
50
- console.log('\n=== Parsing Markdown back ===\n');
50
+ console.log("\n=== Parsing Markdown back ===\n");
51
51
  try {
52
52
  const { metadata: parsedMeta, requirements: parsedReqs } = parseRequirementsFile(markdown);
53
- console.log('Metadata:', JSON.stringify(parsedMeta, null, 2));
54
- console.log('\nRequirements tree:');
53
+ console.log("Metadata:", JSON.stringify(parsedMeta, null, 2));
54
+ console.log("\nRequirements tree:");
55
55
  function printTree(node, indent = 0) {
56
- const spaces = ' '.repeat(indent);
56
+ const spaces = " ".repeat(indent);
57
57
  console.log(`${spaces}${node.id}: ${node.label} → ${node.content}`);
58
58
  for (const child of node.children) {
59
59
  printTree(child, indent + 1);
@@ -62,9 +62,9 @@ try {
62
62
  for (const req of parsedReqs) {
63
63
  printTree(req);
64
64
  }
65
- console.log('\n✅ Round-trip successful!');
65
+ console.log("\n✅ Round-trip successful!");
66
66
  // Verify structure
67
- console.log('\n=== Verifying structure ===');
67
+ console.log("\n=== Verifying structure ===");
68
68
  const req = parsedReqs[0];
69
69
  console.log(`Root ID: ${req.id}`);
70
70
  console.log(`Root label: ${req.label}`);
@@ -75,7 +75,7 @@ try {
75
75
  console.log(`First grandchild ID: ${req.children[1].children[0].id}`);
76
76
  }
77
77
  catch (error) {
78
- console.error('❌ Failed to parse Markdown:', error);
78
+ console.error("❌ Failed to parse Markdown:", error);
79
79
  process.exit(1);
80
80
  }
81
81
  //# sourceMappingURL=test-schema.js.map
@@ -47,7 +47,7 @@ test(requirement('REQ-ID.0'), () => { /* test specific criterion */ });
47
47
  ### MCP Tools
48
48
 
49
49
  **Exploration:**
50
- - `list_all_requirements` - Overview of all requirements
50
+ - `list_requirements` - Overview of all requirements (set `untested: true` to filter to coverage gaps)
51
51
  - `get_requirement` - Requirement tree with test coverage
52
52
  - `search_requirements` - Search by text/regex
53
53
 
@@ -59,5 +59,6 @@ test(requirement('REQ-ID.0'), () => { /* test specific criterion */ });
59
59
 
60
60
  **Testing:**
61
61
  - `get_requirements_by_test` - See requirements a test file covers
62
- - `list_untested_requirements` - Find gaps in coverage
62
+ - `list_requirements` (with `untested: true`) - Find requirements without tests
63
+ - `report_coverage` - Get test coverage from local cache or cloud
63
64
  - `review_test` - Validate tests match requirement intent
@@ -4,7 +4,7 @@
4
4
  * This template is used for both local-only and authenticated init flows.
5
5
  * It demonstrates the dotrequirements markdown format with nested requirements.
6
6
  */
7
- export function generateExampleRequirements(projectId = 'local') {
7
+ export function generateExampleRequirements(projectId = "local") {
8
8
  const now = new Date().toISOString();
9
9
  return `---
10
10
  projectId: ${projectId}
@@ -4,7 +4,9 @@
4
4
  * This template is used for both local-only and authenticated init flows.
5
5
  * It demonstrates the dotrequirements markdown format with nested requirements.
6
6
  */
7
- export function generateExampleRequirements(projectId: string = 'local'): string {
7
+ export function generateExampleRequirements(
8
+ projectId: string = "local",
9
+ ): string {
8
10
  const now = new Date().toISOString();
9
11
 
10
12
  return `---
@@ -23,7 +23,7 @@ Requirements use the \`*.requirements.md\` naming pattern. See the [Requirements
23
23
 
24
24
  - \`dotrequirements pull\` - Sync requirements from cloud
25
25
  - \`dotrequirements push\` - Push local requirements to cloud
26
- - \`dotrequirements test\` - Validate requirement files
26
+ - \`dotrequirements validate\` - Validate requirement files
27
27
 
28
28
  Learn more: https://dotrequirements.io
29
29
  `;
@@ -23,7 +23,7 @@ Requirements use the \`*.requirements.md\` naming pattern. See the [Requirements
23
23
 
24
24
  - \`dotrequirements pull\` - Sync requirements from cloud
25
25
  - \`dotrequirements push\` - Push local requirements to cloud
26
- - \`dotrequirements test\` - Validate requirement files
26
+ - \`dotrequirements validate\` - Validate requirement files
27
27
 
28
28
  Learn more: https://dotrequirements.io
29
29
  `;
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: codebase-to-spec
3
+ description: Generate dotrequirements behavioral specifications from a codebase. Use this when the user wants to capture what an existing codebase does as a set of testable behavioral requirements — e.g. for legacy systems, third-party libraries, or before refactoring.
4
+ ---
5
+
6
+ # Codebase to Spec
7
+
8
+ You are a thin conversational wrapper around the `dotrequirements cts run` CLI. **You do not implement any pipeline logic yourself.** The CLI handles packing, planning, fan-out parallelism, retry, resumability, review loops, and presentation. Your job is to:
9
+
10
+ 1. Confirm the user's scope.
11
+ 2. Run the CLI via the Bash tool.
12
+ 3. Narrate progress as the CLI emits it.
13
+ 4. Surface the final summary and residual notes.
14
+ 5. Offer sensible follow-ups (push to cloud, re-run, narrow scope, etc.).
15
+
16
+ ## When this skill is right
17
+
18
+ Use it when the user wants behavioral requirements *from* code that already exists. Typical phrasings:
19
+ - "Generate requirements from this codebase"
20
+ - "Capture the behavior of this library"
21
+ - "I want a behavioral spec for the auth module"
22
+ - "Document what this service does"
23
+
24
+ Do **not** use it for:
25
+ - New feature design (use `dotreq-requirements` / the capture flow instead)
26
+ - Bug investigation
27
+ - Code review
28
+
29
+ ## Workflow
30
+
31
+ ### Step 1 — Confirm scope
32
+
33
+ If the user passed a scope argument (e.g. a path like `src/auth`), use it. Otherwise, ask one concise question:
34
+
35
+ > "Which part of the codebase should I spec? You can give me a path (e.g. `src/auth`) or say 'the whole thing'."
36
+
37
+ Whole-codebase runs are fine for small repos. For larger ones, encourage scoping to a single area — the CLI will tell you if the working-context budget is exceeded.
38
+
39
+ ### Step 2 — Run the CLI
40
+
41
+ Invoke the pipeline via Bash. The command is:
42
+
43
+ ```bash
44
+ dotrequirements cts run --scope <PATH> --non-interactive
45
+ ```
46
+
47
+ Flags:
48
+ - `--scope <path>` — limit packing to a subdirectory. Omit for the whole repo.
49
+ - `--non-interactive` — important: you're running in an agentic context, so prompts won't work.
50
+ - `--overwrite` — replace existing `.requirements/*.requirements.md` files.
51
+ - `--skip-existing` — leave existing files untouched.
52
+ - `--fresh` — clear the cache before running (use only if a previous run is corrupt).
53
+
54
+ Default to `--non-interactive`. Decide between `--overwrite` and `--skip-existing` based on what the user wants if there are existing `.requirements/` files; otherwise neither flag is needed (the default `fail-fast` policy will surface conflicts).
55
+
56
+ ### Step 3 — Narrate progress
57
+
58
+ The CLI emits lines like:
59
+
60
+ ```
61
+ [CTS] pack/done Packed 16 files → ... (compressed), ... (uncompressed)
62
+ [CTS] plan/done Plan loop converged at turn 1 with verdict: approved-with-revisions
63
+ [CTS] specify/summary Completed: 7 / Skipped (resume): 0 / Failed: 0
64
+ [CTS] spec-review/done Edit loop converged at turn 1 with verdict: approved-with-revisions
65
+ [CTS] present/created created: .../sample.requirements.md
66
+ ```
67
+
68
+ Surface these to the user in conversational form as they appear. **Do not invent progress claims** — narrate only what the CLI has actually emitted. If the CLI is silent for a long stretch (specifier fan-out can take minutes), a single reassuring note ("still working — the specifier fan-out runs in parallel and takes a few minutes for larger areas") is fine. Don't repeat it.
69
+
70
+ ### Step 4 — Surface the final summary
71
+
72
+ When the CLI finishes successfully, it prints a "Pipeline summary:" block with:
73
+ - total areas
74
+ - total top-level requirements
75
+ - outline review turns and verdict
76
+ - specifier completion count
77
+ - spec review turns and verdict
78
+ - the list of output files written
79
+ - residual notes (issues the reviewer flagged in `approved-with-revisions` outcomes)
80
+
81
+ Present that summary back to the user. **Highlight the residual notes** prominently — these are concerns the reviewer wanted the human to verify. Common categories:
82
+ - `outline.coverage_gap` — files the planner didn't assign to any area
83
+ - `spec.coverage_gap` — behaviors the spec missed
84
+ - `spec.framing_error` — requirements written as guidance/explanation rather than observable behavior
85
+ - `spec.cross_area` — duplication across areas
86
+ - `spec.internal_mechanics` — internal vocabulary that leaked into a user-facing spec
87
+
88
+ ### Step 5 — Offer follow-ups
89
+
90
+ After surfacing the summary, ask the user what they want next. Useful options:
91
+
92
+ - **Push to cloud** — `dotrequirements push` syncs the generated `.requirements/*.requirements.md` files to dotrequirements cloud. Only suggest this if the user has a cloud account (you can check by reading `.dotrequirements/config.json` or asking).
93
+ - **Refine a specific area** — re-run `dotrequirements cts specify-area "<name>"` for one area, optionally with `--model <name>` for a stronger model.
94
+ - **Refine the whole spec** — re-run `dotrequirements cts edit-loop` to iterate on the composed spec.
95
+ - **Narrow the scope** — re-run with a smaller `--scope`.
96
+ - **Hand-edit** — the output files are plain Markdown; the user can edit them directly.
97
+
98
+ ## Failure handling
99
+
100
+ The CLI uses stable exit codes (see `dotrequirements cts run --help`). Common cases:
101
+
102
+ - **Exit code 10 (BudgetExceeded)** — the codebase is too large for a single agent's context window after compression. Explain this in plain language and offer to retry with a narrower `--scope`.
103
+ - **Exit code 11 (MaxTurnsHit)** — a review loop hit its cap without converging. The CLI still writes the latest artifact; surface the residual review and ask the user whether to ship it, re-run the stage (`dotrequirements cts edit-loop`), or hand-edit.
104
+ - **Exit code 12 (OverwriteRefused)** — existing `.requirements/` files would be overwritten in non-interactive mode. Offer `--overwrite` or `--skip-existing`.
105
+ - **Exit code 3 (StageFailed)** — a stage errored. Read stderr for details. Offer: resume (re-run the same command — the cache means earlier stages are skipped), restart fresh (`--fresh`), or investigate (read files in `.dotrequirements-cache/`).
106
+ - **Any other non-zero exit** — surface stderr verbatim, then offer the same three options.
107
+
108
+ ## What you must NOT do
109
+
110
+ - Don't try to generate requirements yourself; always use the CLI.
111
+ - Don't paraphrase or summarize the CLI's progress lines in ways that change their meaning.
112
+ - Don't claim the pipeline did something it didn't (e.g. don't say "the reviewer approved this" if the verdict was `approved-with-revisions`).
113
+ - Don't push to cloud without asking — `dotrequirements push` is a destructive sync.
114
+ - Don't suggest editing files inside `.dotrequirements-cache/`; that's internal CLI state.
115
+
116
+ ## Host portability
117
+
118
+ This skill makes no assumptions about subagent mechanisms, Task tools, or framework-specific features. Its only host requirements are: Agent Skills format support, a Bash tool (or equivalent shell-out), and `dotrequirements` installed on the user's machine.
@@ -1,8 +1,8 @@
1
- import chalk from 'chalk';
1
+ import chalk from "chalk";
2
2
  // Cyan color: rgb(8, 145, 178)
3
3
  const bulletColor = chalk.rgb(8, 145, 178);
4
4
  /** Branded name for terminal output (colored bullet) */
5
- export const brand = `dot${bulletColor('•')}requirements`;
5
+ export const brand = `dot${bulletColor("•")}requirements`;
6
6
  /** Branded name for plain text (MCP descriptions, etc) */
7
- export const brandPlain = 'dot•requirements';
7
+ export const brandPlain = "dot•requirements";
8
8
  //# sourceMappingURL=brand.js.map
@@ -6,7 +6,7 @@
6
6
  * Uses the `open` npm package for secure, cross-platform browser launching
7
7
  * without shell command injection vulnerabilities.
8
8
  */
9
- import open from 'open';
9
+ import open from "open";
10
10
  /**
11
11
  * Open a URL in the user's default browser
12
12
  *
@@ -26,10 +26,10 @@ export async function openBrowser(url) {
26
26
  export async function openBrowserWithFallback(url) {
27
27
  try {
28
28
  await openBrowser(url);
29
- console.log('Opening browser for authentication...');
29
+ console.log("Opening browser for authentication...");
30
30
  }
31
- catch (error) {
32
- console.error('Could not automatically open browser.');
31
+ catch {
32
+ console.error("Could not automatically open browser.");
33
33
  console.log(`\nPlease open this URL in your browser manually:\n${url}\n`);
34
34
  }
35
35
  }
@@ -28,7 +28,7 @@ export declare function wrapInSectionMarkers(content: string): string;
28
28
  * Returns: { action: 'created' | 'appended' | 'updated', existingContent?: string }
29
29
  */
30
30
  export declare function appendOrUpdateSection(filePath: string, sectionContent: string): {
31
- action: 'created' | 'appended' | 'updated';
31
+ action: "created" | "appended" | "updated";
32
32
  existingContent?: string;
33
33
  };
34
34
  /**
@@ -1,17 +1,17 @@
1
- import { readFileSync, writeFileSync, existsSync } from 'fs';
2
- import { join, dirname } from 'path';
3
- import { findUp } from 'find-up';
4
- const SECTION_START = '<!-- dotrequirements:start -->';
5
- const SECTION_END = '<!-- dotrequirements:end -->';
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { findUp } from "find-up";
4
+ const SECTION_START = "<!-- dotrequirements:start -->";
5
+ const SECTION_END = "<!-- dotrequirements:end -->";
6
6
  /**
7
7
  * Platform to context file mapping
8
8
  */
9
9
  const PLATFORM_CONTEXT_FILES = {
10
- 'claude-code': 'CLAUDE.md',
11
- 'cursor': 'AGENTS.md',
12
- 'codex': 'AGENTS.md',
13
- 'github-copilot': 'AGENTS.md',
14
- 'antigravity': 'GEMINI.md',
10
+ "claude-code": "CLAUDE.md",
11
+ cursor: "AGENTS.md",
12
+ codex: "AGENTS.md",
13
+ "github-copilot": "AGENTS.md",
14
+ antigravity: "GEMINI.md",
15
15
  };
16
16
  /**
17
17
  * Get the appropriate context file name for a platform
@@ -23,7 +23,7 @@ export function getContextFileName(platform) {
23
23
  * Find the git root directory
24
24
  */
25
25
  export async function findGitRoot() {
26
- const gitDir = await findUp('.git', { type: 'directory' });
26
+ const gitDir = await findUp(".git", { type: "directory" });
27
27
  return gitDir ? dirname(gitDir) : null;
28
28
  }
29
29
  /**
@@ -61,23 +61,23 @@ export function appendOrUpdateSection(filePath, sectionContent) {
61
61
  const wrappedContent = wrapInSectionMarkers(sectionContent);
62
62
  if (!existsSync(filePath)) {
63
63
  // Create new file with just the section
64
- writeFileSync(filePath, wrappedContent + '\n', 'utf-8');
65
- return { action: 'created' };
64
+ writeFileSync(filePath, `${wrappedContent}\n`, "utf-8");
65
+ return { action: "created" };
66
66
  }
67
- const existingFile = readFileSync(filePath, 'utf-8');
67
+ const existingFile = readFileSync(filePath, "utf-8");
68
68
  const existingSection = findDotrequirementsSection(existingFile);
69
69
  if (!existingSection) {
70
70
  // Append section to end of file
71
- const separator = existingFile.endsWith('\n') ? '\n' : '\n\n';
72
- writeFileSync(filePath, existingFile + separator + wrappedContent + '\n', 'utf-8');
73
- return { action: 'appended' };
71
+ const separator = existingFile.endsWith("\n") ? "\n" : "\n\n";
72
+ writeFileSync(filePath, `${existingFile + separator + wrappedContent}\n`, "utf-8");
73
+ return { action: "appended" };
74
74
  }
75
75
  // Replace existing section
76
76
  const newContent = existingFile.slice(0, existingSection.start) +
77
77
  wrappedContent +
78
78
  existingFile.slice(existingSection.end);
79
- writeFileSync(filePath, newContent, 'utf-8');
80
- return { action: 'updated', existingContent: existingSection.content };
79
+ writeFileSync(filePath, newContent, "utf-8");
80
+ return { action: "updated", existingContent: existingSection.content };
81
81
  }
82
82
  /**
83
83
  * Get the full path to the context file for a platform
@@ -102,12 +102,12 @@ export function buildNoGitRepoMessage(fileName) {
102
102
  return [
103
103
  `⚠️ Skipped writing ${fileName}: not inside a git repository.`,
104
104
  ` ${fileName} would have been created at the git repo root, but no .git directory was found.`,
105
- '',
106
- ' To finish setup, either:',
107
- ' • run `git init` here, then re-run `dotreq mcp-setup`, or',
108
- ' • `cd` into an existing project directory and run `dotreq mcp-setup` there.',
109
- '',
110
- ' Note: the MCP server itself was configured successfully — only the context file step was skipped.',
111
- ].join('\n');
105
+ "",
106
+ " To finish setup, either:",
107
+ " • run `git init` here, then re-run `dotreq ai-setup`, or",
108
+ " • `cd` into an existing project directory and run `dotreq ai-setup` there.",
109
+ "",
110
+ " Note: the MCP server itself was configured successfully — only the context file step was skipped.",
111
+ ].join("\n");
112
112
  }
113
113
  //# sourceMappingURL=context-file.js.map