farmon 0.1.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/bin/farmon.js +12 -0
  4. package/dist/bin/farmon.js.map +1 -0
  5. package/dist/execute/agents/index.js +19 -0
  6. package/dist/execute/agents/index.js.map +1 -0
  7. package/dist/execute/agents/instruction-classifier-agent.js +16 -0
  8. package/dist/execute/agents/instruction-classifier-agent.js.map +1 -0
  9. package/dist/execute/agents/mutation-agent.js +272 -0
  10. package/dist/execute/agents/mutation-agent.js.map +1 -0
  11. package/dist/execute/agents/query-agent.js +118 -0
  12. package/dist/execute/agents/query-agent.js.map +1 -0
  13. package/dist/execute/helpers/analyzers.js +8 -0
  14. package/dist/execute/helpers/analyzers.js.map +1 -0
  15. package/dist/execute/helpers/ensurers.js +1053 -0
  16. package/dist/execute/helpers/ensurers.js.map +1 -0
  17. package/dist/execute/helpers/finders.js +1454 -0
  18. package/dist/execute/helpers/finders.js.map +1 -0
  19. package/dist/execute/helpers/general.js +3736 -0
  20. package/dist/execute/helpers/general.js.map +1 -0
  21. package/dist/execute/helpers/import-helpers.js +183 -0
  22. package/dist/execute/helpers/import-helpers.js.map +1 -0
  23. package/dist/execute/helpers/parsers.js +840 -0
  24. package/dist/execute/helpers/parsers.js.map +1 -0
  25. package/dist/execute/helpers/prompt-maker.js +1163 -0
  26. package/dist/execute/helpers/prompt-maker.js.map +1 -0
  27. package/dist/execute/helpers/validators.js +40 -0
  28. package/dist/execute/helpers/validators.js.map +1 -0
  29. package/dist/execute/history/history-manager.js +1030 -0
  30. package/dist/execute/history/history-manager.js.map +1 -0
  31. package/dist/execute/history/rollback-handlers.js +2524 -0
  32. package/dist/execute/history/rollback-handlers.js.map +1 -0
  33. package/dist/execute/index.js +44 -0
  34. package/dist/execute/index.js.map +1 -0
  35. package/dist/execute/llm/call.js +103 -0
  36. package/dist/execute/llm/call.js.map +1 -0
  37. package/dist/execute/tasks/ast.js +3819 -0
  38. package/dist/execute/tasks/ast.js.map +1 -0
  39. package/dist/execute/tasks/generators.js +96 -0
  40. package/dist/execute/tasks/generators.js.map +1 -0
  41. package/dist/execute/tasks/index.js +7 -0
  42. package/dist/execute/tasks/index.js.map +1 -0
  43. package/dist/execute/tasks/mutations.js +8139 -0
  44. package/dist/execute/tasks/mutations.js.map +1 -0
  45. package/dist/execute/tasks/query.js +248 -0
  46. package/dist/execute/tasks/query.js.map +1 -0
  47. package/dist/index.js +6 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/providers/index.js +15 -0
  50. package/dist/providers/index.js.map +1 -0
  51. package/dist/providers/ollama.js +40 -0
  52. package/dist/providers/ollama.js.map +1 -0
  53. package/dist/providers/openai-compatible.js +52 -0
  54. package/dist/providers/openai-compatible.js.map +1 -0
  55. package/dist/runtime/inject.js +250 -0
  56. package/dist/runtime/inject.js.map +1 -0
  57. package/dist/schemas/agent/action.schema.js +935 -0
  58. package/dist/schemas/agent/action.schema.js.map +1 -0
  59. package/dist/schemas/agent/index.js +4 -0
  60. package/dist/schemas/agent/index.js.map +1 -0
  61. package/dist/schemas/agent/llm.schema.js +16 -0
  62. package/dist/schemas/agent/llm.schema.js.map +1 -0
  63. package/dist/schemas/agent/planner.schema.js +17 -0
  64. package/dist/schemas/agent/planner.schema.js.map +1 -0
  65. package/dist/schemas/index.js +7 -0
  66. package/dist/schemas/index.js.map +1 -0
  67. package/dist/schemas/project/context.schema.js +2 -0
  68. package/dist/schemas/project/context.schema.js.map +1 -0
  69. package/dist/schemas/project/index.js +2 -0
  70. package/dist/schemas/project/index.js.map +1 -0
  71. package/dist/schemas/runtime/index.js +4 -0
  72. package/dist/schemas/runtime/index.js.map +1 -0
  73. package/dist/schemas/runtime/injector.schema.js +11 -0
  74. package/dist/schemas/runtime/injector.schema.js.map +1 -0
  75. package/dist/schemas/runtime/runtime.schema.js +73 -0
  76. package/dist/schemas/runtime/runtime.schema.js.map +1 -0
  77. package/dist/schemas/runtime/sse.schema.js +15 -0
  78. package/dist/schemas/runtime/sse.schema.js.map +1 -0
  79. package/dist/schemas/system/index.js +2 -0
  80. package/dist/schemas/system/index.js.map +1 -0
  81. package/dist/schemas/system/logger.schema.js +56 -0
  82. package/dist/schemas/system/logger.schema.js.map +1 -0
  83. package/dist/schemas/task/index.js +9 -0
  84. package/dist/schemas/task/index.js.map +1 -0
  85. package/dist/server/app-context.js +254 -0
  86. package/dist/server/app-context.js.map +1 -0
  87. package/dist/server/config.js +22 -0
  88. package/dist/server/config.js.map +1 -0
  89. package/dist/server/error.js +22 -0
  90. package/dist/server/error.js.map +1 -0
  91. package/dist/server/event-bus.js +60 -0
  92. package/dist/server/event-bus.js.map +1 -0
  93. package/dist/server/logger.js +57 -0
  94. package/dist/server/logger.js.map +1 -0
  95. package/dist/server/run.js +265 -0
  96. package/dist/server/run.js.map +1 -0
  97. package/dist/server/sse.js +143 -0
  98. package/dist/server/sse.js.map +1 -0
  99. package/dist/ui/assets/index-C4ydQSAw.css +2 -0
  100. package/dist/ui/assets/index-Dzo7S5xs.js +85 -0
  101. package/dist/ui/favicon.svg +1 -0
  102. package/dist/ui/icons.svg +24 -0
  103. package/dist/ui/index.html +14 -0
  104. package/dist/workers/prettier.js +11 -0
  105. package/dist/workers/prettier.js.map +1 -0
  106. package/package.json +114 -0
@@ -0,0 +1,1053 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import healpers from "../../execute/helpers/general.js";
4
+ import { ERROR_CODES } from "../../schemas/index.js";
5
+ import finders from "./finders.js";
6
+ import { LoomaError } from "../../server/error.js";
7
+ /**
8
+ * Validator: Ensures that a library exists inside package.json.
9
+ *
10
+ * ------------------------------------------------------------
11
+ * WHAT THIS FUNCTION DOES
12
+ * ------------------------------------------------------------
13
+ *
14
+ * This function guarantees that a dependency exists in either:
15
+ *
16
+ * - dependencies
17
+ * OR
18
+ * - devDependencies
19
+ *
20
+ * If the library already exists:
21
+ * - nothing changes
22
+ *
23
+ * If the library does not exist:
24
+ * - it gets added
25
+ * - but you have to do npm install explecitily
26
+ *
27
+ * ------------------------------------------------------------
28
+ * WHY "ENSURE" IS BETTER THAN "INCLUDE"
29
+ * ------------------------------------------------------------
30
+ *
31
+ * "includeLibrary" suggests:
32
+ * - blindly insert
33
+ *
34
+ * "ensureLibrary" means:
35
+ * - verify existence first
36
+ * - avoid duplicates
37
+ * - maintain deterministic state
38
+ *
39
+ * This is the same idea as:
40
+ * - ensureImport
41
+ * - ensureDirectory
42
+ * - ensureVariable
43
+ *
44
+ * ------------------------------------------------------------
45
+ * Useful commands
46
+ * ------------------------------------------------------------
47
+ * add routing: ensureLibrary("react-router-dom")
48
+ * add tailwind: ensureLibrary("tailwindcss")
49
+ * add redux: ensureLibrary("@reduxjs/toolkit")
50
+ * add charts: ensureLibrary("recharts")
51
+ * add icons: ensureLibrary("lucide-react")
52
+ * add form validation: ensureLibrary("zod")
53
+ * add animations: ensureLibrary("framer-motion")
54
+ *
55
+ *
56
+ * ------------------------------------------------------------
57
+ * IMPORTANT NOTE
58
+ * ------------------------------------------------------------
59
+ *
60
+ * This function ONLY updates package.json.
61
+ *
62
+ * It DOES NOT:
63
+ * - run npm install
64
+ * - run yarn install
65
+ * - download packages
66
+ *
67
+ * Installation should be a separate responsibility.
68
+ *
69
+ * ------------------------------------------------------------
70
+ * EXAMPLE
71
+ * ------------------------------------------------------------
72
+ *
73
+ * ensureLibrary({
74
+ * projectPath: "/my-app",
75
+ * libraryName: "react-router-dom",
76
+ * version: "^7.0.0"
77
+ * });
78
+ *
79
+ * ------------------------------------------------------------
80
+ * PARAMS
81
+ * ------------------------------------------------------------
82
+ *
83
+ * @param {Object} params
84
+ *
85
+ * @param {string} params.projectPath
86
+ * Absolute or relative path of the project root.
87
+ *
88
+ * @param {string} params.libraryName
89
+ * Name of the npm package.
90
+ *
91
+ * @param {string} params.version
92
+ * Version to store in package.json.
93
+ *
94
+ * @param {"dependencies"|"devDependencies"} [params.dependencyType]
95
+ * Which dependency section should contain the package.
96
+ *
97
+ * defaults to:
98
+ * "dependencies"
99
+ *
100
+ * ------------------------------------------------------------
101
+ * RETURNS
102
+ * ------------------------------------------------------------
103
+ *
104
+ * @returns {{
105
+ * modified: boolean,
106
+ * packageJson: Object
107
+ * }}
108
+ *
109
+ * modified:
110
+ * true -> package.json changed
111
+ * false -> package already existed
112
+ *
113
+ */
114
+ function ensureLibrary({ libraryName, version = "v1.0.0" }, context) {
115
+ if (!Object.prototype.hasOwnProperty.call(healpers.getProjectDependencies({
116
+ projectRoot: context.project.root,
117
+ }).allPackages, libraryName)) {
118
+ const message = `Package ${libraryName} in not installed. Please install it`;
119
+ // informUser({
120
+ // message,
121
+ // });
122
+ return {
123
+ success: false,
124
+ message,
125
+ };
126
+ }
127
+ return {
128
+ success: true,
129
+ };
130
+ // ----------------------------------------------------------
131
+ // STEP 1:
132
+ // Build absolute path to package.json
133
+ // ----------------------------------------------------------
134
+ // const packageJsonPath = path.join(projectPath, "package.json");
135
+ // // ----------------------------------------------------------
136
+ // // STEP 2:
137
+ // // Verify package.json exists
138
+ // // ----------------------------------------------------------
139
+ // if (!fs.existsSync(packageJsonPath)) {
140
+ // throw new Error(`package.json not found at: ${packageJsonPath}`);
141
+ // }
142
+ // // ----------------------------------------------------------
143
+ // // STEP 3:
144
+ // // Read package.json file as text
145
+ // // ----------------------------------------------------------
146
+ // const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
147
+ // // ----------------------------------------------------------
148
+ // // STEP 4:
149
+ // // Parse JSON string into JS object
150
+ // // ----------------------------------------------------------
151
+ // const packageJson = JSON.parse(packageJsonContent);
152
+ // // ----------------------------------------------------------
153
+ // // STEP 5:
154
+ // // Ensure dependency section exists
155
+ // //
156
+ // // Example:
157
+ // //
158
+ // // {
159
+ // // dependencies: {}
160
+ // // }
161
+ // //
162
+ // // If missing, create empty object
163
+ // // ----------------------------------------------------------
164
+ // if (!packageJson[dependencyType]) {
165
+ // packageJson[dependencyType] = {};
166
+ // }
167
+ // // ----------------------------------------------------------
168
+ // // STEP 6:
169
+ // // Check if library already exists
170
+ // // ----------------------------------------------------------
171
+ // const alreadyExists = packageJson[dependencyType][libraryName];
172
+ // // ----------------------------------------------------------
173
+ // // STEP 7:
174
+ // // If already exists -> return unchanged
175
+ // // ----------------------------------------------------------
176
+ // if (alreadyExists) {
177
+ // return {
178
+ // success: false,
179
+ // packageJson,
180
+ // };
181
+ // }
182
+ // // ----------------------------------------------------------
183
+ // // STEP 8:
184
+ // // Add library with version
185
+ // //
186
+ // // Example:
187
+ // //
188
+ // // "react-router-dom": "^7.0.0"
189
+ // // ----------------------------------------------------------
190
+ // packageJson[dependencyType][libraryName] = version;
191
+ // // ----------------------------------------------------------
192
+ // // STEP 9:
193
+ // // Sort dependencies alphabetically
194
+ // //
195
+ // // WHY?
196
+ // //
197
+ // // Deterministic ordering:
198
+ // // - cleaner git diffs
199
+ // // - stable formatting
200
+ // // - easier debugging
201
+ // // ----------------------------------------------------------
202
+ // packageJson[dependencyType] = Object.fromEntries(
203
+ // Object.entries(packageJson[dependencyType]).sort(([a], [b]) =>
204
+ // a.localeCompare(b)
205
+ // )
206
+ // );
207
+ // // ----------------------------------------------------------
208
+ // // STEP 10:
209
+ // // Convert object back into formatted JSON string
210
+ // //
211
+ // // JSON.stringify arguments:
212
+ // //
213
+ // // null -> no replacer
214
+ // // 2 -> 2-space indentation
215
+ // // ----------------------------------------------------------
216
+ // const updatedContent = JSON.stringify(packageJson, null, 2);
217
+ // // ----------------------------------------------------------
218
+ // // STEP 11:
219
+ // // Write updated package.json back to disk
220
+ // // ----------------------------------------------------------
221
+ // fs.writeFileSync(packageJsonPath, `${updatedContent}\n`, "utf-8");
222
+ // // ----------------------------------------------------------
223
+ // // STEP 12:
224
+ // // Return success metadata
225
+ // // ----------------------------------------------------------
226
+ // return {
227
+ // success: true,
228
+ // packageJson,
229
+ // };
230
+ }
231
+ /**
232
+ * Validator: Ensures that a component style file exists.
233
+ *
234
+ * ------------------------------------------------------------
235
+ * WHAT THIS FUNCTION DOES
236
+ * ------------------------------------------------------------
237
+ *
238
+ * This function guarantees that:
239
+ *
240
+ * Component.css
241
+ *
242
+ * exists for a given component.
243
+ *
244
+ * If css file does not exist:
245
+ *
246
+ * - creates the css file
247
+ * - optionally inserts initial styles
248
+ *
249
+ * If css file already exists:
250
+ *
251
+ * - does nothing
252
+ *
253
+ * ------------------------------------------------------------
254
+ * WHY THIS FUNCTION EXISTS
255
+ * ------------------------------------------------------------
256
+ *
257
+ * Many Looma operations depend on css file existing.
258
+ *
259
+ * Example:
260
+ *
261
+ * - "make header red"
262
+ * - "increase spacing"
263
+ * - "add hover effect"
264
+ * - "make card responsive"
265
+ *
266
+ * Before inserting styles,
267
+ * we must ensure style file exists.
268
+ *
269
+ * Useful in commands like:
270
+
271
+ make header red
272
+ add hover effect
273
+ increase spacing
274
+ make card responsive
275
+ add styles to navbar
276
+ create component
277
+ extract component
278
+
279
+ It is usually called before:
280
+
281
+ insertStyles
282
+ updateStyles
283
+ removeStyles
284
+ createComponent
285
+ * ------------------------------------------------------------
286
+ * IMPORTANT ARCHITECTURAL IDEA
287
+ * ------------------------------------------------------------
288
+ *
289
+ * Looma treats every component as:
290
+ *
291
+ * Component Unit
292
+ *
293
+ * Example:
294
+ *
295
+ * Header/
296
+ * Header.jsx
297
+ * Header.css
298
+ * index.js
299
+ *
300
+ * This function enforces that convention.
301
+ *
302
+ * ------------------------------------------------------------
303
+ * PARAMS
304
+ * ------------------------------------------------------------
305
+ *
306
+ * @param {Object} params
307
+ *
308
+ * @param {string} params.componentPath
309
+ * Absolute or relative component folder path.
310
+ *
311
+ * Example:
312
+ *
313
+ * "./src/components/Header"
314
+ *
315
+ * @param {string} params.componentName
316
+ * Component name.
317
+ *
318
+ * Example:
319
+ *
320
+ * "Header"
321
+ *
322
+ * @param {string} [params.initialStyles]
323
+ * Optional initial css content.
324
+ *
325
+ * Example:
326
+ *
327
+ * ".header {}"
328
+ *
329
+ * ------------------------------------------------------------
330
+ * RETURNS
331
+ * ------------------------------------------------------------
332
+ *
333
+ * @returns {Object}
334
+ *
335
+ * {
336
+ * created: boolean,
337
+ * cssPath: string
338
+ * }
339
+ *
340
+ */
341
+ function ensureStyleFile({ componentName }, context) {
342
+ // ----------------------------------------------------------
343
+ // STEP 1:
344
+ // Resolve absolute component directory path
345
+ // ----------------------------------------------------------
346
+ const { foundComponent } = finders.findComponentDirectory({
347
+ componentName,
348
+ }, context.appContext);
349
+ const absoluteComponentPath = foundComponent.componentPath;
350
+ // ----------------------------------------------------------
351
+ // STEP 2:
352
+ // Build css file path
353
+ //
354
+ // Example:
355
+ //
356
+ // Header/Header.css
357
+ // ----------------------------------------------------------
358
+ const cssPath = path.join(absoluteComponentPath, `${componentName}.css`);
359
+ // ----------------------------------------------------------
360
+ // STEP 3:
361
+ // Check if css file already exists
362
+ // ----------------------------------------------------------
363
+ const cssFileExists = fs.existsSync(cssPath);
364
+ // ----------------------------------------------------------
365
+ // STEP 4:
366
+ // If css file already exists,
367
+ // return early
368
+ // ----------------------------------------------------------
369
+ if (!cssFileExists) {
370
+ return {
371
+ success: false,
372
+ cssPath,
373
+ };
374
+ }
375
+ return {
376
+ success: true,
377
+ cssPath,
378
+ };
379
+ }
380
+ /**
381
+ * Validator: Normalizes a component into Looma's
382
+ * canonical architecture format.
383
+ *
384
+ * ------------------------------------------------------------
385
+ * WHAT THIS FUNCTION DOES
386
+ * ------------------------------------------------------------
387
+ *
388
+ * This function repairs and standardizes:
389
+ *
390
+ * - component structure
391
+ * - imports
392
+ * - css imports
393
+ * - component naming
394
+ * - export structure
395
+ * - directory consistency
396
+ * - class naming
397
+ * - formatting cleanup
398
+ *
399
+ * into a predictable architecture.
400
+ *
401
+ * Example:
402
+ *
403
+ * BEFORE:
404
+ *
405
+ * components/
406
+ * header.js
407
+ *
408
+ * AFTER:
409
+ *
410
+ * Header/
411
+ * Header.jsx
412
+ * Header.css
413
+ * index.js
414
+ *
415
+ * ------------------------------------------------------------
416
+ * WHY THIS FUNCTION EXISTS
417
+ * ------------------------------------------------------------
418
+ *
419
+ * AI-generated code drifts rapidly.
420
+ *
421
+ * Over time projects become:
422
+ *
423
+ * - inconsistent
424
+ * - structurally chaotic
425
+ * - impossible to reason about
426
+ * - difficult to edit automatically
427
+ *
428
+ * Runtime AI editing REQUIRES:
429
+ *
430
+ * deterministic structure.
431
+ *
432
+ * This function acts as:
433
+ *
434
+ * architecture stabilization layer.
435
+ *
436
+ * ------------------------------------------------------------
437
+ * IMPORTANT ARCHITECTURAL DECISION
438
+ * ------------------------------------------------------------
439
+ *
440
+ * Looma should NEVER allow:
441
+ *
442
+ * arbitrary component structures.
443
+ *
444
+ * Every component should eventually
445
+ * converge toward:
446
+ *
447
+ * ONE predictable structure.
448
+ *
449
+ * because:
450
+ *
451
+ * predictable architecture enables:
452
+ *
453
+ * - safe runtime editing
454
+ * - reliable AST transforms
455
+ * - undo/redo
456
+ * - DOM synchronization
457
+ * - registry synchronization
458
+ * - scalable AI modifications
459
+ *
460
+ * ------------------------------------------------------------
461
+ * IMPORTANT NOTE
462
+ * ------------------------------------------------------------
463
+ *
464
+ * Current implementation focuses on:
465
+ *
466
+ * filesystem normalization.
467
+ *
468
+ * It does NOT yet fully normalize:
469
+ *
470
+ * - hook ordering
471
+ * - jsx formatting
472
+ * - prop ordering
473
+ * - state extraction
474
+ * - logic separation
475
+ * - advanced AST refactors
476
+ *
477
+ * Production version should eventually
478
+ * become AST-driven.
479
+ *
480
+ * ------------------------------------------------------------
481
+ * DEPENDENCIES
482
+ * ------------------------------------------------------------
483
+ *
484
+ * This function complements:
485
+ *
486
+ * - createComponent()
487
+ * - updateComponent()
488
+ * - renameComponent()
489
+ * - moveComponent()
490
+ * - extractComponent()
491
+ * - ensureComponentStructure()
492
+ * - updateComponentImports()
493
+ * - resolveCssClassConflicts()
494
+ * - syncComponentStyles()
495
+ *
496
+ * ------------------------------------------------------------
497
+ * WHERE THIS FUNCTION IS USEFUL
498
+ * ------------------------------------------------------------
499
+ *
500
+ * Useful in commands like:
501
+ *
502
+ * - "cleanup component"
503
+ * - "normalize architecture"
504
+ * - "repair generated code"
505
+ * - "fix component structure"
506
+ * - "optimize project"
507
+ * - "prepare for extraction"
508
+ * - "stabilize AI generated UI"
509
+ *
510
+ * Usually executed:
511
+ *
512
+ * AFTER major AI-generated changes.
513
+ *
514
+ * ------------------------------------------------------------
515
+ * PARAMS
516
+ * ------------------------------------------------------------
517
+ *
518
+ * @param {Object} params
519
+ *
520
+ * @param {string} params.componentPath
521
+ * Component directory path.
522
+ *
523
+ * Example:
524
+ *
525
+ * "./src/components/Header"
526
+ *
527
+ * @param {string} params.componentName
528
+ * Canonical component name.
529
+ *
530
+ * Example:
531
+ *
532
+ * "Header"
533
+ *
534
+ * @param {boolean} [params.ensureCss=true]
535
+ * Whether component should contain css file.
536
+ *
537
+ * @param {boolean} [params.ensureIndex=true]
538
+ * Whether component should contain index.js.
539
+ *
540
+ * @param {boolean} [params.normalizeExports=true]
541
+ * Whether export statements should be normalized.
542
+ *
543
+ * ------------------------------------------------------------
544
+ * RETURNS
545
+ * ------------------------------------------------------------
546
+ *
547
+ * @returns {Object}
548
+ *
549
+ * {
550
+ * normalized: boolean,
551
+ * repairedItems: string[],
552
+ * warnings: string[]
553
+ * }
554
+ *
555
+ */
556
+ function normalizeComponent({ componentPath, componentName, ensureCss = true, ensureIndex = true, normalizeExports = true, }, context) {
557
+ const COMPONENT_STRUCTURE = context.appContext.config.componentStructure;
558
+ // ----------------------------------------------------------
559
+ // STEP 1:
560
+ // Resolve absolute component path
561
+ // ----------------------------------------------------------
562
+ const absoluteComponentPath = path.resolve(componentPath);
563
+ // ----------------------------------------------------------
564
+ // STEP 2:
565
+ // Validate component directory existence
566
+ // ----------------------------------------------------------
567
+ if (!fs.existsSync(absoluteComponentPath)) {
568
+ throw new LoomaError(ERROR_CODES.TASK_EXECUTION_FAILED, `Component directory does not exist: ${absoluteComponentPath}`, {
569
+ payload: {
570
+ componentPath,
571
+ componentName,
572
+ ensureCss: true,
573
+ ensureIndex: true,
574
+ normalizeExports: true,
575
+ },
576
+ });
577
+ }
578
+ // ----------------------------------------------------------
579
+ // STEP 3:
580
+ // Track repaired items
581
+ // ----------------------------------------------------------
582
+ const repairedItems = [];
583
+ // ----------------------------------------------------------
584
+ // STEP 4:
585
+ // Track warnings
586
+ // ----------------------------------------------------------
587
+ const warnings = [];
588
+ // ----------------------------------------------------------
589
+ // STEP 5:
590
+ // Ensure canonical component structure
591
+ // ----------------------------------------------------------
592
+ const jsxPath = path.join(absoluteComponentPath, `${componentName}${COMPONENT_STRUCTURE[0]}`);
593
+ const cssPath = path.join(absoluteComponentPath, `${componentName}${COMPONENT_STRUCTURE[1]}`);
594
+ const indexPath = path.join(absoluteComponentPath, COMPONENT_STRUCTURE[2]);
595
+ // ----------------------------------------------------------
596
+ // STEP 6:
597
+ // Ensure JSX file exists
598
+ // ----------------------------------------------------------
599
+ if (!fs.existsSync(jsxPath)) {
600
+ // --------------------------------------------------------
601
+ // Create minimal JSX template
602
+ // --------------------------------------------------------
603
+ const jsxTemplate = `
604
+ import "./${componentName}.css";
605
+
606
+ function ${componentName}() {
607
+ return (
608
+ <div className="${componentName.toLowerCase()}">
609
+ ${componentName}
610
+ </div>
611
+ );
612
+ }
613
+
614
+ export default ${componentName};
615
+ `;
616
+ fs.writeFileSync(jsxPath, jsxTemplate.trim(), "utf8");
617
+ repairedItems.push("Created missing JSX file");
618
+ }
619
+ // ----------------------------------------------------------
620
+ // STEP 7:
621
+ // Ensure CSS file exists
622
+ // ----------------------------------------------------------
623
+ if (ensureCss && !fs.existsSync(cssPath)) {
624
+ // --------------------------------------------------------
625
+ // Create empty CSS file
626
+ // --------------------------------------------------------
627
+ fs.writeFileSync(cssPath, "", "utf8");
628
+ repairedItems.push("Created missing CSS file");
629
+ }
630
+ // ----------------------------------------------------------
631
+ // STEP 8:
632
+ // Ensure index.js exists
633
+ // ----------------------------------------------------------
634
+ if (ensureIndex && !fs.existsSync(indexPath)) {
635
+ // --------------------------------------------------------
636
+ // Create standard export file
637
+ // --------------------------------------------------------
638
+ const indexTemplate = `
639
+ export { default } from "./${componentName}";
640
+ `;
641
+ fs.writeFileSync(indexPath, indexTemplate.trim(), "utf8");
642
+ repairedItems.push("Created missing index.js");
643
+ }
644
+ // ----------------------------------------------------------
645
+ // STEP 9:
646
+ // Read component source
647
+ // ----------------------------------------------------------
648
+ let componentCode = fs.readFileSync(jsxPath, "utf8");
649
+ // ----------------------------------------------------------
650
+ // STEP 10:
651
+ // Ensure CSS import exists
652
+ // ----------------------------------------------------------
653
+ const cssImportStatement = `import "./${componentName}.css";`;
654
+ // ----------------------------------------------------------
655
+ // Add css import if missing
656
+ // ----------------------------------------------------------
657
+ if (ensureCss && !componentCode.includes(cssImportStatement)) {
658
+ componentCode = cssImportStatement + "\n" + componentCode;
659
+ repairedItems.push("Added missing CSS import");
660
+ }
661
+ // ----------------------------------------------------------
662
+ // STEP 11:
663
+ // Normalize export statement
664
+ // ----------------------------------------------------------
665
+ if (normalizeExports) {
666
+ // --------------------------------------------------------
667
+ // Remove broken exports
668
+ // --------------------------------------------------------
669
+ componentCode = componentCode.replace(/export\s+default\s+[A-Za-z0-9_]+;/g, "");
670
+ // --------------------------------------------------------
671
+ // Add canonical export
672
+ // --------------------------------------------------------
673
+ componentCode += `
674
+
675
+ export default ${componentName};
676
+ `;
677
+ repairedItems.push("Normalized export statement");
678
+ }
679
+ // ----------------------------------------------------------
680
+ // STEP 12:
681
+ // Normalize component declaration
682
+ // ----------------------------------------------------------
683
+ const componentDeclarationRegex = /function\s+[A-Za-z0-9_]+\s*\(/;
684
+ // ----------------------------------------------------------
685
+ // Replace incorrect component names
686
+ // ----------------------------------------------------------
687
+ if (componentDeclarationRegex.test(componentCode)) {
688
+ componentCode = componentCode.replace(componentDeclarationRegex, `function ${componentName}(`);
689
+ repairedItems.push("Normalized component declaration");
690
+ }
691
+ else {
692
+ warnings.push("No function component declaration found");
693
+ }
694
+ // ----------------------------------------------------------
695
+ // STEP 13:
696
+ // Cleanup excessive blank lines
697
+ // ----------------------------------------------------------
698
+ componentCode = componentCode.replace(/\n{3,}/g, "\n\n");
699
+ // ----------------------------------------------------------
700
+ // STEP 14:
701
+ // Write normalized component source
702
+ // ----------------------------------------------------------
703
+ fs.writeFileSync(jsxPath, componentCode.trim(), "utf8");
704
+ // ----------------------------------------------------------
705
+ // STEP 15:
706
+ // Return normalization summary
707
+ // ----------------------------------------------------------
708
+ return {
709
+ success: repairedItems.length > 0,
710
+ repairedItems,
711
+ warnings,
712
+ };
713
+ }
714
+ /**
715
+ * Validator: Ensures that a component follows
716
+ * Looma's required filesystem structure.
717
+ *
718
+ * ------------------------------------------------------------
719
+ * WHAT THIS FUNCTION DOES
720
+ * ------------------------------------------------------------
721
+ *
722
+ * This function validates and fixes:
723
+ *
724
+ * - component directory
725
+ * - jsx file
726
+ * - css file
727
+ * - index.js file
728
+ *
729
+ * structure consistency.
730
+ *
731
+ * Example:
732
+ *
733
+ * REQUIRED STRUCTURE:
734
+ *
735
+ * Header/
736
+ * Header.jsx
737
+ * Header.css
738
+ * index.js
739
+ *
740
+ * If something is missing:
741
+ *
742
+ * - it gets created
743
+ *
744
+ * If structure is invalid:
745
+ *
746
+ * - it gets normalized
747
+ *
748
+ * ------------------------------------------------------------
749
+ * WHY THIS FUNCTION EXISTS
750
+ * ------------------------------------------------------------
751
+ *
752
+ * AI-generated projects drift very quickly.
753
+ *
754
+ * Without strict structure enforcement:
755
+ *
756
+ * - imports become inconsistent
757
+ * - components become fragmented
758
+ * - css locations become unpredictable
759
+ * - registry synchronization breaks
760
+ * - runtime editing becomes unstable
761
+ *
762
+ * This function acts as:
763
+ *
764
+ * architecture enforcement layer.
765
+ *
766
+ * ------------------------------------------------------------
767
+ * IMPORTANT ARCHITECTURAL DECISION
768
+ * ------------------------------------------------------------
769
+ *
770
+ * Looma should NEVER support:
771
+ *
772
+ * random project structures.
773
+ *
774
+ * Deterministic structure is REQUIRED for:
775
+ *
776
+ * - runtime editing
777
+ * - component registry
778
+ * - DOM mapping
779
+ * - AI transformations
780
+ * - undo/redo
781
+ * - AST synchronization
782
+ *
783
+ * Structure consistency is one of the
784
+ * MOST IMPORTANT foundations of Looma.
785
+ *
786
+ * ------------------------------------------------------------
787
+ * IMPORTANT NOTE
788
+ * ------------------------------------------------------------
789
+ *
790
+ * Current implementation:
791
+ *
792
+ * handles filesystem structure only.
793
+ *
794
+ * It does NOT validate:
795
+ *
796
+ * - component correctness
797
+ * - jsx correctness
798
+ * - import correctness
799
+ * - css correctness
800
+ *
801
+ * Those belong to separate validators.
802
+ *
803
+ * ------------------------------------------------------------
804
+ * DEPENDENCIES
805
+ * ------------------------------------------------------------
806
+ *
807
+ * This function complements:
808
+ *
809
+ * - createComponent()
810
+ * - updateComponent()
811
+ * - renameComponent()
812
+ * - moveComponent()
813
+ * - extractComponent()
814
+ * - ensureStyleFile()
815
+ *
816
+ * ------------------------------------------------------------
817
+ * WHERE THIS FUNCTION IS USEFUL
818
+ * ------------------------------------------------------------
819
+ *
820
+ * Useful in commands like:
821
+ *
822
+ * - "create component"
823
+ * - "fix project structure"
824
+ * - "normalize components"
825
+ * - "extract reusable section"
826
+ * - "repair generated code"
827
+ * - "migrate project"
828
+ * - "sync architecture"
829
+ *
830
+ * Usually executed:
831
+ *
832
+ * BEFORE major component mutations.
833
+ *
834
+ * ------------------------------------------------------------
835
+ * PARAMS
836
+ * ------------------------------------------------------------
837
+ *
838
+ * @param {Object} params
839
+ *
840
+ * @param {string} params.componentPath
841
+ * Component directory path.
842
+ *
843
+ * Example:
844
+ *
845
+ * "./src/components/Header"
846
+ *
847
+ * @param {string} params.componentName
848
+ * Component name.
849
+ *
850
+ * Example:
851
+ *
852
+ * "Header"
853
+ *
854
+ * @param {boolean} [params.ensureCss=true]
855
+ * Whether css file should exist.
856
+ *
857
+ * @param {boolean} [params.ensureIndex=true]
858
+ * Whether index.js should exist.
859
+ *
860
+ * @param {boolean} [params.createIfMissing=true]
861
+ * Whether missing directories/files
862
+ * should be automatically created.
863
+ *
864
+ * ------------------------------------------------------------
865
+ * RETURNS
866
+ * ------------------------------------------------------------
867
+ *
868
+ * @returns {Object}
869
+ *
870
+ * {
871
+ * created: string[],
872
+ * existing: string[],
873
+ * repaired: boolean
874
+ * }
875
+ *
876
+ */
877
+ // function ensureComponentStructure({
878
+ // componentPath,
879
+ // componentName,
880
+ // ensureCss = true,
881
+ // ensureIndex = true,
882
+ // createIfMissing = true,
883
+ // }: TaskPayload<"ensureComponentStructure">, context: ExecutionContext): TaskResponse<
884
+ // TaskReturn<"ensureComponentStructure">
885
+ // > {
886
+ // // ----------------------------------------------------------
887
+ // // STEP 1:
888
+ // // Resolve absolute component path
889
+ // // ----------------------------------------------------------
890
+ // const absoluteComponentPath = path.resolve(componentPath);
891
+ // // ----------------------------------------------------------
892
+ // // STEP 2:
893
+ // // Track created files/directories
894
+ // // ----------------------------------------------------------
895
+ // const created = [];
896
+ // // ----------------------------------------------------------
897
+ // // STEP 3:
898
+ // // Track existing files/directories
899
+ // // ----------------------------------------------------------
900
+ // const existing = [];
901
+ // // ----------------------------------------------------------
902
+ // // STEP 4:
903
+ // // Ensure component directory exists
904
+ // // ----------------------------------------------------------
905
+ // if (!fs.existsSync(absoluteComponentPath)) {
906
+ // // --------------------------------------------------------
907
+ // // Throw if auto creation disabled
908
+ // // --------------------------------------------------------
909
+ // if (!createIfMissing) {
910
+ // throw new Error(
911
+ // `Component directory does not exist: ${absoluteComponentPath}`
912
+ // );
913
+ // }
914
+ // // --------------------------------------------------------
915
+ // // Create component directory
916
+ // // --------------------------------------------------------
917
+ // fs.mkdirSync(absoluteComponentPath, {
918
+ // recursive: true,
919
+ // });
920
+ // created.push(absoluteComponentPath);
921
+ // } else {
922
+ // existing.push(absoluteComponentPath);
923
+ // }
924
+ // // ----------------------------------------------------------
925
+ // // STEP 5:
926
+ // // Build JSX file path
927
+ // // ----------------------------------------------------------
928
+ // const jsxPath = path.join(
929
+ // absoluteComponentPath,
930
+ // `${componentName}${CONSTENTS.COMPONENT_STRUCTURE[0]}`
931
+ // );
932
+ // // ----------------------------------------------------------
933
+ // // STEP 6:
934
+ // // Ensure JSX file exists
935
+ // // ----------------------------------------------------------
936
+ // if (!fs.existsSync(jsxPath)) {
937
+ // // --------------------------------------------------------
938
+ // // Throw if creation disabled
939
+ // // --------------------------------------------------------
940
+ // if (!createIfMissing) {
941
+ // throw new Error(`Component JSX file missing: ${jsxPath}`);
942
+ // }
943
+ // // --------------------------------------------------------
944
+ // // Create minimal component template
945
+ // // --------------------------------------------------------
946
+ // const componentTemplate = `
947
+ // import "./${componentName}.css";
948
+ // function ${componentName}() {
949
+ // return (
950
+ // <div className="${componentName.toLowerCase()}">
951
+ // ${componentName}
952
+ // </div>
953
+ // );
954
+ // }
955
+ // export default ${componentName};
956
+ // `;
957
+ // fs.writeFileSync(jsxPath, componentTemplate.trim(), "utf8");
958
+ // created.push(jsxPath);
959
+ // } else {
960
+ // existing.push(jsxPath);
961
+ // }
962
+ // // ----------------------------------------------------------
963
+ // // STEP 7:
964
+ // // Build CSS file path
965
+ // // ----------------------------------------------------------
966
+ // const cssPath = path.join(
967
+ // absoluteComponentPath,
968
+ // `${componentName}${CONSTENTS.COMPONENT_STRUCTURE[1]}`
969
+ // );
970
+ // // ----------------------------------------------------------
971
+ // // STEP 8:
972
+ // // Ensure CSS file exists
973
+ // // ----------------------------------------------------------
974
+ // if (ensureCss) {
975
+ // if (!fs.existsSync(cssPath)) {
976
+ // // ------------------------------------------------------
977
+ // // Throw if creation disabled
978
+ // // ------------------------------------------------------
979
+ // if (!createIfMissing) {
980
+ // throw new Error(`Component CSS file missing: ${cssPath}`);
981
+ // }
982
+ // // ------------------------------------------------------
983
+ // // Create empty css file
984
+ // // ------------------------------------------------------
985
+ // fs.writeFileSync(cssPath, "", "utf8");
986
+ // created.push(cssPath);
987
+ // } else {
988
+ // existing.push(cssPath);
989
+ // }
990
+ // }
991
+ // // ----------------------------------------------------------
992
+ // // STEP 9:
993
+ // // Build index.js path
994
+ // // ----------------------------------------------------------
995
+ // const indexPath = path.join(
996
+ // absoluteComponentPath,
997
+ // CONSTENTS.COMPONENT_STRUCTURE[2]
998
+ // );
999
+ // // ----------------------------------------------------------
1000
+ // // STEP 10:
1001
+ // // Ensure index.js exists
1002
+ // // ----------------------------------------------------------
1003
+ // if (ensureIndex) {
1004
+ // if (!fs.existsSync(indexPath)) {
1005
+ // // ------------------------------------------------------
1006
+ // // Throw if creation disabled
1007
+ // // ------------------------------------------------------
1008
+ // if (!createIfMissing) {
1009
+ // throw new Error(`Component index.js missing: ${indexPath}`);
1010
+ // }
1011
+ // // ------------------------------------------------------
1012
+ // // Create export file
1013
+ // // ------------------------------------------------------
1014
+ // const indexTemplate = `
1015
+ // export { default } from "./${componentName}";
1016
+ // `;
1017
+ // fs.writeFileSync(indexPath, indexTemplate.trim(), "utf8");
1018
+ // created.push(indexPath);
1019
+ // } else {
1020
+ // existing.push(indexPath);
1021
+ // }
1022
+ // }
1023
+ // // ----------------------------------------------------------
1024
+ // // STEP 11:
1025
+ // // Return structure validation summary
1026
+ // // ----------------------------------------------------------
1027
+ // return {
1028
+ // success: true,
1029
+ // existing,
1030
+ // repaired: created.length > 0,
1031
+ // };
1032
+ // }
1033
+ export default {
1034
+ // insertCode,
1035
+ // expose only when planner becomes mature enough to use it directly
1036
+ // ensureImport,
1037
+ // removeImport,
1038
+ // enrichImport,
1039
+ // optimizeImports,
1040
+ // updateComponentImports,
1041
+ // findNodeByLine,
1042
+ // findComponentByName,
1043
+ // findJSXElement,
1044
+ // resolveImportConflicts,
1045
+ // do not expose
1046
+ ensureLibrary,
1047
+ // ensureComponentStructure,
1048
+ normalizeComponent,
1049
+ // findComponentDirectory,
1050
+ // inferComponentName,
1051
+ ensureStyleFile,
1052
+ };
1053
+ //# sourceMappingURL=ensurers.js.map