@popoverai/dotrequirements 0.11.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 (127) hide show
  1. package/README.md +478 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.js +82 -0
  4. package/dist/commands/init.d.ts +6 -0
  5. package/dist/commands/init.js +355 -0
  6. package/dist/commands/link.d.ts +15 -0
  7. package/dist/commands/link.js +156 -0
  8. package/dist/commands/login.d.ts +12 -0
  9. package/dist/commands/login.js +117 -0
  10. package/dist/commands/logout.d.ts +5 -0
  11. package/dist/commands/logout.js +17 -0
  12. package/dist/commands/mcp-setup.d.ts +5 -0
  13. package/dist/commands/mcp-setup.js +367 -0
  14. package/dist/commands/mcp.d.ts +6 -0
  15. package/dist/commands/mcp.js +10 -0
  16. package/dist/commands/pull.d.ts +7 -0
  17. package/dist/commands/pull.js +171 -0
  18. package/dist/commands/push.d.ts +11 -0
  19. package/dist/commands/push.js +310 -0
  20. package/dist/commands/test.d.ts +6 -0
  21. package/dist/commands/test.js +78 -0
  22. package/dist/config.d.ts +5 -0
  23. package/dist/config.js +16 -0
  24. package/dist/convex.d.ts +56 -0
  25. package/dist/convex.js +58 -0
  26. package/dist/harness/cache.d.ts +135 -0
  27. package/dist/harness/cache.js +342 -0
  28. package/dist/harness/convexReporting.d.ts +15 -0
  29. package/dist/harness/convexReporting.js +136 -0
  30. package/dist/harness/coverageCache.d.ts +30 -0
  31. package/dist/harness/coverageCache.js +70 -0
  32. package/dist/harness/finalize.d.ts +48 -0
  33. package/dist/harness/finalize.js +299 -0
  34. package/dist/harness/index.d.ts +70 -0
  35. package/dist/harness/index.js +103 -0
  36. package/dist/harness/localReporting.d.ts +6 -0
  37. package/dist/harness/localReporting.js +49 -0
  38. package/dist/harness/prepare.d.ts +41 -0
  39. package/dist/harness/prepare.js +83 -0
  40. package/dist/harness/requirementsLoader.d.ts +45 -0
  41. package/dist/harness/requirementsLoader.js +201 -0
  42. package/dist/harness/tracking.d.ts +49 -0
  43. package/dist/harness/tracking.js +179 -0
  44. package/dist/harness/types.d.ts +12 -0
  45. package/dist/harness/types.js +6 -0
  46. package/dist/mcp/convexClient.d.ts +43 -0
  47. package/dist/mcp/convexClient.js +101 -0
  48. package/dist/mcp/grep.d.ts +24 -0
  49. package/dist/mcp/grep.js +261 -0
  50. package/dist/mcp/index.d.ts +3 -0
  51. package/dist/mcp/index.js +1758 -0
  52. package/dist/mcp/requirements.d.ts +47 -0
  53. package/dist/mcp/requirements.js +141 -0
  54. package/dist/mcp/testCodeExtractor.d.ts +22 -0
  55. package/dist/mcp/testCodeExtractor.js +152 -0
  56. package/dist/mcp/types.d.ts +27 -0
  57. package/dist/mcp/types.js +2 -0
  58. package/dist/schema/browser.d.ts +12 -0
  59. package/dist/schema/browser.js +24 -0
  60. package/dist/schema/builder.d.ts +25 -0
  61. package/dist/schema/builder.js +125 -0
  62. package/dist/schema/conversions.d.ts +69 -0
  63. package/dist/schema/conversions.js +201 -0
  64. package/dist/schema/index.d.ts +14 -0
  65. package/dist/schema/index.js +24 -0
  66. package/dist/schema/parser-core.d.ts +61 -0
  67. package/dist/schema/parser-core.js +247 -0
  68. package/dist/schema/parser.d.ts +44 -0
  69. package/dist/schema/parser.js +295 -0
  70. package/dist/schema/resolver.d.ts +66 -0
  71. package/dist/schema/resolver.js +185 -0
  72. package/dist/schema/schemas.d.ts +312 -0
  73. package/dist/schema/schemas.js +258 -0
  74. package/dist/schema/test-schema.d.ts +5 -0
  75. package/dist/schema/test-schema.js +81 -0
  76. package/dist/templates/antigravity-gemini.md +3 -0
  77. package/dist/templates/antigravity-overview-rule.md +3 -0
  78. package/dist/templates/antigravity-test-rule.md +3 -0
  79. package/dist/templates/behavioral-core.md +25 -0
  80. package/dist/templates/claude-code-overview-skill.md +6 -0
  81. package/dist/templates/claude-code-skill.md +6 -0
  82. package/dist/templates/claude-code-test-skill.md +6 -0
  83. package/dist/templates/codex-agents.md +3 -0
  84. package/dist/templates/codex-overview-agents.md +3 -0
  85. package/dist/templates/codex-test-agents.md +3 -0
  86. package/dist/templates/cursor-overview-rule.mdc +5 -0
  87. package/dist/templates/cursor-rule.mdc +5 -0
  88. package/dist/templates/cursor-test-rule.mdc +5 -0
  89. package/dist/templates/example-requirements.d.ts +8 -0
  90. package/dist/templates/example-requirements.js +88 -0
  91. package/dist/templates/example-requirements.ts +88 -0
  92. package/dist/templates/overview-core.md +27 -0
  93. package/dist/templates/requirements-readme.d.ts +5 -0
  94. package/dist/templates/requirements-readme.js +31 -0
  95. package/dist/templates/requirements-readme.ts +30 -0
  96. package/dist/templates/test-writing-core.md +72 -0
  97. package/dist/utils/brand.d.ts +5 -0
  98. package/dist/utils/brand.js +8 -0
  99. package/dist/utils/browser-launch.d.ts +19 -0
  100. package/dist/utils/browser-launch.js +36 -0
  101. package/dist/utils/detect-existing-project.d.ts +5 -0
  102. package/dist/utils/detect-existing-project.js +34 -0
  103. package/dist/utils/env.d.ts +19 -0
  104. package/dist/utils/env.js +56 -0
  105. package/dist/utils/gitignore.d.ts +7 -0
  106. package/dist/utils/gitignore.js +29 -0
  107. package/dist/utils/local-project.d.ts +31 -0
  108. package/dist/utils/local-project.js +33 -0
  109. package/dist/utils/oauth-callback-server.d.ts +28 -0
  110. package/dist/utils/oauth-callback-server.js +156 -0
  111. package/dist/utils/oauth-flow.d.ts +22 -0
  112. package/dist/utils/oauth-flow.js +120 -0
  113. package/dist/utils/project-discovery.d.ts +57 -0
  114. package/dist/utils/project-discovery.js +146 -0
  115. package/dist/utils/project-name.d.ts +8 -0
  116. package/dist/utils/project-name.js +48 -0
  117. package/dist/utils/project-selector.d.ts +25 -0
  118. package/dist/utils/project-selector.js +69 -0
  119. package/dist/utils/prompts.d.ts +33 -0
  120. package/dist/utils/prompts.js +60 -0
  121. package/dist/utils/templates.d.ts +29 -0
  122. package/dist/utils/templates.js +67 -0
  123. package/dist/utils/token-refresh.d.ts +24 -0
  124. package/dist/utils/token-refresh.js +69 -0
  125. package/dist/utils/token-storage.d.ts +31 -0
  126. package/dist/utils/token-storage.js +57 -0
  127. package/package.json +82 -0
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Zod schemas for Markdown requirements format.
3
+ * TypeScript types are inferred from these schemas using z.infer<>.
4
+ */
5
+ import { z } from 'zod';
6
+ /**
7
+ * Requirement prefix format: uppercase letters and hyphens, 1-30 chars.
8
+ * First char must be a letter.
9
+ * Examples: "LOGIN", "AUTH", "SYNC-CLI-CREATE", "ADMIN-PROJECT"
10
+ */
11
+ export declare const REQUIREMENT_PREFIX_PATTERN: RegExp;
12
+ /**
13
+ * Requirement key format: PREFIX-number.
14
+ * Examples: "LOGIN-1", "AUTH-42", "USAGE-0"
15
+ */
16
+ export declare const REQUIREMENT_KEY_PATTERN: RegExp;
17
+ /**
18
+ * Zod schema for requirement prefix.
19
+ * Accepts any case input but validates the uppercase form.
20
+ */
21
+ export declare const RequirementPrefixSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
22
+ export type RequirementPrefix = z.infer<typeof RequirementPrefixSchema>;
23
+ /**
24
+ * Zod schema for requirement key (PREFIX-number).
25
+ * Normalizes to uppercase and strips leading zeros from numbers.
26
+ */
27
+ export declare const RequirementKeySchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
28
+ export type RequirementKey = z.infer<typeof RequirementKeySchema>;
29
+ /**
30
+ * Normalize a prefix to uppercase.
31
+ * Does not validate - use validatePrefix for validation.
32
+ */
33
+ export declare function normalizePrefix(prefix: string): string;
34
+ /**
35
+ * Validate a prefix string.
36
+ * Returns the normalized (uppercase) prefix or throws ValidationError.
37
+ */
38
+ export declare function validatePrefix(prefix: string): string;
39
+ /**
40
+ * Validate a requirement key string.
41
+ * Returns the normalized (uppercase) key or throws ValidationError.
42
+ */
43
+ export declare function validateKey(key: string): string;
44
+ /**
45
+ * Parse a requirement key into prefix and number parts.
46
+ * Returns null if the key doesn't match the expected format.
47
+ */
48
+ export declare function parseRequirementKey(key: string): {
49
+ prefix: string;
50
+ number: number;
51
+ } | null;
52
+ /**
53
+ * Build a requirement key from prefix and number.
54
+ */
55
+ export declare function buildRequirementKey(prefix: string, number: number): string;
56
+ /**
57
+ * Metadata block at the top of every requirements file
58
+ */
59
+ export declare const MetadataSchema: z.ZodObject<{
60
+ projectId: z.ZodString;
61
+ projectSlug: z.ZodOptional<z.ZodString>;
62
+ pulledAt: z.ZodOptional<z.ZodString>;
63
+ version: z.ZodNumber;
64
+ document: z.ZodOptional<z.ZodObject<{
65
+ id: z.ZodOptional<z.ZodString>;
66
+ title: z.ZodString;
67
+ defaultPrefix: z.ZodOptional<z.ZodString>;
68
+ }, "strip", z.ZodTypeAny, {
69
+ title: string;
70
+ id?: string | undefined;
71
+ defaultPrefix?: string | undefined;
72
+ }, {
73
+ title: string;
74
+ id?: string | undefined;
75
+ defaultPrefix?: string | undefined;
76
+ }>>;
77
+ }, "strip", z.ZodTypeAny, {
78
+ version: number;
79
+ projectId: string;
80
+ projectSlug?: string | undefined;
81
+ pulledAt?: string | undefined;
82
+ document?: {
83
+ title: string;
84
+ id?: string | undefined;
85
+ defaultPrefix?: string | undefined;
86
+ } | undefined;
87
+ }, {
88
+ version: number;
89
+ projectId: string;
90
+ projectSlug?: string | undefined;
91
+ pulledAt?: string | undefined;
92
+ document?: {
93
+ title: string;
94
+ id?: string | undefined;
95
+ defaultPrefix?: string | undefined;
96
+ } | undefined;
97
+ }>;
98
+ export type Metadata = z.infer<typeof MetadataSchema>;
99
+ /**
100
+ * Convex document ID format: 32 lowercase alphanumeric characters starting with 'j'
101
+ */
102
+ export declare const CONVEX_ID_PATTERN: RegExp;
103
+ /**
104
+ * Result of validating a file for push readiness.
105
+ */
106
+ export type PushValidationResult = {
107
+ valid: true;
108
+ action: 'create' | 'update';
109
+ warning?: string;
110
+ } | {
111
+ valid: false;
112
+ reason: string;
113
+ };
114
+ /**
115
+ * Validate metadata for push readiness.
116
+ * Returns whether the file can be pushed and what action will be taken.
117
+ *
118
+ * SYNC-CLI-CREATE-1: Files without document.id create new documents
119
+ * SYNC-CLI-EDIT-1: Files with valid document.id update existing documents
120
+ */
121
+ export declare function validateForPush(metadata: Metadata): PushValidationResult;
122
+ /**
123
+ * Individual requirement node in the tree.
124
+ * Each node has an ID, label, content, and optional children.
125
+ */
126
+ export declare const RequirementNodeSchema: z.ZodType<RequirementNode>;
127
+ export type RequirementNode = {
128
+ id: string;
129
+ label: string;
130
+ content: string;
131
+ children: RequirementNode[];
132
+ metadata?: {
133
+ convexId?: string;
134
+ rootId?: string;
135
+ position?: string;
136
+ updatedAt?: number;
137
+ externalLinks?: {
138
+ jira?: string;
139
+ notion?: string;
140
+ };
141
+ prefix?: string;
142
+ index?: number;
143
+ documentId?: string;
144
+ };
145
+ };
146
+ /**
147
+ * Complete requirements file structure.
148
+ * Top-level keys are either "_meta" or requirement IDs.
149
+ */
150
+ export declare const RequirementsFileSchema: z.ZodObject<{
151
+ _meta: z.ZodObject<{
152
+ projectId: z.ZodString;
153
+ projectSlug: z.ZodOptional<z.ZodString>;
154
+ pulledAt: z.ZodOptional<z.ZodString>;
155
+ version: z.ZodNumber;
156
+ document: z.ZodOptional<z.ZodObject<{
157
+ id: z.ZodOptional<z.ZodString>;
158
+ title: z.ZodString;
159
+ defaultPrefix: z.ZodOptional<z.ZodString>;
160
+ }, "strip", z.ZodTypeAny, {
161
+ title: string;
162
+ id?: string | undefined;
163
+ defaultPrefix?: string | undefined;
164
+ }, {
165
+ title: string;
166
+ id?: string | undefined;
167
+ defaultPrefix?: string | undefined;
168
+ }>>;
169
+ }, "strip", z.ZodTypeAny, {
170
+ version: number;
171
+ projectId: string;
172
+ projectSlug?: string | undefined;
173
+ pulledAt?: string | undefined;
174
+ document?: {
175
+ title: string;
176
+ id?: string | undefined;
177
+ defaultPrefix?: string | undefined;
178
+ } | undefined;
179
+ }, {
180
+ version: number;
181
+ projectId: string;
182
+ projectSlug?: string | undefined;
183
+ pulledAt?: string | undefined;
184
+ document?: {
185
+ title: string;
186
+ id?: string | undefined;
187
+ defaultPrefix?: string | undefined;
188
+ } | undefined;
189
+ }>;
190
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
191
+ _meta: z.ZodObject<{
192
+ projectId: z.ZodString;
193
+ projectSlug: z.ZodOptional<z.ZodString>;
194
+ pulledAt: z.ZodOptional<z.ZodString>;
195
+ version: z.ZodNumber;
196
+ document: z.ZodOptional<z.ZodObject<{
197
+ id: z.ZodOptional<z.ZodString>;
198
+ title: z.ZodString;
199
+ defaultPrefix: z.ZodOptional<z.ZodString>;
200
+ }, "strip", z.ZodTypeAny, {
201
+ title: string;
202
+ id?: string | undefined;
203
+ defaultPrefix?: string | undefined;
204
+ }, {
205
+ title: string;
206
+ id?: string | undefined;
207
+ defaultPrefix?: string | undefined;
208
+ }>>;
209
+ }, "strip", z.ZodTypeAny, {
210
+ version: number;
211
+ projectId: string;
212
+ projectSlug?: string | undefined;
213
+ pulledAt?: string | undefined;
214
+ document?: {
215
+ title: string;
216
+ id?: string | undefined;
217
+ defaultPrefix?: string | undefined;
218
+ } | undefined;
219
+ }, {
220
+ version: number;
221
+ projectId: string;
222
+ projectSlug?: string | undefined;
223
+ pulledAt?: string | undefined;
224
+ document?: {
225
+ title: string;
226
+ id?: string | undefined;
227
+ defaultPrefix?: string | undefined;
228
+ } | undefined;
229
+ }>;
230
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
231
+ _meta: z.ZodObject<{
232
+ projectId: z.ZodString;
233
+ projectSlug: z.ZodOptional<z.ZodString>;
234
+ pulledAt: z.ZodOptional<z.ZodString>;
235
+ version: z.ZodNumber;
236
+ document: z.ZodOptional<z.ZodObject<{
237
+ id: z.ZodOptional<z.ZodString>;
238
+ title: z.ZodString;
239
+ defaultPrefix: z.ZodOptional<z.ZodString>;
240
+ }, "strip", z.ZodTypeAny, {
241
+ title: string;
242
+ id?: string | undefined;
243
+ defaultPrefix?: string | undefined;
244
+ }, {
245
+ title: string;
246
+ id?: string | undefined;
247
+ defaultPrefix?: string | undefined;
248
+ }>>;
249
+ }, "strip", z.ZodTypeAny, {
250
+ version: number;
251
+ projectId: string;
252
+ projectSlug?: string | undefined;
253
+ pulledAt?: string | undefined;
254
+ document?: {
255
+ title: string;
256
+ id?: string | undefined;
257
+ defaultPrefix?: string | undefined;
258
+ } | undefined;
259
+ }, {
260
+ version: number;
261
+ projectId: string;
262
+ projectSlug?: string | undefined;
263
+ pulledAt?: string | undefined;
264
+ document?: {
265
+ title: string;
266
+ id?: string | undefined;
267
+ defaultPrefix?: string | undefined;
268
+ } | undefined;
269
+ }>;
270
+ }, z.ZodTypeAny, "passthrough">>;
271
+ export type RequirementsFile = z.infer<typeof RequirementsFileSchema> & {
272
+ [key: string]: any;
273
+ };
274
+ /**
275
+ * Parsed criterion from Markdown (position. Label: content format)
276
+ * Example: "0. Given: user has valid credentials"
277
+ */
278
+ export declare const ParsedCriterionSchema: z.ZodObject<{
279
+ position: z.ZodString;
280
+ label: z.ZodString;
281
+ content: z.ZodString;
282
+ }, "strip", z.ZodTypeAny, {
283
+ label: string;
284
+ content: string;
285
+ position: string;
286
+ }, {
287
+ label: string;
288
+ content: string;
289
+ position: string;
290
+ }>;
291
+ export type ParsedCriterion = z.infer<typeof ParsedCriterionSchema>;
292
+ /**
293
+ * Validation error with context
294
+ */
295
+ export declare class ValidationError extends Error {
296
+ path?: string | undefined;
297
+ cause?: unknown | undefined;
298
+ constructor(message: string, path?: string | undefined, cause?: unknown | undefined);
299
+ }
300
+ /**
301
+ * Validate metadata block
302
+ */
303
+ export declare function validateMetadata(data: unknown): Metadata;
304
+ /**
305
+ * Validate requirement node structure
306
+ */
307
+ export declare function validateRequirementNode(data: unknown, path: string): RequirementNode;
308
+ /**
309
+ * Validate complete requirements file
310
+ */
311
+ export declare function validateRequirementsFile(data: unknown): RequirementsFile;
312
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Zod schemas for Markdown requirements format.
3
+ * TypeScript types are inferred from these schemas using z.infer<>.
4
+ */
5
+ import { z } from 'zod';
6
+ import { fromZodError } from 'zod-validation-error';
7
+ // =============================================================================
8
+ // Prefix and Key Validation
9
+ // =============================================================================
10
+ /**
11
+ * Requirement prefix format: uppercase letters and hyphens, 1-30 chars.
12
+ * First char must be a letter.
13
+ * Examples: "LOGIN", "AUTH", "SYNC-CLI-CREATE", "ADMIN-PROJECT"
14
+ */
15
+ export const REQUIREMENT_PREFIX_PATTERN = /^[A-Z][A-Z-]{0,29}$/;
16
+ /**
17
+ * Requirement key format: PREFIX-number.
18
+ * Examples: "LOGIN-1", "AUTH-42", "USAGE-0"
19
+ */
20
+ export const REQUIREMENT_KEY_PATTERN = /^[A-Z][A-Z-]*-[0-9]+$/;
21
+ /**
22
+ * Zod schema for requirement prefix.
23
+ * Accepts any case input but validates the uppercase form.
24
+ */
25
+ export const RequirementPrefixSchema = z
26
+ .string()
27
+ .min(1, 'Prefix must be at least 1 character')
28
+ .max(30, 'Prefix must be at most 30 characters')
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');
31
+ /**
32
+ * Zod schema for requirement key (PREFIX-number).
33
+ * Normalizes to uppercase and strips leading zeros from numbers.
34
+ */
35
+ export const RequirementKeySchema = z
36
+ .string()
37
+ .refine((val) => REQUIREMENT_KEY_PATTERN.test(val.toUpperCase()), 'Key must be in format PREFIX-number (e.g., LOGIN-1, AUTH-42)')
38
+ .transform((val) => {
39
+ const upper = val.toUpperCase();
40
+ // Normalize leading zeros: LOGIN-01 -> LOGIN-1, LOGIN-007 -> LOGIN-7
41
+ return upper.replace(/-0+(\d+)$/, '-$1');
42
+ });
43
+ /**
44
+ * Normalize a prefix to uppercase.
45
+ * Does not validate - use validatePrefix for validation.
46
+ */
47
+ export function normalizePrefix(prefix) {
48
+ return prefix.toUpperCase();
49
+ }
50
+ /**
51
+ * Validate a prefix string.
52
+ * Returns the normalized (uppercase) prefix or throws ValidationError.
53
+ */
54
+ export function validatePrefix(prefix) {
55
+ const result = RequirementPrefixSchema.safeParse(prefix);
56
+ if (!result.success) {
57
+ const validationError = fromZodError(result.error);
58
+ throw new ValidationError(`Invalid prefix "${prefix}": ${validationError.message}`, 'prefix', result.error);
59
+ }
60
+ return result.data;
61
+ }
62
+ /**
63
+ * Validate a requirement key string.
64
+ * Returns the normalized (uppercase) key or throws ValidationError.
65
+ */
66
+ export function validateKey(key) {
67
+ const result = RequirementKeySchema.safeParse(key);
68
+ if (!result.success) {
69
+ const validationError = fromZodError(result.error);
70
+ throw new ValidationError(`Invalid key "${key}": ${validationError.message}`, 'key', result.error);
71
+ }
72
+ return result.data;
73
+ }
74
+ /**
75
+ * Parse a requirement key into prefix and number parts.
76
+ * Returns null if the key doesn't match the expected format.
77
+ */
78
+ export function parseRequirementKey(key) {
79
+ const normalized = key.toUpperCase();
80
+ // Find the last hyphen followed by digits - that's the number part
81
+ const match = normalized.match(/^(.+)-([0-9]+)$/);
82
+ if (!match)
83
+ return null;
84
+ const [, prefix, numStr] = match;
85
+ // Validate the prefix part
86
+ if (!REQUIREMENT_PREFIX_PATTERN.test(prefix))
87
+ return null;
88
+ return { prefix, number: parseInt(numStr, 10) };
89
+ }
90
+ /**
91
+ * Build a requirement key from prefix and number.
92
+ */
93
+ export function buildRequirementKey(prefix, number) {
94
+ const normalizedPrefix = prefix.toUpperCase();
95
+ if (!REQUIREMENT_PREFIX_PATTERN.test(normalizedPrefix)) {
96
+ throw new ValidationError(`Invalid prefix "${prefix}": must match pattern ${REQUIREMENT_PREFIX_PATTERN}`, 'prefix');
97
+ }
98
+ if (number < 0 || !Number.isInteger(number)) {
99
+ throw new ValidationError(`Invalid number "${number}": must be a non-negative integer`, 'number');
100
+ }
101
+ return `${normalizedPrefix}-${number}`;
102
+ }
103
+ // =============================================================================
104
+ // Metadata Schema
105
+ // =============================================================================
106
+ /**
107
+ * Metadata block at the top of every requirements file
108
+ */
109
+ export const MetadataSchema = z.object({
110
+ projectId: z.string(),
111
+ projectSlug: z.string().optional(), // User-friendly slug (e.g., "quick-fox-123")
112
+ pulledAt: z.string().optional(), // ISO 8601 timestamp - required for CLI files, optional for IDE
113
+ version: z.number(),
114
+ // Optional document association (one file = one document)
115
+ document: z.object({
116
+ id: z.string().optional(), // Optional - filled by push when creating new document
117
+ title: z.string(),
118
+ // DOC-HEADER-11: Default prefix for new requirement keys in this document
119
+ defaultPrefix: z.string().optional(),
120
+ }).optional(),
121
+ });
122
+ // =============================================================================
123
+ // Push Readiness Validation
124
+ // =============================================================================
125
+ /**
126
+ * Convex document ID format: 32 lowercase alphanumeric characters starting with 'j'
127
+ */
128
+ export const CONVEX_ID_PATTERN = /^j[a-z0-9]{31}$/;
129
+ /**
130
+ * Validate metadata for push readiness.
131
+ * Returns whether the file can be pushed and what action will be taken.
132
+ *
133
+ * SYNC-CLI-CREATE-1: Files without document.id create new documents
134
+ * SYNC-CLI-EDIT-1: Files with valid document.id update existing documents
135
+ */
136
+ export function validateForPush(metadata) {
137
+ // Must have document section
138
+ if (!metadata.document) {
139
+ return {
140
+ valid: false,
141
+ reason: 'Missing document section. Add a document section with a title to push this file.'
142
+ };
143
+ }
144
+ // Must have title
145
+ if (!metadata.document.title || metadata.document.title.trim() === '') {
146
+ return {
147
+ valid: false,
148
+ reason: 'Document section missing required title.'
149
+ };
150
+ }
151
+ // Check document.id
152
+ if (!metadata.document.id) {
153
+ // No ID = create new document
154
+ return { valid: true, action: 'create' };
155
+ }
156
+ // Has ID - check if it's a valid Convex ID
157
+ if (CONVEX_ID_PATTERN.test(metadata.document.id)) {
158
+ // Valid Convex ID = update existing document
159
+ return { valid: true, action: 'update' };
160
+ }
161
+ // Invalid ID format - will create new document and overwrite
162
+ return {
163
+ valid: true,
164
+ action: 'create',
165
+ warning: `Invalid document ID '${metadata.document.id}' will be replaced with a new ID.`
166
+ };
167
+ }
168
+ /**
169
+ * Individual requirement node in the tree.
170
+ * Each node has an ID, label, content, and optional children.
171
+ */
172
+ export const RequirementNodeSchema = z.lazy(() => z.object({
173
+ id: z.string(), // e.g., "REQ123" or "REQ123.0.1"
174
+ label: z.string(), // e.g., "given", "when", "then", "requirement"
175
+ content: z.string(), // The actual requirement text
176
+ children: z.array(RequirementNodeSchema).default([]),
177
+ // Optional metadata preserved from Convex
178
+ metadata: z.object({
179
+ convexId: z.string().optional(), // Convex _id
180
+ rootId: z.string().optional(), // Convex rootId
181
+ position: z.string().optional(), // Convex position path
182
+ updatedAt: z.number().optional(),
183
+ externalLinks: z.object({
184
+ jira: z.string().optional(),
185
+ notion: z.string().optional(),
186
+ }).optional(),
187
+ // New schema fields for bidirectional sync
188
+ prefix: z.string().optional(), // e.g., "REQ"
189
+ index: z.number().optional(), // e.g., 123
190
+ documentId: z.string().optional(), // Convex document ID
191
+ }).optional(),
192
+ }));
193
+ /**
194
+ * Complete requirements file structure.
195
+ * Top-level keys are either "_meta" or requirement IDs.
196
+ */
197
+ export const RequirementsFileSchema = z.object({
198
+ _meta: MetadataSchema,
199
+ // All other keys are requirement roots
200
+ // We'll validate the structure separately since Zod doesn't handle
201
+ // arbitrary keys with specific structure well
202
+ }).passthrough(); // Allow additional keys
203
+ /**
204
+ * Parsed criterion from Markdown (position. Label: content format)
205
+ * Example: "0. Given: user has valid credentials"
206
+ */
207
+ export const ParsedCriterionSchema = z.object({
208
+ position: z.string(), // e.g., "0", "1.0", "2.3.1"
209
+ label: z.string(), // e.g., "given", "when", "then"
210
+ content: z.string(), // The criterion text
211
+ });
212
+ /**
213
+ * Validation error with context
214
+ */
215
+ export class ValidationError extends Error {
216
+ path;
217
+ cause;
218
+ constructor(message, path, cause) {
219
+ super(message);
220
+ this.path = path;
221
+ this.cause = cause;
222
+ this.name = 'ValidationError';
223
+ }
224
+ }
225
+ /**
226
+ * Validate metadata block
227
+ */
228
+ export function validateMetadata(data) {
229
+ const result = MetadataSchema.safeParse(data);
230
+ if (!result.success) {
231
+ const validationError = fromZodError(result.error);
232
+ throw new ValidationError(`Invalid metadata block:\n${validationError.message}`, '_meta', result.error);
233
+ }
234
+ return result.data;
235
+ }
236
+ /**
237
+ * Validate requirement node structure
238
+ */
239
+ export function validateRequirementNode(data, path) {
240
+ const result = RequirementNodeSchema.safeParse(data);
241
+ if (!result.success) {
242
+ const validationError = fromZodError(result.error);
243
+ throw new ValidationError(`Invalid requirement node structure at ${path}:\n${validationError.message}`, path, result.error);
244
+ }
245
+ return result.data;
246
+ }
247
+ /**
248
+ * Validate complete requirements file
249
+ */
250
+ export function validateRequirementsFile(data) {
251
+ const result = RequirementsFileSchema.safeParse(data);
252
+ if (!result.success) {
253
+ const validationError = fromZodError(result.error);
254
+ throw new ValidationError(`Invalid requirements file structure:\n${validationError.message}`, undefined, result.error);
255
+ }
256
+ return result.data;
257
+ }
258
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Quick test to verify the new YAML schema is valid and round-trips correctly.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=test-schema.d.ts.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Quick test to verify the new YAML schema is valid and round-trips correctly.
3
+ */
4
+ import { buildRequirementsMarkdown, parseRequirementsFile, buildMetadata } from './index.js';
5
+ // Create test data
6
+ const metadata = buildMetadata('test-project-123', 1);
7
+ const testRequirements = [
8
+ {
9
+ id: 'REQ123',
10
+ label: 'requirement',
11
+ content: 'User can authenticate',
12
+ children: [
13
+ {
14
+ id: 'REQ123.0',
15
+ label: 'given',
16
+ content: 'user has valid credentials',
17
+ children: [],
18
+ },
19
+ {
20
+ id: 'REQ123.1',
21
+ label: 'when',
22
+ content: 'user submits login form',
23
+ children: [
24
+ {
25
+ id: 'REQ123.1.0',
26
+ label: 'then',
27
+ content: 'system validates credentials',
28
+ children: [],
29
+ },
30
+ {
31
+ id: 'REQ123.1.1',
32
+ label: 'then',
33
+ content: 'system creates session',
34
+ children: [],
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ id: 'REQ123.2',
40
+ label: 'then',
41
+ content: 'user is redirected to dashboard',
42
+ children: [],
43
+ },
44
+ ],
45
+ },
46
+ ];
47
+ console.log('=== Building Markdown ===\n');
48
+ const markdown = buildRequirementsMarkdown(metadata, testRequirements);
49
+ console.log(markdown);
50
+ console.log('\n=== Parsing Markdown back ===\n');
51
+ try {
52
+ const { metadata: parsedMeta, requirements: parsedReqs } = parseRequirementsFile(markdown);
53
+ console.log('Metadata:', JSON.stringify(parsedMeta, null, 2));
54
+ console.log('\nRequirements tree:');
55
+ function printTree(node, indent = 0) {
56
+ const spaces = ' '.repeat(indent);
57
+ console.log(`${spaces}${node.id}: ${node.label} → ${node.content}`);
58
+ for (const child of node.children) {
59
+ printTree(child, indent + 1);
60
+ }
61
+ }
62
+ for (const req of parsedReqs) {
63
+ printTree(req);
64
+ }
65
+ console.log('\nāœ… Round-trip successful!');
66
+ // Verify structure
67
+ console.log('\n=== Verifying structure ===');
68
+ const req = parsedReqs[0];
69
+ console.log(`Root ID: ${req.id}`);
70
+ console.log(`Root label: ${req.label}`);
71
+ console.log(`Root content: ${req.content}`);
72
+ console.log(`Children count: ${req.children.length}`);
73
+ console.log(`First child ID: ${req.children[0].id}`);
74
+ console.log(`Second child has ${req.children[1].children.length} grandchildren`);
75
+ console.log(`First grandchild ID: ${req.children[1].children[0].id}`);
76
+ }
77
+ catch (error) {
78
+ console.error('āŒ Failed to parse Markdown:', error);
79
+ process.exit(1);
80
+ }
81
+ //# sourceMappingURL=test-schema.js.map
@@ -0,0 +1,3 @@
1
+ <!-- BEGIN dotreq-requirements -->
2
+ {{BEHAVIORAL_CORE}}
3
+ <!-- END dotreq-requirements -->
@@ -0,0 +1,3 @@
1
+ <!-- BEGIN dotreq-overview -->
2
+ {{OVERVIEW_CORE}}
3
+ <!-- END dotreq-overview -->
@@ -0,0 +1,3 @@
1
+ <!-- BEGIN dotreq-tests -->
2
+ {{TEST_WRITING_CORE}}
3
+ <!-- END dotreq-tests -->
@@ -0,0 +1,25 @@
1
+ # Requirements Capture
2
+
3
+ ALWAYS ask about capturing requirements when the user requests new functionality (not bug fixes or refactoring).
4
+
5
+ Ask: "Should we capture what this should do before we build it?"
6
+
7
+ If yes, use the MCP tools for requirements writing.
8
+
9
+ ## Workflow
10
+
11
+ 1. **Ask questions** only if there are major gaps in expected behavior
12
+ 2. **Use requirements as alignment** - Get assumptions on paper quickly instead of playing twenty questions
13
+ 3. **Think MVP** - Don't expand scope without consulting the user
14
+ 4. **After building** - Suggest testing against the requirements using the project's standard testing tools
15
+
16
+ Be helpful, consultative, and encouraging; not obstructive.
17
+
18
+ ## MCP Tools for Requirements
19
+
20
+ - `create_requirement_document` - Get template with codebase-aware format guidance and style principles
21
+ - `validate_requirements` - Check syntax before pushing (works offline)
22
+ - `style_check` - Get AI feedback on writing quality and clarity
23
+ - `push_requirements` - Sync to cloud (shows diff preview, then confirms)
24
+
25
+ Note: Users can also explicitly trigger this workflow with `/capture-requirements`.