@agility/create-next-app 1.0.0-beta.2
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/.claude/settings.json +7 -0
- package/.claude/settings.local.json +24 -0
- package/FEATURE_ROADMAP.md +343 -0
- package/README.md +205 -0
- package/TESTING.md +131 -0
- package/bin/create-agility-app.js +48 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/generateApiKeys.js +99 -0
- package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
- package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/getApiKeys.js +14 -0
- package/dist/agility/api-keys/getApiKeys.js.map +1 -0
- package/dist/agility/index.d.ts +3 -0
- package/dist/agility/index.d.ts.map +1 -0
- package/dist/agility/index.js +8 -0
- package/dist/agility/index.js.map +1 -0
- package/dist/agility/instance/createNewInstance.d.ts +8 -0
- package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
- package/dist/agility/instance/createNewInstance.js +65 -0
- package/dist/agility/instance/createNewInstance.js.map +1 -0
- package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
- package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
- package/dist/agility/instance/getAvailableInstances.js +43 -0
- package/dist/agility/instance/getAvailableInstances.js.map +1 -0
- package/dist/agility/instance/manageInstance.d.ts +9 -0
- package/dist/agility/instance/manageInstance.d.ts.map +1 -0
- package/dist/agility/instance/manageInstance.js +82 -0
- package/dist/agility/instance/manageInstance.js.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
- package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
- package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth/authenticate.d.ts +6 -0
- package/dist/auth/oauth/authenticate.d.ts.map +1 -0
- package/dist/auth/oauth/authenticate.js +162 -0
- package/dist/auth/oauth/authenticate.js.map +1 -0
- package/dist/auth/oauth/constants.d.ts +5 -0
- package/dist/auth/oauth/constants.d.ts.map +1 -0
- package/dist/auth/oauth/constants.js +9 -0
- package/dist/auth/oauth/constants.js.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
- package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +290 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/promptForMissingOptions.d.ts +8 -0
- package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
- package/dist/cli/promptForMissingOptions.js +92 -0
- package/dist/cli/promptForMissingOptions.js.map +1 -0
- package/dist/config/env/createEnvFile.d.ts +6 -0
- package/dist/config/env/createEnvFile.d.ts.map +1 -0
- package/dist/config/env/createEnvFile.js +31 -0
- package/dist/config/env/createEnvFile.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp/createMcpConfig.d.ts +5 -0
- package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
- package/dist/config/mcp/createMcpConfig.js +32 -0
- package/dist/config/mcp/createMcpConfig.js.map +1 -0
- package/dist/config/packages/installAgilityPackages.d.ts +6 -0
- package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
- package/dist/config/packages/installAgilityPackages.js +61 -0
- package/dist/config/packages/installAgilityPackages.js.map +1 -0
- package/dist/config/setupProject.d.ts +8 -0
- package/dist/config/setupProject.d.ts.map +1 -0
- package/dist/config/setupProject.js +32 -0
- package/dist/config/setupProject.js.map +1 -0
- package/dist/create-next-app/createNextApp.d.ts +9 -0
- package/dist/create-next-app/createNextApp.d.ts.map +1 -0
- package/dist/create-next-app/createNextApp.js +83 -0
- package/dist/create-next-app/createNextApp.js.map +1 -0
- package/dist/create-next-app/index.d.ts +3 -0
- package/dist/create-next-app/index.d.ts.map +1 -0
- package/dist/create-next-app/index.js +8 -0
- package/dist/create-next-app/index.js.map +1 -0
- package/dist/scaffold/components/createPageComponents.d.ts +6 -0
- package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
- package/dist/scaffold/components/createPageComponents.js +62 -0
- package/dist/scaffold/components/createPageComponents.js.map +1 -0
- package/dist/scaffold/containers/createContainers.d.ts +6 -0
- package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
- package/dist/scaffold/containers/createContainers.js +48 -0
- package/dist/scaffold/containers/createContainers.js.map +1 -0
- package/dist/scaffold/index.d.ts +2 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +6 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.js +51 -0
- package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
- package/dist/scaffold/models/createContentModels.d.ts +6 -0
- package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
- package/dist/scaffold/models/createContentModels.js +70 -0
- package/dist/scaffold/models/createContentModels.js.map +1 -0
- package/dist/templates/copyDirectory.d.ts +5 -0
- package/dist/templates/copyDirectory.d.ts.map +1 -0
- package/dist/templates/copyDirectory.js +28 -0
- package/dist/templates/copyDirectory.js.map +1 -0
- package/dist/templates/copyTemplates.d.ts +8 -0
- package/dist/templates/copyTemplates.d.ts.map +1 -0
- package/dist/templates/copyTemplates.js +58 -0
- package/dist/templates/copyTemplates.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +6 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +71 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/validation.d.ts +45 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +180 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +45 -0
- package/src/agility/api-keys/generateApiKeys.ts +100 -0
- package/src/agility/api-keys/getApiKeys.ts +13 -0
- package/src/agility/index.ts +3 -0
- package/src/agility/instance/createNewInstance.ts +67 -0
- package/src/agility/instance/getAvailableInstances.ts +49 -0
- package/src/agility/instance/manageInstance.ts +90 -0
- package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
- package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/oauth/authenticate.ts +165 -0
- package/src/auth/oauth/constants.ts +6 -0
- package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
- package/src/cli/index.ts +281 -0
- package/src/cli/promptForMissingOptions.ts +104 -0
- package/src/config/env/createEnvFile.ts +30 -0
- package/src/config/index.ts +2 -0
- package/src/config/mcp/createMcpConfig.ts +30 -0
- package/src/config/packages/installAgilityPackages.ts +63 -0
- package/src/config/setupProject.ts +31 -0
- package/src/create-next-app/createNextApp.ts +75 -0
- package/src/create-next-app/index.ts +3 -0
- package/src/scaffold/components/createPageComponents.ts +74 -0
- package/src/scaffold/containers/createContainers.ts +55 -0
- package/src/scaffold/index.ts +2 -0
- package/src/scaffold/instance/createBlankInstance.ts +55 -0
- package/src/scaffold/models/createContentModels.ts +83 -0
- package/src/templates/copyDirectory.ts +24 -0
- package/src/templates/copyTemplates.ts +57 -0
- package/src/templates/index.ts +2 -0
- package/src/types/index.ts +55 -0
- package/src/utils/git.ts +74 -0
- package/src/utils/validation.ts +184 -0
- package/templates/.claude/QUICK-START.md +230 -0
- package/templates/.claude/README.md +32 -0
- package/templates/.claude/settings.json +8 -0
- package/templates/BLANK-INSTANCE-SETUP.md +375 -0
- package/templates/DEVELOPMENT.md +160 -0
- package/templates/EXAMPLE-PROMPTS.md +643 -0
- package/templates/PROMPTS.md +410 -0
- package/templates/README.md +281 -0
- package/templates/agents.md +429 -0
- package/templates/app/[locale]/[...slug]/error.tsx +17 -0
- package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
- package/templates/app/[locale]/[...slug]/page.tsx +102 -0
- package/templates/app/[locale]/layout.tsx +22 -0
- package/templates/app/[locale]/page.tsx +12 -0
- package/templates/app/api/dynamic-redirect/route.ts +24 -0
- package/templates/app/api/preview/exit/route.ts +34 -0
- package/templates/app/api/preview/route.ts +63 -0
- package/templates/app/api/revalidate/route.ts +118 -0
- package/templates/components/agility-components/RichTextArea.tsx +66 -0
- package/templates/components/agility-components/index.ts +30 -0
- package/templates/components/agility-pages/MainTemplate.tsx +36 -0
- package/templates/components/agility-pages/index.ts +11 -0
- package/templates/docs/01-agility-cms-overview.md +139 -0
- package/templates/docs/02-page-routing.md +251 -0
- package/templates/docs/03-creating-components.md +462 -0
- package/templates/docs/04-data-fetching.md +484 -0
- package/templates/docs/05-containers-and-lists.md +596 -0
- package/templates/docs/06-localization.md +561 -0
- package/templates/docs/07-caching-strategies.md +410 -0
- package/templates/docs/08-common-components.md +756 -0
- package/templates/docs/09-whats-included.md +279 -0
- package/templates/docs/10-mcp-server-setup.md +153 -0
- package/templates/docs/11-linked-nested-content.md +611 -0
- package/templates/docs/README.md +164 -0
- package/templates/lib/cms/getAgilityContext.ts +28 -0
- package/templates/lib/cms/getAgilityPage.ts +51 -0
- package/templates/lib/cms/getAgilitySDK.ts +22 -0
- package/templates/lib/cms/getContentItem.ts +20 -0
- package/templates/lib/cms/getContentList.ts +19 -0
- package/templates/lib/cms/getRedirections.ts +85 -0
- package/templates/lib/cms/getSitemapFlat.ts +19 -0
- package/templates/lib/cms/getSitemapNested.ts +19 -0
- package/templates/lib/env.ts +99 -0
- package/templates/lib/i18n/config.ts +28 -0
- package/templates/proxy.ts +101 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import open from 'open';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import { AGILITY_OAUTH_AUTHORIZE_URL, REDIRECT_URI, PORT } from './constants';
|
|
7
|
+
import { exchangeCodeForToken } from './exchangeCodeForToken';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Authenticates user with Agility CMS using OAuth 2.0
|
|
11
|
+
* @returns Access token
|
|
12
|
+
*/
|
|
13
|
+
export async function authenticate(): Promise<string> {
|
|
14
|
+
return new Promise(async (resolve, reject) => {
|
|
15
|
+
// Build OAuth URL - Agility CMS OAuth doesn't require client_id
|
|
16
|
+
const authUrl = new URL(AGILITY_OAUTH_AUTHORIZE_URL);
|
|
17
|
+
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
18
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
19
|
+
authUrl.searchParams.set('scope', 'openid profile email offline_access');
|
|
20
|
+
|
|
21
|
+
// Ask user for confirmation before opening browser
|
|
22
|
+
try {
|
|
23
|
+
const answer = await inquirer.prompt<{ proceed: boolean }>([
|
|
24
|
+
{
|
|
25
|
+
type: 'confirm',
|
|
26
|
+
name: 'proceed',
|
|
27
|
+
message: 'We need to open a browser window for authentication. Proceed?',
|
|
28
|
+
default: true
|
|
29
|
+
}
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
if (!answer.proceed) {
|
|
33
|
+
reject(new Error('Authentication cancelled by user'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
reject(new Error('Failed to get user confirmation'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const spinner = ora('Opening browser for authentication...').start();
|
|
42
|
+
|
|
43
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
44
|
+
let isResolved = false;
|
|
45
|
+
|
|
46
|
+
const cleanup = () => {
|
|
47
|
+
if (timeoutId) {
|
|
48
|
+
clearTimeout(timeoutId);
|
|
49
|
+
timeoutId = null;
|
|
50
|
+
}
|
|
51
|
+
server.closeAllConnections();
|
|
52
|
+
server.close(() => {
|
|
53
|
+
// Server closed, allow process to exit
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Start local server to receive callback
|
|
58
|
+
const server = http.createServer((req, res) => {
|
|
59
|
+
if (req.url?.startsWith('/callback')) {
|
|
60
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
61
|
+
const code = url.searchParams.get('code');
|
|
62
|
+
const error = url.searchParams.get('error');
|
|
63
|
+
|
|
64
|
+
if (error) {
|
|
65
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
66
|
+
res.end(`
|
|
67
|
+
<html>
|
|
68
|
+
<body>
|
|
69
|
+
<h1>Authentication Failed</h1>
|
|
70
|
+
<p>${error}</p>
|
|
71
|
+
<p>You can close this window.</p>
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
74
|
+
`);
|
|
75
|
+
if (!isResolved) {
|
|
76
|
+
isResolved = true;
|
|
77
|
+
cleanup();
|
|
78
|
+
spinner.fail('Authentication failed');
|
|
79
|
+
reject(new Error(`OAuth error: ${error}`));
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (code) {
|
|
85
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
86
|
+
res.end(`
|
|
87
|
+
<html>
|
|
88
|
+
<body>
|
|
89
|
+
<h1>Authentication Successful!</h1>
|
|
90
|
+
<p>You can close this window and return to the terminal.</p>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
`);
|
|
94
|
+
if (!isResolved) {
|
|
95
|
+
isResolved = true;
|
|
96
|
+
cleanup();
|
|
97
|
+
|
|
98
|
+
// Exchange code for token
|
|
99
|
+
spinner.text = 'Exchanging authorization code for token...';
|
|
100
|
+
exchangeCodeForToken(code)
|
|
101
|
+
.then((token) => {
|
|
102
|
+
spinner.succeed('Authentication successful');
|
|
103
|
+
resolve(token);
|
|
104
|
+
})
|
|
105
|
+
.catch((err) => {
|
|
106
|
+
spinner.fail('Failed to exchange code for token');
|
|
107
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
108
|
+
console.error(chalk.red('\n❌ Token exchange error:'));
|
|
109
|
+
console.error(chalk.red(` ${errorMessage}`));
|
|
110
|
+
console.error(chalk.gray(' You can skip authentication and configure Agility CMS manually.\n'));
|
|
111
|
+
reject(err);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
116
|
+
res.end(`
|
|
117
|
+
<html>
|
|
118
|
+
<body>
|
|
119
|
+
<h1>Authentication Failed</h1>
|
|
120
|
+
<p>No authorization code received.</p>
|
|
121
|
+
<p>You can close this window.</p>
|
|
122
|
+
</body>
|
|
123
|
+
</html>
|
|
124
|
+
`);
|
|
125
|
+
if (!isResolved) {
|
|
126
|
+
isResolved = true;
|
|
127
|
+
cleanup();
|
|
128
|
+
spinner.fail('Authentication failed');
|
|
129
|
+
reject(new Error('No authorization code received'));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
server.listen(PORT, async () => {
|
|
136
|
+
try {
|
|
137
|
+
// Open browser
|
|
138
|
+
await open(authUrl.toString());
|
|
139
|
+
spinner.text = 'Waiting for authentication in browser...';
|
|
140
|
+
console.log(chalk.cyan('\nA browser window should open for authentication.'));
|
|
141
|
+
console.log(chalk.gray('If it doesn\'t, please open this URL manually:'));
|
|
142
|
+
console.log(chalk.blue(authUrl.toString()) + '\n');
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (!isResolved) {
|
|
145
|
+
isResolved = true;
|
|
146
|
+
cleanup();
|
|
147
|
+
spinner.fail('Failed to open browser');
|
|
148
|
+
console.log(chalk.yellow(`\nPlease open this URL in your browser: ${authUrl.toString()}\n`));
|
|
149
|
+
reject(err);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Timeout after 5 minutes
|
|
155
|
+
timeoutId = setTimeout(() => {
|
|
156
|
+
if (!isResolved) {
|
|
157
|
+
isResolved = true;
|
|
158
|
+
cleanup();
|
|
159
|
+
spinner.fail('Authentication timeout');
|
|
160
|
+
reject(new Error('Authentication timeout - please try again'));
|
|
161
|
+
}
|
|
162
|
+
}, 5 * 60 * 1000);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// OAuth configuration for Agility CMS
|
|
2
|
+
export const AGILITY_OAUTH_AUTHORIZE_URL = 'https://mgmt.aglty.io/oauth/authorize';
|
|
3
|
+
export const AGILITY_OAUTH_TOKEN_URL = 'https://mgmt.aglty.io/oauth/token';
|
|
4
|
+
export const REDIRECT_URI = 'http://localhost:3001/callback';
|
|
5
|
+
export const PORT = 3001;
|
|
6
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AGILITY_OAUTH_TOKEN_URL, REDIRECT_URI } from './constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Exchanges authorization code for access token
|
|
5
|
+
* @param code - Authorization code
|
|
6
|
+
* @returns Access token
|
|
7
|
+
*/
|
|
8
|
+
export async function exchangeCodeForToken(code: string): Promise<string> {
|
|
9
|
+
// Exchange authorization code for access token using Agility CMS OAuth token endpoint
|
|
10
|
+
// Agility CMS OAuth doesn't require client_id or client_secret
|
|
11
|
+
|
|
12
|
+
// Use Node.js built-in fetch (Node 18+)
|
|
13
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
14
|
+
throw new Error('OAuth token exchange requires Node.js 18+ (with built-in fetch).');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Make token exchange request to Agility CMS OAuth token endpoint
|
|
18
|
+
const response = await fetch(AGILITY_OAUTH_TOKEN_URL, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
22
|
+
},
|
|
23
|
+
body: new URLSearchParams({
|
|
24
|
+
grant_type: 'authorization_code',
|
|
25
|
+
code: code,
|
|
26
|
+
redirect_uri: REDIRECT_URI,
|
|
27
|
+
}).toString(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const errorText = await response.text();
|
|
32
|
+
throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const data = await response.json() as { access_token?: string; refresh_token?: string; expires_in?: number };
|
|
36
|
+
|
|
37
|
+
if (!data.access_token) {
|
|
38
|
+
throw new Error('No access token received from OAuth server');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return data.access_token;
|
|
42
|
+
}
|
|
43
|
+
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import open from 'open';
|
|
7
|
+
import boxen from 'boxen';
|
|
8
|
+
import updateNotifier from 'update-notifier';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { createNextApp } from '../create-next-app';
|
|
11
|
+
import * as auth from '../auth';
|
|
12
|
+
import * as agility from '../agility';
|
|
13
|
+
import * as config from '../config';
|
|
14
|
+
import * as templates from '../templates';
|
|
15
|
+
import type { CliOptions } from '../types';
|
|
16
|
+
import { promptForMissingOptions } from './promptForMissingOptions';
|
|
17
|
+
import {
|
|
18
|
+
validateProjectName,
|
|
19
|
+
validateProjectPath,
|
|
20
|
+
validateNodeVersion,
|
|
21
|
+
displayValidationError,
|
|
22
|
+
isGitInstalled,
|
|
23
|
+
} from '../utils/validation';
|
|
24
|
+
import { initializeGit, isGitAvailable } from '../utils/git';
|
|
25
|
+
|
|
26
|
+
// Check for updates
|
|
27
|
+
const pkg = require('../../package.json');
|
|
28
|
+
updateNotifier({ pkg }).notify({
|
|
29
|
+
isGlobal: true,
|
|
30
|
+
message: 'Update available: {currentVersion} → {latestVersion}\nRun {updateCommand} to update',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Handle errors with helpful recovery suggestions
|
|
35
|
+
*/
|
|
36
|
+
function handleError(error: Error): void {
|
|
37
|
+
console.error('\n' + chalk.red.bold('✖ Error occurred:'));
|
|
38
|
+
console.error(chalk.red(error.message) + '\n');
|
|
39
|
+
|
|
40
|
+
// Provide specific recovery suggestions based on error type
|
|
41
|
+
if (error.message.includes('EACCES') || error.message.includes('permission')) {
|
|
42
|
+
console.error(chalk.yellow('Possible solutions:'));
|
|
43
|
+
console.error(chalk.white(' • Check directory permissions'));
|
|
44
|
+
console.error(chalk.white(' • Try running without sudo (not recommended)'));
|
|
45
|
+
console.error(chalk.white(' • Choose a different directory\n'));
|
|
46
|
+
} else if (error.message.includes('ENOSPC')) {
|
|
47
|
+
console.error(chalk.yellow('Possible solutions:'));
|
|
48
|
+
console.error(chalk.white(' • Free up disk space'));
|
|
49
|
+
console.error(chalk.white(' • Choose a different disk/directory\n'));
|
|
50
|
+
} else if (error.message.includes('ENOTFOUND') || error.message.includes('network')) {
|
|
51
|
+
console.error(chalk.yellow('Possible solutions:'));
|
|
52
|
+
console.error(chalk.white(' • Check your internet connection'));
|
|
53
|
+
console.error(chalk.white(' • Try again in a few moments'));
|
|
54
|
+
console.error(chalk.white(' • Check if you are behind a proxy\n'));
|
|
55
|
+
} else if (error.message.includes('authentication') || error.message.includes('OAuth')) {
|
|
56
|
+
console.error(chalk.yellow('Possible solutions:'));
|
|
57
|
+
console.error(chalk.white(' • Choose "manual setup" option'));
|
|
58
|
+
console.error(chalk.white(' • Configure .env.local manually after project creation'));
|
|
59
|
+
console.error(chalk.white(' • Get API keys from: https://manager.agilitycms.com\n'));
|
|
60
|
+
} else {
|
|
61
|
+
console.error(chalk.yellow('Need help?'));
|
|
62
|
+
console.error(chalk.white(' • Check the documentation: https://github.com/agility/create-next-agility-app'));
|
|
63
|
+
console.error(chalk.white(' • Open an issue: https://github.com/agility/create-next-agility-app/issues\n'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (process.env.DEBUG) {
|
|
67
|
+
console.error(chalk.gray('Stack trace:'));
|
|
68
|
+
console.error(chalk.gray(error.stack || 'No stack trace available'));
|
|
69
|
+
} else {
|
|
70
|
+
console.error(chalk.gray('Run with DEBUG=1 for detailed error information\n'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Display success message with next steps
|
|
78
|
+
*/
|
|
79
|
+
function displaySuccessMessage(
|
|
80
|
+
projectName: string,
|
|
81
|
+
instanceGuid: string | undefined,
|
|
82
|
+
apiKeys: { fetchKey: string; previewKey: string; securityKey: string } | null
|
|
83
|
+
): void {
|
|
84
|
+
const hasAgilityConfig = Boolean(instanceGuid && apiKeys);
|
|
85
|
+
|
|
86
|
+
const message =
|
|
87
|
+
chalk.green.bold('✓ Success!') +
|
|
88
|
+
chalk.white(` Your project is ready at: ${chalk.cyan(projectName)}\n\n`) +
|
|
89
|
+
chalk.cyan.bold('Next steps:\n') +
|
|
90
|
+
chalk.white(` 1. ${chalk.bold(`cd ${projectName}`)}\n`) +
|
|
91
|
+
chalk.white(` 2. ${chalk.bold('npm run dev')}\n`) +
|
|
92
|
+
chalk.white(` 3. Open ${chalk.cyan('http://localhost:3000')}\n\n`) +
|
|
93
|
+
chalk.cyan.bold('Documentation:\n') +
|
|
94
|
+
chalk.white(` • ${chalk.bold('.claude/agents.md')} - AI assistant guide\n`) +
|
|
95
|
+
chalk.white(` • ${chalk.bold('docs/')} - Full documentation\n`) +
|
|
96
|
+
chalk.white(` • ${chalk.bold('README.md')} - Getting started\n\n`) +
|
|
97
|
+
(hasAgilityConfig
|
|
98
|
+
? chalk.green('✓ Agility CMS is configured and ready!\n\n')
|
|
99
|
+
: chalk.yellow('⚠ Configure Agility CMS:\n') +
|
|
100
|
+
chalk.white(` Edit ${chalk.bold('.env.local')} with your credentials\n`) +
|
|
101
|
+
chalk.gray(' Get your API keys from: https://manager.agilitycms.com\n\n')) +
|
|
102
|
+
chalk.cyan.bold('Get help:\n') +
|
|
103
|
+
chalk.white(` • ${chalk.underline('https://agilitycms.com/docs')}\n`) +
|
|
104
|
+
chalk.white(` • ${chalk.underline('https://github.com/agility/create-next-agility-app')}`);
|
|
105
|
+
|
|
106
|
+
console.log('\n' + boxen(message, {
|
|
107
|
+
padding: 1,
|
|
108
|
+
margin: 1,
|
|
109
|
+
borderStyle: 'round',
|
|
110
|
+
borderColor: 'green',
|
|
111
|
+
}) + '\n');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const program = new Command();
|
|
115
|
+
|
|
116
|
+
program
|
|
117
|
+
.name('create-next-agility-app')
|
|
118
|
+
.description('Create a new Next.js project with Agility CMS integration')
|
|
119
|
+
.version('1.0.0')
|
|
120
|
+
.argument('[project-name]', 'Name of the project')
|
|
121
|
+
.option('--typescript, --ts', 'Initialize as a TypeScript project')
|
|
122
|
+
.option('--javascript, --js', 'Initialize as a JavaScript project')
|
|
123
|
+
.option('--tailwind', 'Initialize with Tailwind CSS')
|
|
124
|
+
.option('--no-tailwind', 'Initialize without Tailwind CSS')
|
|
125
|
+
.option('--eslint', 'Initialize with ESLint')
|
|
126
|
+
.option('--no-eslint', 'Initialize without ESLint')
|
|
127
|
+
.option('--app', 'Initialize with App Router')
|
|
128
|
+
.option('--src-dir', 'Initialize with src/ directory')
|
|
129
|
+
.option('--import-alias <alias>', 'Specify import alias (default: @/*)')
|
|
130
|
+
.option('--use-npm', 'Use npm as package manager')
|
|
131
|
+
.option('--use-pnpm', 'Use pnpm as package manager')
|
|
132
|
+
.option('--use-yarn', 'Use yarn as package manager')
|
|
133
|
+
.option('--use-bun', 'Use bun as package manager')
|
|
134
|
+
.option('--skip-install', 'Skip package installation')
|
|
135
|
+
.action(async (projectName: string | undefined, options: CliOptions) => {
|
|
136
|
+
try {
|
|
137
|
+
console.log(chalk.blue.bold('\n🚀 Creating Next.js project with Agility CMS...\n'));
|
|
138
|
+
|
|
139
|
+
// Validate Node.js version
|
|
140
|
+
const nodeValidation = validateNodeVersion();
|
|
141
|
+
if (!nodeValidation.valid) {
|
|
142
|
+
displayValidationError(nodeValidation.message!);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Step 1: Agility CMS setup (ask questions first)
|
|
147
|
+
console.log(chalk.magenta.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
148
|
+
console.log(chalk.magenta.bold('✨ STEP 1: Agility CMS integration'));
|
|
149
|
+
console.log(chalk.magenta.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
150
|
+
let instanceGuid: string | undefined;
|
|
151
|
+
let apiKeys: { fetchKey: string; previewKey: string; securityKey: string } | null = null;
|
|
152
|
+
let instanceName: string | undefined;
|
|
153
|
+
|
|
154
|
+
// Step 1a: Ask user what they want to do
|
|
155
|
+
const agilityChoice = await inquirer.prompt<{ action: 'existing' | 'new' | 'manual' }>([
|
|
156
|
+
{
|
|
157
|
+
type: 'list',
|
|
158
|
+
name: 'action',
|
|
159
|
+
message: 'Choose an option:',
|
|
160
|
+
choices: [
|
|
161
|
+
{ name: '1. Connect to an existing instance', value: 'existing' },
|
|
162
|
+
{ name: '2. Create a new Agility instance', value: 'new' },
|
|
163
|
+
{ name: '3. Let me connect it manually later', value: 'manual' }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
if (agilityChoice.action === 'existing') {
|
|
169
|
+
try {
|
|
170
|
+
console.log(chalk.magenta('🔐 Authenticating with Agility CMS...\n'));
|
|
171
|
+
const accessToken = await auth.authenticate();
|
|
172
|
+
|
|
173
|
+
// Step 1b: Instance management
|
|
174
|
+
console.log(chalk.magenta('📋 Setting up Agility CMS instance...\n'));
|
|
175
|
+
const instanceData = await agility.manageInstance(accessToken, options);
|
|
176
|
+
instanceGuid = instanceData.guid;
|
|
177
|
+
apiKeys = instanceData.apiKeys;
|
|
178
|
+
instanceName = instanceData.name;
|
|
179
|
+
} catch (authError) {
|
|
180
|
+
console.log(chalk.yellow('\n⚠️ Authentication failed. You can configure Agility CMS later.'));
|
|
181
|
+
console.log(chalk.gray(' Edit .env.local with your Agility credentials when ready.\n'));
|
|
182
|
+
}
|
|
183
|
+
} else if (agilityChoice.action === 'new') {
|
|
184
|
+
// Will open browser after project is created
|
|
185
|
+
console.log(chalk.gray('Skipping Agility instance connection. You can configure it manually later.\n'));
|
|
186
|
+
} else {
|
|
187
|
+
// Manual connection later
|
|
188
|
+
console.log(chalk.gray('Skipping Agility instance connection. You can configure it manually later.\n'));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Sanitize instance name to use as default project name
|
|
192
|
+
const defaultProjectName = instanceName
|
|
193
|
+
? instanceName
|
|
194
|
+
.toLowerCase()
|
|
195
|
+
.replace(/\s+/g, '-')
|
|
196
|
+
.replace(/[^a-z0-9-_]/g, '')
|
|
197
|
+
.replace(/-+/g, '-')
|
|
198
|
+
.replace(/^-|-$/g, '') || undefined
|
|
199
|
+
: undefined;
|
|
200
|
+
|
|
201
|
+
// Prompt for missing information if not provided via flags
|
|
202
|
+
// Use instance name as default if available and project name not provided
|
|
203
|
+
const finalOptions = await promptForMissingOptions(
|
|
204
|
+
projectName, // Only pass projectName if explicitly provided
|
|
205
|
+
options,
|
|
206
|
+
defaultProjectName // Use sanitized instance name as default
|
|
207
|
+
);
|
|
208
|
+
const finalProjectName = finalOptions.projectName || 'my-app';
|
|
209
|
+
|
|
210
|
+
// Validate project name
|
|
211
|
+
const nameValidation = validateProjectName(finalProjectName);
|
|
212
|
+
if (!nameValidation.valid) {
|
|
213
|
+
displayValidationError(nameValidation.message!);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Validate project path
|
|
218
|
+
const projectPath = path.resolve(process.cwd(), finalProjectName);
|
|
219
|
+
const pathValidation = validateProjectPath(projectPath);
|
|
220
|
+
if (!pathValidation.valid) {
|
|
221
|
+
displayValidationError(pathValidation.message!);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Step 2: Create Next.js app
|
|
226
|
+
console.log(chalk.cyan.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
227
|
+
console.log(chalk.cyan.bold('📦 STEP 2: Creating base Next.js project'));
|
|
228
|
+
console.log(chalk.cyan.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
229
|
+
const createdProjectPath = await createNextApp(finalProjectName, finalOptions);
|
|
230
|
+
|
|
231
|
+
// Step 3: Configure project (with Agility settings)
|
|
232
|
+
try {
|
|
233
|
+
await config.setupProject(createdProjectPath, {
|
|
234
|
+
instanceGuid,
|
|
235
|
+
apiKeys,
|
|
236
|
+
...options
|
|
237
|
+
});
|
|
238
|
+
} catch (configError) {
|
|
239
|
+
console.log(chalk.yellow('\n⚠️ Some configuration steps failed. You may need to configure manually.\n'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Step 4: Copy template files
|
|
243
|
+
try {
|
|
244
|
+
await templates.copyTemplates(createdProjectPath, options);
|
|
245
|
+
} catch (templateError) {
|
|
246
|
+
console.log(chalk.yellow('\n⚠️ Some template files could not be copied. Check the project structure.\n'));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Step 5: Initialize git repository
|
|
250
|
+
if (isGitAvailable()) {
|
|
251
|
+
await initializeGit(createdProjectPath);
|
|
252
|
+
} else {
|
|
253
|
+
console.log(chalk.yellow('⚠️ Git is not installed. Skipping git initialization.'));
|
|
254
|
+
console.log(chalk.gray(' Install git and run: git init\n'));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Display success message
|
|
258
|
+
displaySuccessMessage(finalProjectName, instanceGuid, apiKeys);
|
|
259
|
+
|
|
260
|
+
// If user chose to create a new instance, open browser now
|
|
261
|
+
if (agilityChoice.action === 'new') {
|
|
262
|
+
console.log(chalk.cyan('\n🌐 Opening Agility CMS signup page in your browser...\n'));
|
|
263
|
+
try {
|
|
264
|
+
await open('https://agilitycms.com/free');
|
|
265
|
+
console.log(chalk.green('✅ Browser opened. Please create your instance.\n'));
|
|
266
|
+
console.log(chalk.yellow('After creating your instance, you can link it by manually configuring your .env.local file.\n'));
|
|
267
|
+
} catch (openError) {
|
|
268
|
+
console.log(chalk.yellow('\n⚠️ Could not open browser automatically.'));
|
|
269
|
+
console.log(chalk.cyan('Please visit: https://agilitycms.com/free\n'));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Ensure process exits cleanly
|
|
274
|
+
process.exit(0);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
handleError(error as Error);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
program.parse();
|
|
281
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import type { CliOptions } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prompts for missing options interactively
|
|
6
|
+
*/
|
|
7
|
+
export async function promptForMissingOptions(
|
|
8
|
+
projectName: string | undefined,
|
|
9
|
+
options: CliOptions,
|
|
10
|
+
defaultProjectName?: string
|
|
11
|
+
): Promise<CliOptions & { projectName?: string }> {
|
|
12
|
+
const questions: any[] = [];
|
|
13
|
+
|
|
14
|
+
// Prompt for project name if not provided
|
|
15
|
+
if (!projectName) {
|
|
16
|
+
questions.push({
|
|
17
|
+
type: 'input',
|
|
18
|
+
name: 'projectName',
|
|
19
|
+
message: 'What is your project named?',
|
|
20
|
+
default: defaultProjectName || 'my-app',
|
|
21
|
+
validate: (input: string) => {
|
|
22
|
+
if (!input || input.trim().length === 0) {
|
|
23
|
+
return 'Project name is required';
|
|
24
|
+
}
|
|
25
|
+
// Check for valid project name (no spaces, special chars)
|
|
26
|
+
if (!/^[a-z0-9-_]+$/i.test(input)) {
|
|
27
|
+
return 'Project name can only contain letters, numbers, hyphens, and underscores';
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// TypeScript and App Router are always enabled by default (no prompt)
|
|
35
|
+
// Only override if explicitly set via flags
|
|
36
|
+
|
|
37
|
+
// Prompt for Tailwind CSS if not specified
|
|
38
|
+
if (options.tailwind === undefined) {
|
|
39
|
+
questions.push({
|
|
40
|
+
type: 'confirm',
|
|
41
|
+
name: 'tailwind',
|
|
42
|
+
message: 'Would you like to use Tailwind CSS?',
|
|
43
|
+
default: true
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prompt for ESLint if not specified
|
|
48
|
+
if (options.eslint === undefined) {
|
|
49
|
+
questions.push({
|
|
50
|
+
type: 'confirm',
|
|
51
|
+
name: 'eslint',
|
|
52
|
+
message: 'Would you like to use ESLint?',
|
|
53
|
+
default: true
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Prompt for src directory if not specified
|
|
58
|
+
if (options.srcDir === undefined) {
|
|
59
|
+
questions.push({
|
|
60
|
+
type: 'confirm',
|
|
61
|
+
name: 'srcDir',
|
|
62
|
+
message: 'Would you like to use the src directory?',
|
|
63
|
+
default: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Default to TypeScript and App Router (unless explicitly overridden)
|
|
68
|
+
const mergedOptions: CliOptions & { projectName?: string } = {
|
|
69
|
+
...options,
|
|
70
|
+
projectName: projectName
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (!options.typescript && !options.javascript) {
|
|
74
|
+
mergedOptions.typescript = true;
|
|
75
|
+
mergedOptions.javascript = false;
|
|
76
|
+
}
|
|
77
|
+
if (options.app === undefined) {
|
|
78
|
+
mergedOptions.app = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Only prompt if we have questions
|
|
82
|
+
if (questions.length === 0) {
|
|
83
|
+
return mergedOptions;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const answers = await inquirer.prompt(questions);
|
|
87
|
+
|
|
88
|
+
// Merge answers with existing options
|
|
89
|
+
mergedOptions.projectName = projectName || answers.projectName;
|
|
90
|
+
|
|
91
|
+
// Handle boolean options from prompts
|
|
92
|
+
if (answers.tailwind !== undefined) {
|
|
93
|
+
mergedOptions.tailwind = answers.tailwind;
|
|
94
|
+
}
|
|
95
|
+
if (answers.eslint !== undefined) {
|
|
96
|
+
mergedOptions.eslint = answers.eslint;
|
|
97
|
+
}
|
|
98
|
+
if (answers.srcDir !== undefined) {
|
|
99
|
+
mergedOptions.srcDir = answers.srcDir;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return mergedOptions;
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import type { ProjectConfig } from '../../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates .env.local file with Agility configuration
|
|
8
|
+
*/
|
|
9
|
+
export async function createEnvFile(projectPath: string, config: ProjectConfig): Promise<void> {
|
|
10
|
+
const envPath = path.join(projectPath, '.env.local');
|
|
11
|
+
|
|
12
|
+
const envVars: Record<string, string> = {
|
|
13
|
+
AGILITY_GUID: config.instanceGuid || '',
|
|
14
|
+
AGILITY_API_FETCH_KEY: config.apiKeys?.fetchKey || '',
|
|
15
|
+
AGILITY_API_PREVIEW_KEY: config.apiKeys?.previewKey || '',
|
|
16
|
+
AGILITY_SECURITY_KEY: config.apiKeys?.securityKey || '',
|
|
17
|
+
AGILITY_LOCALES: 'en-us',
|
|
18
|
+
AGILITY_SITEMAP: 'website',
|
|
19
|
+
AGILITY_FETCH_CACHE_DURATION: '60',
|
|
20
|
+
AGILITY_PATH_REVALIDATE_DURATION: '60'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const envContent = Object.entries(envVars)
|
|
24
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
25
|
+
.join('\n');
|
|
26
|
+
|
|
27
|
+
fs.writeFileSync(envPath, envContent);
|
|
28
|
+
console.log(chalk.green('✓ Created .env.local'));
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates .vscode/mcp.json file with Agility CMS MCP server configuration
|
|
7
|
+
*/
|
|
8
|
+
export async function createMcpConfig(projectPath: string): Promise<void> {
|
|
9
|
+
const vscodeDir = path.join(projectPath, '.vscode');
|
|
10
|
+
const mcpConfigPath = path.join(vscodeDir, 'mcp.json');
|
|
11
|
+
|
|
12
|
+
// Ensure .vscode directory exists
|
|
13
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
14
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const mcpConfig = {
|
|
18
|
+
servers: {
|
|
19
|
+
'Agility CMS': {
|
|
20
|
+
url: 'https://mcp.agilitycms.com/api/mcp',
|
|
21
|
+
type: 'http'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
inputs: []
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, '\t'));
|
|
28
|
+
console.log(chalk.green('✓ Created .vscode/mcp.json'));
|
|
29
|
+
}
|
|
30
|
+
|