@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.
- package/dist/agent-generator/context-enricher.d.ts.map +1 -1
- package/dist/agent-generator/context-enricher.js +35 -3
- package/dist/agent-generator/context-enricher.js.map +1 -1
- package/dist/agent-generator/domain-inferrer.d.ts.map +1 -1
- package/dist/agent-generator/domain-inferrer.js +10 -0
- package/dist/agent-generator/domain-inferrer.js.map +1 -1
- package/dist/agent-generator/index.d.ts +14 -0
- package/dist/agent-generator/index.d.ts.map +1 -1
- package/dist/agent-generator/index.js +82 -14
- package/dist/agent-generator/index.js.map +1 -1
- package/dist/agent-generator/stack-detector.d.ts +3 -1
- package/dist/agent-generator/stack-detector.d.ts.map +1 -1
- package/dist/agent-generator/stack-detector.js +29 -33
- package/dist/agent-generator/stack-detector.js.map +1 -1
- package/dist/agent-generator/templates/core/agents.d.ts.map +1 -1
- package/dist/agent-generator/templates/core/agents.js +5 -1
- package/dist/agent-generator/templates/core/agents.js.map +1 -1
- package/dist/agent-generator/templates/core/hooks-generator.d.ts +21 -0
- package/dist/agent-generator/templates/core/hooks-generator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/hooks-generator.js +233 -0
- package/dist/agent-generator/templates/core/hooks-generator.js.map +1 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts +13 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts.map +1 -1
- package/dist/agent-generator/templates/core/skills-generator.js +339 -0
- package/dist/agent-generator/templates/core/skills-generator.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +11 -1
- package/dist/analyzer.js.map +1 -1
- package/dist/anti-patterns.d.ts +7 -0
- package/dist/anti-patterns.d.ts.map +1 -1
- package/dist/anti-patterns.js +25 -6
- package/dist/anti-patterns.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +48 -11
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/project-summarizer.d.ts +20 -0
- package/dist/project-summarizer.d.ts.map +1 -1
- package/dist/project-summarizer.js +171 -14
- package/dist/project-summarizer.js.map +1 -1
- package/dist/scanner.d.ts +8 -2
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +153 -113
- package/dist/scanner.js.map +1 -1
- package/dist/scorer.d.ts.map +1 -1
- package/dist/scorer.js +24 -11
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agent-generator/context-enricher.ts +32 -3
- package/src/agent-generator/domain-inferrer.ts +10 -0
- package/src/agent-generator/index.ts +94 -15
- package/src/agent-generator/stack-detector.ts +32 -20
- package/src/agent-generator/templates/core/agents.ts +5 -2
- package/src/agent-generator/templates/core/hooks-generator.ts +242 -0
- package/src/agent-generator/templates/core/skills-generator.ts +349 -0
- package/src/analyzer.ts +13 -1
- package/src/anti-patterns.ts +29 -6
- package/src/config.ts +52 -11
- package/src/index.ts +1 -1
- package/src/project-summarizer.ts +189 -15
- package/src/scanner.ts +136 -90
- package/src/scorer.ts +26 -11
- package/src/types.ts +17 -0
- package/tests/agent-generator.test.ts +16 -0
- package/tests/fixtures/monorepo/package.json +6 -0
- package/tests/fixtures/monorepo/packages/app/package.json +12 -0
- package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
- package/tests/fixtures/monorepo/packages/core/package.json +7 -0
- package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
- package/tests/monorepo-scan.test.ts +170 -0
- package/tests/scanner.test.ts +1 -1
- package/tests/scorer.test.ts +22 -16
- package/tests/stack-detector.test.ts +19 -19
- package/__test_agent_output__/INDEX.md +0 -1
- package/__test_agent_output__/agents/AGENT-ORCHESTRATOR.md +0 -1
- package/__test_agent_output__/agents/DATABASE-ENGINEER.md +0 -174
- package/__test_agent_output__/agents/QA-TEST-ENGINEER.md +0 -138
- package/__test_agent_output__/agents/SECURITY-AUDITOR.md +0 -106
- package/__test_agent_output__/agents/TECH-DEBT-CONTROLLER.md +0 -104
- package/__test_agent_output__/agents/TYPESCRIPT-BACKEND-DEVELOPER.md +0 -135
- package/__test_agent_output__/guards/CODE-REVIEW-CHECKLIST.md +0 -95
- package/__test_agent_output__/guards/PREFLIGHT.md +0 -200
- package/__test_agent_output__/guards/QUALITY-GATES.md +0 -1
- package/__test_agent_output__/rules/00-general.md +0 -229
- package/__test_agent_output__/rules/01-architecture.md +0 -191
- package/__test_agent_output__/rules/02-security.md +0 -402
- package/__test_agent_output__/rules/03-nestjs.md +0 -124
- package/__test_agent_output__/templates/ADR.md +0 -95
- package/__test_agent_output__/templates/BDD.md +0 -58
- package/__test_agent_output__/templates/C4.md +0 -68
- package/__test_agent_output__/templates/TDD.md +0 -86
- package/__test_agent_output__/templates/THREAT-MODEL.md +0 -82
- package/__test_agent_output__/workflows/fix-bug.md +0 -228
- package/__test_agent_output__/workflows/new-feature.md +0 -311
- package/__test_agent_output__/workflows/review.md +0 -95
- package/__test_context_7RvUrO/src/modules/empty/empty.ts +0 -0
- package/__test_context_Rf5fNJ/src/modules/mixed/mixed.ts +0 -5
- package/__test_context_WRCnYH/src/modules/test/test.ts +0 -10
- package/__test_context_YsnVS3/src/modules/test/test.ts +0 -10
- package/__test_context_w7XZeH/src/modules/mixed/mixed.ts +0 -5
- package/__test_context_y5noh6/src/modules/empty/empty.ts +0 -0
- package/__test_framework__24OjAu/package.json +0 -1
- package/__test_framework__3ZDZsx/pyproject.toml +0 -8
- package/__test_framework__4T54Jn/package.json +0 -1
- package/__test_framework__4tlXu9/pyproject.toml +0 -8
- package/__test_framework__6boWqQ/Pipfile +0 -6
- package/__test_framework__6gygMU/pom.xml +0 -10
- package/__test_framework__6kxj0N/go.mod +0 -8
- package/__test_framework__7CEoXw/pom.xml +0 -10
- package/__test_framework__85DDz0/Pipfile +0 -6
- package/__test_framework__9WrRIr/pom.xml +0 -7
- package/__test_framework__ANqGKl/Gemfile +0 -5
- package/__test_framework__BCXTEM/go.mod +0 -3
- package/__test_framework__BHiPNq/setup.py +0 -2
- package/__test_framework__BqkiKv/package.json +0 -1
- package/__test_framework__C5yd8X/Pipfile.lock +0 -1
- package/__test_framework__C5yd8X/requirements.txt +0 -1
- package/__test_framework__C87d3a/manage.py +0 -1
- package/__test_framework__C87d3a/requirements.txt +0 -2
- package/__test_framework__DXNwc5/build.gradle +0 -7
- package/__test_framework__GhHSt3/build.gradle.kts +0 -4
- package/__test_framework__GzklJP/Cargo.toml +0 -7
- package/__test_framework__H4hd13/go.mod +0 -8
- package/__test_framework__HKjOXO/composer.json +0 -1
- package/__test_framework__HaDN45/Gemfile +0 -3
- package/__test_framework__IBO7YG/pyproject.toml +0 -9
- package/__test_framework__JwSOyF/pyproject.toml +0 -6
- package/__test_framework__K6HrCr/build.gradle +0 -2
- package/__test_framework__KzRPlh/pubspec.yaml +0 -9
- package/__test_framework__L6uIym/pyproject.toml +0 -6
- package/__test_framework__LOdoGK/requirements.txt +0 -4
- package/__test_framework__LgHzss/package.json +0 -1
- package/__test_framework__M76M6q/Gemfile +0 -5
- package/__test_framework__Mr9vWW/composer.json +0 -1
- package/__test_framework__N03Gnv/package.json +0 -1
- package/__test_framework__Num4UE/requirements +0 -1
- package/__test_framework__OAGw3Y/build.gradle +0 -7
- package/__test_framework__OQc8yG/pubspec.yaml +0 -9
- package/__test_framework__OwKZcd/requirements.txt +0 -3
- package/__test_framework__P0gFv7/requirements +0 -1
- package/__test_framework__PN55Rq/package.json +0 -1
- package/__test_framework__PQiqX8/pubspec.yaml +0 -3
- package/__test_framework__RBHsg7/composer.json +0 -1
- package/__test_framework__RHxif4/Cargo.toml +0 -7
- package/__test_framework__T0v0p1/Cargo.toml +0 -4
- package/__test_framework__Tu0clt/Pipfile.lock +0 -1
- package/__test_framework__Tu0clt/requirements.txt +0 -1
- package/__test_framework__TwDj9P/Cargo.toml +0 -4
- package/__test_framework__VQJNC4/pom.xml +0 -7
- package/__test_framework__W6sm05/package.json +0 -1
- package/__test_framework__W7vBLy/pyproject.toml +0 -4
- package/__test_framework__WNJOWT/setup.py +0 -2
- package/__test_framework__WSJs7U/package.json +0 -1
- package/__test_framework__YQ5VpA/build.gradle.kts +0 -4
- package/__test_framework__ZNEUEs/package.json +0 -1
- package/__test_framework__Znt922/pom.xml +0 -7
- package/__test_framework__azyg0h/pom.xml +0 -7
- package/__test_framework__c6otLr/package.json +0 -1
- package/__test_framework__cl9S9G/build.gradle +0 -2
- package/__test_framework__eilvV4/composer.json +0 -1
- package/__test_framework__gQZxXO/manage.py +0 -1
- package/__test_framework__gQZxXO/requirements.txt +0 -2
- package/__test_framework__ghvl26/poetry.lock +0 -1
- package/__test_framework__ghvl26/pyproject.toml +0 -2
- package/__test_framework__hR7b9U/Makefile +0 -11
- package/__test_framework__iESVsi/composer.json +0 -1
- package/__test_framework__jm6TJy/package.json +0 -1
- package/__test_framework__kBUpjs/pyproject.toml +0 -9
- package/__test_framework__kqoZrw/requirements.txt +0 -4
- package/__test_framework__lWkoyO/pyproject.toml +0 -4
- package/__test_framework__mTKnUO/package.json +0 -1
- package/__test_framework__nCeZwe/Makefile +0 -11
- package/__test_framework__oljsU0/package.json +0 -1
- package/__test_framework__osRG4q/go.mod +0 -3
- package/__test_framework__pCHH4F/package.json +0 -1
- package/__test_framework__pExx6E/Gemfile +0 -3
- package/__test_framework__pyBoGd/pyproject.toml +0 -5
- package/__test_framework__qw16VQ/package.json +0 -1
- package/__test_framework__rRayrG/package.json +0 -1
- package/__test_framework__s82zO5/package.json +0 -1
- package/__test_framework__tp8MFK/pyproject.toml +0 -5
- package/__test_framework__w44k4w/composer.json +0 -1
- package/__test_framework__yefPZY/poetry.lock +0 -1
- package/__test_framework__yefPZY/pyproject.toml +0 -2
- package/__test_framework__zCiyDT/requirements.txt +0 -3
- package/__test_framework__zGZN3j/pubspec.yaml +0 -3
- 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
|
|
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(
|
|
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
|
|
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
|
|
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,
|
|
234
|
-
const generated = this.generateMissing(agentDir, audit,
|
|
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,
|
|
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
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
// ──
|
|
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
|
-
*
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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.
|
|
56
|
-
|
|
65
|
+
const hasFrontend = [...frameworks].some(f => FRONTEND_FRAMEWORKS.has(f)) ||
|
|
66
|
+
extensions.has('tsx') || extensions.has('jsx');
|
|
57
67
|
|
|
58
|
-
const hasMobile =
|
|
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
|
-
|
|
152
|
-
|
|
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
|
+
}
|