fraim-framework 2.0.36 → 2.0.37

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 (165) hide show
  1. package/bin/fraim.js +5 -52
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/fraim-config.js +61 -0
  4. package/dist/registry/scripts/generic-issues-api.js +100 -0
  5. package/dist/registry/scripts/openapi-generator.js +664 -0
  6. package/dist/registry/scripts/performance/profile-server.js +390 -0
  7. package/dist/scripts/build-stub-registry.js +108 -0
  8. package/dist/src/cli/commands/doctor.js +5 -5
  9. package/dist/src/cli/commands/sync.js +33 -19
  10. package/dist/test-utils.js +96 -0
  11. package/dist/tests/esm-compat.js +11 -0
  12. package/dist/tests/test-chalk-esm-issue.js +159 -0
  13. package/dist/tests/test-chalk-real-world.js +265 -0
  14. package/dist/tests/test-chalk-regression.js +2 -18
  15. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  16. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  17. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  18. package/dist/tests/test-package-size.js +88 -0
  19. package/dist/tests/test-prep-issue.js +34 -1
  20. package/dist/tests/test-stub-registry.js +120 -0
  21. package/dist/tests/test-sync-stubs.js +143 -0
  22. package/package.json +6 -8
  23. package/registry/agent-guardrails.md +62 -62
  24. package/registry/scripts/code-quality-check.sh +559 -559
  25. package/registry/scripts/detect-tautological-tests.sh +38 -38
  26. package/registry/scripts/prep-issue.sh +61 -30
  27. package/registry/scripts/validate-openapi-limits.ts +366 -366
  28. package/registry/scripts/validate-test-coverage.ts +280 -280
  29. package/registry/scripts/verify-pr-comments.sh +70 -70
  30. package/registry/stubs/workflows/bootstrap/create-architecture.md +11 -0
  31. package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +11 -0
  32. package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +11 -0
  33. package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +11 -0
  34. package/registry/stubs/workflows/business-development/create-business-plan.md +11 -0
  35. package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +11 -0
  36. package/registry/stubs/workflows/business-development/price-product.md +18 -0
  37. package/registry/stubs/workflows/convert-to-pdf.md +11 -0
  38. package/registry/stubs/workflows/customer-development/insight-analysis.md +11 -0
  39. package/registry/stubs/workflows/customer-development/insight-triage.md +11 -0
  40. package/registry/stubs/workflows/customer-development/interview-preparation.md +11 -0
  41. package/registry/stubs/workflows/customer-development/linkedin-outreach.md +11 -0
  42. package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +11 -0
  43. package/registry/stubs/workflows/customer-development/thank-customers.md +11 -0
  44. package/registry/stubs/workflows/customer-development/weekly-newsletter.md +11 -0
  45. package/registry/stubs/workflows/deploy/cloud-deployment.md +11 -0
  46. package/registry/stubs/workflows/improve-fraim/contribute.md +11 -0
  47. package/registry/stubs/workflows/improve-fraim/file-issue.md +11 -0
  48. package/registry/stubs/workflows/marketing/content-creation.md +11 -0
  49. package/registry/stubs/workflows/marketing/hbr-article.md +11 -0
  50. package/registry/stubs/workflows/marketing/launch-checklist.md +11 -0
  51. package/registry/stubs/workflows/marketing/marketing-strategy.md +11 -0
  52. package/registry/stubs/workflows/marketing/storytelling.md +11 -0
  53. package/registry/stubs/workflows/performance/analyze-performance.md +11 -0
  54. package/registry/stubs/workflows/product-building/design.md +11 -0
  55. package/registry/stubs/workflows/product-building/implement.md +12 -0
  56. package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +11 -0
  57. package/registry/stubs/workflows/product-building/prep-issue.md +11 -0
  58. package/registry/stubs/workflows/product-building/prototype.md +11 -0
  59. package/registry/stubs/workflows/product-building/resolve.md +11 -0
  60. package/registry/stubs/workflows/product-building/retrospect.md +11 -0
  61. package/registry/stubs/workflows/product-building/spec.md +11 -0
  62. package/registry/stubs/workflows/product-building/test.md +11 -0
  63. package/registry/stubs/workflows/quality-assurance/browser-validation.md +11 -0
  64. package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +11 -0
  65. package/registry/stubs/workflows/replicate/replicate-discovery.md +11 -0
  66. package/registry/stubs/workflows/replicate/replicate-to-issues.md +11 -0
  67. package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +11 -0
  68. package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +11 -0
  69. package/registry/stubs/workflows/startup-credits/aws-activate-application.md +11 -0
  70. package/registry/stubs/workflows/startup-credits/google-cloud-application.md +11 -0
  71. package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +11 -0
  72. package/.github/workflows/ci.yml +0 -65
  73. package/.github/workflows/deploy-fraim.yml +0 -87
  74. package/.github/workflows/phase-change.yml +0 -251
  75. package/.github/workflows/status-change.yml +0 -68
  76. package/.github/workflows/sync-on-pr-review.yml +0 -66
  77. package/examples/simple-webapp/TESTING.md +0 -62
  78. package/examples/simple-webapp/example-test.ts +0 -186
  79. package/registry/github/workflows/ci.yml +0 -51
  80. package/registry/github/workflows/phase-change.yml +0 -251
  81. package/registry/github/workflows/status-change.yml +0 -68
  82. package/registry/github/workflows/sync-on-pr-review.yml +0 -66
  83. package/registry/mcp-template.jsonc +0 -29
  84. package/registry/rules/agent-success-criteria.md +0 -52
  85. package/registry/rules/agent-testing-guidelines.md +0 -502
  86. package/registry/rules/architecture.md +0 -52
  87. package/registry/rules/communication.md +0 -122
  88. package/registry/rules/continuous-learning.md +0 -55
  89. package/registry/rules/debugging-multitenancy-issues.md +0 -85
  90. package/registry/rules/ephemeral-execution.md +0 -57
  91. package/registry/rules/git-safe-commands.md +0 -34
  92. package/registry/rules/hitl-ppe-record-analysis.md +0 -302
  93. package/registry/rules/integrity-and-test-ethics.md +0 -275
  94. package/registry/rules/local-development.md +0 -254
  95. package/registry/rules/merge-requirements.md +0 -231
  96. package/registry/rules/simplicity.md +0 -118
  97. package/registry/rules/software-development-lifecycle.md +0 -105
  98. package/registry/rules/spike-first-development.md +0 -205
  99. package/registry/rules/successful-debugging-patterns.md +0 -491
  100. package/registry/rules/telemetry.md +0 -67
  101. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +0 -53
  102. package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +0 -37
  103. package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +0 -35
  104. package/registry/templates/business-development/IDEATION-REPORT-TEMPLATE.md +0 -29
  105. package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +0 -126
  106. package/registry/templates/customer-development/customer-interview-template.md +0 -99
  107. package/registry/templates/customer-development/follow-up-email-templates.md +0 -132
  108. package/registry/templates/customer-development/insight-analysis-template.md +0 -74
  109. package/registry/templates/customer-development/strategic-recommendations-template.md +0 -53
  110. package/registry/templates/customer-development/thank-you-email-template.html +0 -124
  111. package/registry/templates/customer-development/thank-you-note-template.md +0 -16
  112. package/registry/templates/customer-development/triage-log-template.md +0 -278
  113. package/registry/templates/customer-development/weekly-newsletter-template.html +0 -204
  114. package/registry/templates/evidence/Design-Evidence.md +0 -30
  115. package/registry/templates/evidence/Implementation-BugEvidence.md +0 -86
  116. package/registry/templates/evidence/Implementation-FeatureEvidence.md +0 -121
  117. package/registry/templates/evidence/Spec-Evidence.md +0 -19
  118. package/registry/templates/help/HelpNeeded.md +0 -14
  119. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +0 -66
  120. package/registry/templates/replicate/implementation-checklist.md +0 -39
  121. package/registry/templates/replicate/use-cases-template.md +0 -88
  122. package/registry/templates/retrospective/RETROSPECTIVE-TEMPLATE.md +0 -55
  123. package/registry/templates/specs/BUGSPEC-TEMPLATE.md +0 -37
  124. package/registry/templates/specs/FEATURESPEC-TEMPLATE.md +0 -29
  125. package/registry/templates/specs/TECHSPEC-TEMPLATE.md +0 -39
  126. package/registry/workflows/bootstrap/create-architecture.md +0 -38
  127. package/registry/workflows/bootstrap/evaluate-code-quality.md +0 -36
  128. package/registry/workflows/bootstrap/verify-test-coverage.md +0 -37
  129. package/registry/workflows/business-development/create-business-plan.md +0 -737
  130. package/registry/workflows/business-development/ideate-business-opportunity.md +0 -55
  131. package/registry/workflows/business-development/price-product.md +0 -325
  132. package/registry/workflows/convert-to-pdf.md +0 -235
  133. package/registry/workflows/customer-development/insight-analysis.md +0 -156
  134. package/registry/workflows/customer-development/insight-triage.md +0 -933
  135. package/registry/workflows/customer-development/interview-preparation.md +0 -421
  136. package/registry/workflows/customer-development/linkedin-outreach.md +0 -593
  137. package/registry/workflows/customer-development/strategic-brainstorming.md +0 -146
  138. package/registry/workflows/customer-development/thank-customers.md +0 -203
  139. package/registry/workflows/customer-development/weekly-newsletter.md +0 -366
  140. package/registry/workflows/deploy/cloud-deployment.md +0 -310
  141. package/registry/workflows/improve-fraim/contribute.md +0 -32
  142. package/registry/workflows/improve-fraim/file-issue.md +0 -32
  143. package/registry/workflows/marketing/content-creation.md +0 -37
  144. package/registry/workflows/marketing/hbr-article.md +0 -73
  145. package/registry/workflows/marketing/launch-checklist.md +0 -37
  146. package/registry/workflows/marketing/marketing-strategy.md +0 -45
  147. package/registry/workflows/performance/analyze-performance.md +0 -65
  148. package/registry/workflows/product-building/design.md +0 -130
  149. package/registry/workflows/product-building/implement.md +0 -315
  150. package/registry/workflows/product-building/iterate-on-pr-comments.md +0 -70
  151. package/registry/workflows/product-building/prep-issue.md +0 -43
  152. package/registry/workflows/product-building/prototype.md +0 -60
  153. package/registry/workflows/product-building/resolve.md +0 -164
  154. package/registry/workflows/product-building/retrospect.md +0 -86
  155. package/registry/workflows/product-building/spec.md +0 -117
  156. package/registry/workflows/product-building/test.md +0 -120
  157. package/registry/workflows/quality-assurance/browser-validation.md +0 -221
  158. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +0 -562
  159. package/registry/workflows/replicate/replicate-discovery.md +0 -336
  160. package/registry/workflows/replicate/replicate-to-issues.md +0 -319
  161. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +0 -632
  162. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -669
  163. package/registry/workflows/startup-credits/aws-activate-application.md +0 -535
  164. package/registry/workflows/startup-credits/google-cloud-application.md +0 -647
  165. package/registry/workflows/startup-credits/microsoft-azure-application.md +0 -538
@@ -1,366 +1,366 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * FRAIM Generic OpenAPI Limits Validator
5
- * This script validates OpenAPI file sizes against specified limits.
6
- *
7
- * Validates ChatGPT limitations for any project:
8
- * - instructions.txt: 8,000 character limit
9
- * - OpenAPI endpoints: 35 endpoint limit
10
- * - OpenAPI descriptions: 300 character limit per operation
11
- * - Missing operationIds
12
- * - Missing schema definitions
13
- * - x-openai-isConsequential on non-GET endpoints
14
- *
15
- * Usage:
16
- * npx tsx scripts/validate-openapi-limits.ts [instructions-file] [openapi-file]
17
- *
18
- * Defaults:
19
- * instructions-file: src/openapi/instructions.txt
20
- * openapi-file: src/openapi/openapi.json
21
- */
22
-
23
- import { readFileSync, existsSync } from 'fs';
24
- import { join } from 'path';
25
-
26
- const INSTRUCTIONS_LIMIT = 8000;
27
- const OPENAPI_ENDPOINT_LIMIT = 35;
28
- const OPENAPI_DESC_LIMIT = 300;
29
-
30
- interface ValidationResult {
31
- passed: boolean;
32
- errors: string[];
33
- warnings: string[];
34
- }
35
-
36
- /**
37
- * Collect all schema references from an object recursively
38
- */
39
- function collectSchemaRefs(obj: any, refs: Set<string> = new Set()): Set<string> {
40
- if (typeof obj !== 'object' || obj === null) {
41
- return refs;
42
- }
43
-
44
- if (Array.isArray(obj)) {
45
- obj.forEach(item => collectSchemaRefs(item, refs));
46
- return refs;
47
- }
48
-
49
- if (obj.$ref && typeof obj.$ref === 'string') {
50
- // Extract schema name from #/components/schemas/SchemaName
51
- const match = obj.$ref.match(/#\/components\/schemas\/(.+)$/);
52
- if (match) {
53
- refs.add(match[1]);
54
- }
55
- }
56
-
57
- Object.values(obj).forEach(value => collectSchemaRefs(value, refs));
58
-
59
- return refs;
60
- }
61
-
62
- /**
63
- * Validate instructions.txt length
64
- */
65
- function validateInstructions(instructionsPath: string): ValidationResult {
66
- const result: ValidationResult = {
67
- passed: true,
68
- errors: [],
69
- warnings: []
70
- };
71
-
72
- if (!existsSync(instructionsPath)) {
73
- result.warnings.push(`instructions.txt not found at ${instructionsPath}`);
74
- return result;
75
- }
76
-
77
- try {
78
- const instructionsContent = readFileSync(instructionsPath, 'utf-8');
79
- const instructionsLength = instructionsContent.length;
80
-
81
- console.log(`šŸ“„ instructions.txt: ${instructionsLength} characters (limit: ${INSTRUCTIONS_LIMIT})`);
82
-
83
- if (instructionsLength > INSTRUCTIONS_LIMIT) {
84
- result.passed = false;
85
- result.errors.push(
86
- `instructions.txt exceeds ${INSTRUCTIONS_LIMIT} character limit (${instructionsLength} chars). Move detailed content to KB files.`
87
- );
88
- console.log(` āŒ FAILED: Exceeds limit`);
89
- } else {
90
- console.log(` āœ… PASSED: Within limit\n`);
91
- }
92
- } catch (error: any) {
93
- result.passed = false;
94
- result.errors.push(`Failed to read instructions.txt: ${error.message}`);
95
- }
96
-
97
- return result;
98
- }
99
-
100
- /**
101
- * Validate OpenAPI specification
102
- */
103
- function validateOpenAPI(openapiPath: string): ValidationResult {
104
- const result: ValidationResult = {
105
- passed: true,
106
- errors: [],
107
- warnings: []
108
- };
109
-
110
- if (!existsSync(openapiPath)) {
111
- result.warnings.push(`OpenAPI file not found at ${openapiPath}`);
112
- return result;
113
- }
114
-
115
- try {
116
- const openapiContent = readFileSync(openapiPath, 'utf-8');
117
- const openapiSpec = JSON.parse(openapiContent);
118
-
119
- // Count endpoints (operations) and validate
120
- let endpointCount = 0;
121
- const endpointList: string[] = [];
122
- const longDescriptions: Array<{ path: string; method: string; length: number }> = [];
123
- const missingOperationIds: Array<{ path: string; method: string }> = [];
124
- const allSchemaRefs = new Set<string>();
125
- const missingConsequential: Array<{ path: string; method: string }> = [];
126
- const wrongConsequential: Array<{ path: string; method: string; value: any }> = [];
127
-
128
- if (openapiSpec.paths) {
129
- for (const [path, methods] of Object.entries(openapiSpec.paths)) {
130
- for (const [method, details] of Object.entries(methods as any)) {
131
- if (typeof details === 'object' && details !== null) {
132
- // Skip if it's a parameter definition or response definition
133
- if (['parameters', 'responses', 'requestBody'].includes(method)) {
134
- continue;
135
- }
136
-
137
- endpointCount++;
138
- endpointList.push(`${method.toUpperCase()} ${path}`);
139
-
140
- // Check for operationId
141
- if (!(details as any).operationId) {
142
- missingOperationIds.push({
143
- path,
144
- method: method.toUpperCase()
145
- });
146
- }
147
-
148
- // Check description length
149
- if ((details as any).description) {
150
- const descLength = (details as any).description.length;
151
- if (descLength > OPENAPI_DESC_LIMIT) {
152
- longDescriptions.push({
153
- path,
154
- method: method.toUpperCase(),
155
- length: descLength
156
- });
157
- }
158
- }
159
-
160
- // Collect all schema references from this operation
161
- collectSchemaRefs(details, allSchemaRefs);
162
-
163
- // Check for x-openai-isConsequential on non-GET endpoints
164
- const nonGetMethods = ['post', 'put', 'patch', 'delete'];
165
- if (nonGetMethods.includes(method.toLowerCase())) {
166
- const hasConsequential = details.hasOwnProperty('x-openai-isConsequential');
167
- const consequentialValue = (details as any)['x-openai-isConsequential'];
168
-
169
- // ALL non-GET endpoints should have x-openai-isConsequential: false
170
- if (!hasConsequential) {
171
- missingConsequential.push({
172
- path,
173
- method: method.toUpperCase()
174
- });
175
- } else if (consequentialValue !== false) {
176
- wrongConsequential.push({
177
- path,
178
- method: method.toUpperCase(),
179
- value: consequentialValue
180
- });
181
- }
182
- }
183
- }
184
- }
185
- }
186
- }
187
-
188
- // Also collect schema refs from components/responses and components/parameters if they exist
189
- if (openapiSpec.components) {
190
- if (openapiSpec.components.responses) {
191
- Object.values(openapiSpec.components.responses).forEach((response: any) => {
192
- collectSchemaRefs(response, allSchemaRefs);
193
- });
194
- }
195
- if (openapiSpec.components.parameters) {
196
- Object.values(openapiSpec.components.parameters).forEach((param: any) => {
197
- collectSchemaRefs(param, allSchemaRefs);
198
- });
199
- }
200
- }
201
-
202
- // Check for missing schema definitions
203
- const definedSchemas = new Set<string>();
204
- if (openapiSpec.components && openapiSpec.components.schemas) {
205
- Object.keys(openapiSpec.components.schemas).forEach(schemaName => {
206
- definedSchemas.add(schemaName);
207
- });
208
- }
209
-
210
- const missingSchemas: string[] = [];
211
- allSchemaRefs.forEach(refName => {
212
- if (!definedSchemas.has(refName)) {
213
- missingSchemas.push(refName);
214
- }
215
- });
216
-
217
- // Validate endpoint count
218
- console.log(`šŸ“‹ OpenAPI endpoints: ${endpointCount} (limit: ${OPENAPI_ENDPOINT_LIMIT})`);
219
-
220
- if (endpointCount > OPENAPI_ENDPOINT_LIMIT) {
221
- result.passed = false;
222
- result.errors.push(
223
- `OpenAPI spec exceeds ${OPENAPI_ENDPOINT_LIMIT} endpoint limit (${endpointCount} endpoints). ChatGPT cannot handle more than ${OPENAPI_ENDPOINT_LIMIT} endpoints.`
224
- );
225
- console.log(` āŒ FAILED: Too many endpoints`);
226
- console.log(` Endpoints:`);
227
- endpointList.slice(0, 10).forEach(ep => console.log(` - ${ep}`));
228
- if (endpointCount > 10) {
229
- console.log(` ... and ${endpointCount - 10} more`);
230
- }
231
- } else {
232
- console.log(` āœ… PASSED: Within limit`);
233
- }
234
-
235
- // Check description lengths
236
- if (longDescriptions.length > 0) {
237
- console.log(`\nšŸ“ OpenAPI descriptions: ${longDescriptions.length} exceed ${OPENAPI_DESC_LIMIT} char limit`);
238
- longDescriptions.forEach(({ path, method, length }) => {
239
- result.passed = false;
240
- result.errors.push(
241
- `OpenAPI operation ${method} ${path} description exceeds ${OPENAPI_DESC_LIMIT} character limit (${length} chars). Shorten description or move details to KB files.`
242
- );
243
- console.log(` āŒ ${method} ${path}: ${length} chars`);
244
- });
245
- } else {
246
- console.log(`\nšŸ“ OpenAPI descriptions: All within ${OPENAPI_DESC_LIMIT} char limit`);
247
- console.log(` āœ… PASSED: All descriptions within limit`);
248
- }
249
-
250
- // Check for missing operationIds
251
- if (missingOperationIds.length > 0) {
252
- console.log(`\nšŸ”‘ OpenAPI operationIds: ${missingOperationIds.length} missing`);
253
- missingOperationIds.forEach(({ path, method }) => {
254
- result.passed = false;
255
- result.errors.push(
256
- `OpenAPI operation ${method} ${path} is missing required 'operationId' field. Add a unique operationId (e.g., "getTask", "createTask").`
257
- );
258
- console.log(` āŒ ${method} ${path}: Missing operationId`);
259
- });
260
- } else {
261
- console.log(`\nšŸ”‘ OpenAPI operationIds: All operations have operationId`);
262
- console.log(` āœ… PASSED: All operations have operationId`);
263
- }
264
-
265
- // Check for missing schema definitions
266
- if (missingSchemas.length > 0) {
267
- console.log(`\nšŸ“š OpenAPI schemas: ${missingSchemas.length} referenced but not defined`);
268
- missingSchemas.forEach(schemaName => {
269
- result.passed = false;
270
- result.errors.push(
271
- `OpenAPI schema '${schemaName}' is referenced but not defined in components.schemas. Add the schema definition to components.schemas.`
272
- );
273
- console.log(` āŒ Missing schema: ${schemaName}`);
274
- });
275
- } else {
276
- console.log(`\nšŸ“š OpenAPI schemas: All referenced schemas are defined`);
277
- console.log(` āœ… PASSED: All schema references resolve`);
278
- }
279
-
280
- // Check for missing or incorrect x-openai-isConsequential on non-GET endpoints
281
- const allConsequentialIssues = [...missingConsequential, ...wrongConsequential];
282
- if (allConsequentialIssues.length > 0) {
283
- console.log(`\nšŸ” OpenAPI x-openai-isConsequential: ${allConsequentialIssues.length} non-GET endpoint(s) missing or incorrectly set`);
284
- missingConsequential.forEach(({ path, method }) => {
285
- result.passed = false;
286
- result.errors.push(
287
- `OpenAPI operation ${method} ${path} is missing required 'x-openai-isConsequential: false' field. Add this field to bypass ChatGPT confirmation prompts.`
288
- );
289
- console.log(` āŒ ${method} ${path}: Missing x-openai-isConsequential`);
290
- });
291
- wrongConsequential.forEach(({ path, method, value }) => {
292
- result.passed = false;
293
- result.errors.push(
294
- `OpenAPI operation ${method} ${path} has x-openai-isConsequential set to ${JSON.stringify(value)} but should be false. Non-GET endpoints should have x-openai-isConsequential: false to bypass ChatGPT confirmation prompts.`
295
- );
296
- console.log(` āŒ ${method} ${path}: x-openai-isConsequential is ${JSON.stringify(value)}, should be false`);
297
- });
298
- } else {
299
- console.log(`\nšŸ” OpenAPI x-openai-isConsequential: All non-GET endpoints have x-openai-isConsequential: false`);
300
- console.log(` āœ… PASSED: All non-GET endpoints configured to bypass confirmation`);
301
- }
302
-
303
- } catch (error: any) {
304
- result.passed = false;
305
- result.errors.push(`Failed to parse OpenAPI JSON: ${error.message}`);
306
- console.log(` āŒ ERROR: ${error.message}`);
307
- }
308
-
309
- return result;
310
- }
311
-
312
- /**
313
- * Main validation function
314
- */
315
- export function validateAll(
316
- instructionsPath: string = 'src/openapi/instructions.txt',
317
- openapiPath: string = 'src/openapi/openapi.json'
318
- ): boolean {
319
- console.log('šŸ” Validating OpenAPI and instructions limits...\n');
320
-
321
- const instructionsResult = validateInstructions(instructionsPath);
322
- const openapiResult = validateOpenAPI(openapiPath);
323
-
324
- // Combine results
325
- const allErrors = [...instructionsResult.errors, ...openapiResult.errors];
326
- const allWarnings = [...instructionsResult.warnings, ...openapiResult.warnings];
327
- const passed = instructionsResult.passed && openapiResult.passed;
328
-
329
- // Summary
330
- console.log('\n' + '='.repeat(50));
331
- console.log('šŸ“Š VALIDATION SUMMARY');
332
- console.log('='.repeat(50));
333
-
334
- if (allWarnings.length > 0) {
335
- console.log('\nāš ļø WARNINGS:');
336
- allWarnings.forEach(w => console.log(` - ${w}`));
337
- }
338
-
339
- if (allErrors.length > 0) {
340
- console.log('\nāŒ ERRORS:');
341
- allErrors.forEach(e => console.log(` - ${e}`));
342
- console.log('\nšŸ’” Solutions:');
343
- console.log(' - Move detailed content from instructions.txt to KB files (KB-*.txt)');
344
- console.log(' - Reduce number of OpenAPI endpoints (merge or remove unused endpoints)');
345
- console.log(' - Shorten OpenAPI operation descriptions, reference KB files for details');
346
- console.log(' - Add missing operationId fields to all operations (e.g., "getTask", "createTask")');
347
- console.log(' - Define missing schemas in components.schemas section');
348
- console.log(' - Add x-openai-isConsequential: false to all non-GET endpoints (POST, PUT, PATCH, DELETE)');
349
- console.log('\nāŒ BUILD FAILED: Fix errors above before proceeding\n');
350
- return false;
351
- } else {
352
- console.log('\nāœ… ALL CHECKS PASSED');
353
- console.log(' Instructions and OpenAPI spec are within ChatGPT limits\n');
354
- return true;
355
- }
356
- }
357
-
358
- // Run if executed directly
359
- // @ts-ignore
360
- if (import.meta.url === `file://${process.argv[1]}`) {
361
- const instructionsPath = process.argv[2] || 'src/openapi/instructions.txt';
362
- const openapiPath = process.argv[3] || 'src/openapi/openapi.json';
363
-
364
- const passed = validateAll(instructionsPath, openapiPath);
365
- process.exit(passed ? 0 : 1);
366
- }
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FRAIM Generic OpenAPI Limits Validator
5
+ * This script validates OpenAPI file sizes against specified limits.
6
+ *
7
+ * Validates ChatGPT limitations for any project:
8
+ * - instructions.txt: 8,000 character limit
9
+ * - OpenAPI endpoints: 35 endpoint limit
10
+ * - OpenAPI descriptions: 300 character limit per operation
11
+ * - Missing operationIds
12
+ * - Missing schema definitions
13
+ * - x-openai-isConsequential on non-GET endpoints
14
+ *
15
+ * Usage:
16
+ * npx tsx scripts/validate-openapi-limits.ts [instructions-file] [openapi-file]
17
+ *
18
+ * Defaults:
19
+ * instructions-file: src/openapi/instructions.txt
20
+ * openapi-file: src/openapi/openapi.json
21
+ */
22
+
23
+ import { readFileSync, existsSync } from 'fs';
24
+ import { join } from 'path';
25
+
26
+ const INSTRUCTIONS_LIMIT = 8000;
27
+ const OPENAPI_ENDPOINT_LIMIT = 35;
28
+ const OPENAPI_DESC_LIMIT = 300;
29
+
30
+ interface ValidationResult {
31
+ passed: boolean;
32
+ errors: string[];
33
+ warnings: string[];
34
+ }
35
+
36
+ /**
37
+ * Collect all schema references from an object recursively
38
+ */
39
+ function collectSchemaRefs(obj: any, refs: Set<string> = new Set()): Set<string> {
40
+ if (typeof obj !== 'object' || obj === null) {
41
+ return refs;
42
+ }
43
+
44
+ if (Array.isArray(obj)) {
45
+ obj.forEach(item => collectSchemaRefs(item, refs));
46
+ return refs;
47
+ }
48
+
49
+ if (obj.$ref && typeof obj.$ref === 'string') {
50
+ // Extract schema name from #/components/schemas/SchemaName
51
+ const match = obj.$ref.match(/#\/components\/schemas\/(.+)$/);
52
+ if (match) {
53
+ refs.add(match[1]);
54
+ }
55
+ }
56
+
57
+ Object.values(obj).forEach(value => collectSchemaRefs(value, refs));
58
+
59
+ return refs;
60
+ }
61
+
62
+ /**
63
+ * Validate instructions.txt length
64
+ */
65
+ function validateInstructions(instructionsPath: string): ValidationResult {
66
+ const result: ValidationResult = {
67
+ passed: true,
68
+ errors: [],
69
+ warnings: []
70
+ };
71
+
72
+ if (!existsSync(instructionsPath)) {
73
+ result.warnings.push(`instructions.txt not found at ${instructionsPath}`);
74
+ return result;
75
+ }
76
+
77
+ try {
78
+ const instructionsContent = readFileSync(instructionsPath, 'utf-8');
79
+ const instructionsLength = instructionsContent.length;
80
+
81
+ console.log(`šŸ“„ instructions.txt: ${instructionsLength} characters (limit: ${INSTRUCTIONS_LIMIT})`);
82
+
83
+ if (instructionsLength > INSTRUCTIONS_LIMIT) {
84
+ result.passed = false;
85
+ result.errors.push(
86
+ `instructions.txt exceeds ${INSTRUCTIONS_LIMIT} character limit (${instructionsLength} chars). Move detailed content to KB files.`
87
+ );
88
+ console.log(` āŒ FAILED: Exceeds limit`);
89
+ } else {
90
+ console.log(` āœ… PASSED: Within limit\n`);
91
+ }
92
+ } catch (error: any) {
93
+ result.passed = false;
94
+ result.errors.push(`Failed to read instructions.txt: ${error.message}`);
95
+ }
96
+
97
+ return result;
98
+ }
99
+
100
+ /**
101
+ * Validate OpenAPI specification
102
+ */
103
+ function validateOpenAPI(openapiPath: string): ValidationResult {
104
+ const result: ValidationResult = {
105
+ passed: true,
106
+ errors: [],
107
+ warnings: []
108
+ };
109
+
110
+ if (!existsSync(openapiPath)) {
111
+ result.warnings.push(`OpenAPI file not found at ${openapiPath}`);
112
+ return result;
113
+ }
114
+
115
+ try {
116
+ const openapiContent = readFileSync(openapiPath, 'utf-8');
117
+ const openapiSpec = JSON.parse(openapiContent);
118
+
119
+ // Count endpoints (operations) and validate
120
+ let endpointCount = 0;
121
+ const endpointList: string[] = [];
122
+ const longDescriptions: Array<{ path: string; method: string; length: number }> = [];
123
+ const missingOperationIds: Array<{ path: string; method: string }> = [];
124
+ const allSchemaRefs = new Set<string>();
125
+ const missingConsequential: Array<{ path: string; method: string }> = [];
126
+ const wrongConsequential: Array<{ path: string; method: string; value: any }> = [];
127
+
128
+ if (openapiSpec.paths) {
129
+ for (const [path, methods] of Object.entries(openapiSpec.paths)) {
130
+ for (const [method, details] of Object.entries(methods as any)) {
131
+ if (typeof details === 'object' && details !== null) {
132
+ // Skip if it's a parameter definition or response definition
133
+ if (['parameters', 'responses', 'requestBody'].includes(method)) {
134
+ continue;
135
+ }
136
+
137
+ endpointCount++;
138
+ endpointList.push(`${method.toUpperCase()} ${path}`);
139
+
140
+ // Check for operationId
141
+ if (!(details as any).operationId) {
142
+ missingOperationIds.push({
143
+ path,
144
+ method: method.toUpperCase()
145
+ });
146
+ }
147
+
148
+ // Check description length
149
+ if ((details as any).description) {
150
+ const descLength = (details as any).description.length;
151
+ if (descLength > OPENAPI_DESC_LIMIT) {
152
+ longDescriptions.push({
153
+ path,
154
+ method: method.toUpperCase(),
155
+ length: descLength
156
+ });
157
+ }
158
+ }
159
+
160
+ // Collect all schema references from this operation
161
+ collectSchemaRefs(details, allSchemaRefs);
162
+
163
+ // Check for x-openai-isConsequential on non-GET endpoints
164
+ const nonGetMethods = ['post', 'put', 'patch', 'delete'];
165
+ if (nonGetMethods.includes(method.toLowerCase())) {
166
+ const hasConsequential = details.hasOwnProperty('x-openai-isConsequential');
167
+ const consequentialValue = (details as any)['x-openai-isConsequential'];
168
+
169
+ // ALL non-GET endpoints should have x-openai-isConsequential: false
170
+ if (!hasConsequential) {
171
+ missingConsequential.push({
172
+ path,
173
+ method: method.toUpperCase()
174
+ });
175
+ } else if (consequentialValue !== false) {
176
+ wrongConsequential.push({
177
+ path,
178
+ method: method.toUpperCase(),
179
+ value: consequentialValue
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ // Also collect schema refs from components/responses and components/parameters if they exist
189
+ if (openapiSpec.components) {
190
+ if (openapiSpec.components.responses) {
191
+ Object.values(openapiSpec.components.responses).forEach((response: any) => {
192
+ collectSchemaRefs(response, allSchemaRefs);
193
+ });
194
+ }
195
+ if (openapiSpec.components.parameters) {
196
+ Object.values(openapiSpec.components.parameters).forEach((param: any) => {
197
+ collectSchemaRefs(param, allSchemaRefs);
198
+ });
199
+ }
200
+ }
201
+
202
+ // Check for missing schema definitions
203
+ const definedSchemas = new Set<string>();
204
+ if (openapiSpec.components && openapiSpec.components.schemas) {
205
+ Object.keys(openapiSpec.components.schemas).forEach(schemaName => {
206
+ definedSchemas.add(schemaName);
207
+ });
208
+ }
209
+
210
+ const missingSchemas: string[] = [];
211
+ allSchemaRefs.forEach(refName => {
212
+ if (!definedSchemas.has(refName)) {
213
+ missingSchemas.push(refName);
214
+ }
215
+ });
216
+
217
+ // Validate endpoint count
218
+ console.log(`šŸ“‹ OpenAPI endpoints: ${endpointCount} (limit: ${OPENAPI_ENDPOINT_LIMIT})`);
219
+
220
+ if (endpointCount > OPENAPI_ENDPOINT_LIMIT) {
221
+ result.passed = false;
222
+ result.errors.push(
223
+ `OpenAPI spec exceeds ${OPENAPI_ENDPOINT_LIMIT} endpoint limit (${endpointCount} endpoints). ChatGPT cannot handle more than ${OPENAPI_ENDPOINT_LIMIT} endpoints.`
224
+ );
225
+ console.log(` āŒ FAILED: Too many endpoints`);
226
+ console.log(` Endpoints:`);
227
+ endpointList.slice(0, 10).forEach(ep => console.log(` - ${ep}`));
228
+ if (endpointCount > 10) {
229
+ console.log(` ... and ${endpointCount - 10} more`);
230
+ }
231
+ } else {
232
+ console.log(` āœ… PASSED: Within limit`);
233
+ }
234
+
235
+ // Check description lengths
236
+ if (longDescriptions.length > 0) {
237
+ console.log(`\nšŸ“ OpenAPI descriptions: ${longDescriptions.length} exceed ${OPENAPI_DESC_LIMIT} char limit`);
238
+ longDescriptions.forEach(({ path, method, length }) => {
239
+ result.passed = false;
240
+ result.errors.push(
241
+ `OpenAPI operation ${method} ${path} description exceeds ${OPENAPI_DESC_LIMIT} character limit (${length} chars). Shorten description or move details to KB files.`
242
+ );
243
+ console.log(` āŒ ${method} ${path}: ${length} chars`);
244
+ });
245
+ } else {
246
+ console.log(`\nšŸ“ OpenAPI descriptions: All within ${OPENAPI_DESC_LIMIT} char limit`);
247
+ console.log(` āœ… PASSED: All descriptions within limit`);
248
+ }
249
+
250
+ // Check for missing operationIds
251
+ if (missingOperationIds.length > 0) {
252
+ console.log(`\nšŸ”‘ OpenAPI operationIds: ${missingOperationIds.length} missing`);
253
+ missingOperationIds.forEach(({ path, method }) => {
254
+ result.passed = false;
255
+ result.errors.push(
256
+ `OpenAPI operation ${method} ${path} is missing required 'operationId' field. Add a unique operationId (e.g., "getTask", "createTask").`
257
+ );
258
+ console.log(` āŒ ${method} ${path}: Missing operationId`);
259
+ });
260
+ } else {
261
+ console.log(`\nšŸ”‘ OpenAPI operationIds: All operations have operationId`);
262
+ console.log(` āœ… PASSED: All operations have operationId`);
263
+ }
264
+
265
+ // Check for missing schema definitions
266
+ if (missingSchemas.length > 0) {
267
+ console.log(`\nšŸ“š OpenAPI schemas: ${missingSchemas.length} referenced but not defined`);
268
+ missingSchemas.forEach(schemaName => {
269
+ result.passed = false;
270
+ result.errors.push(
271
+ `OpenAPI schema '${schemaName}' is referenced but not defined in components.schemas. Add the schema definition to components.schemas.`
272
+ );
273
+ console.log(` āŒ Missing schema: ${schemaName}`);
274
+ });
275
+ } else {
276
+ console.log(`\nšŸ“š OpenAPI schemas: All referenced schemas are defined`);
277
+ console.log(` āœ… PASSED: All schema references resolve`);
278
+ }
279
+
280
+ // Check for missing or incorrect x-openai-isConsequential on non-GET endpoints
281
+ const allConsequentialIssues = [...missingConsequential, ...wrongConsequential];
282
+ if (allConsequentialIssues.length > 0) {
283
+ console.log(`\nšŸ” OpenAPI x-openai-isConsequential: ${allConsequentialIssues.length} non-GET endpoint(s) missing or incorrectly set`);
284
+ missingConsequential.forEach(({ path, method }) => {
285
+ result.passed = false;
286
+ result.errors.push(
287
+ `OpenAPI operation ${method} ${path} is missing required 'x-openai-isConsequential: false' field. Add this field to bypass ChatGPT confirmation prompts.`
288
+ );
289
+ console.log(` āŒ ${method} ${path}: Missing x-openai-isConsequential`);
290
+ });
291
+ wrongConsequential.forEach(({ path, method, value }) => {
292
+ result.passed = false;
293
+ result.errors.push(
294
+ `OpenAPI operation ${method} ${path} has x-openai-isConsequential set to ${JSON.stringify(value)} but should be false. Non-GET endpoints should have x-openai-isConsequential: false to bypass ChatGPT confirmation prompts.`
295
+ );
296
+ console.log(` āŒ ${method} ${path}: x-openai-isConsequential is ${JSON.stringify(value)}, should be false`);
297
+ });
298
+ } else {
299
+ console.log(`\nšŸ” OpenAPI x-openai-isConsequential: All non-GET endpoints have x-openai-isConsequential: false`);
300
+ console.log(` āœ… PASSED: All non-GET endpoints configured to bypass confirmation`);
301
+ }
302
+
303
+ } catch (error: any) {
304
+ result.passed = false;
305
+ result.errors.push(`Failed to parse OpenAPI JSON: ${error.message}`);
306
+ console.log(` āŒ ERROR: ${error.message}`);
307
+ }
308
+
309
+ return result;
310
+ }
311
+
312
+ /**
313
+ * Main validation function
314
+ */
315
+ export function validateAll(
316
+ instructionsPath: string = 'src/openapi/instructions.txt',
317
+ openapiPath: string = 'src/openapi/openapi.json'
318
+ ): boolean {
319
+ console.log('šŸ” Validating OpenAPI and instructions limits...\n');
320
+
321
+ const instructionsResult = validateInstructions(instructionsPath);
322
+ const openapiResult = validateOpenAPI(openapiPath);
323
+
324
+ // Combine results
325
+ const allErrors = [...instructionsResult.errors, ...openapiResult.errors];
326
+ const allWarnings = [...instructionsResult.warnings, ...openapiResult.warnings];
327
+ const passed = instructionsResult.passed && openapiResult.passed;
328
+
329
+ // Summary
330
+ console.log('\n' + '='.repeat(50));
331
+ console.log('šŸ“Š VALIDATION SUMMARY');
332
+ console.log('='.repeat(50));
333
+
334
+ if (allWarnings.length > 0) {
335
+ console.log('\nāš ļø WARNINGS:');
336
+ allWarnings.forEach(w => console.log(` - ${w}`));
337
+ }
338
+
339
+ if (allErrors.length > 0) {
340
+ console.log('\nāŒ ERRORS:');
341
+ allErrors.forEach(e => console.log(` - ${e}`));
342
+ console.log('\nšŸ’” Solutions:');
343
+ console.log(' - Move detailed content from instructions.txt to KB files (KB-*.txt)');
344
+ console.log(' - Reduce number of OpenAPI endpoints (merge or remove unused endpoints)');
345
+ console.log(' - Shorten OpenAPI operation descriptions, reference KB files for details');
346
+ console.log(' - Add missing operationId fields to all operations (e.g., "getTask", "createTask")');
347
+ console.log(' - Define missing schemas in components.schemas section');
348
+ console.log(' - Add x-openai-isConsequential: false to all non-GET endpoints (POST, PUT, PATCH, DELETE)');
349
+ console.log('\nāŒ BUILD FAILED: Fix errors above before proceeding\n');
350
+ return false;
351
+ } else {
352
+ console.log('\nāœ… ALL CHECKS PASSED');
353
+ console.log(' Instructions and OpenAPI spec are within ChatGPT limits\n');
354
+ return true;
355
+ }
356
+ }
357
+
358
+ // Run if executed directly
359
+ // @ts-ignore
360
+ if (import.meta.url === `file://${process.argv[1]}`) {
361
+ const instructionsPath = process.argv[2] || 'src/openapi/instructions.txt';
362
+ const openapiPath = process.argv[3] || 'src/openapi/openapi.json';
363
+
364
+ const passed = validateAll(instructionsPath, openapiPath);
365
+ process.exit(passed ? 0 : 1);
366
+ }