Skip to content

Commit b61c8b1

Browse files
authored
chore: improve agent-cli by generating amazonq (#5327)
1 parent 113b667 commit b61c8b1

File tree

12 files changed

+219
-111
lines changed

12 files changed

+219
-111
lines changed

.changeset/sixty-cases-bake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@db-ux/agent-cli": patch
3+
---
4+
5+
chore: generate AmazonQ rule file with @db-ux/agent-cli

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@ showcases/patternhub/public/iframe-resizer/*
7373
/output/**/docs/
7474
/output/**/agent/
7575
/packages/agent-cli/test/.github/copilot-instructions.md
76+
/packages/agent-cli/test/.amazonq/rules/db-ux.md
7677
/core-web.iml
7778
/build-storybooks/

packages/agent-cli/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"copy-build:package.json": "cpr package.json ../../build-outputs/agent-cli/package.json --overwrite",
2525
"copy-build:readme": "cpr README.md ../../build-outputs/agent-cli/README.md --overwrite",
2626
"test": "vitest run --config vitest.config.ts",
27-
"test:cli": "tsx src/cli.ts --help"
27+
"test:cli": "tsx src/cli.ts --help",
28+
"test:update": "vitest run --config vitest.config.ts --update"
2829
},
2930
"dependencies": {
3031
"commander": "14.0.1"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { getInstructions } from '../utils';
4+
5+
export const generateAmazonQ = (rootPath: string) => {
6+
const outputFolder = path.resolve(rootPath, '.amazonq', 'rules');
7+
8+
if (!fs.existsSync(outputFolder)) {
9+
fs.mkdirSync(outputFolder, { recursive: true });
10+
}
11+
12+
const amazonqInstructionsPath = path.join(outputFolder, 'db-ux.md');
13+
if (!fs.existsSync(amazonqInstructionsPath)) {
14+
fs.writeFileSync(amazonqInstructionsPath, '');
15+
}
16+
17+
const amazonqInstructionsContent = getInstructions(rootPath);
18+
19+
if (amazonqInstructionsContent) {
20+
let amazonqFileContent = fs.readFileSync(
21+
amazonqInstructionsPath,
22+
'utf8'
23+
);
24+
const startMarker =
25+
'--- START: DB UX Amazon Q Instructions – do not edit below ---';
26+
const endMarker =
27+
'--- END: DB UX Amazon Q Instructions – do not edit above ---';
28+
const startIndex = amazonqFileContent.indexOf(startMarker);
29+
const endIndex = amazonqFileContent.indexOf(endMarker);
30+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
31+
amazonqFileContent = (
32+
amazonqFileContent.slice(0, startIndex) +
33+
amazonqFileContent.slice(endIndex + endMarker.length)
34+
).trim();
35+
}
36+
37+
amazonqFileContent += `
38+
${startMarker}
39+
40+
> **NOTE**: Always use "DB UX" components and variables as project's conventions
41+
${amazonqInstructionsContent}
42+
43+
${endMarker}
44+
`;
45+
46+
fs.writeFileSync(amazonqInstructionsPath, amazonqFileContent);
47+
}
48+
};

packages/agent-cli/src/cli.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
#!/usr/bin/env node
22

33
import { program } from 'commander';
4-
import { generateCopilot } from './copilot';
5-
6-
const action = async (rootPath: string = '.') => {
7-
generateCopilot(rootPath);
8-
};
4+
import { action } from './index';
95

106
const startProgram = (
117
name: string,

packages/agent-cli/src/copilot/index.ts

Lines changed: 11 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,12 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
3-
4-
function findAllNodeModulesDirectories(
5-
directory: string,
6-
found: string[] = []
7-
): string[] {
8-
if (!fs.existsSync(directory)) {
9-
return found;
10-
}
11-
12-
const entries = fs
13-
.readdirSync(directory, { withFileTypes: true })
14-
.sort((a, b) => a.name.localeCompare(b.name, 'en'));
15-
for (const entry of entries) {
16-
if (entry.isDirectory()) {
17-
if (entry.name === 'node_modules') {
18-
found.push(path.join(directory, entry.name));
19-
} else if (!entry.name.startsWith('.')) {
20-
findAllNodeModulesDirectories(
21-
path.join(directory, entry.name),
22-
found
23-
);
24-
}
25-
}
26-
}
27-
28-
return found;
29-
}
3+
import { getInstructions } from '../utils';
304

315
export const generateCopilot = (rootPath: string) => {
326
const outputFolder = path.resolve(rootPath, '.github');
337

34-
const nodeModulesDirectories = findAllNodeModulesDirectories(rootPath);
35-
if (nodeModulesDirectories.length === 0) {
36-
console.error('No node_modules folders found.');
37-
return;
38-
}
39-
40-
let copilotInstructionsContent = '';
41-
42-
for (const nodeModulesPath of nodeModulesDirectories) {
43-
const databaseUxPaths = [
44-
path.join(nodeModulesPath, '@db-ux/'),
45-
path.join(nodeModulesPath, '@db-ux-inner-source/')
46-
];
47-
48-
for (const databaseUxPath of databaseUxPaths) {
49-
if (!fs.existsSync(databaseUxPath)) {
50-
continue;
51-
}
52-
53-
const packages = fs.readdirSync(databaseUxPath, {
54-
withFileTypes: true
55-
});
56-
for (const package_ of packages) {
57-
if (package_.isDirectory()) {
58-
const instructionsPath = path.join(
59-
databaseUxPath,
60-
package_.name,
61-
'agent',
62-
'_instructions.md'
63-
);
64-
if (fs.existsSync(instructionsPath)) {
65-
let content = fs.readFileSync(instructionsPath, 'utf8');
66-
const relativePath = path.relative(
67-
rootPath,
68-
path.join(databaseUxPath, package_.name)
69-
);
70-
content = content
71-
.replaceAll(
72-
'__agent-path__',
73-
relativePath.replaceAll('\\', '/')
74-
)
75-
.replaceAll(
76-
'**agent-path**',
77-
relativePath.replaceAll('\\', '/')
78-
);
79-
copilotInstructionsContent += `\n# ${path.basename(databaseUxPath)}/${package_.name}\n${content}\n`;
80-
}
81-
}
82-
}
83-
}
84-
}
85-
868
if (!fs.existsSync(outputFolder)) {
87-
fs.mkdirSync(outputFolder);
9+
fs.mkdirSync(outputFolder, { recursive: true });
8810
}
8911

9012
const copilotInstructionsPath = path.join(
@@ -95,7 +17,9 @@ export const generateCopilot = (rootPath: string) => {
9517
fs.writeFileSync(copilotInstructionsPath, '');
9618
}
9719

98-
if (copilotInstructionsContent.trim()) {
20+
const copilotInstructionsContent = getInstructions(rootPath);
21+
22+
if (copilotInstructionsContent) {
9923
let copilotFileContent = fs.readFileSync(
10024
copilotInstructionsPath,
10125
'utf8'
@@ -114,10 +38,12 @@ export const generateCopilot = (rootPath: string) => {
11438
}
11539

11640
copilotFileContent += `
117-
${startMarker}
118-
> **NOTE**: Always use "DB UX" components and variables as project's conventions
119-
${copilotInstructionsContent}
120-
${endMarker}
41+
${startMarker}
42+
43+
> **NOTE**: Always use "DB UX" components and variables as project's conventions
44+
${copilotInstructionsContent}
45+
46+
${endMarker}
12147
`;
12248

12349
fs.writeFileSync(copilotInstructionsPath, copilotFileContent);

packages/agent-cli/src/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { generateAmazonQ } from './amazonq';
4+
import { generateCopilot } from './copilot';
5+
6+
export const action = async (rootPath: string = '.') => {
7+
const hasCopilot = fs.existsSync(
8+
path.join(rootPath, '.github', 'copilot-instructions.md')
9+
);
10+
const hasAmazonQ = fs.existsSync(path.join(rootPath, '.amazonq', 'rules'));
11+
12+
if (!hasCopilot && !hasAmazonQ) {
13+
generateCopilot(rootPath);
14+
generateAmazonQ(rootPath);
15+
} else if (hasCopilot && hasAmazonQ) {
16+
generateCopilot(rootPath);
17+
generateAmazonQ(rootPath);
18+
} else if (hasCopilot) {
19+
generateCopilot(rootPath);
20+
} else {
21+
generateAmazonQ(rootPath);
22+
}
23+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
4+
function findAllNodeModulesDirectories(
5+
directory: string,
6+
found: string[] = []
7+
): string[] {
8+
if (!fs.existsSync(directory)) {
9+
return found;
10+
}
11+
12+
const entries = fs
13+
.readdirSync(directory, { withFileTypes: true })
14+
.sort((a, b) => a.name.localeCompare(b.name, 'en'));
15+
for (const entry of entries) {
16+
if (entry.isDirectory()) {
17+
if (entry.name === 'node_modules') {
18+
found.push(path.join(directory, entry.name));
19+
} else if (!entry.name.startsWith('.')) {
20+
findAllNodeModulesDirectories(
21+
path.join(directory, entry.name),
22+
found
23+
);
24+
}
25+
}
26+
}
27+
28+
return found;
29+
}
30+
31+
export const getInstructions = (rootPath: string): string => {
32+
let copilotInstructionsContent = '';
33+
const nodeModulesDirectories = findAllNodeModulesDirectories(rootPath);
34+
if (nodeModulesDirectories.length === 0) {
35+
console.error('No node_modules folders found.');
36+
return '';
37+
}
38+
39+
for (const nodeModulesPath of nodeModulesDirectories) {
40+
const databaseUxPaths = [
41+
path.join(nodeModulesPath, '@db-ux/'),
42+
path.join(nodeModulesPath, '@db-ux-inner-source/')
43+
];
44+
45+
for (const databaseUxPath of databaseUxPaths) {
46+
if (!fs.existsSync(databaseUxPath)) {
47+
continue;
48+
}
49+
50+
const packages = fs.readdirSync(databaseUxPath, {
51+
withFileTypes: true
52+
});
53+
for (const package_ of packages) {
54+
if (package_.isDirectory()) {
55+
const instructionsPath = path.join(
56+
databaseUxPath,
57+
package_.name,
58+
'agent',
59+
'_instructions.md'
60+
);
61+
if (fs.existsSync(instructionsPath)) {
62+
let content = fs.readFileSync(instructionsPath, 'utf8');
63+
const relativePath = path.relative(
64+
rootPath,
65+
path.join(databaseUxPath, package_.name)
66+
);
67+
content = content
68+
.replaceAll(
69+
'__agent-path__',
70+
relativePath.replaceAll('\\', '/')
71+
)
72+
.replaceAll(
73+
'**agent-path**',
74+
relativePath.replaceAll('\\', '/')
75+
);
76+
copilotInstructionsContent += `\n# ${path.basename(databaseUxPath)}/${package_.name}\n${content}\n`;
77+
}
78+
}
79+
}
80+
}
81+
}
82+
83+
copilotInstructionsContent = copilotInstructionsContent.trim();
84+
85+
return copilotInstructionsContent;
86+
};

packages/agent-cli/test/__snapshots__/index.spec.ts.snap

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,32 @@
22

33
exports[`default > check if docs are created 1`] = `
44
"
5-
--- START: DB UX Copilot Instructions – do not edit below ---
6-
> **NOTE**: Always use "DB UX" components and variables as project's conventions
7-
5+
--- START: DB UX Copilot Instructions – do not edit below ---
6+
7+
> **NOTE**: Always use "DB UX" components and variables as project's conventions
88
# @db-ux/components
99
- use y for frontend/node_modules/@db-ux/components/test.md
1010
1111
1212
# @db-ux/foundations
1313
- use x for frontend/node_modules/@db-ux/foundations/test.md
1414
15-
16-
--- END: DB UX Copilot Instructions – do not edit above ---
15+
--- END: DB UX Copilot Instructions – do not edit above ---
1716
"
1817
`;
18+
19+
exports[`default > check if docs are created 2`] = `
20+
"
21+
--- START: DB UX Amazon Q Instructions – do not edit below ---
22+
23+
> **NOTE**: Always use "DB UX" components and variables as project's conventions
24+
# @db-ux/components
25+
- use y for frontend/node_modules/@db-ux/components/test.md
26+
27+
28+
# @db-ux/foundations
29+
- use x for frontend/node_modules/@db-ux/foundations/test.md
30+
31+
--- END: DB UX Amazon Q Instructions – do not edit above ---
32+
"
33+
`;
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
33
import { describe, expect, test } from 'vitest';
4-
import { generateCopilot } from '../src/copilot';
4+
import { action } from '../src';
55

66
describe('default', () => {
77
test('check if docs are created', async () => {
88
const copilotFile = path.resolve(
99
'test/.github/copilot-instructions.md'
1010
);
11-
generateCopilot('test');
11+
const amazonqFile = path.resolve('test/.amazonq/rules/db-ux.md');
12+
13+
await action('test');
1214

1315
expect(fs.readFileSync(copilotFile).toString()).toMatchSnapshot();
16+
expect(fs.readFileSync(amazonqFile).toString()).toMatchSnapshot();
1417
});
1518
});

0 commit comments

Comments
 (0)