@codedrifters/configulator 0.0.153 → 0.0.155
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/lib/index.d.mts +751 -11
- package/lib/index.d.ts +743 -3
- package/lib/index.js +1534 -283
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1516 -280
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -172,14 +172,548 @@ var require_lib = __commonJS({
|
|
|
172
172
|
}
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
// src/
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
// src/agent/agent-config.ts
|
|
176
|
+
import { Component as Component7 } from "projen";
|
|
177
|
+
|
|
178
|
+
// src/agent/types.ts
|
|
179
|
+
var AGENT_RULE_SCOPE = {
|
|
180
|
+
ALWAYS: "always",
|
|
181
|
+
FILE_PATTERN: "file-pattern"
|
|
182
|
+
};
|
|
183
|
+
var AGENT_PLATFORM = {
|
|
184
|
+
CURSOR: "cursor",
|
|
185
|
+
CLAUDE: "claude",
|
|
186
|
+
CODEX: "codex",
|
|
187
|
+
COPILOT: "copilot"
|
|
188
|
+
};
|
|
189
|
+
var CLAUDE_RULE_TARGET = {
|
|
190
|
+
SCOPED_FILE: "scoped-file",
|
|
191
|
+
AGENTS_MD: "agents-md",
|
|
192
|
+
CLAUDE_MD: "claude-md"
|
|
193
|
+
};
|
|
194
|
+
var AGENT_MODEL = {
|
|
195
|
+
INHERIT: "inherit",
|
|
196
|
+
FAST: "fast",
|
|
197
|
+
BALANCED: "balanced",
|
|
198
|
+
POWERFUL: "powerful"
|
|
199
|
+
};
|
|
200
|
+
var MCP_TRANSPORT = {
|
|
201
|
+
STDIO: "stdio",
|
|
202
|
+
HTTP: "http",
|
|
203
|
+
SSE: "sse"
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/agent/bundles/utils.ts
|
|
207
|
+
function hasComponent(project, ctor) {
|
|
208
|
+
if (project.components.some((c) => c instanceof ctor)) return true;
|
|
209
|
+
return project.subprojects.some(
|
|
210
|
+
(sub) => sub.components.some((c) => c instanceof ctor)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
function hasDep(project, name) {
|
|
214
|
+
if (project.deps.all.some((d) => d.name === name)) return true;
|
|
215
|
+
return project.subprojects.some(
|
|
216
|
+
(sub) => sub.deps.all.some((d) => d.name === name)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
function hasFile(project, filename) {
|
|
220
|
+
if (project.tryFindFile(filename) !== void 0) return true;
|
|
221
|
+
return project.subprojects.some(
|
|
222
|
+
(sub) => sub.tryFindFile(filename) !== void 0
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/agent/bundles/aws-cdk.ts
|
|
227
|
+
var awsCdkBundle = {
|
|
228
|
+
name: "aws-cdk",
|
|
229
|
+
description: "AWS CDK construct patterns, L2/L3 conventions, IAM best practices",
|
|
230
|
+
appliesWhen: (project) => hasDep(project, "aws-cdk-lib"),
|
|
231
|
+
rules: [
|
|
232
|
+
{
|
|
233
|
+
name: "aws-cdk-conventions",
|
|
234
|
+
description: "AWS CDK construct patterns and best practices",
|
|
235
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
236
|
+
filePatterns: ["**/*.ts"],
|
|
237
|
+
content: [
|
|
238
|
+
"# AWS CDK Conventions",
|
|
239
|
+
"",
|
|
240
|
+
"## Construct Structure",
|
|
241
|
+
"",
|
|
242
|
+
"- All constructs extend `Construct` from `constructs`",
|
|
243
|
+
"- Use `readonly` for all props interfaces",
|
|
244
|
+
"- Include minimal JSDoc for configuration options",
|
|
245
|
+
"- Follow AWS CDK best practices for resource naming and organization",
|
|
246
|
+
"- Use proper TypeScript types from `aws-cdk-lib`",
|
|
247
|
+
"- Export constructs from `index.ts` for public API",
|
|
248
|
+
"",
|
|
249
|
+
"## CDK Construct Pattern",
|
|
250
|
+
"",
|
|
251
|
+
"```typescript",
|
|
252
|
+
"export interface MyConstructProps {",
|
|
253
|
+
" /** Brief description. */",
|
|
254
|
+
" readonly myProperty: string;",
|
|
255
|
+
"}",
|
|
256
|
+
"",
|
|
257
|
+
"export class MyConstruct extends Construct {",
|
|
258
|
+
" constructor(scope: Construct, id: string, props: MyConstructProps) {",
|
|
259
|
+
" super(scope, id);",
|
|
260
|
+
" // Implementation",
|
|
261
|
+
" }",
|
|
262
|
+
"}",
|
|
263
|
+
"```",
|
|
264
|
+
"",
|
|
265
|
+
"## AWS Best Practices",
|
|
266
|
+
"",
|
|
267
|
+
"- Use AWS CDK v2 (`aws-cdk-lib`)",
|
|
268
|
+
"- Follow AWS best practices for security and resource configuration",
|
|
269
|
+
"- Use proper IAM permissions (principle of least privilege)",
|
|
270
|
+
"- Include proper tags and descriptions for resources",
|
|
271
|
+
"- Use SSM parameters for cross-stack references when needed",
|
|
272
|
+
"- Do not pass values between stacks; use SSM parameters instead",
|
|
273
|
+
"",
|
|
274
|
+
"## CDK Testing",
|
|
275
|
+
"",
|
|
276
|
+
"- Mock `Code.fromAsset` in any test file that synthesizes CDK stacks",
|
|
277
|
+
"- Use static S3 values (`mock-assets-bucket`, `mock-asset-key.zip`) so snapshots are stable",
|
|
278
|
+
"- Add the mock in `beforeAll` and restore in `afterAll`",
|
|
279
|
+
"- Normalize the template before snapshotting when the stack includes asset-based Lambdas"
|
|
280
|
+
].join("\n"),
|
|
281
|
+
tags: ["infrastructure"]
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// src/agent/bundles/base.ts
|
|
287
|
+
var baseBundle = {
|
|
288
|
+
name: "base",
|
|
289
|
+
description: "Core rules: project overview, interaction style, and general coding conventions",
|
|
290
|
+
appliesWhen: () => true,
|
|
291
|
+
rules: [
|
|
292
|
+
{
|
|
293
|
+
name: "project-overview",
|
|
294
|
+
description: "Project context and technology stack overview",
|
|
295
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
296
|
+
content: [
|
|
297
|
+
"# Project Overview",
|
|
298
|
+
"",
|
|
299
|
+
"## Important Notes",
|
|
300
|
+
"",
|
|
301
|
+
"- **Never edit generated files** \u2014 they are marked with `// ~~ Generated by projen`",
|
|
302
|
+
"- **After modifying Projen configuration**, the user should run `npx projen` locally. The agent must not run `npx projen`, `pnpm install`, or `pnpm i`.",
|
|
303
|
+
"- **Configure dependencies through Projen** \u2014 never use `npm install`, `pnpm add`, or `yarn add`. Add them to `deps` or `devDeps` in Projen config.",
|
|
304
|
+
"- **Export from index.ts** to maintain clean public APIs"
|
|
305
|
+
].join("\n"),
|
|
306
|
+
tags: ["project"]
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "interaction-style",
|
|
310
|
+
description: "Interaction style \u2014 ask questions one at a time and wait for feedback",
|
|
311
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
312
|
+
content: [
|
|
313
|
+
"# Interaction Style Guidelines",
|
|
314
|
+
"",
|
|
315
|
+
"When responding to requests, follow these interaction principles.",
|
|
316
|
+
"",
|
|
317
|
+
"1. **Ask questions one at a time**: When clarification is needed, ask a single question and wait for the user's response before proceeding.",
|
|
318
|
+
"2. **Wait for feedback**: After asking a question, pause and wait for the user's answer before asking additional questions or making assumptions.",
|
|
319
|
+
"3. **Avoid question overload**: Do not ask multiple questions in a single response. If multiple clarifications are needed, prioritize the most important question first.",
|
|
320
|
+
"4. **Progressive clarification**: Once the user answers your first question, you may then ask the next most important question if needed.",
|
|
321
|
+
"5. **Confirm understanding**: After receiving feedback, acknowledge the user's response before proceeding with the next step or question.",
|
|
322
|
+
"6. **Be patient**: Do not rush ahead with assumptions. It is better to ask one clarifying question than to proceed with incorrect assumptions."
|
|
323
|
+
].join("\n"),
|
|
324
|
+
tags: ["project"]
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "general-conventions",
|
|
328
|
+
description: "Code formatting (Prettier/ESLint), import conventions (ES modules, import order), error handling (async/await, no floating promises)",
|
|
329
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
330
|
+
filePatterns: ["**/*.ts", "**/*.tsx"],
|
|
331
|
+
content: [
|
|
332
|
+
"# General Conventions",
|
|
333
|
+
"",
|
|
334
|
+
"## Code Formatting",
|
|
335
|
+
"",
|
|
336
|
+
"- Use **Prettier** for formatting (runs automatically on save in VS Code)",
|
|
337
|
+
"- Always use curly braces for control flow, even single-line statements",
|
|
338
|
+
"- Prefer `const` over `let`; avoid `var`",
|
|
339
|
+
"- Use trailing commas in multi-line objects/arrays",
|
|
340
|
+
"",
|
|
341
|
+
"### ESLint Rules to Follow",
|
|
342
|
+
"",
|
|
343
|
+
"- `curly`: Always use curly braces (multi-line, consistent)",
|
|
344
|
+
"- `dot-notation`: Use dot notation over bracket notation",
|
|
345
|
+
"- `no-bitwise`: No bitwise operators",
|
|
346
|
+
"- `@typescript-eslint/no-shadow`: No variable shadowing",
|
|
347
|
+
"- `@typescript-eslint/member-ordering`: Follow member order",
|
|
348
|
+
"",
|
|
349
|
+
"## Import Conventions",
|
|
350
|
+
"",
|
|
351
|
+
"- **Always use ES modules** (`import`/`export`), never `require()`",
|
|
352
|
+
"- Import order:",
|
|
353
|
+
" 1. Built-in Node.js modules (e.g., `node:path`, `node:fs`)",
|
|
354
|
+
" 2. External dependencies (alphabetically sorted)",
|
|
355
|
+
" 3. Internal imports (relative paths)",
|
|
356
|
+
"- Group imports with blank lines between groups",
|
|
357
|
+
"- Alphabetize imports within each group (case-insensitive)",
|
|
358
|
+
"",
|
|
359
|
+
"## Error Handling",
|
|
360
|
+
"",
|
|
361
|
+
"- Always handle promises properly with `await`",
|
|
362
|
+
"- Use `@typescript-eslint/return-await` rule (always return await)",
|
|
363
|
+
"- Never leave floating promises (`@typescript-eslint/no-floating-promises`)",
|
|
364
|
+
"- Use proper error types and meaningful error messages",
|
|
365
|
+
"- Do not swallow errors or use empty catch blocks",
|
|
366
|
+
"- Prefer async/await over raw promises"
|
|
367
|
+
].join("\n"),
|
|
368
|
+
tags: ["coding"]
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "pull-request-conventions",
|
|
372
|
+
description: "Conventional commit PR titles, closing keywords, change summaries",
|
|
373
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
374
|
+
content: [
|
|
375
|
+
"# Pull Request Conventions",
|
|
376
|
+
"",
|
|
377
|
+
"## PR Title Prefix",
|
|
378
|
+
"",
|
|
379
|
+
"**Always** use a **conventional commit prefix** in the PR `title`. Format: `type: description` or `type(scope): description`.",
|
|
380
|
+
"",
|
|
381
|
+
"| Prefix | Use for |",
|
|
382
|
+
"|--------|---------|",
|
|
383
|
+
"| `feat:` | New features or functionality |",
|
|
384
|
+
"| `fix:` | Bug fixes |",
|
|
385
|
+
"| `docs:` | Documentation-only changes |",
|
|
386
|
+
"| `chore:` | Maintenance: deps, tooling, config |",
|
|
387
|
+
"| `refactor:` | Code restructure, no behavior change |",
|
|
388
|
+
"| `release:` | Release preparation, version bumps |",
|
|
389
|
+
"| `hotfix:` | Urgent production fixes |",
|
|
390
|
+
"",
|
|
391
|
+
"## Link to the Issue",
|
|
392
|
+
"",
|
|
393
|
+
"When the PR addresses an issue, **always** include a closing keyword in the PR body:",
|
|
394
|
+
"- `Closes #<issue>`, `Fixes #<issue>`, or `Resolves #<issue>`",
|
|
395
|
+
"",
|
|
396
|
+
"## Summary of Changes",
|
|
397
|
+
"",
|
|
398
|
+
"Every PR must include a **summary of the changes** made. Use bullet points or short paragraphs. Do not leave the description empty.",
|
|
399
|
+
"",
|
|
400
|
+
"## Commit Messages",
|
|
401
|
+
"",
|
|
402
|
+
"Use **conventional commits** for git commit messages: `type: short description`. Do not add AI co-author or attribution."
|
|
403
|
+
].join("\n"),
|
|
404
|
+
tags: ["workflow"]
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "branch-naming-conventions",
|
|
408
|
+
description: "Branch format (type/[issue-]description), create-on-GitHub-then-fetch workflow",
|
|
409
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
410
|
+
content: [
|
|
411
|
+
"# Branch Naming Conventions",
|
|
412
|
+
"",
|
|
413
|
+
"## Format",
|
|
414
|
+
"",
|
|
415
|
+
"```",
|
|
416
|
+
"<type>/[<issue>-]<description>",
|
|
417
|
+
"```",
|
|
418
|
+
"",
|
|
419
|
+
"- **type** (required): One of `feat`, `fix`, `docs`, `chore`, `refactor`, `release`, `hotfix`",
|
|
420
|
+
"- **issue** (optional): Issue number (e.g., `25`). Include when known.",
|
|
421
|
+
"- **description** (required): Short, lowercase, kebab-case summary",
|
|
422
|
+
"",
|
|
423
|
+
"## Examples",
|
|
424
|
+
"",
|
|
425
|
+
"- `feat/25-add-cursor-rules`",
|
|
426
|
+
"- `fix/23-rename-cursor-rules-mdc`",
|
|
427
|
+
"- `chore/upgrade-eslint`",
|
|
428
|
+
"- `docs/update-readme`"
|
|
429
|
+
].join("\n"),
|
|
430
|
+
tags: ["workflow"]
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: "issue-conventions",
|
|
434
|
+
description: "Issue title prefixes, GitHub issue type mapping, prerequisite issues",
|
|
435
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
436
|
+
content: [
|
|
437
|
+
"# Issue Title Conventions",
|
|
438
|
+
"",
|
|
439
|
+
"## Format",
|
|
440
|
+
"",
|
|
441
|
+
"```",
|
|
442
|
+
"<type>: <description>",
|
|
443
|
+
"```",
|
|
444
|
+
"",
|
|
445
|
+
"## Types",
|
|
446
|
+
"",
|
|
447
|
+
"| Prefix | Use for |",
|
|
448
|
+
"|--------|---------|",
|
|
449
|
+
"| `epic:` | Large initiatives spanning multiple child issues |",
|
|
450
|
+
"| `feat:` | New features or functionality |",
|
|
451
|
+
"| `fix:` | Bug fixes |",
|
|
452
|
+
"| `chore:` | Maintenance: deps, tooling, config |",
|
|
453
|
+
"| `docs:` | Documentation-only work |",
|
|
454
|
+
"| `refactor:` | Code restructure, no behavior change |",
|
|
455
|
+
"| `release:` | Release preparation, version bumps |",
|
|
456
|
+
"| `hotfix:` | Urgent production fixes |",
|
|
457
|
+
"",
|
|
458
|
+
"## Prerequisite Issues",
|
|
459
|
+
"",
|
|
460
|
+
"Include any prerequisite (blocking) issues in the issue body when they exist.",
|
|
461
|
+
"Use a **Dependencies** section or `**Depends on:** #123`."
|
|
462
|
+
].join("\n"),
|
|
463
|
+
tags: ["workflow"]
|
|
464
|
+
}
|
|
465
|
+
]
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// src/agent/bundles/jest.ts
|
|
469
|
+
var jestBundle = {
|
|
470
|
+
name: "jest",
|
|
471
|
+
description: "Jest testing conventions and patterns",
|
|
472
|
+
appliesWhen: (project) => hasDep(project, "jest"),
|
|
473
|
+
rules: [
|
|
474
|
+
{
|
|
475
|
+
name: "jest-testing",
|
|
476
|
+
description: "Jest testing conventions and patterns",
|
|
477
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
478
|
+
filePatterns: ["**/*.test.ts", "**/*.spec.ts"],
|
|
479
|
+
content: [
|
|
480
|
+
"# Jest Testing Patterns",
|
|
481
|
+
"",
|
|
482
|
+
"## Mandatory Requirements",
|
|
483
|
+
"",
|
|
484
|
+
"- **Tests MUST be created or updated whenever code functionality is added or changed**",
|
|
485
|
+
"- This applies to all code changes, including:",
|
|
486
|
+
" - New features and functionality",
|
|
487
|
+
" - Bug fixes",
|
|
488
|
+
" - Refactoring that changes behavior",
|
|
489
|
+
" - API changes",
|
|
490
|
+
" - Configuration changes that affect functionality",
|
|
491
|
+
"- Tests should be written or updated as part of the same change that modifies the code",
|
|
492
|
+
"",
|
|
493
|
+
"## Test Structure",
|
|
494
|
+
"",
|
|
495
|
+
"- Use **Jest** with SWC for fast compilation",
|
|
496
|
+
"- Test files: `.spec.ts` or `.test.ts` (co-located with source)",
|
|
497
|
+
"- Use descriptive test names: `describe('ClassName', () => { it('should do something specific', () => {}) })`",
|
|
498
|
+
"- Prefer snapshot tests for complex object structures",
|
|
499
|
+
"- Mock external dependencies appropriately",
|
|
500
|
+
"",
|
|
501
|
+
"## Test Organization",
|
|
502
|
+
"",
|
|
503
|
+
"- Co-locate test files with source files",
|
|
504
|
+
"- Test files can use dev dependencies (ESLint rule override)",
|
|
505
|
+
"- Use `@swc/jest` for fast compilation",
|
|
506
|
+
"- Configure Jest in `jest.config.json` (not in package.json)",
|
|
507
|
+
"",
|
|
508
|
+
"## Example Test Structure",
|
|
509
|
+
"",
|
|
510
|
+
"```typescript",
|
|
511
|
+
"import { MyClass } from './my-class';",
|
|
512
|
+
"",
|
|
513
|
+
"describe('MyClass', () => {",
|
|
514
|
+
" it('should do something specific', () => {",
|
|
515
|
+
" // Test implementation",
|
|
516
|
+
" });",
|
|
517
|
+
"});",
|
|
518
|
+
"```"
|
|
519
|
+
].join("\n"),
|
|
520
|
+
tags: ["testing"]
|
|
521
|
+
}
|
|
522
|
+
]
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/pnpm/pnpm-workspace.ts
|
|
526
|
+
import { relative } from "path";
|
|
527
|
+
import { Component, YamlFile } from "projen";
|
|
528
|
+
var MIMIMUM_RELEASE_AGE = {
|
|
529
|
+
ZERO_DAYS: 0,
|
|
530
|
+
ONE_HOUR: 60,
|
|
531
|
+
SIX_HOURS: 360,
|
|
532
|
+
TWELVE_HOURS: 720,
|
|
533
|
+
ONE_DAY: 1440,
|
|
534
|
+
TWO_DAYS: 2880,
|
|
535
|
+
THREE_DAYS: 4320,
|
|
536
|
+
FOUR_DAYS: 5760,
|
|
537
|
+
FIVE_DAYS: 7200,
|
|
538
|
+
SIX_DAYS: 8640,
|
|
539
|
+
ONE_WEEK: 10080
|
|
540
|
+
};
|
|
541
|
+
var PnpmWorkspace = class _PnpmWorkspace extends Component {
|
|
542
|
+
/**
|
|
543
|
+
* Get the pnpm workspace component of a project. If it does not exist,
|
|
544
|
+
* return undefined.
|
|
545
|
+
*
|
|
546
|
+
* @param project
|
|
547
|
+
* @returns
|
|
548
|
+
*/
|
|
549
|
+
static of(project) {
|
|
550
|
+
const isDefined = (c) => c instanceof _PnpmWorkspace;
|
|
551
|
+
return project.root.components.find(isDefined);
|
|
552
|
+
}
|
|
553
|
+
constructor(project, options = {}) {
|
|
554
|
+
super(project);
|
|
555
|
+
project.tryFindObjectFile("package.json")?.addDeletionOverride("pnpm");
|
|
556
|
+
this.fileName = options.fileName ?? "pnpm-workspace.yaml";
|
|
557
|
+
this.minimumReleaseAge = options.minimumReleaseAge ?? MIMIMUM_RELEASE_AGE.ONE_DAY;
|
|
558
|
+
this.minimumReleaseAgeExclude = options.minimumReleaseAgeExclude ? ["@codedrifters/*", ...options.minimumReleaseAgeExclude] : ["@codedrifters/*"];
|
|
559
|
+
this.onlyBuiltDependencies = options.onlyBuiltDependencies ? options.onlyBuiltDependencies : [];
|
|
560
|
+
this.ignoredBuiltDependencies = options.ignoredBuiltDependencies ? options.ignoredBuiltDependencies : [];
|
|
561
|
+
this.subprojects = options.subprojects ?? [];
|
|
562
|
+
this.defaultCatalog = options.defaultCatalog;
|
|
563
|
+
this.namedCatalogs = options.namedCatalogs;
|
|
564
|
+
project.addPackageIgnore(this.fileName);
|
|
565
|
+
new YamlFile(this.project, this.fileName, {
|
|
566
|
+
obj: () => {
|
|
567
|
+
const pnpmConfig = {};
|
|
568
|
+
const packages = new Array();
|
|
569
|
+
for (const subproject of project.subprojects) {
|
|
570
|
+
packages.push(relative(this.project.outdir, subproject.outdir));
|
|
571
|
+
}
|
|
572
|
+
const packageSet = new Set(packages);
|
|
573
|
+
for (const subprojectPath of this.subprojects) {
|
|
574
|
+
packageSet.add(subprojectPath);
|
|
575
|
+
}
|
|
576
|
+
if (this.subprojects.length > 0) {
|
|
577
|
+
packages.length = 0;
|
|
578
|
+
packages.push(...packageSet);
|
|
579
|
+
}
|
|
580
|
+
pnpmConfig.minimumReleaseAge = this.minimumReleaseAge;
|
|
581
|
+
if (this.minimumReleaseAgeExclude.length > 0) {
|
|
582
|
+
pnpmConfig.minimumReleaseAgeExclude = this.minimumReleaseAgeExclude;
|
|
583
|
+
}
|
|
584
|
+
if (this.onlyBuiltDependencies.length > 0) {
|
|
585
|
+
pnpmConfig.onlyBuiltDependencies = this.onlyBuiltDependencies;
|
|
586
|
+
}
|
|
587
|
+
if (this.ignoredBuiltDependencies.length > 0) {
|
|
588
|
+
pnpmConfig.ignoreBuiltDependencies = this.ignoredBuiltDependencies;
|
|
589
|
+
}
|
|
590
|
+
if (this.defaultCatalog && Object.keys(this.defaultCatalog).length > 0) {
|
|
591
|
+
pnpmConfig.catalog = this.defaultCatalog;
|
|
592
|
+
}
|
|
593
|
+
if (this.namedCatalogs && Object.keys(this.namedCatalogs).length > 0) {
|
|
594
|
+
pnpmConfig.namedCatalogs = this.namedCatalogs;
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
...packages.length > 0 ? { packages } : {},
|
|
598
|
+
...pnpmConfig ? { ...pnpmConfig } : {}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/agent/bundles/pnpm.ts
|
|
606
|
+
var pnpmBundle = {
|
|
607
|
+
name: "pnpm",
|
|
608
|
+
description: "PNPM workspace rules and dependency management conventions",
|
|
609
|
+
appliesWhen: (project) => hasComponent(project, PnpmWorkspace),
|
|
610
|
+
rules: [
|
|
611
|
+
{
|
|
612
|
+
name: "pnpm-workspace",
|
|
613
|
+
description: "PNPM workspace and dependency management conventions",
|
|
614
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
615
|
+
filePatterns: ["package.json", "pnpm-workspace.yaml", "pnpm-lock.yaml"],
|
|
616
|
+
content: [
|
|
617
|
+
"# PNPM Workspace Conventions",
|
|
618
|
+
"",
|
|
619
|
+
"## Package Management",
|
|
620
|
+
"",
|
|
621
|
+
"- Use **PNPM** for package management",
|
|
622
|
+
"- Workspace configuration in `pnpm-workspace.yaml`",
|
|
623
|
+
"- Dependencies managed through Projen, not directly in `package.json`",
|
|
624
|
+
"- **Always use workspace dependencies** (`workspace:*` or `workspace:^`) for packages within this monorepo",
|
|
625
|
+
"- Never use published NPM versions of internal packages; always reference the local workspace version",
|
|
626
|
+
"",
|
|
627
|
+
"## Dependency Management",
|
|
628
|
+
"",
|
|
629
|
+
"**DO:**",
|
|
630
|
+
"- Configure dependencies in Projen configuration files (`.projenrc.ts` or `projenrc/*.ts`)",
|
|
631
|
+
"- Add dependencies to `deps`, `devDeps`, or `peerDeps` arrays in project configuration",
|
|
632
|
+
'- Use catalog dependencies when available (e.g., `"aws-cdk-lib@catalog:"`)',
|
|
633
|
+
"- Ask the user to run `npx projen` and `pnpm install` after updating dependency configuration",
|
|
634
|
+
"",
|
|
635
|
+
"**DO NOT:**",
|
|
636
|
+
"- Run `npm install some-package`",
|
|
637
|
+
"- Run `pnpm add some-package`",
|
|
638
|
+
"- Run `yarn add some-package`",
|
|
639
|
+
"- Manually edit `package.json` dependencies",
|
|
640
|
+
"",
|
|
641
|
+
"Manual package installation creates conflicts with Projen-managed files."
|
|
642
|
+
].join("\n"),
|
|
643
|
+
tags: ["workflow"]
|
|
644
|
+
}
|
|
645
|
+
]
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// src/agent/bundles/projen.ts
|
|
649
|
+
var projenBundle = {
|
|
650
|
+
name: "projen",
|
|
651
|
+
description: "Projen conventions, synthesis workflow, .projenrc.ts patterns",
|
|
652
|
+
appliesWhen: (project) => hasDep(project, "projen"),
|
|
653
|
+
rules: [
|
|
654
|
+
{
|
|
655
|
+
name: "projen-conventions",
|
|
656
|
+
description: "Projen configuration patterns and best practices",
|
|
657
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
658
|
+
filePatterns: ["projenrc/**/*.ts", ".projenrc.ts"],
|
|
659
|
+
content: [
|
|
660
|
+
"# Projen Conventions",
|
|
661
|
+
"",
|
|
662
|
+
"## Configuration Management",
|
|
663
|
+
"",
|
|
664
|
+
"- **Never edit generated files** (`package.json`, `tsconfig.json`, etc. marked with `// ~~ Generated by projen`)",
|
|
665
|
+
"- Edit Projen configuration in:",
|
|
666
|
+
" - `.projenrc.ts` (root)",
|
|
667
|
+
" - `projenrc/*.ts` (package-specific)",
|
|
668
|
+
"- After making Projen changes, ask the user to run `npx projen` locally (agent must not run it)",
|
|
669
|
+
"",
|
|
670
|
+
"## Custom Projen Components",
|
|
671
|
+
"",
|
|
672
|
+
"All custom components must extend `Component` from Projen and use a static `.of()` factory for discovery:",
|
|
673
|
+
"",
|
|
674
|
+
"```typescript",
|
|
675
|
+
"export class MyComponent extends Component {",
|
|
676
|
+
" public static of(project: Project): MyComponent | undefined {",
|
|
677
|
+
" const isDefined = (c: Component): c is MyComponent =>",
|
|
678
|
+
" c instanceof MyComponent;",
|
|
679
|
+
" return project.components.find(isDefined);",
|
|
680
|
+
" }",
|
|
681
|
+
"",
|
|
682
|
+
" constructor(project: Project, options: MyComponentOptions) {",
|
|
683
|
+
" super(project);",
|
|
684
|
+
" // Implementation",
|
|
685
|
+
" }",
|
|
686
|
+
"}",
|
|
687
|
+
"```",
|
|
688
|
+
"",
|
|
689
|
+
"## Component Authoring",
|
|
690
|
+
"",
|
|
691
|
+
"- Export project types and utilities from `index.ts`",
|
|
692
|
+
"- Follow Projen's component lifecycle patterns",
|
|
693
|
+
"- Use `merge` from `ts-deepmerge` for deep merging configuration objects",
|
|
694
|
+
"- Components should be reusable and configurable; use TypeScript interfaces for component options",
|
|
695
|
+
"- Provide sensible defaults while allowing customization",
|
|
696
|
+
"- Document public APIs with minimal JSDoc; put extended documentation in markdown",
|
|
697
|
+
"",
|
|
698
|
+
"## Dependencies",
|
|
699
|
+
"",
|
|
700
|
+
"- Dependencies managed through Projen configuration, not directly in `package.json`",
|
|
701
|
+
'- Use catalog dependencies when available (e.g., `"projen": "catalog:"`)',
|
|
702
|
+
"- Peer dependencies for shared libraries (e.g., `constructs`, `projen`, `aws-cdk-lib`)",
|
|
703
|
+
'- Use workspace protocol for internal packages: `"@org/pkg@workspace:*"`'
|
|
704
|
+
].join("\n"),
|
|
705
|
+
tags: ["workflow"]
|
|
706
|
+
}
|
|
707
|
+
]
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// src/turbo/turbo-repo.ts
|
|
711
|
+
import { Component as Component3, FileBase, JsonFile } from "projen/lib";
|
|
712
|
+
import { JobPermission } from "projen/lib/github/workflows-model";
|
|
179
713
|
|
|
180
714
|
// src/turbo/turbo-repo-task.ts
|
|
181
|
-
import { Component } from "projen/lib";
|
|
182
|
-
var TurboRepoTask = class extends
|
|
715
|
+
import { Component as Component2 } from "projen/lib";
|
|
716
|
+
var TurboRepoTask = class extends Component2 {
|
|
183
717
|
constructor(project, options) {
|
|
184
718
|
super(project);
|
|
185
719
|
this.project = project;
|
|
@@ -218,11 +752,9 @@ var TurboRepoTask = class extends Component {
|
|
|
218
752
|
};
|
|
219
753
|
|
|
220
754
|
// src/turbo/turbo-repo.ts
|
|
221
|
-
import { Component as Component2, FileBase, JsonFile } from "projen/lib";
|
|
222
|
-
import { JobPermission } from "projen/lib/github/workflows-model";
|
|
223
755
|
var ROOT_TURBO_TASK_NAME = "turbo:build";
|
|
224
756
|
var ROOT_CI_TASK_NAME = "build:all";
|
|
225
|
-
var _TurboRepo = class _TurboRepo extends
|
|
757
|
+
var _TurboRepo = class _TurboRepo extends Component3 {
|
|
226
758
|
constructor(project, options = {}) {
|
|
227
759
|
super(project);
|
|
228
760
|
this.project = project;
|
|
@@ -399,35 +931,936 @@ var _TurboRepo = class _TurboRepo extends Component2 {
|
|
|
399
931
|
)
|
|
400
932
|
}
|
|
401
933
|
});
|
|
402
|
-
super.preSynthesize();
|
|
934
|
+
super.preSynthesize();
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
_TurboRepo.buildWorkflowOptions = (remoteCacheOptions) => {
|
|
938
|
+
return {
|
|
939
|
+
env: {
|
|
940
|
+
GIT_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
|
|
941
|
+
},
|
|
942
|
+
permissions: {
|
|
943
|
+
contents: JobPermission.WRITE,
|
|
944
|
+
idToken: JobPermission.WRITE
|
|
945
|
+
},
|
|
946
|
+
preBuildSteps: [
|
|
947
|
+
{
|
|
948
|
+
name: "AWS Creds for SSM",
|
|
949
|
+
uses: "aws-actions/configure-aws-credentials@v4",
|
|
950
|
+
with: {
|
|
951
|
+
["role-to-assume"]: remoteCacheOptions.oidcRole,
|
|
952
|
+
["aws-region"]: "us-east-1",
|
|
953
|
+
["role-duration-seconds"]: "900"
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
]
|
|
957
|
+
};
|
|
958
|
+
};
|
|
959
|
+
var TurboRepo = _TurboRepo;
|
|
960
|
+
|
|
961
|
+
// src/agent/bundles/turborepo.ts
|
|
962
|
+
var turborepoBundle = {
|
|
963
|
+
name: "turborepo",
|
|
964
|
+
description: "Turborepo workspace rules and task pipeline conventions",
|
|
965
|
+
appliesWhen: (project) => hasComponent(project, TurboRepo),
|
|
966
|
+
rules: [
|
|
967
|
+
{
|
|
968
|
+
name: "turborepo-conventions",
|
|
969
|
+
description: "Turborepo build system and task pipeline conventions",
|
|
970
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
971
|
+
filePatterns: ["turbo.json", "package.json"],
|
|
972
|
+
content: [
|
|
973
|
+
"# Turborepo Conventions",
|
|
974
|
+
"",
|
|
975
|
+
"## Build System",
|
|
976
|
+
"",
|
|
977
|
+
"- **Build**: `pnpm build:all` (uses Turborepo)",
|
|
978
|
+
"- **Test**: `pnpm test` or `pnpm test:watch`",
|
|
979
|
+
"- **Lint**: `pnpm eslint`",
|
|
980
|
+
"",
|
|
981
|
+
"## Task Pipeline",
|
|
982
|
+
"",
|
|
983
|
+
"- Uses remote caching (requires AWS credentials)",
|
|
984
|
+
"- Only rebuilds changed packages",
|
|
985
|
+
"- Cache key based on file hashes and dependency graph",
|
|
986
|
+
"- Configured in `turbo.json`",
|
|
987
|
+
"",
|
|
988
|
+
"## Workspace Rules",
|
|
989
|
+
"",
|
|
990
|
+
"- Source files: `src/` directory",
|
|
991
|
+
"- Tests: Co-located with source files (`.spec.ts` or `.test.ts`)",
|
|
992
|
+
"- Exports: Use `index.ts` files for clean public APIs",
|
|
993
|
+
"- Configuration: Managed by Projen (edit `.projenrc.ts` or `projenrc/*.ts`)"
|
|
994
|
+
].join("\n"),
|
|
995
|
+
tags: ["workflow"]
|
|
996
|
+
}
|
|
997
|
+
]
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
// src/agent/bundles/typescript.ts
|
|
1001
|
+
var typescriptBundle = {
|
|
1002
|
+
name: "typescript",
|
|
1003
|
+
description: "TypeScript conventions, type safety, naming, JSDoc, member ordering",
|
|
1004
|
+
appliesWhen: (project) => hasFile(project, "tsconfig.json"),
|
|
1005
|
+
rules: [
|
|
1006
|
+
{
|
|
1007
|
+
name: "typescript-conventions",
|
|
1008
|
+
description: "TypeScript coding conventions and style guide",
|
|
1009
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
1010
|
+
filePatterns: ["**/*.ts", "**/*.tsx"],
|
|
1011
|
+
content: [
|
|
1012
|
+
"# TypeScript Conventions",
|
|
1013
|
+
"",
|
|
1014
|
+
"## Type Safety",
|
|
1015
|
+
"",
|
|
1016
|
+
"- Use **strict TypeScript** with all strict checks enabled",
|
|
1017
|
+
"- Always use explicit types; avoid `any` unless absolutely necessary",
|
|
1018
|
+
"- Use `readonly` for immutable properties",
|
|
1019
|
+
"- Prefer interfaces over types for object shapes",
|
|
1020
|
+
"- Use proper TypeScript types from libraries",
|
|
1021
|
+
"",
|
|
1022
|
+
"## Code Organization",
|
|
1023
|
+
"",
|
|
1024
|
+
"- Follow the member ordering rule:",
|
|
1025
|
+
" 1. Public static fields/methods",
|
|
1026
|
+
" 2. Protected static fields/methods",
|
|
1027
|
+
" 3. Private static fields/methods",
|
|
1028
|
+
" 4. Instance fields",
|
|
1029
|
+
" 5. Constructor",
|
|
1030
|
+
" 6. Methods",
|
|
1031
|
+
"",
|
|
1032
|
+
"## Documentation",
|
|
1033
|
+
"",
|
|
1034
|
+
"- **Keep JSDoc minimal** so that the code remains human-readable. Use brief summaries only.",
|
|
1035
|
+
"- Use JSDoc for:",
|
|
1036
|
+
" - Public classes and interfaces (short description)",
|
|
1037
|
+
" - Public methods and properties (brief purpose; parameter and return when not obvious)",
|
|
1038
|
+
" - Configuration options (one-line description)",
|
|
1039
|
+
"- **Extended documentation** belongs in **markdown** files. Link from JSDoc via `@see` or an inline link.",
|
|
1040
|
+
"",
|
|
1041
|
+
"## Naming Conventions",
|
|
1042
|
+
"",
|
|
1043
|
+
"- **Classes**: PascalCase (e.g., `TypeScriptProject`, `StaticHosting`)",
|
|
1044
|
+
"- **Interfaces**: PascalCase, often with `Props` suffix for configuration (e.g., `StaticHostingProps`)",
|
|
1045
|
+
"- **Functions/Methods**: camelCase (e.g., `configureProject`)",
|
|
1046
|
+
"- **Constants**: UPPER_SNAKE_CASE (e.g., `VERSION`, `AWS_STAGE_TYPE`)",
|
|
1047
|
+
"- **Files**: kebab-case for multi-word files (e.g., `aws-deployment-config.ts`)",
|
|
1048
|
+
"- **Private members**: No prefix needed (TypeScript handles visibility)"
|
|
1049
|
+
].join("\n"),
|
|
1050
|
+
tags: ["coding"]
|
|
1051
|
+
}
|
|
1052
|
+
]
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
// src/vitest/vitest-component.ts
|
|
1056
|
+
import { Component as Component4 } from "projen";
|
|
1057
|
+
import { Jest } from "projen/lib/javascript";
|
|
1058
|
+
import { TextFile } from "projen/lib/textfile";
|
|
1059
|
+
|
|
1060
|
+
// src/versions.ts
|
|
1061
|
+
var VERSION = {
|
|
1062
|
+
/**
|
|
1063
|
+
* CDK CLI for workflows and command line operations.
|
|
1064
|
+
*
|
|
1065
|
+
* CLI and lib are versioned separately, so this is the CLI version.
|
|
1066
|
+
*/
|
|
1067
|
+
AWS_CDK_CLI_VERSION: "2.1117.0",
|
|
1068
|
+
/**
|
|
1069
|
+
* CDK Version to use for construct projects.
|
|
1070
|
+
*
|
|
1071
|
+
* CLI and lib are versioned separately, so this is the lib version.
|
|
1072
|
+
*/
|
|
1073
|
+
AWS_CDK_LIB_VERSION: "2.248.0",
|
|
1074
|
+
/**
|
|
1075
|
+
* Version of the AWS Constructs library to use.
|
|
1076
|
+
*/
|
|
1077
|
+
AWS_CONSTRUCTS_VERSION: "10.6.0",
|
|
1078
|
+
/**
|
|
1079
|
+
* Version of Node.js to use in CI workflows at github actions.
|
|
1080
|
+
*/
|
|
1081
|
+
NODE_WORKFLOWS: "24",
|
|
1082
|
+
/**
|
|
1083
|
+
* Version of PNPM to use in workflows at github actions.
|
|
1084
|
+
*/
|
|
1085
|
+
PNPM_VERSION: "10.33.0",
|
|
1086
|
+
/**
|
|
1087
|
+
* Version of Projen to use.
|
|
1088
|
+
*/
|
|
1089
|
+
PROJEN_VERSION: "0.99.34",
|
|
1090
|
+
/**
|
|
1091
|
+
* What version of the turborepo library should we use?
|
|
1092
|
+
*/
|
|
1093
|
+
TURBO_VERSION: "2.9.4",
|
|
1094
|
+
/**
|
|
1095
|
+
* What version of Vite to use (pnpm override). Pinned to 5.x so Vitest 4.x
|
|
1096
|
+
* can load config (Vite 6+/7+ are ESM-only; see issue #142). Remove override
|
|
1097
|
+
* when moving to ESM-only test setup.
|
|
1098
|
+
*/
|
|
1099
|
+
VITE_VERSION: "5.4.11",
|
|
1100
|
+
/**
|
|
1101
|
+
* What version of Vitest to use when testRunner is 'vitest'.
|
|
1102
|
+
* Pinned to 3.x so it works with Vite 5 (Vitest 4 requires Vite 6). See issue #142.
|
|
1103
|
+
*/
|
|
1104
|
+
VITEST_VERSION: "3.2.4"
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
// src/vitest/vitest-component.ts
|
|
1108
|
+
var Vitest = class _Vitest extends Component4 {
|
|
1109
|
+
constructor(project, options = {}) {
|
|
1110
|
+
super(project);
|
|
1111
|
+
this.project = project;
|
|
1112
|
+
this.configFilePath = options.configFilePath ?? "vitest.config.ts";
|
|
1113
|
+
const config = options.config ?? {};
|
|
1114
|
+
this.include = config.include ?? ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
|
|
1115
|
+
this.exclude = config.exclude ?? [
|
|
1116
|
+
"**/node_modules/**",
|
|
1117
|
+
"**/dist/**",
|
|
1118
|
+
"**/lib/**",
|
|
1119
|
+
"**/.?*"
|
|
1120
|
+
];
|
|
1121
|
+
this.environment = config.environment ?? "node";
|
|
1122
|
+
this.passWithNoTests = config.passWithNoTests ?? true;
|
|
1123
|
+
this.coverageEnabled = config.coverageEnabled ?? true;
|
|
1124
|
+
this.coverageDirectory = config.coverageDirectory ?? "coverage";
|
|
1125
|
+
this.coverageReporters = config.coverageReporters ?? ["text", "lcov"];
|
|
1126
|
+
this.version = options.vitestVersion ?? VERSION.VITEST_VERSION;
|
|
1127
|
+
project.addDevDeps(`vitest@${this.version}`);
|
|
1128
|
+
if (this.coverageEnabled) {
|
|
1129
|
+
project.addDevDeps(`@vitest/coverage-v8@${this.version}`);
|
|
1130
|
+
}
|
|
1131
|
+
const coveragePath = `/${this.coverageDirectory}/`;
|
|
1132
|
+
project.gitignore.addPatterns(coveragePath);
|
|
1133
|
+
project.npmignore?.exclude(coveragePath);
|
|
1134
|
+
this.addTestTasks();
|
|
1135
|
+
this.synthesizeConfig();
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Find the Vitest component on a project.
|
|
1139
|
+
*/
|
|
1140
|
+
static of(project) {
|
|
1141
|
+
const isVitest = (c) => c instanceof _Vitest;
|
|
1142
|
+
return project.components.find(isVitest);
|
|
1143
|
+
}
|
|
1144
|
+
preSynthesize() {
|
|
1145
|
+
super.preSynthesize();
|
|
1146
|
+
for (const component of this.project.components) {
|
|
1147
|
+
if (component instanceof Jest) {
|
|
1148
|
+
throw new Error("Vitest cannot be used together with Jest");
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
addTestTasks() {
|
|
1153
|
+
this.project.testTask.exec("vitest run --update");
|
|
1154
|
+
if (!this.project.tasks.tryFind("test:watch")) {
|
|
1155
|
+
this.project.addTask("test:watch", {
|
|
1156
|
+
description: "Run tests in watch mode",
|
|
1157
|
+
exec: "vitest watch",
|
|
1158
|
+
receiveArgs: true
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
synthesizeConfig() {
|
|
1163
|
+
this.project.tryRemoveFile(this.configFilePath);
|
|
1164
|
+
new TextFile(this, this.configFilePath, {
|
|
1165
|
+
lines: this.renderConfig()
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
renderConfig() {
|
|
1169
|
+
return [
|
|
1170
|
+
'import { defineConfig } from "vitest/config";',
|
|
1171
|
+
"",
|
|
1172
|
+
"export default defineConfig({",
|
|
1173
|
+
" test: {",
|
|
1174
|
+
` include: ${JSON.stringify(this.include)},`,
|
|
1175
|
+
` exclude: ${JSON.stringify(this.exclude)},`,
|
|
1176
|
+
` environment: "${this.environment}",`,
|
|
1177
|
+
` passWithNoTests: ${this.passWithNoTests},`,
|
|
1178
|
+
" coverage: {",
|
|
1179
|
+
` enabled: ${this.coverageEnabled},`,
|
|
1180
|
+
` provider: "v8",`,
|
|
1181
|
+
` reportsDirectory: "${this.coverageDirectory}",`,
|
|
1182
|
+
` reporter: ${JSON.stringify(this.coverageReporters)},`,
|
|
1183
|
+
" },",
|
|
1184
|
+
" },",
|
|
1185
|
+
"});"
|
|
1186
|
+
];
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// src/agent/bundles/vitest.ts
|
|
1191
|
+
var vitestBundle = {
|
|
1192
|
+
name: "vitest",
|
|
1193
|
+
description: "Vitest testing conventions, patterns, and file scoping",
|
|
1194
|
+
appliesWhen: (project) => hasComponent(project, Vitest),
|
|
1195
|
+
rules: [
|
|
1196
|
+
{
|
|
1197
|
+
name: "vitest-testing",
|
|
1198
|
+
description: "Vitest testing conventions and patterns",
|
|
1199
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
1200
|
+
filePatterns: ["**/*.test.ts", "**/*.spec.ts"],
|
|
1201
|
+
content: [
|
|
1202
|
+
"# Vitest Testing Patterns",
|
|
1203
|
+
"",
|
|
1204
|
+
"## Mandatory Requirements",
|
|
1205
|
+
"",
|
|
1206
|
+
"- **Tests MUST be created or updated whenever code functionality is added or changed**",
|
|
1207
|
+
"- This applies to all code changes, including:",
|
|
1208
|
+
" - New features and functionality",
|
|
1209
|
+
" - Bug fixes",
|
|
1210
|
+
" - Refactoring that changes behavior",
|
|
1211
|
+
" - API changes",
|
|
1212
|
+
" - Configuration changes that affect functionality",
|
|
1213
|
+
"- Tests should be written or updated as part of the same change that modifies the code",
|
|
1214
|
+
"",
|
|
1215
|
+
"## Test Structure",
|
|
1216
|
+
"",
|
|
1217
|
+
"- Use **Vitest** as the test runner",
|
|
1218
|
+
"- Test files: `.spec.ts` or `.test.ts` (co-located with source)",
|
|
1219
|
+
"- Use descriptive test names: `describe('ClassName', () => { it('should do something specific', () => {}) })`",
|
|
1220
|
+
"- Prefer snapshot tests for complex object structures",
|
|
1221
|
+
"- Mock external dependencies appropriately",
|
|
1222
|
+
"",
|
|
1223
|
+
"## Test Organization",
|
|
1224
|
+
"",
|
|
1225
|
+
"- Co-locate test files with source files",
|
|
1226
|
+
"- Test files can use dev dependencies (ESLint rule override)",
|
|
1227
|
+
"- Import from `vitest`: `import { describe, expect, it } from 'vitest'`",
|
|
1228
|
+
"",
|
|
1229
|
+
"## Example Test Structure",
|
|
1230
|
+
"",
|
|
1231
|
+
"```typescript",
|
|
1232
|
+
"import { describe, expect, it } from 'vitest';",
|
|
1233
|
+
"import { MyClass } from './my-class';",
|
|
1234
|
+
"",
|
|
1235
|
+
"describe('MyClass', () => {",
|
|
1236
|
+
" it('should do something specific', () => {",
|
|
1237
|
+
" // Test implementation",
|
|
1238
|
+
" });",
|
|
1239
|
+
"});",
|
|
1240
|
+
"```"
|
|
1241
|
+
].join("\n"),
|
|
1242
|
+
tags: ["testing"]
|
|
1243
|
+
}
|
|
1244
|
+
]
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
// src/agent/bundles/index.ts
|
|
1248
|
+
var BUILT_IN_BUNDLES = [
|
|
1249
|
+
baseBundle,
|
|
1250
|
+
typescriptBundle,
|
|
1251
|
+
vitestBundle,
|
|
1252
|
+
jestBundle,
|
|
1253
|
+
turborepoBundle,
|
|
1254
|
+
pnpmBundle,
|
|
1255
|
+
awsCdkBundle,
|
|
1256
|
+
projenBundle
|
|
1257
|
+
];
|
|
1258
|
+
|
|
1259
|
+
// src/agent/renderers/claude-renderer.ts
|
|
1260
|
+
import { JsonFile as JsonFile2 } from "projen";
|
|
1261
|
+
import { TextFile as TextFile2 } from "projen/lib/textfile";
|
|
1262
|
+
var GENERATED_MARKER = "<!-- ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~ -->";
|
|
1263
|
+
var ClaudeRenderer = class _ClaudeRenderer {
|
|
1264
|
+
/**
|
|
1265
|
+
* Render all Claude Code configuration files.
|
|
1266
|
+
*/
|
|
1267
|
+
static render(component, rules, skills, subAgents, mcpServers, settings) {
|
|
1268
|
+
_ClaudeRenderer.renderClaudeMd(component, rules);
|
|
1269
|
+
_ClaudeRenderer.renderScopedRules(component, rules);
|
|
1270
|
+
_ClaudeRenderer.renderSettings(component, mcpServers, settings);
|
|
1271
|
+
_ClaudeRenderer.renderSkills(component, skills);
|
|
1272
|
+
_ClaudeRenderer.renderSubAgents(component, subAgents);
|
|
1273
|
+
}
|
|
1274
|
+
static renderClaudeMd(component, rules) {
|
|
1275
|
+
const claudeMdRules = rules.filter((r) => {
|
|
1276
|
+
if (r.platforms?.claude?.exclude) return false;
|
|
1277
|
+
const target = r.platforms?.claude?.target ?? _ClaudeRenderer.defaultTarget(r);
|
|
1278
|
+
return target === CLAUDE_RULE_TARGET.CLAUDE_MD;
|
|
1279
|
+
});
|
|
1280
|
+
if (claudeMdRules.length === 0) return;
|
|
1281
|
+
const lines = [GENERATED_MARKER, ""];
|
|
1282
|
+
for (let i = 0; i < claudeMdRules.length; i++) {
|
|
1283
|
+
if (i > 0) lines.push("", "---", "");
|
|
1284
|
+
lines.push(...claudeMdRules[i].content.split("\n"));
|
|
1285
|
+
}
|
|
1286
|
+
new TextFile2(component, "CLAUDE.md", { lines });
|
|
1287
|
+
}
|
|
1288
|
+
static renderScopedRules(component, rules) {
|
|
1289
|
+
const scopedRules = rules.filter((r) => {
|
|
1290
|
+
if (r.platforms?.claude?.exclude) return false;
|
|
1291
|
+
const target = r.platforms?.claude?.target ?? _ClaudeRenderer.defaultTarget(r);
|
|
1292
|
+
return target === CLAUDE_RULE_TARGET.SCOPED_FILE;
|
|
1293
|
+
});
|
|
1294
|
+
for (const rule of scopedRules) {
|
|
1295
|
+
const lines = [];
|
|
1296
|
+
if (rule.filePatterns && rule.filePatterns.length > 0) {
|
|
1297
|
+
lines.push("---");
|
|
1298
|
+
lines.push("paths:");
|
|
1299
|
+
for (const pattern of rule.filePatterns) {
|
|
1300
|
+
lines.push(` - "${pattern}"`);
|
|
1301
|
+
}
|
|
1302
|
+
lines.push("---");
|
|
1303
|
+
lines.push("");
|
|
1304
|
+
}
|
|
1305
|
+
lines.push(...rule.content.split("\n"));
|
|
1306
|
+
new TextFile2(component, `.claude/rules/${rule.name}.md`, { lines });
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
static renderSettings(component, mcpServers, settings) {
|
|
1310
|
+
const obj = {};
|
|
1311
|
+
let hasContent = false;
|
|
1312
|
+
if (settings?.defaultMode) {
|
|
1313
|
+
obj.defaultMode = settings.defaultMode;
|
|
1314
|
+
hasContent = true;
|
|
1315
|
+
}
|
|
1316
|
+
if (settings?.permissions) {
|
|
1317
|
+
const perms = {};
|
|
1318
|
+
if (settings.permissions.allow?.length) {
|
|
1319
|
+
perms.allow = [...settings.permissions.allow];
|
|
1320
|
+
}
|
|
1321
|
+
if (settings.permissions.deny?.length) {
|
|
1322
|
+
perms.deny = [...settings.permissions.deny];
|
|
1323
|
+
}
|
|
1324
|
+
if (settings.permissions.ask?.length) {
|
|
1325
|
+
perms.ask = [...settings.permissions.ask];
|
|
1326
|
+
}
|
|
1327
|
+
if (settings.permissions.additionalDirectories?.length) {
|
|
1328
|
+
perms.additionalDirectories = [
|
|
1329
|
+
...settings.permissions.additionalDirectories
|
|
1330
|
+
];
|
|
1331
|
+
}
|
|
1332
|
+
if (Object.keys(perms).length > 0) {
|
|
1333
|
+
obj.permissions = perms;
|
|
1334
|
+
hasContent = true;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
if (settings?.hooks) {
|
|
1338
|
+
const hooks = {};
|
|
1339
|
+
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
1340
|
+
if (entries && entries.length > 0) {
|
|
1341
|
+
hooks[event] = entries;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
if (Object.keys(hooks).length > 0) {
|
|
1345
|
+
obj.hooks = hooks;
|
|
1346
|
+
hasContent = true;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
const allMcpServers = {};
|
|
1350
|
+
for (const [name, config] of Object.entries(mcpServers)) {
|
|
1351
|
+
const server = {};
|
|
1352
|
+
if (config.command) server.command = config.command;
|
|
1353
|
+
if (config.args) server.args = [...config.args];
|
|
1354
|
+
if (config.url) server.url = config.url;
|
|
1355
|
+
if (config.env) server.env = { ...config.env };
|
|
1356
|
+
allMcpServers[name] = server;
|
|
1357
|
+
}
|
|
1358
|
+
if (settings?.mcpServers) {
|
|
1359
|
+
for (const [name, config] of Object.entries(settings.mcpServers)) {
|
|
1360
|
+
const server = {};
|
|
1361
|
+
if (config.command) server.command = config.command;
|
|
1362
|
+
if (config.args) server.args = [...config.args];
|
|
1363
|
+
if (config.url) server.url = config.url;
|
|
1364
|
+
if (config.env) server.env = { ...config.env };
|
|
1365
|
+
allMcpServers[name] = server;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (Object.keys(allMcpServers).length > 0) {
|
|
1369
|
+
obj.mcpServers = allMcpServers;
|
|
1370
|
+
hasContent = true;
|
|
1371
|
+
}
|
|
1372
|
+
if (settings?.allowedMcpServers?.length) {
|
|
1373
|
+
obj.allowedMcpServers = [...settings.allowedMcpServers];
|
|
1374
|
+
hasContent = true;
|
|
1375
|
+
}
|
|
1376
|
+
if (settings?.env && Object.keys(settings.env).length > 0) {
|
|
1377
|
+
obj.env = { ...settings.env };
|
|
1378
|
+
hasContent = true;
|
|
1379
|
+
}
|
|
1380
|
+
if (settings?.sandbox) {
|
|
1381
|
+
obj.sandbox = _ClaudeRenderer.buildSandboxObj(settings.sandbox);
|
|
1382
|
+
hasContent = true;
|
|
1383
|
+
}
|
|
1384
|
+
if (settings?.autoMode) {
|
|
1385
|
+
const autoMode = {};
|
|
1386
|
+
if (settings.autoMode.environment?.length) {
|
|
1387
|
+
autoMode.environment = [...settings.autoMode.environment];
|
|
1388
|
+
}
|
|
1389
|
+
if (settings.autoMode.allow?.length) {
|
|
1390
|
+
autoMode.allow = [...settings.autoMode.allow];
|
|
1391
|
+
}
|
|
1392
|
+
if (settings.autoMode.soft_deny?.length) {
|
|
1393
|
+
autoMode.soft_deny = [...settings.autoMode.soft_deny];
|
|
1394
|
+
}
|
|
1395
|
+
if (Object.keys(autoMode).length > 0) {
|
|
1396
|
+
obj.autoMode = autoMode;
|
|
1397
|
+
hasContent = true;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (settings?.disableBypassPermissionsMode) {
|
|
1401
|
+
obj.disableBypassPermissionsMode = settings.disableBypassPermissionsMode;
|
|
1402
|
+
hasContent = true;
|
|
1403
|
+
}
|
|
1404
|
+
if (settings?.disableAutoMode) {
|
|
1405
|
+
obj.disableAutoMode = settings.disableAutoMode;
|
|
1406
|
+
hasContent = true;
|
|
1407
|
+
}
|
|
1408
|
+
if (settings?.disableAllHooks !== void 0) {
|
|
1409
|
+
obj.disableAllHooks = settings.disableAllHooks;
|
|
1410
|
+
hasContent = true;
|
|
1411
|
+
}
|
|
1412
|
+
if (settings?.excludeSensitivePatterns?.length) {
|
|
1413
|
+
obj.excludeSensitivePatterns = [...settings.excludeSensitivePatterns];
|
|
1414
|
+
hasContent = true;
|
|
1415
|
+
}
|
|
1416
|
+
if (settings?.attribution) {
|
|
1417
|
+
obj.attribution = settings.attribution;
|
|
1418
|
+
hasContent = true;
|
|
1419
|
+
}
|
|
1420
|
+
if (!hasContent) return;
|
|
1421
|
+
new JsonFile2(component, ".claude/settings.json", { obj });
|
|
1422
|
+
}
|
|
1423
|
+
static buildSandboxObj(sandbox) {
|
|
1424
|
+
const obj = {};
|
|
1425
|
+
if (sandbox.enabled !== void 0) obj.enabled = sandbox.enabled;
|
|
1426
|
+
if (sandbox.mode) obj.mode = sandbox.mode;
|
|
1427
|
+
if (sandbox.failIfUnavailable !== void 0) {
|
|
1428
|
+
obj.failIfUnavailable = sandbox.failIfUnavailable;
|
|
1429
|
+
}
|
|
1430
|
+
if (sandbox.autoAllowBashIfSandboxed !== void 0) {
|
|
1431
|
+
obj.autoAllowBashIfSandboxed = sandbox.autoAllowBashIfSandboxed;
|
|
1432
|
+
}
|
|
1433
|
+
if (sandbox.excludedCommands?.length) {
|
|
1434
|
+
obj.excludedCommands = [...sandbox.excludedCommands];
|
|
1435
|
+
}
|
|
1436
|
+
if (sandbox.filesystem) {
|
|
1437
|
+
const fs = {};
|
|
1438
|
+
if (sandbox.filesystem.allowRead?.length) {
|
|
1439
|
+
fs.allowRead = [...sandbox.filesystem.allowRead];
|
|
1440
|
+
}
|
|
1441
|
+
if (sandbox.filesystem.denyRead?.length) {
|
|
1442
|
+
fs.denyRead = [...sandbox.filesystem.denyRead];
|
|
1443
|
+
}
|
|
1444
|
+
if (sandbox.filesystem.allowWrite?.length) {
|
|
1445
|
+
fs.allowWrite = [...sandbox.filesystem.allowWrite];
|
|
1446
|
+
}
|
|
1447
|
+
if (sandbox.filesystem.denyWrite?.length) {
|
|
1448
|
+
fs.denyWrite = [...sandbox.filesystem.denyWrite];
|
|
1449
|
+
}
|
|
1450
|
+
if (Object.keys(fs).length > 0) obj.filesystem = fs;
|
|
1451
|
+
}
|
|
1452
|
+
if (sandbox.network) {
|
|
1453
|
+
const net = {};
|
|
1454
|
+
if (sandbox.network.allowedDomains?.length) {
|
|
1455
|
+
net.allowedDomains = [...sandbox.network.allowedDomains];
|
|
1456
|
+
}
|
|
1457
|
+
if (sandbox.network.denyDomains?.length) {
|
|
1458
|
+
net.denyDomains = [...sandbox.network.denyDomains];
|
|
1459
|
+
}
|
|
1460
|
+
if (Object.keys(net).length > 0) obj.network = net;
|
|
1461
|
+
}
|
|
1462
|
+
return obj;
|
|
1463
|
+
}
|
|
1464
|
+
static renderSkills(component, skills) {
|
|
1465
|
+
for (const skill of skills) {
|
|
1466
|
+
const lines = [];
|
|
1467
|
+
lines.push("---");
|
|
1468
|
+
lines.push(`name: "${skill.name}"`);
|
|
1469
|
+
lines.push(`description: "${skill.description}"`);
|
|
1470
|
+
if (skill.disableModelInvocation) {
|
|
1471
|
+
lines.push(`disable-model-invocation: true`);
|
|
1472
|
+
}
|
|
1473
|
+
if (skill.userInvocable === false) {
|
|
1474
|
+
lines.push(`user-invocable: false`);
|
|
1475
|
+
}
|
|
1476
|
+
if (skill.model) {
|
|
1477
|
+
lines.push(`model: "${skill.model}"`);
|
|
1478
|
+
}
|
|
1479
|
+
if (skill.effort) {
|
|
1480
|
+
lines.push(`effort: "${skill.effort}"`);
|
|
1481
|
+
}
|
|
1482
|
+
if (skill.paths && skill.paths.length > 0) {
|
|
1483
|
+
lines.push(`paths:`);
|
|
1484
|
+
for (const p of skill.paths) {
|
|
1485
|
+
lines.push(` - "${p}"`);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
1489
|
+
lines.push(`allowed-tools:`);
|
|
1490
|
+
for (const tool of skill.allowedTools) {
|
|
1491
|
+
lines.push(` - "${tool}"`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
lines.push("---");
|
|
1495
|
+
lines.push("");
|
|
1496
|
+
lines.push(...skill.instructions.split("\n"));
|
|
1497
|
+
new TextFile2(component, `.claude/skills/${skill.name}/SKILL.md`, {
|
|
1498
|
+
lines
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
static renderSubAgents(component, subAgents) {
|
|
1503
|
+
for (const agent of subAgents) {
|
|
1504
|
+
if (agent.platforms?.claude?.exclude) continue;
|
|
1505
|
+
const lines = [];
|
|
1506
|
+
lines.push("---");
|
|
1507
|
+
lines.push(`name: ${agent.name}`);
|
|
1508
|
+
lines.push(`description: >-`);
|
|
1509
|
+
lines.push(` ${agent.description}`);
|
|
1510
|
+
if (agent.model) {
|
|
1511
|
+
lines.push(`model: ${agent.model}`);
|
|
1512
|
+
}
|
|
1513
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
1514
|
+
lines.push(`tools:`);
|
|
1515
|
+
for (const tool of agent.tools) {
|
|
1516
|
+
lines.push(` - "${tool}"`);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (agent.maxTurns) {
|
|
1520
|
+
lines.push(`max_turns: ${agent.maxTurns}`);
|
|
1521
|
+
}
|
|
1522
|
+
if (agent.platforms?.claude?.permissionMode) {
|
|
1523
|
+
lines.push(`permission_mode: ${agent.platforms.claude.permissionMode}`);
|
|
1524
|
+
}
|
|
1525
|
+
if (agent.platforms?.claude?.isolation) {
|
|
1526
|
+
lines.push(`isolation: ${agent.platforms.claude.isolation}`);
|
|
1527
|
+
}
|
|
1528
|
+
if (agent.platforms?.claude?.effort) {
|
|
1529
|
+
lines.push(`effort: ${agent.platforms.claude.effort}`);
|
|
1530
|
+
}
|
|
1531
|
+
lines.push("---");
|
|
1532
|
+
lines.push("");
|
|
1533
|
+
lines.push(...agent.prompt.split("\n"));
|
|
1534
|
+
new TextFile2(component, `.claude/agents/${agent.name}.md`, { lines });
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Determine the default Claude rule target based on rule scope.
|
|
1539
|
+
* ALWAYS-scoped rules default to CLAUDE_MD; FILE_PATTERN rules default to SCOPED_FILE.
|
|
1540
|
+
*/
|
|
1541
|
+
static defaultTarget(rule) {
|
|
1542
|
+
return rule.scope === AGENT_RULE_SCOPE.ALWAYS ? CLAUDE_RULE_TARGET.CLAUDE_MD : CLAUDE_RULE_TARGET.SCOPED_FILE;
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
// src/agent/renderers/codex-renderer.ts
|
|
1547
|
+
var CodexRenderer = class {
|
|
1548
|
+
static render(_component, _rules, _skills, _subAgents) {
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1552
|
+
// src/agent/renderers/copilot-renderer.ts
|
|
1553
|
+
var CopilotRenderer = class {
|
|
1554
|
+
static render(_component, _rules, _skills, _subAgents) {
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
// src/agent/renderers/cursor-renderer.ts
|
|
1559
|
+
import { JsonFile as JsonFile3 } from "projen";
|
|
1560
|
+
import { TextFile as TextFile3 } from "projen/lib/textfile";
|
|
1561
|
+
var GENERATED_MARKER2 = "# ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~";
|
|
1562
|
+
var CursorRenderer = class _CursorRenderer {
|
|
1563
|
+
/**
|
|
1564
|
+
* Render all Cursor configuration files.
|
|
1565
|
+
*/
|
|
1566
|
+
static render(component, rules, skills, subAgents, mcpServers, settings) {
|
|
1567
|
+
_CursorRenderer.renderRules(component, rules);
|
|
1568
|
+
_CursorRenderer.renderSkills(component, skills);
|
|
1569
|
+
_CursorRenderer.renderSubAgents(component, subAgents);
|
|
1570
|
+
_CursorRenderer.renderMcpServers(component, mcpServers);
|
|
1571
|
+
_CursorRenderer.renderHooks(component, settings);
|
|
1572
|
+
_CursorRenderer.renderIgnoreFiles(component, settings);
|
|
1573
|
+
}
|
|
1574
|
+
static renderRules(component, rules) {
|
|
1575
|
+
for (const rule of rules) {
|
|
1576
|
+
if (rule.platforms?.cursor?.exclude) continue;
|
|
1577
|
+
const lines = [];
|
|
1578
|
+
const description = rule.platforms?.cursor?.description ?? rule.description;
|
|
1579
|
+
const isAlways = rule.scope === AGENT_RULE_SCOPE.ALWAYS;
|
|
1580
|
+
lines.push("---");
|
|
1581
|
+
lines.push(`description: "${description}"`);
|
|
1582
|
+
lines.push(`alwaysApply: ${isAlways}`);
|
|
1583
|
+
if (!isAlways && rule.filePatterns && rule.filePatterns.length > 0) {
|
|
1584
|
+
lines.push(`path: ${JSON.stringify([...rule.filePatterns])}`);
|
|
1585
|
+
}
|
|
1586
|
+
lines.push("---");
|
|
1587
|
+
lines.push("");
|
|
1588
|
+
lines.push(...rule.content.split("\n"));
|
|
1589
|
+
new TextFile3(component, `.cursor/rules/${rule.name}.mdc`, { lines });
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
static renderSkills(component, skills) {
|
|
1593
|
+
for (const skill of skills) {
|
|
1594
|
+
const lines = [];
|
|
1595
|
+
lines.push("---");
|
|
1596
|
+
lines.push(`name: "${skill.name}"`);
|
|
1597
|
+
lines.push(`description: "${skill.description}"`);
|
|
1598
|
+
if (skill.disableModelInvocation) {
|
|
1599
|
+
lines.push(`disable-model-invocation: true`);
|
|
1600
|
+
}
|
|
1601
|
+
if (skill.userInvocable === false) {
|
|
1602
|
+
lines.push(`user-invocable: false`);
|
|
1603
|
+
}
|
|
1604
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
1605
|
+
lines.push(`allowed-tools:`);
|
|
1606
|
+
for (const tool of skill.allowedTools) {
|
|
1607
|
+
lines.push(` - "${tool}"`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
lines.push("---");
|
|
1611
|
+
lines.push("");
|
|
1612
|
+
lines.push(...skill.instructions.split("\n"));
|
|
1613
|
+
new TextFile3(component, `.cursor/skills/${skill.name}/SKILL.md`, {
|
|
1614
|
+
lines
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
static renderSubAgents(component, subAgents) {
|
|
1619
|
+
for (const agent of subAgents) {
|
|
1620
|
+
if (agent.platforms?.cursor?.exclude) continue;
|
|
1621
|
+
const lines = [];
|
|
1622
|
+
lines.push("---");
|
|
1623
|
+
lines.push(`name: ${agent.name}`);
|
|
1624
|
+
lines.push(`description: >-`);
|
|
1625
|
+
lines.push(` ${agent.description}`);
|
|
1626
|
+
if (agent.model) {
|
|
1627
|
+
lines.push(`model: ${agent.model}`);
|
|
1628
|
+
}
|
|
1629
|
+
if (agent.platforms?.cursor?.readonly) {
|
|
1630
|
+
lines.push(`readonly: true`);
|
|
1631
|
+
}
|
|
1632
|
+
if (agent.platforms?.cursor?.isBackground) {
|
|
1633
|
+
lines.push(`is_background: true`);
|
|
1634
|
+
}
|
|
1635
|
+
lines.push("---");
|
|
1636
|
+
lines.push("");
|
|
1637
|
+
lines.push(...agent.prompt.split("\n"));
|
|
1638
|
+
new TextFile3(component, `.cursor/agents/${agent.name}.md`, { lines });
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
static renderMcpServers(component, mcpServers) {
|
|
1642
|
+
const serverNames = Object.keys(mcpServers);
|
|
1643
|
+
if (serverNames.length === 0) return;
|
|
1644
|
+
const obj = { mcpServers: {} };
|
|
1645
|
+
const servers = obj.mcpServers;
|
|
1646
|
+
for (const [name, config] of Object.entries(mcpServers)) {
|
|
1647
|
+
const server = {};
|
|
1648
|
+
if (config.command) server.command = config.command;
|
|
1649
|
+
if (config.args) server.args = [...config.args];
|
|
1650
|
+
if (config.url) server.url = config.url;
|
|
1651
|
+
if (config.env) server.env = { ...config.env };
|
|
1652
|
+
servers[name] = server;
|
|
1653
|
+
}
|
|
1654
|
+
new JsonFile3(component, ".cursor/mcp.json", { obj });
|
|
1655
|
+
}
|
|
1656
|
+
static renderHooks(component, settings) {
|
|
1657
|
+
if (!settings?.hooks) return;
|
|
1658
|
+
const hooks = {};
|
|
1659
|
+
const {
|
|
1660
|
+
beforeSubmitPrompt,
|
|
1661
|
+
beforeShellExecution,
|
|
1662
|
+
beforeMCPExecution,
|
|
1663
|
+
beforeReadFile,
|
|
1664
|
+
afterFileEdit,
|
|
1665
|
+
stop
|
|
1666
|
+
} = settings.hooks;
|
|
1667
|
+
if (beforeSubmitPrompt?.length) {
|
|
1668
|
+
hooks.beforeSubmitPrompt = beforeSubmitPrompt.map((h) => ({
|
|
1669
|
+
command: h.command
|
|
1670
|
+
}));
|
|
1671
|
+
}
|
|
1672
|
+
if (beforeShellExecution?.length) {
|
|
1673
|
+
hooks.beforeShellExecution = beforeShellExecution.map((h) => ({
|
|
1674
|
+
command: h.command
|
|
1675
|
+
}));
|
|
1676
|
+
}
|
|
1677
|
+
if (beforeMCPExecution?.length) {
|
|
1678
|
+
hooks.beforeMCPExecution = beforeMCPExecution.map((h) => ({
|
|
1679
|
+
command: h.command
|
|
1680
|
+
}));
|
|
1681
|
+
}
|
|
1682
|
+
if (beforeReadFile?.length) {
|
|
1683
|
+
hooks.beforeReadFile = beforeReadFile.map((h) => ({
|
|
1684
|
+
command: h.command
|
|
1685
|
+
}));
|
|
1686
|
+
}
|
|
1687
|
+
if (afterFileEdit?.length) {
|
|
1688
|
+
hooks.afterFileEdit = afterFileEdit.map((h) => ({ command: h.command }));
|
|
1689
|
+
}
|
|
1690
|
+
if (stop?.length) hooks.stop = stop.map((h) => ({ command: h.command }));
|
|
1691
|
+
if (Object.keys(hooks).length === 0) return;
|
|
1692
|
+
new JsonFile3(component, ".cursor/hooks.json", {
|
|
1693
|
+
obj: { version: 1, hooks }
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
static renderIgnoreFiles(component, settings) {
|
|
1697
|
+
if (settings?.ignorePatterns && settings.ignorePatterns.length > 0) {
|
|
1698
|
+
new TextFile3(component, ".cursorignore", {
|
|
1699
|
+
lines: [GENERATED_MARKER2, "", ...settings.ignorePatterns]
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
if (settings?.indexingIgnorePatterns && settings.indexingIgnorePatterns.length > 0) {
|
|
1703
|
+
new TextFile3(component, ".cursorindexingignore", {
|
|
1704
|
+
lines: [GENERATED_MARKER2, "", ...settings.indexingIgnorePatterns]
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
// src/agent/agent-config.ts
|
|
1711
|
+
var AgentConfig = class _AgentConfig extends Component7 {
|
|
1712
|
+
/**
|
|
1713
|
+
* Find the AgentConfig component on a project.
|
|
1714
|
+
*/
|
|
1715
|
+
static of(project) {
|
|
1716
|
+
const isAgentConfig = (c) => c instanceof _AgentConfig;
|
|
1717
|
+
return project.components.find(isAgentConfig);
|
|
1718
|
+
}
|
|
1719
|
+
constructor(project, options = {}) {
|
|
1720
|
+
super(project);
|
|
1721
|
+
this.options = options;
|
|
1722
|
+
}
|
|
1723
|
+
preSynthesize() {
|
|
1724
|
+
super.preSynthesize();
|
|
1725
|
+
const platforms = this.resolvePlatforms();
|
|
1726
|
+
const rules = this.resolveRules();
|
|
1727
|
+
const skills = this.resolveSkills();
|
|
1728
|
+
const subAgents = this.resolveSubAgents();
|
|
1729
|
+
const mcpServers = this.options.mcpServers ?? {};
|
|
1730
|
+
if (platforms.includes(AGENT_PLATFORM.CURSOR)) {
|
|
1731
|
+
CursorRenderer.render(
|
|
1732
|
+
this,
|
|
1733
|
+
rules,
|
|
1734
|
+
skills,
|
|
1735
|
+
subAgents,
|
|
1736
|
+
mcpServers,
|
|
1737
|
+
this.options.cursorSettings
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
if (platforms.includes(AGENT_PLATFORM.CLAUDE)) {
|
|
1741
|
+
ClaudeRenderer.render(
|
|
1742
|
+
this,
|
|
1743
|
+
rules,
|
|
1744
|
+
skills,
|
|
1745
|
+
subAgents,
|
|
1746
|
+
mcpServers,
|
|
1747
|
+
this.options.claudeSettings
|
|
1748
|
+
);
|
|
1749
|
+
}
|
|
1750
|
+
if (platforms.includes(AGENT_PLATFORM.CODEX)) {
|
|
1751
|
+
CodexRenderer.render(this, rules, skills, subAgents);
|
|
1752
|
+
}
|
|
1753
|
+
if (platforms.includes(AGENT_PLATFORM.COPILOT)) {
|
|
1754
|
+
CopilotRenderer.render(this, rules, skills, subAgents);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
resolvePlatforms() {
|
|
1758
|
+
return this.options.platforms ?? [AGENT_PLATFORM.CURSOR, AGENT_PLATFORM.CLAUDE];
|
|
1759
|
+
}
|
|
1760
|
+
resolveRules() {
|
|
1761
|
+
const ruleMap = /* @__PURE__ */ new Map();
|
|
1762
|
+
if (this.options.autoDetectBundles !== false) {
|
|
1763
|
+
for (const bundle of BUILT_IN_BUNDLES) {
|
|
1764
|
+
if (this.options.excludeBundles?.includes(bundle.name)) continue;
|
|
1765
|
+
if (bundle.name === "base" && this.options.includeBaseRules === false) {
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
if (bundle.appliesWhen(this.project)) {
|
|
1769
|
+
for (const rule of bundle.rules) {
|
|
1770
|
+
ruleMap.set(rule.name, rule);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (this.options.includeBundles) {
|
|
1776
|
+
for (const bundleName of this.options.includeBundles) {
|
|
1777
|
+
const bundle = BUILT_IN_BUNDLES.find((b) => b.name === bundleName);
|
|
1778
|
+
if (bundle) {
|
|
1779
|
+
for (const rule of bundle.rules) {
|
|
1780
|
+
ruleMap.set(rule.name, rule);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
if (this.options.rules) {
|
|
1786
|
+
for (const rule of this.options.rules) {
|
|
1787
|
+
ruleMap.set(rule.name, rule);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
if (this.options.excludeRules) {
|
|
1791
|
+
for (const name of this.options.excludeRules) {
|
|
1792
|
+
ruleMap.delete(name);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
if (this.options.ruleExtensions) {
|
|
1796
|
+
for (const [name, extra] of Object.entries(this.options.ruleExtensions)) {
|
|
1797
|
+
const existing = ruleMap.get(name);
|
|
1798
|
+
if (existing) {
|
|
1799
|
+
ruleMap.set(name, {
|
|
1800
|
+
...existing,
|
|
1801
|
+
content: `${existing.content}
|
|
1802
|
+
|
|
1803
|
+
---
|
|
1804
|
+
|
|
1805
|
+
${extra}`
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return [...ruleMap.values()].sort((a, b) => {
|
|
1811
|
+
if (a.name === "project-overview") return -1;
|
|
1812
|
+
if (b.name === "project-overview") return 1;
|
|
1813
|
+
const tagA = a.tags?.[0] ?? "\uFFFF";
|
|
1814
|
+
const tagB = b.tags?.[0] ?? "\uFFFF";
|
|
1815
|
+
if (tagA !== tagB) return tagA.localeCompare(tagB);
|
|
1816
|
+
return a.name.localeCompare(b.name);
|
|
1817
|
+
});
|
|
403
1818
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
},
|
|
414
|
-
preBuildSteps: [
|
|
415
|
-
{
|
|
416
|
-
name: "AWS Creds for SSM",
|
|
417
|
-
uses: "aws-actions/configure-aws-credentials@v4",
|
|
418
|
-
with: {
|
|
419
|
-
["role-to-assume"]: remoteCacheOptions.oidcRole,
|
|
420
|
-
["aws-region"]: "us-east-1",
|
|
421
|
-
["role-duration-seconds"]: "900"
|
|
1819
|
+
resolveSkills() {
|
|
1820
|
+
const skillMap = /* @__PURE__ */ new Map();
|
|
1821
|
+
if (this.options.autoDetectBundles !== false) {
|
|
1822
|
+
for (const bundle of BUILT_IN_BUNDLES) {
|
|
1823
|
+
if (this.options.excludeBundles?.includes(bundle.name)) continue;
|
|
1824
|
+
if (bundle.appliesWhen(this.project) && bundle.skills) {
|
|
1825
|
+
for (const skill of bundle.skills) {
|
|
1826
|
+
skillMap.set(skill.name, skill);
|
|
1827
|
+
}
|
|
422
1828
|
}
|
|
423
1829
|
}
|
|
424
|
-
|
|
425
|
-
|
|
1830
|
+
}
|
|
1831
|
+
if (this.options.skills) {
|
|
1832
|
+
for (const skill of this.options.skills) {
|
|
1833
|
+
skillMap.set(skill.name, skill);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
return [...skillMap.values()];
|
|
1837
|
+
}
|
|
1838
|
+
resolveSubAgents() {
|
|
1839
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
1840
|
+
if (this.options.autoDetectBundles !== false) {
|
|
1841
|
+
for (const bundle of BUILT_IN_BUNDLES) {
|
|
1842
|
+
if (this.options.excludeBundles?.includes(bundle.name)) continue;
|
|
1843
|
+
if (bundle.appliesWhen(this.project) && bundle.subAgents) {
|
|
1844
|
+
for (const agent of bundle.subAgents) {
|
|
1845
|
+
agentMap.set(agent.name, agent);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
if (this.options.subAgents) {
|
|
1851
|
+
for (const agent of this.options.subAgents) {
|
|
1852
|
+
agentMap.set(agent.name, agent);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
return [...agentMap.values()];
|
|
1856
|
+
}
|
|
426
1857
|
};
|
|
427
|
-
var TurboRepo = _TurboRepo;
|
|
428
1858
|
|
|
429
1859
|
// src/aws/aws-deployment-config.ts
|
|
430
|
-
var
|
|
1860
|
+
var import_utils8 = __toESM(require_lib());
|
|
1861
|
+
import { join, relative as relative2 } from "path";
|
|
1862
|
+
import { Component as Component8 } from "projen";
|
|
1863
|
+
var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component8 {
|
|
431
1864
|
constructor(project) {
|
|
432
1865
|
super(project);
|
|
433
1866
|
/**
|
|
@@ -451,8 +1884,8 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component3 {
|
|
|
451
1884
|
this.env = {
|
|
452
1885
|
GIT_BRANCH_NAME: '$(echo "${GIT_BRANCH_NAME:-$(git branch --show-current)}")'
|
|
453
1886
|
};
|
|
454
|
-
this.projectPath =
|
|
455
|
-
this.rootPath =
|
|
1887
|
+
this.projectPath = relative2(project.root.outdir, project.outdir);
|
|
1888
|
+
this.rootPath = relative2(project.outdir, project.root.outdir);
|
|
456
1889
|
this.rootCdkOut = join("dist", this.projectPath, "cdk.out");
|
|
457
1890
|
this.cdkOut = join(this.rootPath, "dist", this.projectPath, "cdk.out");
|
|
458
1891
|
["deploy", "watch"].forEach((taskName) => {
|
|
@@ -482,17 +1915,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component3 {
|
|
|
482
1915
|
*/
|
|
483
1916
|
get prodTargets() {
|
|
484
1917
|
return this.awsDeploymentTargets.filter(
|
|
485
|
-
(target) => target.awsStageType ===
|
|
1918
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD
|
|
486
1919
|
);
|
|
487
1920
|
}
|
|
488
1921
|
get prodTargetsForCI() {
|
|
489
1922
|
return this.awsDeploymentTargets.filter(
|
|
490
|
-
(target) => target.awsStageType ===
|
|
1923
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.ciDeployment
|
|
491
1924
|
);
|
|
492
1925
|
}
|
|
493
1926
|
get prodTargetsForLocal() {
|
|
494
1927
|
return this.awsDeploymentTargets.filter(
|
|
495
|
-
(target) => target.awsStageType ===
|
|
1928
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.localDeployment
|
|
496
1929
|
);
|
|
497
1930
|
}
|
|
498
1931
|
/**
|
|
@@ -501,17 +1934,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component3 {
|
|
|
501
1934
|
*/
|
|
502
1935
|
get stageTargets() {
|
|
503
1936
|
return this.awsDeploymentTargets.filter(
|
|
504
|
-
(target) => target.awsStageType ===
|
|
1937
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE
|
|
505
1938
|
);
|
|
506
1939
|
}
|
|
507
1940
|
get stageTargetsForCI() {
|
|
508
1941
|
return this.awsDeploymentTargets.filter(
|
|
509
|
-
(target) => target.awsStageType ===
|
|
1942
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.ciDeployment
|
|
510
1943
|
);
|
|
511
1944
|
}
|
|
512
1945
|
get stageTargetsForLocal() {
|
|
513
1946
|
return this.awsDeploymentTargets.filter(
|
|
514
|
-
(target) => target.awsStageType ===
|
|
1947
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.localDeployment
|
|
515
1948
|
);
|
|
516
1949
|
}
|
|
517
1950
|
/**
|
|
@@ -520,17 +1953,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component3 {
|
|
|
520
1953
|
*/
|
|
521
1954
|
get devTargets() {
|
|
522
1955
|
return this.awsDeploymentTargets.filter(
|
|
523
|
-
(target) => target.awsStageType ===
|
|
1956
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV
|
|
524
1957
|
);
|
|
525
1958
|
}
|
|
526
1959
|
get devTargetsForCI() {
|
|
527
1960
|
return this.awsDeploymentTargets.filter(
|
|
528
|
-
(target) => target.awsStageType ===
|
|
1961
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.ciDeployment
|
|
529
1962
|
);
|
|
530
1963
|
}
|
|
531
1964
|
get devTargetsForLocal() {
|
|
532
1965
|
return this.awsDeploymentTargets.filter(
|
|
533
|
-
(target) => target.awsStageType ===
|
|
1966
|
+
(target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.localDeployment
|
|
534
1967
|
);
|
|
535
1968
|
}
|
|
536
1969
|
preSynthesize() {
|
|
@@ -543,9 +1976,9 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component3 {
|
|
|
543
1976
|
};
|
|
544
1977
|
|
|
545
1978
|
// src/aws/aws-deployment-target.ts
|
|
546
|
-
var
|
|
547
|
-
import { Component as
|
|
548
|
-
var AwsDeploymentTarget = class _AwsDeploymentTarget extends
|
|
1979
|
+
var import_utils9 = __toESM(require_lib());
|
|
1980
|
+
import { Component as Component9 } from "projen";
|
|
1981
|
+
var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component9 {
|
|
549
1982
|
constructor(project, options) {
|
|
550
1983
|
super(project);
|
|
551
1984
|
/**
|
|
@@ -610,11 +2043,11 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component4 {
|
|
|
610
2043
|
};
|
|
611
2044
|
this.account = options.account;
|
|
612
2045
|
this.region = options.region;
|
|
613
|
-
this.awsStageType = options.awsStageType ||
|
|
614
|
-
const role = options.deploymentTargetRole ?? options.awsEnvironmentType ??
|
|
2046
|
+
this.awsStageType = options.awsStageType || import_utils9.AWS_STAGE_TYPE.DEV;
|
|
2047
|
+
const role = options.deploymentTargetRole ?? options.awsEnvironmentType ?? import_utils9.DEPLOYMENT_TARGET_ROLE.PRIMARY;
|
|
615
2048
|
this.deploymentTargetRole = role;
|
|
616
2049
|
this.awsEnvironmentType = role;
|
|
617
|
-
this.branches = options.branches ?? (this.awsStageType ===
|
|
2050
|
+
this.branches = options.branches ?? (this.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD ? [
|
|
618
2051
|
{
|
|
619
2052
|
branch: "main"
|
|
620
2053
|
}
|
|
@@ -623,7 +2056,7 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component4 {
|
|
|
623
2056
|
branch: "feature/*"
|
|
624
2057
|
}
|
|
625
2058
|
]);
|
|
626
|
-
this.localDeployment = options.localDeployment ?? this.awsStageType ===
|
|
2059
|
+
this.localDeployment = options.localDeployment ?? this.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV;
|
|
627
2060
|
if (this.localDeployment) {
|
|
628
2061
|
const roleName = options.localDeploymentConfig?.roleName?.toLowerCase() || "poweruseraccess";
|
|
629
2062
|
const profile = options.localDeploymentConfig?.profile || `${roleName}-${this.awsStageType}-${this.account}-${this.region}`;
|
|
@@ -758,61 +2191,14 @@ var VERSION_KEYS_SKIP = [
|
|
|
758
2191
|
// Pinned to 3.x for Vite 5 compatibility; skip until ESM-only (issue #142)
|
|
759
2192
|
];
|
|
760
2193
|
|
|
761
|
-
// src/versions.ts
|
|
762
|
-
var VERSION = {
|
|
763
|
-
/**
|
|
764
|
-
* CDK CLI for workflows and command line operations.
|
|
765
|
-
*
|
|
766
|
-
* CLI and lib are versioned separately, so this is the CLI version.
|
|
767
|
-
*/
|
|
768
|
-
AWS_CDK_CLI_VERSION: "2.1117.0",
|
|
769
|
-
/**
|
|
770
|
-
* CDK Version to use for construct projects.
|
|
771
|
-
*
|
|
772
|
-
* CLI and lib are versioned separately, so this is the lib version.
|
|
773
|
-
*/
|
|
774
|
-
AWS_CDK_LIB_VERSION: "2.248.0",
|
|
775
|
-
/**
|
|
776
|
-
* Version of the AWS Constructs library to use.
|
|
777
|
-
*/
|
|
778
|
-
AWS_CONSTRUCTS_VERSION: "10.6.0",
|
|
779
|
-
/**
|
|
780
|
-
* Version of Node.js to use in CI workflows at github actions.
|
|
781
|
-
*/
|
|
782
|
-
NODE_WORKFLOWS: "24",
|
|
783
|
-
/**
|
|
784
|
-
* Version of PNPM to use in workflows at github actions.
|
|
785
|
-
*/
|
|
786
|
-
PNPM_VERSION: "10.33.0",
|
|
787
|
-
/**
|
|
788
|
-
* Version of Projen to use.
|
|
789
|
-
*/
|
|
790
|
-
PROJEN_VERSION: "0.99.34",
|
|
791
|
-
/**
|
|
792
|
-
* What version of the turborepo library should we use?
|
|
793
|
-
*/
|
|
794
|
-
TURBO_VERSION: "2.9.4",
|
|
795
|
-
/**
|
|
796
|
-
* What version of Vite to use (pnpm override). Pinned to 5.x so Vitest 4.x
|
|
797
|
-
* can load config (Vite 6+/7+ are ESM-only; see issue #142). Remove override
|
|
798
|
-
* when moving to ESM-only test setup.
|
|
799
|
-
*/
|
|
800
|
-
VITE_VERSION: "5.4.11",
|
|
801
|
-
/**
|
|
802
|
-
* What version of Vitest to use when testRunner is 'vitest'.
|
|
803
|
-
* Pinned to 3.x so it works with Vite 5 (Vitest 4 requires Vite 6). See issue #142.
|
|
804
|
-
*/
|
|
805
|
-
VITEST_VERSION: "3.2.4"
|
|
806
|
-
};
|
|
807
|
-
|
|
808
2194
|
// src/jsii/jsii-faker.ts
|
|
809
2195
|
import * as spec from "@jsii/spec";
|
|
810
|
-
import { Component as
|
|
2196
|
+
import { Component as Component10, JsonFile as JsonFile4 } from "projen";
|
|
811
2197
|
var ProjenBaseFqn = {
|
|
812
2198
|
TYPESCRIPT_PROJECT: "projen.typescript.TypeScriptProject",
|
|
813
2199
|
TYPESCRIPT_PROJECT_OPTIONS: "projen.typescript.TypeScriptProjectOptions"
|
|
814
2200
|
};
|
|
815
|
-
var JsiiFaker = class _JsiiFaker extends
|
|
2201
|
+
var JsiiFaker = class _JsiiFaker extends Component10 {
|
|
816
2202
|
constructor(project) {
|
|
817
2203
|
super(project);
|
|
818
2204
|
this.project = project;
|
|
@@ -823,7 +2209,7 @@ var JsiiFaker = class _JsiiFaker extends Component5 {
|
|
|
823
2209
|
};
|
|
824
2210
|
};
|
|
825
2211
|
this._assemblyName = this.project.package.packageName;
|
|
826
|
-
new
|
|
2212
|
+
new JsonFile4(project, ".jsii", {
|
|
827
2213
|
obj: () => {
|
|
828
2214
|
return {
|
|
829
2215
|
name: this._assemblyName,
|
|
@@ -860,86 +2246,6 @@ var JsiiFaker = class _JsiiFaker extends Component5 {
|
|
|
860
2246
|
}
|
|
861
2247
|
};
|
|
862
2248
|
|
|
863
|
-
// src/pnpm/pnpm-workspace.ts
|
|
864
|
-
import { relative as relative2 } from "path";
|
|
865
|
-
import { Component as Component6, YamlFile } from "projen";
|
|
866
|
-
var MIMIMUM_RELEASE_AGE = {
|
|
867
|
-
ZERO_DAYS: 0,
|
|
868
|
-
ONE_HOUR: 60,
|
|
869
|
-
SIX_HOURS: 360,
|
|
870
|
-
TWELVE_HOURS: 720,
|
|
871
|
-
ONE_DAY: 1440,
|
|
872
|
-
TWO_DAYS: 2880,
|
|
873
|
-
THREE_DAYS: 4320,
|
|
874
|
-
FOUR_DAYS: 5760,
|
|
875
|
-
FIVE_DAYS: 7200,
|
|
876
|
-
SIX_DAYS: 8640,
|
|
877
|
-
ONE_WEEK: 10080
|
|
878
|
-
};
|
|
879
|
-
var PnpmWorkspace = class _PnpmWorkspace extends Component6 {
|
|
880
|
-
/**
|
|
881
|
-
* Get the pnpm workspace component of a project. If it does not exist,
|
|
882
|
-
* return undefined.
|
|
883
|
-
*
|
|
884
|
-
* @param project
|
|
885
|
-
* @returns
|
|
886
|
-
*/
|
|
887
|
-
static of(project) {
|
|
888
|
-
const isDefined = (c) => c instanceof _PnpmWorkspace;
|
|
889
|
-
return project.root.components.find(isDefined);
|
|
890
|
-
}
|
|
891
|
-
constructor(project, options = {}) {
|
|
892
|
-
super(project);
|
|
893
|
-
project.tryFindObjectFile("package.json")?.addDeletionOverride("pnpm");
|
|
894
|
-
this.fileName = options.fileName ?? "pnpm-workspace.yaml";
|
|
895
|
-
this.minimumReleaseAge = options.minimumReleaseAge ?? MIMIMUM_RELEASE_AGE.ONE_DAY;
|
|
896
|
-
this.minimumReleaseAgeExclude = options.minimumReleaseAgeExclude ? ["@codedrifters/*", ...options.minimumReleaseAgeExclude] : ["@codedrifters/*"];
|
|
897
|
-
this.onlyBuiltDependencies = options.onlyBuiltDependencies ? options.onlyBuiltDependencies : [];
|
|
898
|
-
this.ignoredBuiltDependencies = options.ignoredBuiltDependencies ? options.ignoredBuiltDependencies : [];
|
|
899
|
-
this.subprojects = options.subprojects ?? [];
|
|
900
|
-
this.defaultCatalog = options.defaultCatalog;
|
|
901
|
-
this.namedCatalogs = options.namedCatalogs;
|
|
902
|
-
project.addPackageIgnore(this.fileName);
|
|
903
|
-
new YamlFile(this.project, this.fileName, {
|
|
904
|
-
obj: () => {
|
|
905
|
-
const pnpmConfig = {};
|
|
906
|
-
const packages = new Array();
|
|
907
|
-
for (const subproject of project.subprojects) {
|
|
908
|
-
packages.push(relative2(this.project.outdir, subproject.outdir));
|
|
909
|
-
}
|
|
910
|
-
const packageSet = new Set(packages);
|
|
911
|
-
for (const subprojectPath of this.subprojects) {
|
|
912
|
-
packageSet.add(subprojectPath);
|
|
913
|
-
}
|
|
914
|
-
if (this.subprojects.length > 0) {
|
|
915
|
-
packages.length = 0;
|
|
916
|
-
packages.push(...packageSet);
|
|
917
|
-
}
|
|
918
|
-
pnpmConfig.minimumReleaseAge = this.minimumReleaseAge;
|
|
919
|
-
if (this.minimumReleaseAgeExclude.length > 0) {
|
|
920
|
-
pnpmConfig.minimumReleaseAgeExclude = this.minimumReleaseAgeExclude;
|
|
921
|
-
}
|
|
922
|
-
if (this.onlyBuiltDependencies.length > 0) {
|
|
923
|
-
pnpmConfig.onlyBuiltDependencies = this.onlyBuiltDependencies;
|
|
924
|
-
}
|
|
925
|
-
if (this.ignoredBuiltDependencies.length > 0) {
|
|
926
|
-
pnpmConfig.ignoreBuiltDependencies = this.ignoredBuiltDependencies;
|
|
927
|
-
}
|
|
928
|
-
if (this.defaultCatalog && Object.keys(this.defaultCatalog).length > 0) {
|
|
929
|
-
pnpmConfig.catalog = this.defaultCatalog;
|
|
930
|
-
}
|
|
931
|
-
if (this.namedCatalogs && Object.keys(this.namedCatalogs).length > 0) {
|
|
932
|
-
pnpmConfig.namedCatalogs = this.namedCatalogs;
|
|
933
|
-
}
|
|
934
|
-
return {
|
|
935
|
-
...packages.length > 0 ? { packages } : {},
|
|
936
|
-
...pnpmConfig ? { ...pnpmConfig } : {}
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
};
|
|
942
|
-
|
|
943
2249
|
// src/projects/monorepo-project.ts
|
|
944
2250
|
import {
|
|
945
2251
|
NodePackageManager as NodePackageManager2,
|
|
@@ -951,7 +2257,7 @@ import {
|
|
|
951
2257
|
import { merge as merge2 } from "ts-deepmerge";
|
|
952
2258
|
|
|
953
2259
|
// src/tasks/reset-task.ts
|
|
954
|
-
import { Component as
|
|
2260
|
+
import { Component as Component11 } from "projen";
|
|
955
2261
|
|
|
956
2262
|
// src/projects/typescript-project.ts
|
|
957
2263
|
import { typescript } from "projen";
|
|
@@ -962,94 +2268,6 @@ import {
|
|
|
962
2268
|
} from "projen/lib/javascript";
|
|
963
2269
|
import { ReleaseTrigger } from "projen/lib/release";
|
|
964
2270
|
import { merge } from "ts-deepmerge";
|
|
965
|
-
|
|
966
|
-
// src/vitest/vitest-component.ts
|
|
967
|
-
import { Component as Component7 } from "projen";
|
|
968
|
-
import { Jest } from "projen/lib/javascript";
|
|
969
|
-
import { TextFile } from "projen/lib/textfile";
|
|
970
|
-
var Vitest = class _Vitest extends Component7 {
|
|
971
|
-
constructor(project, options = {}) {
|
|
972
|
-
super(project);
|
|
973
|
-
this.project = project;
|
|
974
|
-
this.configFilePath = options.configFilePath ?? "vitest.config.ts";
|
|
975
|
-
const config = options.config ?? {};
|
|
976
|
-
this.include = config.include ?? ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
|
|
977
|
-
this.exclude = config.exclude ?? [
|
|
978
|
-
"**/node_modules/**",
|
|
979
|
-
"**/dist/**",
|
|
980
|
-
"**/lib/**",
|
|
981
|
-
"**/.?*"
|
|
982
|
-
];
|
|
983
|
-
this.environment = config.environment ?? "node";
|
|
984
|
-
this.passWithNoTests = config.passWithNoTests ?? true;
|
|
985
|
-
this.coverageEnabled = config.coverageEnabled ?? true;
|
|
986
|
-
this.coverageDirectory = config.coverageDirectory ?? "coverage";
|
|
987
|
-
this.coverageReporters = config.coverageReporters ?? ["text", "lcov"];
|
|
988
|
-
this.version = options.vitestVersion ?? VERSION.VITEST_VERSION;
|
|
989
|
-
project.addDevDeps(`vitest@${this.version}`);
|
|
990
|
-
if (this.coverageEnabled) {
|
|
991
|
-
project.addDevDeps(`@vitest/coverage-v8@${this.version}`);
|
|
992
|
-
}
|
|
993
|
-
const coveragePath = `/${this.coverageDirectory}/`;
|
|
994
|
-
project.gitignore.addPatterns(coveragePath);
|
|
995
|
-
project.npmignore?.exclude(coveragePath);
|
|
996
|
-
this.addTestTasks();
|
|
997
|
-
this.synthesizeConfig();
|
|
998
|
-
}
|
|
999
|
-
/**
|
|
1000
|
-
* Find the Vitest component on a project.
|
|
1001
|
-
*/
|
|
1002
|
-
static of(project) {
|
|
1003
|
-
const isVitest = (c) => c instanceof _Vitest;
|
|
1004
|
-
return project.components.find(isVitest);
|
|
1005
|
-
}
|
|
1006
|
-
preSynthesize() {
|
|
1007
|
-
super.preSynthesize();
|
|
1008
|
-
for (const component of this.project.components) {
|
|
1009
|
-
if (component instanceof Jest) {
|
|
1010
|
-
throw new Error("Vitest cannot be used together with Jest");
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
addTestTasks() {
|
|
1015
|
-
this.project.testTask.exec("vitest run --update");
|
|
1016
|
-
if (!this.project.tasks.tryFind("test:watch")) {
|
|
1017
|
-
this.project.addTask("test:watch", {
|
|
1018
|
-
description: "Run tests in watch mode",
|
|
1019
|
-
exec: "vitest watch",
|
|
1020
|
-
receiveArgs: true
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
synthesizeConfig() {
|
|
1025
|
-
this.project.tryRemoveFile(this.configFilePath);
|
|
1026
|
-
new TextFile(this, this.configFilePath, {
|
|
1027
|
-
lines: this.renderConfig()
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
renderConfig() {
|
|
1031
|
-
return [
|
|
1032
|
-
'import { defineConfig } from "vitest/config";',
|
|
1033
|
-
"",
|
|
1034
|
-
"export default defineConfig({",
|
|
1035
|
-
" test: {",
|
|
1036
|
-
` include: ${JSON.stringify(this.include)},`,
|
|
1037
|
-
` exclude: ${JSON.stringify(this.exclude)},`,
|
|
1038
|
-
` environment: "${this.environment}",`,
|
|
1039
|
-
` passWithNoTests: ${this.passWithNoTests},`,
|
|
1040
|
-
" coverage: {",
|
|
1041
|
-
` enabled: ${this.coverageEnabled},`,
|
|
1042
|
-
` provider: "v8",`,
|
|
1043
|
-
` reportsDirectory: "${this.coverageDirectory}",`,
|
|
1044
|
-
` reporter: ${JSON.stringify(this.coverageReporters)},`,
|
|
1045
|
-
" },",
|
|
1046
|
-
" },",
|
|
1047
|
-
"});"
|
|
1048
|
-
];
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
|
|
1052
|
-
// src/projects/typescript-project.ts
|
|
1053
2271
|
var TestRunner = {
|
|
1054
2272
|
JEST: "jest",
|
|
1055
2273
|
VITEST: "vitest"
|
|
@@ -1214,7 +2432,7 @@ var TypeScriptProject = class extends typescript.TypeScriptProject {
|
|
|
1214
2432
|
};
|
|
1215
2433
|
|
|
1216
2434
|
// src/tasks/reset-task.ts
|
|
1217
|
-
var ResetTask = class _ResetTask extends
|
|
2435
|
+
var ResetTask = class _ResetTask extends Component11 {
|
|
1218
2436
|
constructor(project, options = {}) {
|
|
1219
2437
|
super(project);
|
|
1220
2438
|
this.project = project;
|
|
@@ -1287,8 +2505,8 @@ var ResetTask = class _ResetTask extends Component8 {
|
|
|
1287
2505
|
};
|
|
1288
2506
|
|
|
1289
2507
|
// src/vscode/vscode.ts
|
|
1290
|
-
import { Component as
|
|
1291
|
-
var VSCodeConfig = class extends
|
|
2508
|
+
import { Component as Component12, vscode } from "projen";
|
|
2509
|
+
var VSCodeConfig = class extends Component12 {
|
|
1292
2510
|
constructor(project) {
|
|
1293
2511
|
super(project);
|
|
1294
2512
|
const vsConfig = new vscode.VsCode(project);
|
|
@@ -1664,6 +2882,9 @@ var MonorepoProject = class extends TypeScriptAppProject {
|
|
|
1664
2882
|
if (options.approveMergeUpgradeOptions) {
|
|
1665
2883
|
addApproveMergeUpgradeWorkflow(this, options.approveMergeUpgradeOptions);
|
|
1666
2884
|
}
|
|
2885
|
+
if (options.agentConfig) {
|
|
2886
|
+
new AgentConfig(this, options.agentConfigOptions);
|
|
2887
|
+
}
|
|
1667
2888
|
if (this.buildWorkflow) {
|
|
1668
2889
|
addBuildCompleteJob(this.buildWorkflow);
|
|
1669
2890
|
}
|
|
@@ -1722,9 +2943,9 @@ var MonorepoProject = class extends TypeScriptAppProject {
|
|
|
1722
2943
|
|
|
1723
2944
|
// src/typescript/typescript-config.ts
|
|
1724
2945
|
import { relative as relative3 } from "path";
|
|
1725
|
-
import { Component as
|
|
2946
|
+
import { Component as Component13 } from "projen";
|
|
1726
2947
|
import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
|
|
1727
|
-
var TypeScriptConfig = class extends
|
|
2948
|
+
var TypeScriptConfig = class extends Component13 {
|
|
1728
2949
|
constructor(project) {
|
|
1729
2950
|
super(project);
|
|
1730
2951
|
let tsPaths = {};
|
|
@@ -1751,13 +2972,13 @@ var TypeScriptConfig = class extends Component10 {
|
|
|
1751
2972
|
};
|
|
1752
2973
|
|
|
1753
2974
|
// src/workflows/aws-deploy-workflow.ts
|
|
1754
|
-
var
|
|
1755
|
-
import { Component as
|
|
2975
|
+
var import_utils10 = __toESM(require_lib());
|
|
2976
|
+
import { Component as Component14 } from "projen";
|
|
1756
2977
|
import { BuildWorkflow } from "projen/lib/build";
|
|
1757
2978
|
import { GitHub } from "projen/lib/github";
|
|
1758
2979
|
import { JobPermission as JobPermission4 } from "projen/lib/github/workflows-model";
|
|
1759
2980
|
var PROD_DEPLOY_NAME = "prod-deploy";
|
|
1760
|
-
var AwsDeployWorkflow = class _AwsDeployWorkflow extends
|
|
2981
|
+
var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component14 {
|
|
1761
2982
|
constructor(project, options = {}) {
|
|
1762
2983
|
super(project);
|
|
1763
2984
|
this.project = project;
|
|
@@ -1768,7 +2989,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component11 {
|
|
|
1768
2989
|
* @deprecated Use deployment target role terminology elsewhere. This property is maintained for backward compatibility.
|
|
1769
2990
|
* @default 'primary' (this is the only type supported currently)
|
|
1770
2991
|
*/
|
|
1771
|
-
this.awsEnvironmentType =
|
|
2992
|
+
this.awsEnvironmentType = import_utils10.DEPLOYMENT_TARGET_ROLE.PRIMARY;
|
|
1772
2993
|
this.setupNode = () => {
|
|
1773
2994
|
return [
|
|
1774
2995
|
{
|
|
@@ -1878,7 +3099,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component11 {
|
|
|
1878
3099
|
}
|
|
1879
3100
|
const turbo = TurboRepo.of(this.rootProject);
|
|
1880
3101
|
const buildWorkflowOptions = turbo?.remoteCacheOptions ? TurboRepo.buildWorkflowOptions(turbo.remoteCacheOptions) : {};
|
|
1881
|
-
this.awsStageType = options.awsStageType ??
|
|
3102
|
+
this.awsStageType = options.awsStageType ?? import_utils10.AWS_STAGE_TYPE.DEV;
|
|
1882
3103
|
this.awsDeploymentTargets = options.awsDeploymentTargets ?? AwsDeploymentConfig.of(project)?.awsDeploymentTargets.filter(
|
|
1883
3104
|
(target) => target.awsStageType === this.awsStageType && target.ciDeployment
|
|
1884
3105
|
) ?? [];
|
|
@@ -2021,11 +3242,18 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component11 {
|
|
|
2021
3242
|
}
|
|
2022
3243
|
};
|
|
2023
3244
|
export {
|
|
3245
|
+
AGENT_MODEL,
|
|
3246
|
+
AGENT_PLATFORM,
|
|
3247
|
+
AGENT_RULE_SCOPE,
|
|
3248
|
+
AgentConfig,
|
|
2024
3249
|
AwsDeployWorkflow,
|
|
2025
3250
|
AwsDeploymentConfig,
|
|
2026
3251
|
AwsDeploymentTarget,
|
|
3252
|
+
BUILT_IN_BUNDLES,
|
|
3253
|
+
CLAUDE_RULE_TARGET,
|
|
2027
3254
|
COMPLETE_JOB_ID,
|
|
2028
3255
|
JsiiFaker,
|
|
3256
|
+
MCP_TRANSPORT,
|
|
2029
3257
|
MERGE_METHODS,
|
|
2030
3258
|
MIMIMUM_RELEASE_AGE,
|
|
2031
3259
|
MonorepoProject,
|
|
@@ -2046,6 +3274,14 @@ export {
|
|
|
2046
3274
|
Vitest,
|
|
2047
3275
|
addApproveMergeUpgradeWorkflow,
|
|
2048
3276
|
addBuildCompleteJob,
|
|
2049
|
-
|
|
3277
|
+
awsCdkBundle,
|
|
3278
|
+
baseBundle,
|
|
3279
|
+
getLatestEligibleVersion,
|
|
3280
|
+
jestBundle,
|
|
3281
|
+
pnpmBundle,
|
|
3282
|
+
projenBundle,
|
|
3283
|
+
turborepoBundle,
|
|
3284
|
+
typescriptBundle,
|
|
3285
|
+
vitestBundle
|
|
2050
3286
|
};
|
|
2051
3287
|
//# sourceMappingURL=index.mjs.map
|