@girardelli/architect 4.0.0 → 5.0.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 (191) hide show
  1. package/dist/agent-generator/context-enricher.d.ts.map +1 -1
  2. package/dist/agent-generator/context-enricher.js +35 -3
  3. package/dist/agent-generator/context-enricher.js.map +1 -1
  4. package/dist/agent-generator/domain-inferrer.d.ts.map +1 -1
  5. package/dist/agent-generator/domain-inferrer.js +10 -0
  6. package/dist/agent-generator/domain-inferrer.js.map +1 -1
  7. package/dist/agent-generator/index.d.ts +14 -0
  8. package/dist/agent-generator/index.d.ts.map +1 -1
  9. package/dist/agent-generator/index.js +82 -14
  10. package/dist/agent-generator/index.js.map +1 -1
  11. package/dist/agent-generator/stack-detector.d.ts +3 -1
  12. package/dist/agent-generator/stack-detector.d.ts.map +1 -1
  13. package/dist/agent-generator/stack-detector.js +29 -33
  14. package/dist/agent-generator/stack-detector.js.map +1 -1
  15. package/dist/agent-generator/templates/core/agents.d.ts.map +1 -1
  16. package/dist/agent-generator/templates/core/agents.js +5 -1
  17. package/dist/agent-generator/templates/core/agents.js.map +1 -1
  18. package/dist/agent-generator/templates/core/hooks-generator.d.ts +21 -0
  19. package/dist/agent-generator/templates/core/hooks-generator.d.ts.map +1 -0
  20. package/dist/agent-generator/templates/core/hooks-generator.js +233 -0
  21. package/dist/agent-generator/templates/core/hooks-generator.js.map +1 -0
  22. package/dist/agent-generator/templates/core/skills-generator.d.ts +13 -0
  23. package/dist/agent-generator/templates/core/skills-generator.d.ts.map +1 -1
  24. package/dist/agent-generator/templates/core/skills-generator.js +339 -0
  25. package/dist/agent-generator/templates/core/skills-generator.js.map +1 -1
  26. package/dist/analyzer.d.ts.map +1 -1
  27. package/dist/analyzer.js +11 -1
  28. package/dist/analyzer.js.map +1 -1
  29. package/dist/anti-patterns.d.ts +7 -0
  30. package/dist/anti-patterns.d.ts.map +1 -1
  31. package/dist/anti-patterns.js +25 -6
  32. package/dist/anti-patterns.js.map +1 -1
  33. package/dist/config.d.ts +6 -0
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/config.js +48 -11
  36. package/dist/config.js.map +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/project-summarizer.d.ts +20 -0
  40. package/dist/project-summarizer.d.ts.map +1 -1
  41. package/dist/project-summarizer.js +171 -14
  42. package/dist/project-summarizer.js.map +1 -1
  43. package/dist/scanner.d.ts +8 -2
  44. package/dist/scanner.d.ts.map +1 -1
  45. package/dist/scanner.js +153 -113
  46. package/dist/scanner.js.map +1 -1
  47. package/dist/scorer.d.ts.map +1 -1
  48. package/dist/scorer.js +24 -11
  49. package/dist/scorer.js.map +1 -1
  50. package/dist/types.d.ts +16 -0
  51. package/dist/types.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/agent-generator/context-enricher.ts +32 -3
  54. package/src/agent-generator/domain-inferrer.ts +10 -0
  55. package/src/agent-generator/index.ts +94 -15
  56. package/src/agent-generator/stack-detector.ts +32 -20
  57. package/src/agent-generator/templates/core/agents.ts +5 -2
  58. package/src/agent-generator/templates/core/hooks-generator.ts +242 -0
  59. package/src/agent-generator/templates/core/skills-generator.ts +349 -0
  60. package/src/analyzer.ts +13 -1
  61. package/src/anti-patterns.ts +29 -6
  62. package/src/config.ts +52 -11
  63. package/src/index.ts +1 -1
  64. package/src/project-summarizer.ts +189 -15
  65. package/src/scanner.ts +136 -90
  66. package/src/scorer.ts +26 -11
  67. package/src/types.ts +17 -0
  68. package/tests/agent-generator.test.ts +16 -0
  69. package/tests/fixtures/monorepo/package.json +6 -0
  70. package/tests/fixtures/monorepo/packages/app/package.json +12 -0
  71. package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
  72. package/tests/fixtures/monorepo/packages/core/package.json +7 -0
  73. package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
  74. package/tests/monorepo-scan.test.ts +170 -0
  75. package/tests/scanner.test.ts +1 -1
  76. package/tests/scorer.test.ts +22 -16
  77. package/tests/stack-detector.test.ts +19 -19
  78. package/__test_agent_output__/INDEX.md +0 -1
  79. package/__test_agent_output__/agents/AGENT-ORCHESTRATOR.md +0 -1
  80. package/__test_agent_output__/agents/DATABASE-ENGINEER.md +0 -174
  81. package/__test_agent_output__/agents/QA-TEST-ENGINEER.md +0 -138
  82. package/__test_agent_output__/agents/SECURITY-AUDITOR.md +0 -106
  83. package/__test_agent_output__/agents/TECH-DEBT-CONTROLLER.md +0 -104
  84. package/__test_agent_output__/agents/TYPESCRIPT-BACKEND-DEVELOPER.md +0 -135
  85. package/__test_agent_output__/guards/CODE-REVIEW-CHECKLIST.md +0 -95
  86. package/__test_agent_output__/guards/PREFLIGHT.md +0 -200
  87. package/__test_agent_output__/guards/QUALITY-GATES.md +0 -1
  88. package/__test_agent_output__/rules/00-general.md +0 -229
  89. package/__test_agent_output__/rules/01-architecture.md +0 -191
  90. package/__test_agent_output__/rules/02-security.md +0 -402
  91. package/__test_agent_output__/rules/03-nestjs.md +0 -124
  92. package/__test_agent_output__/templates/ADR.md +0 -95
  93. package/__test_agent_output__/templates/BDD.md +0 -58
  94. package/__test_agent_output__/templates/C4.md +0 -68
  95. package/__test_agent_output__/templates/TDD.md +0 -86
  96. package/__test_agent_output__/templates/THREAT-MODEL.md +0 -82
  97. package/__test_agent_output__/workflows/fix-bug.md +0 -228
  98. package/__test_agent_output__/workflows/new-feature.md +0 -311
  99. package/__test_agent_output__/workflows/review.md +0 -95
  100. package/__test_context_7RvUrO/src/modules/empty/empty.ts +0 -0
  101. package/__test_context_Rf5fNJ/src/modules/mixed/mixed.ts +0 -5
  102. package/__test_context_WRCnYH/src/modules/test/test.ts +0 -10
  103. package/__test_context_YsnVS3/src/modules/test/test.ts +0 -10
  104. package/__test_context_w7XZeH/src/modules/mixed/mixed.ts +0 -5
  105. package/__test_context_y5noh6/src/modules/empty/empty.ts +0 -0
  106. package/__test_framework__24OjAu/package.json +0 -1
  107. package/__test_framework__3ZDZsx/pyproject.toml +0 -8
  108. package/__test_framework__4T54Jn/package.json +0 -1
  109. package/__test_framework__4tlXu9/pyproject.toml +0 -8
  110. package/__test_framework__6boWqQ/Pipfile +0 -6
  111. package/__test_framework__6gygMU/pom.xml +0 -10
  112. package/__test_framework__6kxj0N/go.mod +0 -8
  113. package/__test_framework__7CEoXw/pom.xml +0 -10
  114. package/__test_framework__85DDz0/Pipfile +0 -6
  115. package/__test_framework__9WrRIr/pom.xml +0 -7
  116. package/__test_framework__ANqGKl/Gemfile +0 -5
  117. package/__test_framework__BCXTEM/go.mod +0 -3
  118. package/__test_framework__BHiPNq/setup.py +0 -2
  119. package/__test_framework__BqkiKv/package.json +0 -1
  120. package/__test_framework__C5yd8X/Pipfile.lock +0 -1
  121. package/__test_framework__C5yd8X/requirements.txt +0 -1
  122. package/__test_framework__C87d3a/manage.py +0 -1
  123. package/__test_framework__C87d3a/requirements.txt +0 -2
  124. package/__test_framework__DXNwc5/build.gradle +0 -7
  125. package/__test_framework__GhHSt3/build.gradle.kts +0 -4
  126. package/__test_framework__GzklJP/Cargo.toml +0 -7
  127. package/__test_framework__H4hd13/go.mod +0 -8
  128. package/__test_framework__HKjOXO/composer.json +0 -1
  129. package/__test_framework__HaDN45/Gemfile +0 -3
  130. package/__test_framework__IBO7YG/pyproject.toml +0 -9
  131. package/__test_framework__JwSOyF/pyproject.toml +0 -6
  132. package/__test_framework__K6HrCr/build.gradle +0 -2
  133. package/__test_framework__KzRPlh/pubspec.yaml +0 -9
  134. package/__test_framework__L6uIym/pyproject.toml +0 -6
  135. package/__test_framework__LOdoGK/requirements.txt +0 -4
  136. package/__test_framework__LgHzss/package.json +0 -1
  137. package/__test_framework__M76M6q/Gemfile +0 -5
  138. package/__test_framework__Mr9vWW/composer.json +0 -1
  139. package/__test_framework__N03Gnv/package.json +0 -1
  140. package/__test_framework__Num4UE/requirements +0 -1
  141. package/__test_framework__OAGw3Y/build.gradle +0 -7
  142. package/__test_framework__OQc8yG/pubspec.yaml +0 -9
  143. package/__test_framework__OwKZcd/requirements.txt +0 -3
  144. package/__test_framework__P0gFv7/requirements +0 -1
  145. package/__test_framework__PN55Rq/package.json +0 -1
  146. package/__test_framework__PQiqX8/pubspec.yaml +0 -3
  147. package/__test_framework__RBHsg7/composer.json +0 -1
  148. package/__test_framework__RHxif4/Cargo.toml +0 -7
  149. package/__test_framework__T0v0p1/Cargo.toml +0 -4
  150. package/__test_framework__Tu0clt/Pipfile.lock +0 -1
  151. package/__test_framework__Tu0clt/requirements.txt +0 -1
  152. package/__test_framework__TwDj9P/Cargo.toml +0 -4
  153. package/__test_framework__VQJNC4/pom.xml +0 -7
  154. package/__test_framework__W6sm05/package.json +0 -1
  155. package/__test_framework__W7vBLy/pyproject.toml +0 -4
  156. package/__test_framework__WNJOWT/setup.py +0 -2
  157. package/__test_framework__WSJs7U/package.json +0 -1
  158. package/__test_framework__YQ5VpA/build.gradle.kts +0 -4
  159. package/__test_framework__ZNEUEs/package.json +0 -1
  160. package/__test_framework__Znt922/pom.xml +0 -7
  161. package/__test_framework__azyg0h/pom.xml +0 -7
  162. package/__test_framework__c6otLr/package.json +0 -1
  163. package/__test_framework__cl9S9G/build.gradle +0 -2
  164. package/__test_framework__eilvV4/composer.json +0 -1
  165. package/__test_framework__gQZxXO/manage.py +0 -1
  166. package/__test_framework__gQZxXO/requirements.txt +0 -2
  167. package/__test_framework__ghvl26/poetry.lock +0 -1
  168. package/__test_framework__ghvl26/pyproject.toml +0 -2
  169. package/__test_framework__hR7b9U/Makefile +0 -11
  170. package/__test_framework__iESVsi/composer.json +0 -1
  171. package/__test_framework__jm6TJy/package.json +0 -1
  172. package/__test_framework__kBUpjs/pyproject.toml +0 -9
  173. package/__test_framework__kqoZrw/requirements.txt +0 -4
  174. package/__test_framework__lWkoyO/pyproject.toml +0 -4
  175. package/__test_framework__mTKnUO/package.json +0 -1
  176. package/__test_framework__nCeZwe/Makefile +0 -11
  177. package/__test_framework__oljsU0/package.json +0 -1
  178. package/__test_framework__osRG4q/go.mod +0 -3
  179. package/__test_framework__pCHH4F/package.json +0 -1
  180. package/__test_framework__pExx6E/Gemfile +0 -3
  181. package/__test_framework__pyBoGd/pyproject.toml +0 -5
  182. package/__test_framework__qw16VQ/package.json +0 -1
  183. package/__test_framework__rRayrG/package.json +0 -1
  184. package/__test_framework__s82zO5/package.json +0 -1
  185. package/__test_framework__tp8MFK/pyproject.toml +0 -5
  186. package/__test_framework__w44k4w/composer.json +0 -1
  187. package/__test_framework__yefPZY/poetry.lock +0 -1
  188. package/__test_framework__yefPZY/pyproject.toml +0 -2
  189. package/__test_framework__zCiyDT/requirements.txt +0 -3
  190. package/__test_framework__zGZN3j/pubspec.yaml +0 -3
  191. package/__test_framework__zXpnxL/package.json +0 -1
@@ -55,7 +55,10 @@ import {
55
55
  } from './templates/domain/index.js';
56
56
 
57
57
  // ── Skills Generator ──
58
- import { generateProjectSkills } from './templates/core/skills-generator.js';
58
+ import { generateProjectSkills, generateArchitectIntegrationSkill, generateCIPipelineSkill, generateMonorepoGuideSkill } from './templates/core/skills-generator.js';
59
+
60
+ // ── Hooks Generator ──
61
+ import { generatePreCommitHook, generatePrePushHook, generatePostAnalysisHook } from './templates/core/hooks-generator.js';
59
62
 
60
63
  // Re-export types for backward compatibility
61
64
  export type { StackInfo, AgentAuditFinding, AgentItem, AgentItemStatus, AgentSuggestion, EnrichedTemplateContext, DomainInsights, ModuleDetail, DetectedEndpoint, FrameworkInfo, DetectedToolchain };
@@ -71,6 +74,61 @@ export class AgentGenerator {
71
74
  private stackDetector = new StackDetector();
72
75
  private contextEnricher = new ContextEnricher();
73
76
 
77
+ /** Max lines for any single generated .agent file */
78
+ private static readonly MAX_FILE_LINES = 500;
79
+
80
+ /** Paths that indicate third-party code — filter from agent context */
81
+ private static readonly EXCLUDED_SEGMENTS = [
82
+ 'node_modules', '/dist/', '/build/', '/coverage/',
83
+ '/.next/', '/venv/', '/__pycache__/', '/target/',
84
+ ];
85
+
86
+ /**
87
+ * Sanitize the report before passing to agent generation.
88
+ * Removes anti-patterns, dependency nodes, and suggestions
89
+ * that reference node_modules or build artifacts.
90
+ */
91
+ private sanitizeReport(report: AnalysisReport): AnalysisReport {
92
+ const isProjectPath = (path: string): boolean => {
93
+ const normalized = path.replace(/\\/g, '/');
94
+ return !AgentGenerator.EXCLUDED_SEGMENTS.some(seg => normalized.includes(seg));
95
+ };
96
+
97
+ return {
98
+ ...report,
99
+ antiPatterns: report.antiPatterns.filter(ap => {
100
+ if (!isProjectPath(ap.location)) return false;
101
+ if (ap.affectedFiles?.some(f => !isProjectPath(f))) {
102
+ // Keep the pattern but clean affected files
103
+ ap.affectedFiles = ap.affectedFiles.filter(f => isProjectPath(f));
104
+ }
105
+ return true;
106
+ }),
107
+ dependencyGraph: {
108
+ nodes: report.dependencyGraph.nodes.filter(n => isProjectPath(n)),
109
+ edges: report.dependencyGraph.edges.filter(
110
+ e => isProjectPath(e.from) && isProjectPath(e.to)
111
+ ),
112
+ },
113
+ suggestions: report.suggestions.filter(
114
+ s => !s.description.includes('node_modules')
115
+ ),
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Cap content to max lines to prevent oversized agent files.
121
+ */
122
+ private capContent(content: string): string {
123
+ const lines = content.split('\n');
124
+ if (lines.length <= AgentGenerator.MAX_FILE_LINES) return content;
125
+
126
+ const truncated = lines.slice(0, AgentGenerator.MAX_FILE_LINES);
127
+ truncated.push('');
128
+ truncated.push('<!-- Content truncated at ' + AgentGenerator.MAX_FILE_LINES + ' lines. Run `architect agents` to regenerate. -->');
129
+ return truncated.join('\n');
130
+ }
131
+
74
132
  /**
75
133
  * Suggest agents without writing files — for unified report.
76
134
  */
@@ -79,7 +137,8 @@ export class AgentGenerator {
79
137
  plan: RefactoringPlan,
80
138
  projectPath: string,
81
139
  ): AgentSuggestion {
82
- const stack = this.stackDetector.detect(report);
140
+ const cleanReport = this.sanitizeReport(report);
141
+ const stack = this.stackDetector.detect(cleanReport);
83
142
  const agentDir = join(projectPath, '.agent');
84
143
  const isExisting = existsSync(agentDir);
85
144
 
@@ -94,7 +153,7 @@ export class AgentGenerator {
94
153
  audit = this.auditExisting(agentDir, stack, report, plan);
95
154
  }
96
155
 
97
- const ctx = this.buildContext(report, plan, stack, projectPath);
156
+ const ctx = this.buildContext(cleanReport, plan, stack, projectPath);
98
157
 
99
158
  const existingAgents = existingFiles('agents');
100
159
  const existingRules = existingFiles('rules');
@@ -125,9 +184,12 @@ export class AgentGenerator {
125
184
  desc: `Especialista em ${stack.primary} — APIs, serviços, lógica de negócio, integration docs`,
126
185
  });
127
186
  if (stack.hasFrontend) {
128
- const fw = stack.frameworks.find(f => ['Angular', 'Vue', 'Next.js', 'React'].includes(f)) || 'FRONTEND';
187
+ const FRONTEND_FWS = ['Angular', 'Vue', 'Vue.js', 'Next.js', 'React', 'Nuxt', 'Svelte', 'Remix'];
188
+ const detectedFw = ctx.detectedFrameworks?.find(f => FRONTEND_FWS.includes(f.name));
189
+ const fw = detectedFw?.name ||
190
+ stack.frameworks.find(f => FRONTEND_FWS.includes(f)) || 'FRONTEND';
129
191
  agentDefs.push({
130
- name: `${fw.toUpperCase().replace('.', '')}-FRONTEND-DEVELOPER`,
192
+ name: `${fw.toUpperCase().replace('.', '').replace(/\s/g, '-')}-FRONTEND-DEVELOPER`,
131
193
  desc: `Componentes ${fw}, state management, UX responsiva, todos os estados UI`,
132
194
  });
133
195
  }
@@ -225,17 +287,18 @@ export class AgentGenerator {
225
287
  projectPath: string,
226
288
  outputDir?: string
227
289
  ): { generated: string[]; audit: AgentAuditFinding[] } {
228
- const stack = this.stackDetector.detect(report);
290
+ const cleanReport = this.sanitizeReport(report);
291
+ const stack = this.stackDetector.detect(cleanReport);
229
292
  const agentDir = outputDir || join(projectPath, '.agent');
230
293
  const isExisting = existsSync(agentDir);
231
294
 
232
295
  if (isExisting) {
233
- const audit = this.auditExisting(agentDir, stack, report, plan);
234
- const generated = this.generateMissing(agentDir, audit, report, plan, stack, projectPath);
296
+ const audit = this.auditExisting(agentDir, stack, cleanReport, plan);
297
+ const generated = this.generateMissing(agentDir, audit, cleanReport, plan, stack, projectPath);
235
298
  return { generated, audit };
236
299
  }
237
300
 
238
- const generated = this.generateFull(agentDir, report, plan, stack, projectPath);
301
+ const generated = this.generateFull(agentDir, cleanReport, plan, stack, projectPath);
239
302
  return { generated, audit: [] };
240
303
  }
241
304
 
@@ -291,7 +354,7 @@ export class AgentGenerator {
291
354
  const ctx = this.buildContext(report, plan, stack, projectPath);
292
355
 
293
356
  // Create directories
294
- const dirs = ['agents', 'rules', 'guards', 'workflows', 'templates', 'skills'];
357
+ const dirs = ['agents', 'rules', 'guards', 'workflows', 'templates', 'skills', 'hooks'];
295
358
  for (const d of dirs) mkdirSync(join(agentDir, d), { recursive: true });
296
359
 
297
360
  // ── Core files (Enterprise-Grade) ──
@@ -314,9 +377,11 @@ export class AgentGenerator {
314
377
  coreFiles[`agents/${stack.primary.toUpperCase()}-BACKEND-DEVELOPER.md`] = generateBackendAgent(ctx);
315
378
  }
316
379
  if (stack.hasFrontend) {
317
- const fwName = stack.frameworks.find(f =>
318
- ['Angular', 'Vue', 'Next.js', 'React'].includes(f)) || 'Frontend';
319
- coreFiles[`agents/${fwName.toUpperCase().replace('.', '')}-FRONTEND-DEVELOPER.md`] = generateFrontendAgent(ctx);
380
+ const FRONTEND_FWS = ['Angular', 'Vue', 'Vue.js', 'Next.js', 'React', 'Nuxt', 'Svelte', 'Remix'];
381
+ const detectedFw = ctx.detectedFrameworks?.find(f => FRONTEND_FWS.includes(f.name));
382
+ const fwName = detectedFw?.name ||
383
+ stack.frameworks.find(f => FRONTEND_FWS.includes(f)) || 'Frontend';
384
+ coreFiles[`agents/${fwName.toUpperCase().replace('.', '').replace(/\s/g, '-')}-FRONTEND-DEVELOPER.md`] = generateFrontendAgent(ctx);
320
385
  }
321
386
  if (stack.hasMobile) {
322
387
  coreFiles['agents/FLUTTER-UI-DEVELOPER.md'] = generateMobileAgent(ctx);
@@ -348,12 +413,26 @@ export class AgentGenerator {
348
413
  coreFiles['skills/PROJECT-PATTERNS.md'] = skillsContent;
349
414
  }
350
415
 
351
- // ── Write all files ──
416
+ // ── Data-driven Skills (real project data) ──
417
+ coreFiles['skills/ARCHITECT-INTEGRATION.md'] = generateArchitectIntegrationSkill(ctx);
418
+ coreFiles['skills/CI-PIPELINE.md'] = generateCIPipelineSkill(ctx);
419
+
420
+ const monorepoGuide = generateMonorepoGuideSkill(ctx);
421
+ if (monorepoGuide) {
422
+ coreFiles['skills/MONOREPO-GUIDE.md'] = monorepoGuide;
423
+ }
424
+
425
+ // ── Executable Hooks ──
426
+ coreFiles['hooks/pre-commit.sh'] = generatePreCommitHook(ctx);
427
+ coreFiles['hooks/pre-push.sh'] = generatePrePushHook(ctx);
428
+ coreFiles['hooks/post-analysis.sh'] = generatePostAnalysisHook(ctx);
429
+
430
+ // ── Write all files (with size cap) ──
352
431
  for (const [path, content] of Object.entries(coreFiles)) {
353
432
  const fullPath = join(agentDir, path);
354
433
  const dir = join(fullPath, '..');
355
434
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
356
- writeFileSync(fullPath, content);
435
+ writeFileSync(fullPath, this.capContent(content));
357
436
  generated.push(path);
358
437
  }
359
438
 
@@ -3,7 +3,9 @@ import { StackInfo } from './types.js';
3
3
 
4
4
  /**
5
5
  * Detects the technology stack from an AnalysisReport.
6
- * Extracted from the monolithic AgentGenerator for single-responsibility.
6
+ *
7
+ * v5.1: Uses report.projectInfo.frameworks (precise, from scanner v5.0)
8
+ * instead of naive string-matching on file paths.
7
9
  */
8
10
  export class StackDetector {
9
11
  detect(report: AnalysisReport): StackInfo {
@@ -29,34 +31,44 @@ export class StackDetector {
29
31
  if (extensions.has('php')) languages.add('PHP');
30
32
  if (extensions.has('cs')) languages.add('C#');
31
33
 
32
- // ── Framework Detection from file patterns ──
33
- const allFiles = files.join(' ');
34
- if (allFiles.includes('manage.py') || allFiles.includes('django')) frameworks.add('Django');
35
- if (allFiles.includes('flask') || allFiles.includes('app.py')) frameworks.add('Flask');
36
- if (allFiles.includes('fastapi')) frameworks.add('FastAPI');
37
- if (allFiles.includes('.module.ts') || allFiles.includes('nest')) frameworks.add('NestJS');
38
- if (allFiles.includes('.component.ts') || allFiles.includes('angular')) frameworks.add('Angular');
39
- if (allFiles.includes('.vue')) frameworks.add('Vue');
40
- if (allFiles.includes('.tsx') && allFiles.includes('next')) frameworks.add('Next.js');
41
- if (allFiles.includes('.dart')) frameworks.add('Flutter');
42
- if (allFiles.includes('go.mod')) frameworks.add('Go Modules');
43
- if (allFiles.includes('Cargo.toml')) frameworks.add('Cargo');
44
- if (allFiles.includes('pom.xml') || allFiles.includes('build.gradle')) frameworks.add('Spring');
45
- if (allFiles.includes('rails') || allFiles.includes('Gemfile')) frameworks.add('Rails');
46
- if (allFiles.includes('laravel') || allFiles.includes('artisan')) frameworks.add('Laravel');
34
+ // ── Framework Detection: Trust report.projectInfo.frameworks ──
35
+ // These come from scanner v5.0 which reads actual package.json dependencies
36
+ const reportFrameworks = report.projectInfo?.frameworks || [];
37
+ for (const fw of reportFrameworks) {
38
+ frameworks.add(fw);
39
+ }
47
40
 
48
41
  // ── Derived Properties ──
49
42
  const primary = languages.size > 0 ? [...languages][0] : 'Unknown';
50
43
 
51
- const hasBackend = languages.has('Python') || languages.has('TypeScript') ||
44
+ // Frontend frameworks that indicate hasFrontend
45
+ const FRONTEND_FRAMEWORKS = new Set([
46
+ 'Angular', 'Vue', 'Vue.js', 'Next.js', 'React', 'Nuxt', 'Nuxt.js',
47
+ 'Svelte', 'SvelteKit', 'Remix', 'Gatsby', 'Vite',
48
+ ]);
49
+
50
+ // Backend frameworks that indicate hasBackend (language-based is fallback)
51
+ const BACKEND_FRAMEWORKS = new Set([
52
+ 'Express', 'Express.js', 'NestJS', 'Fastify', 'Koa', 'Hapi',
53
+ 'Django', 'Flask', 'FastAPI', 'Spring Boot', 'Laravel', 'Rails',
54
+ 'Ruby on Rails', 'Gin', 'Echo', 'Fiber', 'Actix Web', 'Rocket',
55
+ 'Probot', 'MCP SDK',
56
+ ]);
57
+
58
+ const MOBILE_FRAMEWORKS = new Set(['Flutter', 'React Native', 'Expo']);
59
+
60
+ const hasBackend = [...frameworks].some(f => BACKEND_FRAMEWORKS.has(f)) ||
61
+ languages.has('Python') || languages.has('TypeScript') ||
52
62
  languages.has('Go') || languages.has('Java/Kotlin') || languages.has('Ruby') ||
53
63
  languages.has('PHP') || languages.has('C#') || languages.has('Rust');
54
64
 
55
- const hasFrontend = frameworks.has('Angular') || frameworks.has('Vue') ||
56
- frameworks.has('Next.js') || frameworks.has('React') || extensions.has('html');
65
+ const hasFrontend = [...frameworks].some(f => FRONTEND_FRAMEWORKS.has(f)) ||
66
+ extensions.has('tsx') || extensions.has('jsx');
57
67
 
58
- const hasMobile = languages.has('Dart') || frameworks.has('Flutter');
68
+ const hasMobile = [...frameworks].some(f => MOBILE_FRAMEWORKS.has(f)) ||
69
+ languages.has('Dart');
59
70
 
71
+ const allFiles = files.join(' ').toLowerCase();
60
72
  const hasDatabase = allFiles.includes('migration') || allFiles.includes('entity') ||
61
73
  allFiles.includes('model') || allFiles.includes('schema') ||
62
74
  allFiles.includes('prisma') || allFiles.includes('typeorm');
@@ -148,8 +148,11 @@ ${crossRef('backend', ctx)}
148
148
  export function generateFrontendAgent(ctx: TemplateContext | EnrichedTemplateContext): string {
149
149
  const { stack, projectName, config, report } = ctx;
150
150
  const enriched = getEnriched(ctx);
151
- const fw = stack.frameworks.find(f =>
152
- ['Angular', 'Vue', 'Next.js', 'React'].includes(f)) || 'Frontend';
151
+ // v5.1: Use enriched primaryFramework or detect from all frameworks
152
+ const FRONTEND_FWS = ['Angular', 'Vue', 'Vue.js', 'Next.js', 'React', 'Nuxt', 'Svelte', 'Remix'];
153
+ const detectedFw = enriched.detectedFrameworks?.find(f => FRONTEND_FWS.includes(f.name));
154
+ const fw = detectedFw?.name ||
155
+ stack.frameworks.find(f => FRONTEND_FWS.includes(f)) || 'Frontend';
153
156
 
154
157
  // Build endpoints integration guide if available
155
158
  const endpointsGuide = enriched.endpoints && enriched.endpoints.length > 0
@@ -0,0 +1,242 @@
1
+ import { TemplateContext, EnrichedTemplateContext } from '../../types.js';
2
+ import { getEnriched } from '../template-helpers.js';
3
+
4
+ /**
5
+ * Generates executable hook scripts (.sh) for the .agent/hooks/ directory.
6
+ * These are real, runnable shell scripts tied to git lifecycle events.
7
+ *
8
+ * v5.1: Uses project-specific toolchain commands, score thresholds,
9
+ * and coverage targets from the enriched context.
10
+ */
11
+
12
+ /**
13
+ * Pre-commit hook: lint + build check
14
+ */
15
+ export function generatePreCommitHook(ctx: TemplateContext | EnrichedTemplateContext): string {
16
+ const enriched = getEnriched(ctx);
17
+ const tc = enriched.toolchain;
18
+ const config = ctx.config;
19
+ const projectName = ctx.projectName;
20
+
21
+ const lintCmd = tc?.lintCmd || 'npx eslint .';
22
+ const buildCmd = tc?.buildCmd || 'npm run build';
23
+
24
+ return `#!/bin/bash
25
+ # ============================================================
26
+ # PRE-COMMIT HOOK — ${projectName}
27
+ # Gerado por Architect v5.1
28
+ #
29
+ # Instalação:
30
+ # cp .agent/hooks/pre-commit.sh .git/hooks/pre-commit
31
+ # chmod +x .git/hooks/pre-commit
32
+ # ============================================================
33
+
34
+ set -e
35
+
36
+ echo "🔍 [pre-commit] Verificando qualidade do código..."
37
+
38
+ # ── 1. Branch Safety ──
39
+ BRANCH=$(git branch --show-current)
40
+ if [[ "$BRANCH" == "main" || "$BRANCH" == "master" || "$BRANCH" == "staging" || "$BRANCH" == "develop" ]]; then
41
+ echo "❌ BLOQUEADO: Commit direto em '$BRANCH' é proibido."
42
+ echo " Crie uma branch: git checkout -b feature/<nome>"
43
+ exit 1
44
+ fi
45
+
46
+ # ── 2. Secrets Check ──
47
+ echo "🔐 Verificando secrets..."
48
+ SECRETS_PATTERN='(password|secret|api[_-]?key|access[_-]?token|private[_-]?key)\\s*[:=]\\s*["\\'\\x60][^"\\x60]{8,}'
49
+
50
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
51
+ if echo "$STAGED_FILES" | xargs grep -ilE "$SECRETS_PATTERN" 2>/dev/null; then
52
+ echo "❌ BLOQUEADO: Possível secret detectado nos arquivos staged."
53
+ echo " Remova o secret e use variáveis de ambiente."
54
+ exit 1
55
+ fi
56
+
57
+ # ── 3. File Size Check ──
58
+ echo "📏 Verificando tamanho de arquivos..."
59
+ for file in $STAGED_FILES; do
60
+ if [ -f "$file" ]; then
61
+ LINES=$(wc -l < "$file" 2>/dev/null || echo "0")
62
+ if [ "$LINES" -gt 500 ]; then
63
+ echo "⚠️ AVISO: $file tem $LINES linhas (limite: 500)"
64
+ fi
65
+ fi
66
+ done
67
+
68
+ # ── 4. Lint ──
69
+ echo "🧹 Executando lint..."
70
+ ${lintCmd} --quiet 2>/dev/null || {
71
+ echo "❌ BLOQUEADO: Lint falhou."
72
+ echo " Corrija: ${lintCmd}"
73
+ exit 1
74
+ }
75
+
76
+ # ── 5. Build ──
77
+ echo "🔨 Verificando build..."
78
+ ${buildCmd} 2>/dev/null || {
79
+ echo "❌ BLOQUEADO: Build falhou."
80
+ echo " Corrija: ${buildCmd}"
81
+ exit 1
82
+ }
83
+
84
+ # ── 6. Debug Statements ──
85
+ echo "🐛 Verificando debug statements..."
86
+ if echo "$STAGED_FILES" | xargs grep -n 'console\\.log\\|debugger\\|print(' 2>/dev/null | grep -v 'node_modules' | grep -v '.test.'; then
87
+ echo "⚠️ AVISO: Debug statements encontrados. Remova antes do merge."
88
+ fi
89
+
90
+ echo "✅ [pre-commit] Tudo OK — commit liberado."
91
+ exit 0
92
+ `;
93
+ }
94
+
95
+ /**
96
+ * Pre-push hook: test + score gate
97
+ */
98
+ export function generatePrePushHook(ctx: TemplateContext | EnrichedTemplateContext): string {
99
+ const enriched = getEnriched(ctx);
100
+ const tc = enriched.toolchain;
101
+ const config = ctx.config;
102
+ const projectName = ctx.projectName;
103
+
104
+ const testCmd = tc?.testCmd || 'npm test';
105
+ const coverageCmd = tc?.coverageCmd || 'npm run test -- --coverage';
106
+
107
+ return `#!/bin/bash
108
+ # ============================================================
109
+ # PRE-PUSH HOOK — ${projectName}
110
+ # Gerado por Architect v5.1
111
+ #
112
+ # Instalação:
113
+ # cp .agent/hooks/pre-push.sh .git/hooks/pre-push
114
+ # chmod +x .git/hooks/pre-push
115
+ # ============================================================
116
+
117
+ set -e
118
+
119
+ echo "🚀 [pre-push] Verificação completa antes do push..."
120
+
121
+ # ── 1. Branch Safety ──
122
+ BRANCH=$(git branch --show-current)
123
+ if [[ "$BRANCH" == "main" || "$BRANCH" == "master" ]]; then
124
+ echo "❌ BLOQUEADO: Push direto para '$BRANCH' é proibido."
125
+ echo " Use Pull Request."
126
+ exit 1
127
+ fi
128
+
129
+ # ── 2. Full Test Suite ──
130
+ echo "🧪 Executando testes completos..."
131
+ ${testCmd} || {
132
+ echo "❌ BLOQUEADO: Testes falharam."
133
+ echo " Corrija antes de fazer push."
134
+ exit 1
135
+ }
136
+
137
+ # ── 3. Coverage Gate ──
138
+ echo "📊 Verificando cobertura (mínimo: ${config.coverageMinimum}%)..."
139
+ COVERAGE_OUTPUT=$(${coverageCmd} 2>&1 || true)
140
+ echo "$COVERAGE_OUTPUT" | tail -5
141
+
142
+ # Parse coverage percentage (works with Jest/Istanbul output)
143
+ COVERAGE=$(echo "$COVERAGE_OUTPUT" | grep -oP 'All files[^|]*\\|\\s*[\\d.]+' | grep -oP '[\\d.]+$' || echo "0")
144
+ if [ -n "$COVERAGE" ]; then
145
+ COVERAGE_INT=\${COVERAGE%.*}
146
+ if [ "$COVERAGE_INT" -lt ${config.coverageMinimum} ] 2>/dev/null; then
147
+ echo "⚠️ AVISO: Cobertura ($COVERAGE_INT%) abaixo do mínimo (${config.coverageMinimum}%)"
148
+ fi
149
+ fi
150
+
151
+ # ── 4. Architecture Score Gate ──
152
+ echo "🏗️ Verificando score de arquitetura (mínimo: ${config.scoreThreshold}/100)..."
153
+ if command -v architect &> /dev/null; then
154
+ SCORE=$(architect score . --json 2>/dev/null | grep -oP '"overall":\\s*\\K[0-9]+' || echo "N/A")
155
+ if [ "$SCORE" != "N/A" ] && [ "$SCORE" -lt ${config.scoreThreshold} ] 2>/dev/null; then
156
+ echo "❌ BLOQUEADO: Score ($SCORE/100) abaixo do mínimo (${config.scoreThreshold}/100)"
157
+ exit 1
158
+ fi
159
+ echo " Score: $SCORE/100 ✅"
160
+ else
161
+ echo " ⏭️ architect não instalado — skip score gate"
162
+ fi
163
+
164
+ echo "✅ [pre-push] Tudo OK — push liberado."
165
+ exit 0
166
+ `;
167
+ }
168
+
169
+ /**
170
+ * Post-analysis hook: remediation actions
171
+ */
172
+ export function generatePostAnalysisHook(ctx: TemplateContext | EnrichedTemplateContext): string {
173
+ const enriched = getEnriched(ctx);
174
+ const projectName = ctx.projectName;
175
+ const report = ctx.report;
176
+
177
+ return `#!/bin/bash
178
+ # ============================================================
179
+ # POST-ANALYSIS HOOK — ${projectName}
180
+ # Gerado por Architect v5.1
181
+ #
182
+ # Executa após 'architect analyze .' para gerar ações
183
+ # de remediação automáticas.
184
+ #
185
+ # Uso:
186
+ # architect analyze . && bash .agent/hooks/post-analysis.sh
187
+ # ============================================================
188
+
189
+ set -e
190
+
191
+ echo "🔧 [post-analysis] Processando resultados da análise..."
192
+
193
+ REPORT_JSON="architect-report.json"
194
+ REPORT_HTML="architect-report.html"
195
+
196
+ # ── 1. Score Evolution ──
197
+ CURRENT_SCORE=${report.score.overall}
198
+ echo "📊 Score atual: $CURRENT_SCORE/100"
199
+ echo " Meta curto prazo: ${Math.min(100, report.score.overall + 5)}/100"
200
+ echo " Meta médio prazo: ${Math.min(100, report.score.overall + 10)}/100"
201
+
202
+ # ── 2. Critical Anti-Patterns Alert ──
203
+ CRITICAL_COUNT=$(grep -c '"severity":"CRITICAL"' "$REPORT_JSON" 2>/dev/null || echo "0")
204
+ HIGH_COUNT=$(grep -c '"severity":"HIGH"' "$REPORT_JSON" 2>/dev/null || echo "0")
205
+
206
+ if [ "$CRITICAL_COUNT" -gt 0 ]; then
207
+ echo ""
208
+ echo "🔴 ATENÇÃO: $CRITICAL_COUNT anti-patterns CRÍTICOS detectados!"
209
+ echo " Resolva antes do próximo sprint."
210
+ fi
211
+
212
+ if [ "$HIGH_COUNT" -gt 0 ]; then
213
+ echo "🟠 $HIGH_COUNT anti-patterns HIGH detectados."
214
+ fi
215
+
216
+ # ── 3. Generate Summary ──
217
+ echo ""
218
+ echo "📋 Resumo:"
219
+ echo " Arquivos: ${report.projectInfo.totalFiles}"
220
+ echo " Linhas: ${report.projectInfo.totalLines.toLocaleString()}"
221
+ echo " Anti-patterns: ${report.antiPatterns.length}"
222
+ echo ""
223
+
224
+ # ── 4. Suggestions ──
225
+ if [ -f "$REPORT_JSON" ]; then
226
+ echo "💡 Top 5 sugestões:"
227
+ cat "$REPORT_JSON" | python3 -c "
228
+ import json, sys
229
+ data = json.load(sys.stdin)
230
+ for s in data.get('suggestions', [])[:5]:
231
+ print(f' → {s.get(\"description\", \"\")}')
232
+ " 2>/dev/null || echo " (instale python3 para ver sugestões)"
233
+ fi
234
+
235
+ echo ""
236
+ echo "📄 Report HTML: $REPORT_HTML"
237
+ echo "📦 Report JSON: $REPORT_JSON"
238
+ echo ""
239
+ echo "✅ [post-analysis] Concluído."
240
+ exit 0
241
+ `;
242
+ }