@auto-engineer/component-implementer 0.10.5 → 0.11.10

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @auto-engineer/frontend-implementer
2
2
 
3
+ ## 0.11.10
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @auto-engineer/ai-gateway@0.11.10
9
+ - @auto-engineer/message-bus@0.11.10
10
+
3
11
  ## 0.10.5
4
12
 
5
13
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env tsx
2
- import { runAIAgent } from './agent';
2
+ import { runAIAgent } from './agent.js';
3
3
  const [, , projectDir, iaSchemeDir, designSystemPath] = process.argv;
4
4
  if (!projectDir) {
5
5
  console.error('Usage: agent-cli <project-directory> <ia-scheme-directory>');
@@ -1,7 +1,7 @@
1
1
  export declare function callAI(prompt: string, options?: {
2
2
  temperature?: number;
3
3
  maxTokens?: number;
4
- }): Promise<any>;
4
+ }): Promise<string>;
5
5
  interface Scheme {
6
6
  generatedComponents?: {
7
7
  type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/agent.ts"],"names":[],"mappings":"AAqHA,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,gBAOlG;AAGD,UAAU,MAAM;IACd,mBAAmB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;IAC1E,KAAK,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,KAAK,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CACZ,MAAM,EACN;YACE,KAAK,EAAE,MAAM,CAAC;YACd,WAAW,EAAE,MAAM,CAAC;YACpB,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CACF,CAAC;KACH,CAAC;CACH;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,EAAE,CAAC;IACnE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAGD,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuBjF;AA0ED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,cAAc,CAAC,CAiCzB;AA+gBD,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAAE,iBA2CnB"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/agent.ts"],"names":[],"mappings":"AAqHA,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,mBAOlG;AAGD,UAAU,MAAM;IACd,mBAAmB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;IAC1E,KAAK,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IACF,KAAK,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CACZ,MAAM,EACN;YACE,KAAK,EAAE,MAAM,CAAC;YACd,WAAW,EAAE,MAAM,CAAC;YACpB,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CACF,CAAC;KACH,CAAC;CACH;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,EAAE,CAAC;IACnE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAGD,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuBjF;AA0ED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,cAAc,CAAC,CAiCzB;AA+gBD,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAAE,iBA2CnB"}
@@ -4,6 +4,7 @@ export type ImplementComponentCommand = Command<'ImplementComponent', {
4
4
  iaSchemeDir: string;
5
5
  designSystemPath: string;
6
6
  componentType: 'atom' | 'molecule' | 'organism' | 'page';
7
+ filePath: string;
7
8
  componentName: string;
8
9
  failures?: string[];
9
10
  }>;
@@ -18,7 +19,21 @@ export type ComponentImplementationFailedEvent = Event<'ComponentImplementationF
18
19
  error: string;
19
20
  componentType: string;
20
21
  componentName: string;
22
+ filePath: string;
21
23
  }>;
22
- export declare const commandHandler: any;
23
- export default commandHandler;
24
+ export declare const commandHandler: import("@auto-engineer/message-bus").UnifiedCommandHandler<Readonly<{
25
+ type: "ImplementComponent";
26
+ data: Readonly<{
27
+ projectDir: string;
28
+ iaSchemeDir: string;
29
+ designSystemPath: string;
30
+ componentType: "atom" | "molecule" | "organism" | "page";
31
+ filePath: string;
32
+ componentName: string;
33
+ failures?: string[];
34
+ }>;
35
+ timestamp?: Date;
36
+ requestId?: string;
37
+ correlationId?: string;
38
+ }>>;
24
39
  //# sourceMappingURL=implement-component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"implement-component.d.ts","sourceRoot":"","sources":["../../../src/commands/implement-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAwB,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAQ5F,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAC7C,oBAAoB,EACpB;IACE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,CACF,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,KAAK,CAC3C,sBAAsB,EACtB;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CACF,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG,KAAK,CACpD,+BAA+B,EAC/B;IACE,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB,CACF,CAAC;AAEF,eAAO,MAAM,cAAc,KA2CzB,CAAC;AAoIH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"implement-component.d.ts","sourceRoot":"","sources":["../../../src/commands/implement-component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAwB,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAa5F,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAC7C,oBAAoB,EACpB;IACE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,CACF,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,KAAK,CAC3C,sBAAsB,EACtB;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CACF,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG,KAAK,CACpD,+BAA+B,EAC/B;IACE,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CACF,CAAC;AAEF,eAAO,MAAM,cAAc;;;oBA/BX,MAAM;qBACL,MAAM;0BACD,MAAM;uBACT,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM;kBAC9C,MAAM;uBACD,MAAM;mBACV,MAAM,EAAE;;;;;GA6DrB,CAAC"}
@@ -1,9 +1,15 @@
1
+ // noinspection ExceptionCaughtLocallyJS
1
2
  import { defineCommandHandler } from '@auto-engineer/message-bus';
2
3
  import * as fs from 'fs/promises';
3
4
  import * as path from 'path';
4
5
  import createDebug from 'debug';
5
- import { callAI, getProjectContext, loadScheme } from '../agent';
6
- const debug = createDebug('frontend-implementer:implement-component');
6
+ import { callAI, loadScheme } from '../agent.js';
7
+ import { execa } from 'execa';
8
+ import { performance } from 'perf_hooks';
9
+ const debug = createDebug('auto:client-implementer:component');
10
+ const debugTypeCheck = createDebug('auto:client-implementer:component:typecheck');
11
+ const debugProcess = createDebug('auto:client-implementer:component:process');
12
+ const debugResult = createDebug('auto:client-implementer:component:result');
7
13
  export const commandHandler = defineCommandHandler({
8
14
  name: 'ImplementComponent',
9
15
  alias: 'implement:component',
@@ -18,11 +24,9 @@ export const commandHandler = defineCommandHandler({
18
24
  description: 'Type of component: atom|molecule|organism|page',
19
25
  required: true,
20
26
  },
27
+ filePath: { description: 'Component file path', required: true },
21
28
  componentName: { description: 'Name of component to implement', required: true },
22
- failures: {
23
- description: 'Any failures from previous implementations',
24
- required: false,
25
- },
29
+ failures: { description: 'Any failures from previous implementations', required: false },
26
30
  },
27
31
  examples: [
28
32
  '$ auto implement:component --project-dir=./client --ia-scheme-dir=./.context --design-system-path=./design-system.md --component-type=molecule --component-name=SurveyCard',
@@ -31,63 +35,107 @@ export const commandHandler = defineCommandHandler({
31
35
  handle: async (command) => {
32
36
  const result = await handleImplementComponentCommandInternal(command);
33
37
  if (result.type === 'ComponentImplemented') {
34
- debug('Component implemented: %s/%s at %s', result.data.componentType, result.data.componentName, result.data.filePath);
38
+ debug('Component implemented successfully: %s/%s', result.data.componentType, result.data.componentName);
35
39
  }
36
40
  else {
37
- debug('Failed: %s', result.data.error);
41
+ debug('Component implementation failed: %s', result.data.error);
38
42
  }
39
43
  return result;
40
44
  },
41
45
  });
46
+ // eslint-disable-next-line complexity
42
47
  async function handleImplementComponentCommandInternal(command) {
43
- const { projectDir, iaSchemeDir, designSystemPath, componentType, componentName, failures = [] } = command.data;
48
+ const { projectDir, iaSchemeDir, designSystemPath, componentType, componentName, filePath } = command.data;
44
49
  try {
45
- const userPreferencesFile = path.resolve(projectDir, 'design-system-principles.md');
46
- const [userPreferences, designSystem] = await Promise.all([
47
- fs.readFile(userPreferencesFile, 'utf-8'),
48
- fs.readFile(designSystemPath, 'utf-8'),
49
- ]);
50
+ const start = performance.now();
51
+ debugProcess(`Starting ${componentType}:${componentName}`);
52
+ const t1 = performance.now();
50
53
  const scheme = await loadScheme(iaSchemeDir);
51
- if (!scheme) {
54
+ debugProcess(`[1] Loaded IA scheme in ${(performance.now() - t1).toFixed(2)} ms`);
55
+ if (!scheme)
52
56
  throw new Error('IA scheme not found');
53
- }
54
57
  const pluralKey = `${componentType}s`;
55
58
  const collection = scheme[pluralKey];
56
- if (!isValidCollection(collection)) {
59
+ if (!isValidCollection(collection))
57
60
  throw new Error(`Invalid IA schema structure for ${pluralKey}`);
58
- }
59
61
  const items = collection.items;
60
62
  const componentDef = items[componentName];
61
- if (!componentDef) {
63
+ if (!componentDef)
62
64
  throw new Error(`Component ${componentType}:${componentName} not found in IA schema`);
65
+ const outPath = path.join(projectDir, '..', filePath);
66
+ const t2 = performance.now();
67
+ let existingScaffold = '';
68
+ try {
69
+ existingScaffold = await fs.readFile(outPath, 'utf-8');
70
+ debugProcess(`[2] Found existing scaffold in ${(performance.now() - t2).toFixed(2)} ms`);
63
71
  }
64
- const ctx = await getProjectContext(projectDir, iaSchemeDir, userPreferences, designSystem, failures);
65
- const prompt = makeComponentPrompt(ctx, componentType, componentName, componentDef);
66
- const code = await callAI(prompt);
67
- const outPath = path.join(projectDir, 'src/components', `${componentType}s`, `${componentName}.tsx`);
72
+ catch {
73
+ debugProcess(`[2] No existing scaffold found (${(performance.now() - t2).toFixed(2)} ms)`);
74
+ }
75
+ const t3 = performance.now();
76
+ const projectConfig = await readAllTopLevelFiles(projectDir);
77
+ debugProcess(`[3] Loaded project + gql/graphql files in ${(performance.now() - t3).toFixed(2)} ms`);
78
+ const t4 = performance.now();
79
+ const designSystemReference = await readDesignSystem(designSystemPath, { projectDir, iaSchemeDir });
80
+ debugProcess(`[4] Loaded design system reference in ${(performance.now() - t4).toFixed(2)} ms`);
81
+ const dependencyList = await resolveDependenciesRecursively(scheme, componentType, componentName);
82
+ debugProcess(`[5] Resolved ${dependencyList.length} dependencies for ${componentName}`);
83
+ const dependencySources = {};
84
+ for (const dep of dependencyList) {
85
+ const depSource = await readComponentSource(projectDir, dep.type, dep.name);
86
+ if (depSource != null)
87
+ dependencySources[`${dep.type}/${dep.name}`] = depSource;
88
+ }
89
+ const basePrompt = makeBasePrompt(componentType, componentName, componentDef, existingScaffold, projectConfig, designSystemReference, dependencySources);
68
90
  await fs.mkdir(path.dirname(outPath), { recursive: true });
69
- await fs.writeFile(outPath, code, 'utf-8');
70
- return {
71
- type: 'ComponentImplemented',
72
- data: {
73
- filePath: outPath,
74
- componentType,
75
- componentName,
76
- composition: extractComposition(componentDef),
77
- specs: extractSpecs(componentDef),
78
- },
79
- timestamp: new Date(),
80
- requestId: command.requestId,
81
- correlationId: command.correlationId,
82
- };
91
+ let attempt = 1;
92
+ let code = '';
93
+ let lastErrors = '';
94
+ const maxAttempts = 3;
95
+ while (attempt <= maxAttempts) {
96
+ const genStart = performance.now();
97
+ const prompt = attempt === 1
98
+ ? makeImplementPrompt(basePrompt)
99
+ : makeRetryPrompt(basePrompt, componentType, componentName, code, lastErrors);
100
+ const aiRaw = await callAI(prompt);
101
+ code = extractCodeBlock(aiRaw);
102
+ await fs.writeFile(outPath, code, 'utf-8');
103
+ debugProcess(`[6.${attempt}] AI output written (${code.length} chars) in ${(performance.now() - genStart).toFixed(2)} ms`);
104
+ const checkStart = performance.now();
105
+ const { success, errors } = await runTypeCheckForFile(projectDir, outPath);
106
+ debugTypeCheck(`[7.${attempt}] Type check in ${(performance.now() - checkStart).toFixed(2)} ms (success: ${success})`);
107
+ if (success) {
108
+ debugResult(`[✓] Implementation succeeded in ${(performance.now() - start).toFixed(2)} ms total`);
109
+ return {
110
+ type: 'ComponentImplemented',
111
+ data: {
112
+ filePath: outPath,
113
+ componentType,
114
+ componentName,
115
+ composition: extractComposition(componentDef),
116
+ specs: extractSpecs(componentDef),
117
+ },
118
+ timestamp: new Date(),
119
+ requestId: command.requestId,
120
+ correlationId: command.correlationId,
121
+ };
122
+ }
123
+ lastErrors = errors;
124
+ if (attempt === maxAttempts)
125
+ throw new Error(`Type errors persist after ${attempt} attempts:\n${errors}`);
126
+ attempt += 1;
127
+ }
128
+ throw new Error('Unreachable state');
83
129
  }
84
130
  catch (error) {
131
+ debug('[Error] Component implementation failed: %O', error);
85
132
  return {
86
133
  type: 'ComponentImplementationFailed',
87
134
  data: {
88
135
  error: error instanceof Error ? error.message : String(error),
89
136
  componentType,
90
137
  componentName,
138
+ filePath,
91
139
  },
92
140
  timestamp: new Date(),
93
141
  requestId: command.requestId,
@@ -95,46 +143,319 @@ async function handleImplementComponentCommandInternal(command) {
95
143
  };
96
144
  }
97
145
  }
98
- function extractComposition(componentDef) {
99
- if ('composition' in componentDef && Boolean(componentDef.composition)) {
100
- const comp = componentDef.composition;
101
- if ('atoms' in comp && Array.isArray(comp.atoms)) {
102
- return comp.atoms;
146
+ // eslint-disable-next-line complexity
147
+ async function resolveDependenciesRecursively(scheme, type, name, visited = new Set()) {
148
+ const key = `${type}:${name}`;
149
+ if (visited.has(key))
150
+ return [];
151
+ visited.add(key);
152
+ const collection = scheme[`${type}s`];
153
+ //
154
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
155
+ if (!collection || !isValidCollection(collection))
156
+ return [];
157
+ const def = collection.items[name];
158
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
159
+ if (!def || typeof def !== 'object' || !('composition' in def))
160
+ return [];
161
+ const result = [];
162
+ const composition = def.composition;
163
+ for (const [subType, subNames] of Object.entries(composition)) {
164
+ if (!Array.isArray(subNames))
165
+ continue;
166
+ for (const subName of subNames) {
167
+ result.push({ type: subType, name: subName });
168
+ const nested = await resolveDependenciesRecursively(scheme, subType, subName, visited);
169
+ result.push(...nested);
103
170
  }
104
- if ('molecules' in comp && Array.isArray(comp.molecules)) {
105
- return comp.molecules;
171
+ }
172
+ return result;
173
+ }
174
+ async function readComponentSource(projectDir, type, name) {
175
+ const file = path.join(projectDir, 'src', 'components', type, `${name}.tsx`);
176
+ try {
177
+ return await fs.readFile(file, 'utf-8');
178
+ }
179
+ catch {
180
+ return null;
181
+ }
182
+ }
183
+ function extractCodeBlock(text) {
184
+ return text
185
+ .replace(/```(?:tsx|ts|typescript)?/g, '')
186
+ .replace(/```/g, '')
187
+ .trim();
188
+ }
189
+ async function readAllTopLevelFiles(projectDir) {
190
+ debugProcess('[readAllTopLevelFiles] Reading project files from %s', projectDir);
191
+ const start = performance.now();
192
+ const config = {};
193
+ async function readRecursive(currentDir) {
194
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
195
+ for (const entry of entries) {
196
+ const fullPath = path.join(currentDir, entry.name);
197
+ const relativePath = path.relative(projectDir, fullPath);
198
+ if (entry.isDirectory()) {
199
+ if (['node_modules', 'dist', 'build', '.next', '.turbo'].includes(entry.name))
200
+ continue;
201
+ await readRecursive(fullPath);
202
+ }
203
+ else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx'))) {
204
+ try {
205
+ config[relativePath] = await fs.readFile(fullPath, 'utf-8');
206
+ }
207
+ catch (err) {
208
+ debugProcess(`Failed to read ${relativePath}: ${err.message}`);
209
+ }
210
+ }
106
211
  }
107
212
  }
108
- return [];
213
+ await readRecursive(projectDir);
214
+ debugProcess(`[readAllTopLevelFiles] Completed in ${(performance.now() - start).toFixed(2)} ms`);
215
+ return config;
109
216
  }
110
- function extractSpecs(componentDef) {
111
- if ('specs' in componentDef && Array.isArray(componentDef.specs)) {
112
- return componentDef.specs;
217
+ async function runTypeCheckForFile(projectDir, filePath) {
218
+ const start = performance.now();
219
+ try {
220
+ const tsconfigRoot = await findProjectRoot(projectDir);
221
+ const relativeFilePath = path.relative(tsconfigRoot, filePath).replace(/\\/g, '/');
222
+ const normalizedRelative = relativeFilePath.replace(/^client\//, '');
223
+ const result = await execa('npx', ['tsc', '--noEmit', '--skipLibCheck', '--pretty', 'false'], {
224
+ cwd: tsconfigRoot,
225
+ stdio: 'pipe',
226
+ reject: false,
227
+ });
228
+ const output = (result.stdout ?? '') + (result.stderr ?? '');
229
+ debugTypeCheck(`[runTypeCheckForFile] Finished tsc in ${(performance.now() - start).toFixed(2)} ms`);
230
+ if (result.exitCode === 0 && !output.includes('error TS'))
231
+ return { success: true, errors: '' };
232
+ const filteredErrors = output
233
+ .split('\n')
234
+ .filter((line) => {
235
+ const hasError = line.includes('error TS');
236
+ const notNodeModules = !line.includes('node_modules');
237
+ const matchesTarget = line.includes(relativeFilePath) ||
238
+ line.includes(normalizedRelative) ||
239
+ line.includes(path.basename(filePath));
240
+ return hasError && notNodeModules && matchesTarget;
241
+ })
242
+ .join('\n');
243
+ if (filteredErrors.trim().length === 0)
244
+ return { success: true, errors: '' };
245
+ return { success: false, errors: filteredErrors };
113
246
  }
114
- return [];
247
+ catch (err) {
248
+ const message = err instanceof Error ? err.message : String(err);
249
+ return { success: false, errors: message };
250
+ }
251
+ }
252
+ async function findProjectRoot(startDir) {
253
+ let dir = startDir;
254
+ while (dir !== path.dirname(dir)) {
255
+ try {
256
+ await fs.access(path.join(dir, 'package.json'));
257
+ await fs.access(path.join(dir, 'tsconfig.json'));
258
+ return dir;
259
+ }
260
+ catch {
261
+ dir = path.dirname(dir);
262
+ }
263
+ }
264
+ throw new Error('Could not find project root (no package.json or tsconfig.json found)');
115
265
  }
116
- function makeComponentPrompt(ctx, componentType, componentName, componentDef) {
266
+ // eslint-disable-next-line complexity
267
+ function makeBasePrompt(componentType, componentName, componentDef, existingScaffold, projectConfig, designSystemReference, dependencySources) {
268
+ const hasScaffold = Boolean(existingScaffold?.trim());
269
+ const gqlFiles = {};
270
+ const graphqlFiles = {};
271
+ const otherFiles = {};
272
+ for (const [filePath, content] of Object.entries(projectConfig)) {
273
+ const lower = filePath.toLowerCase();
274
+ if (lower.includes('src/gql/'))
275
+ gqlFiles[filePath] = content;
276
+ else if (lower.includes('src/graphql/'))
277
+ graphqlFiles[filePath] = content;
278
+ else
279
+ otherFiles[filePath] = content;
280
+ }
281
+ const queriesFile = Object.entries(graphqlFiles).find(([n]) => n.endsWith('queries.ts'))?.[1] ?? '';
282
+ const mutationsFile = Object.entries(graphqlFiles).find(([n]) => n.endsWith('mutations.ts'))?.[1] ?? '';
283
+ const gqlSection = Object.entries(gqlFiles)
284
+ .map(([p, c]) => `### ${p}\n${c}`)
285
+ .join('\n\n') || '(No gql folder found)';
286
+ const graphqlSection = Object.entries(graphqlFiles)
287
+ .map(([p, c]) => `### ${p}\n${c}`)
288
+ .join('\n\n') || '(No graphql folder found)';
289
+ const configSection = Object.entries(otherFiles)
290
+ .map(([p, c]) => `### ${p}\n${c}`)
291
+ .join('\n\n') || '(No additional config files)';
292
+ const designSystemBlock = designSystemReference.trim()
293
+ ? designSystemReference
294
+ : '(No design system content provided)';
295
+ const dependencySection = Object.entries(dependencySources)
296
+ .map(([name, src]) => `### ${name}\n${src}`)
297
+ .join('\n\n') || '(No dependencies found)';
117
298
  return `
118
- You are Auto, a masterful frontend engineer.
119
- Implement the following ${componentType}: **${componentName}** from the IA schema.
299
+ # Implementation Brief: ${componentName} (${componentType})
300
+
301
+ You are a senior frontend engineer specializing in **React + TypeScript + Apollo Client**.
302
+ Your task is to build a visually excellent, type-safe, and production-ready ${componentType} component.
303
+ The goal is to deliver elegant, minimal, and robust code that integrates perfectly with the existing system.
304
+
305
+ ---
120
306
 
121
- Component Definition:
307
+ ## Objective
308
+ Implement **${componentName}** as defined in the IA schema and design system.
309
+ Your component must:
310
+ - Compile cleanly with no TypeScript errors.
311
+ - Follow established design tokens, colors, and spacing.
312
+ - Be visually polished, responsive, and accessible.
313
+ - Reuse existing atoms/molecules/organisms wherever possible.
314
+ - Use valid imports only — no new dependencies or mock data.
315
+
316
+ ---
317
+
318
+ ## Project Context
319
+
320
+ **File Path:** src/components/${componentType}/${componentName}.tsx
321
+ **Purpose:** A reusable UI element connected to the GraphQL layer and design system.
322
+
323
+ ### IA Schema
122
324
  ${JSON.stringify(componentDef, null, 2)}
123
325
 
124
- Project Snapshot:
125
- ${JSON.stringify(ctx.fileTreeSummary, null, 2)}
326
+ ### Existing Scaffold
327
+ ${hasScaffold ? existingScaffold : '(No existing scaffold found)'}
328
+
329
+ ### Design System Reference
330
+ ${designSystemBlock}
126
331
 
127
- Available Atoms:
128
- ${JSON.stringify(ctx.atoms, null, 2)}
332
+ ### Related Components (Dependencies)
333
+ ${dependencySection}
129
334
 
130
- GraphQL Operations:
131
- ${Object.keys(ctx.graphqlOperations).join(', ')}
335
+ ### GraphQL Context (src/graphql)
336
+ ${graphqlSection}
132
337
 
133
- Theme:
134
- ${ctx.theme}
338
+ #### queries.ts
339
+ ${queriesFile || '(queries.ts not found)'}
135
340
 
136
- Output: ONLY the full TypeScript React component code (no markdown, no explanations).
137
- `;
341
+ #### mutations.ts
342
+ ${mutationsFile || '(mutations.ts not found)'}
343
+
344
+ ### GraphQL Codegen (src/gql)
345
+ ${gqlSection}
346
+
347
+ ### Other Relevant Files
348
+ ${configSection}
349
+
350
+ ---
351
+
352
+ ## Engineering Guidelines
353
+
354
+ **Type Safety**
355
+ - Explicitly type all props, state, and GraphQL responses.
356
+ - Avoid \`any\` — prefer discriminated unions, interfaces, and generics.
357
+
358
+ **React Practices**
359
+ - Never call setState during render.
360
+ - Always use dependency arrays in effects.
361
+ - Memoize computed values and callbacks.
362
+ - Keep rendering pure and predictable.
363
+
364
+ **Error Handling**
365
+ - Wrap async operations in try/catch with graceful fallback UI.
366
+ - Check for null/undefined using optional chaining (?.) and defaults (??).
367
+
368
+ **Visual & UX Quality**
369
+ - Perfect spacing and alignment using Tailwind or the design system tokens.
370
+ - Add subtle hover, focus, and loading states.
371
+ - Use accessible HTML semantics and ARIA attributes.
372
+ - Animate with Framer Motion when appropriate.
373
+
374
+ **Performance**
375
+ - Prevent unnecessary re-renders with React.memo and stable references.
376
+ - Avoid redundant state and computations.
377
+
378
+ **Consistency**
379
+ - Follow established color, typography, and spacing scales.
380
+ - Match button, card, and badge styles with existing components.
381
+
382
+ **Prohibited**
383
+ - No placeholder data, TODOs, or pseudo-logic.
384
+ - No new external packages.
385
+ - No commented-out or partial implementations.
386
+
387
+ ---
388
+
389
+ ## Visual Quality Checklist
390
+ - Consistent vertical rhythm and alignment.
391
+ - Smooth hover and transition states.
392
+ - Responsive design that looks intentional at all breakpoints.
393
+ - Uses design tokens and existing components wherever possible.
394
+ - Clear visual hierarchy and accessible structure.
395
+
396
+ ---
397
+
398
+ ## Validation Checklist
399
+ - Compiles cleanly with \`tsc --noEmit\`.
400
+ - Imports exist and resolve correctly.
401
+ - Component matches the design system and IA schema.
402
+ - No unused props, variables, or imports.
403
+ - Visually and functionally complete.
404
+
405
+ ---
406
+
407
+ **Final Output Requirement:**
408
+ Return only the complete \`.tsx\` source code for this component — no markdown fences, commentary, or extra text.
409
+ `.trim();
410
+ }
411
+ function makeImplementPrompt(basePrompt) {
412
+ return `${basePrompt}
413
+
414
+ ---
415
+
416
+ Generate the **complete final implementation** for \`${basePrompt}\`.
417
+ Begin directly with import statements and end with the export statement.
418
+ Do not include markdown fences, comments, or explanations — only the valid .tsx file content.
419
+ `.trim();
420
+ }
421
+ function makeRetryPrompt(basePrompt, componentType, componentName, previousCode, previousErrors) {
422
+ return `
423
+ ${basePrompt}
424
+
425
+ ---
426
+
427
+ ### Correction Task
428
+ The previously generated ${componentType} component **${componentName}** failed TypeScript validation.
429
+ Fix only the issues listed below without altering logic or layout.
430
+
431
+ **Errors**
432
+ ${previousErrors}
433
+
434
+ **Previous Code**
435
+ ${previousCode}
436
+
437
+ ---
438
+
439
+ ### Correction Rules
440
+ - Fix only TypeScript or import errors.
441
+ - Do not change working logic or structure.
442
+ - Keep eslint directives and formatting intact.
443
+ - Return the corrected \`.tsx\` file only, with no markdown fences or commentary.
444
+ `.trim();
445
+ }
446
+ /* -------------------------------------------------------------------------- */
447
+ function extractComposition(componentDef) {
448
+ if ('composition' in componentDef && Boolean(componentDef.composition)) {
449
+ const comp = componentDef.composition;
450
+ if ('atoms' in comp && Array.isArray(comp.atoms))
451
+ return comp.atoms;
452
+ if ('molecules' in comp && Array.isArray(comp.molecules))
453
+ return comp.molecules;
454
+ }
455
+ return [];
456
+ }
457
+ function extractSpecs(componentDef) {
458
+ return Array.isArray(componentDef.specs) ? componentDef.specs : [];
138
459
  }
139
460
  function isValidCollection(collection) {
140
461
  if (collection === null || collection === undefined)
@@ -146,5 +467,27 @@ function isValidCollection(collection) {
146
467
  const items = collection.items;
147
468
  return typeof items === 'object' && items !== null;
148
469
  }
149
- export default commandHandler;
470
+ async function readDesignSystem(providedPath, refs) {
471
+ const start = performance.now();
472
+ const candidates = [];
473
+ if (providedPath) {
474
+ candidates.push(providedPath);
475
+ if (!path.isAbsolute(providedPath)) {
476
+ candidates.push(path.resolve(refs.projectDir, providedPath));
477
+ candidates.push(path.resolve(refs.iaSchemeDir, providedPath));
478
+ }
479
+ }
480
+ for (const candidate of candidates) {
481
+ try {
482
+ const content = await fs.readFile(candidate, 'utf-8');
483
+ debugProcess(`[readDesignSystem] Loaded from ${candidate} in ${(performance.now() - start).toFixed(2)} ms`);
484
+ return content;
485
+ }
486
+ catch {
487
+ debugProcess(`[readDesignSystem] Could not read design system from %s`, candidate);
488
+ }
489
+ }
490
+ debugProcess(`[readDesignSystem] Design system not found, elapsed ${(performance.now() - start).toFixed(2)} ms`);
491
+ return '';
492
+ }
150
493
  //# sourceMappingURL=implement-component.js.map