@codedrifters/configulator 0.0.154 → 0.0.156

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.mjs CHANGED
@@ -173,14 +173,7 @@ var require_lib = __commonJS({
173
173
  });
174
174
 
175
175
  // src/agent/agent-config.ts
176
- import { Component as Component3 } from "projen";
177
-
178
- // src/agent/bundles/index.ts
179
- var BUILT_IN_BUNDLES = [];
180
-
181
- // src/agent/renderers/claude-renderer.ts
182
- import { JsonFile } from "projen";
183
- import { TextFile } from "projen/lib/textfile";
176
+ import { Component as Component8 } from "projen";
184
177
 
185
178
  // src/agent/types.ts
186
179
  var AGENT_RULE_SCOPE = {
@@ -210,7 +203,1135 @@ var MCP_TRANSPORT = {
210
203
  SSE: "sse"
211
204
  };
212
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
+ "**Repository:** {{repository.owner}}/{{repository.name}}",
300
+ "**Default branch:** {{repository.defaultBranch}}",
301
+ "",
302
+ "## Important Notes",
303
+ "",
304
+ "- **Never edit generated files** \u2014 they are marked with `// ~~ Generated by projen`",
305
+ "- **After modifying Projen configuration**, the user should run `npx projen` locally. The agent must not run `npx projen`, `pnpm install`, or `pnpm i`.",
306
+ "- **Configure dependencies through Projen** \u2014 never use `npm install`, `pnpm add`, or `yarn add`. Add them to `deps` or `devDeps` in Projen config.",
307
+ "- **Export from index.ts** to maintain clean public APIs"
308
+ ].join("\n"),
309
+ tags: ["project"]
310
+ },
311
+ {
312
+ name: "interaction-style",
313
+ description: "Interaction style \u2014 ask questions one at a time and wait for feedback",
314
+ scope: AGENT_RULE_SCOPE.ALWAYS,
315
+ content: [
316
+ "# Interaction Style Guidelines",
317
+ "",
318
+ "When responding to requests, follow these interaction principles.",
319
+ "",
320
+ "1. **Ask questions one at a time**: When clarification is needed, ask a single question and wait for the user's response before proceeding.",
321
+ "2. **Wait for feedback**: After asking a question, pause and wait for the user's answer before asking additional questions or making assumptions.",
322
+ "3. **Avoid question overload**: Do not ask multiple questions in a single response. If multiple clarifications are needed, prioritize the most important question first.",
323
+ "4. **Progressive clarification**: Once the user answers your first question, you may then ask the next most important question if needed.",
324
+ "5. **Confirm understanding**: After receiving feedback, acknowledge the user's response before proceeding with the next step or question.",
325
+ "6. **Be patient**: Do not rush ahead with assumptions. It is better to ask one clarifying question than to proceed with incorrect assumptions."
326
+ ].join("\n"),
327
+ tags: ["project"]
328
+ },
329
+ {
330
+ name: "general-conventions",
331
+ description: "Code formatting (Prettier/ESLint), import conventions (ES modules, import order), error handling (async/await, no floating promises)",
332
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
333
+ filePatterns: ["**/*.ts", "**/*.tsx"],
334
+ content: [
335
+ "# General Conventions",
336
+ "",
337
+ "## Code Formatting",
338
+ "",
339
+ "- Use **Prettier** for formatting (runs automatically on save in VS Code)",
340
+ "- Always use curly braces for control flow, even single-line statements",
341
+ "- Prefer `const` over `let`; avoid `var`",
342
+ "- Use trailing commas in multi-line objects/arrays",
343
+ "",
344
+ "### ESLint Rules to Follow",
345
+ "",
346
+ "- `curly`: Always use curly braces (multi-line, consistent)",
347
+ "- `dot-notation`: Use dot notation over bracket notation",
348
+ "- `no-bitwise`: No bitwise operators",
349
+ "- `@typescript-eslint/no-shadow`: No variable shadowing",
350
+ "- `@typescript-eslint/member-ordering`: Follow member order",
351
+ "",
352
+ "## Import Conventions",
353
+ "",
354
+ "- **Always use ES modules** (`import`/`export`), never `require()`",
355
+ "- Import order:",
356
+ " 1. Built-in Node.js modules (e.g., `node:path`, `node:fs`)",
357
+ " 2. External dependencies (alphabetically sorted)",
358
+ " 3. Internal imports (relative paths)",
359
+ "- Group imports with blank lines between groups",
360
+ "- Alphabetize imports within each group (case-insensitive)",
361
+ "",
362
+ "## Error Handling",
363
+ "",
364
+ "- Always handle promises properly with `await`",
365
+ "- Use `@typescript-eslint/return-await` rule (always return await)",
366
+ "- Never leave floating promises (`@typescript-eslint/no-floating-promises`)",
367
+ "- Use proper error types and meaningful error messages",
368
+ "- Do not swallow errors or use empty catch blocks",
369
+ "- Prefer async/await over raw promises"
370
+ ].join("\n"),
371
+ tags: ["coding"]
372
+ },
373
+ {
374
+ name: "pull-request-conventions",
375
+ description: "Conventional commit PR titles, closing keywords, change summaries",
376
+ scope: AGENT_RULE_SCOPE.ALWAYS,
377
+ content: [
378
+ "# Pull Request Conventions",
379
+ "",
380
+ "## PR Title Prefix",
381
+ "",
382
+ "**Always** use a **conventional commit prefix** in the PR `title`. Format: `type: description` or `type(scope): description`.",
383
+ "",
384
+ "| Prefix | Use for |",
385
+ "|--------|---------|",
386
+ "| `feat:` | New features or functionality |",
387
+ "| `fix:` | Bug fixes |",
388
+ "| `docs:` | Documentation-only changes |",
389
+ "| `chore:` | Maintenance: deps, tooling, config |",
390
+ "| `refactor:` | Code restructure, no behavior change |",
391
+ "| `release:` | Release preparation, version bumps |",
392
+ "| `hotfix:` | Urgent production fixes |",
393
+ "",
394
+ "## Link to the Issue",
395
+ "",
396
+ "When the PR addresses an issue, **always** include a closing keyword in the PR body:",
397
+ "- `Closes #<issue>`, `Fixes #<issue>`, or `Resolves #<issue>`",
398
+ "",
399
+ "## Summary of Changes",
400
+ "",
401
+ "Every PR must include a **summary of the changes** made. Use bullet points or short paragraphs. Do not leave the description empty.",
402
+ "",
403
+ "## Commit Messages",
404
+ "",
405
+ "Use **conventional commits** for git commit messages: `type: short description`. Do not add AI co-author or attribution."
406
+ ].join("\n"),
407
+ tags: ["workflow"]
408
+ },
409
+ {
410
+ name: "branch-naming-conventions",
411
+ description: "Branch format (type/[issue-]description), create-on-GitHub-then-fetch workflow",
412
+ scope: AGENT_RULE_SCOPE.ALWAYS,
413
+ content: [
414
+ "# Branch Naming Conventions",
415
+ "",
416
+ "## Format",
417
+ "",
418
+ "```",
419
+ "<type>/[<issue>-]<description>",
420
+ "```",
421
+ "",
422
+ "- **type** (required): One of `feat`, `fix`, `docs`, `chore`, `refactor`, `release`, `hotfix`",
423
+ "- **issue** (optional): Issue number (e.g., `25`). Include when known.",
424
+ "- **description** (required): Short, lowercase, kebab-case summary",
425
+ "",
426
+ "## Examples",
427
+ "",
428
+ "- `feat/25-add-cursor-rules`",
429
+ "- `fix/23-rename-cursor-rules-mdc`",
430
+ "- `chore/upgrade-eslint`",
431
+ "- `docs/update-readme`"
432
+ ].join("\n"),
433
+ tags: ["workflow"]
434
+ },
435
+ {
436
+ name: "issue-conventions",
437
+ description: "Issue title prefixes, GitHub issue type mapping, prerequisite issues",
438
+ scope: AGENT_RULE_SCOPE.ALWAYS,
439
+ content: [
440
+ "# Issue Title Conventions",
441
+ "",
442
+ "## Format",
443
+ "",
444
+ "```",
445
+ "<type>: <description>",
446
+ "```",
447
+ "",
448
+ "## Types",
449
+ "",
450
+ "| Prefix | Use for |",
451
+ "|--------|---------|",
452
+ "| `epic:` | Large initiatives spanning multiple child issues |",
453
+ "| `feat:` | New features or functionality |",
454
+ "| `fix:` | Bug fixes |",
455
+ "| `chore:` | Maintenance: deps, tooling, config |",
456
+ "| `docs:` | Documentation-only work |",
457
+ "| `refactor:` | Code restructure, no behavior change |",
458
+ "| `release:` | Release preparation, version bumps |",
459
+ "| `hotfix:` | Urgent production fixes |",
460
+ "",
461
+ "## Prerequisite Issues",
462
+ "",
463
+ "Include any prerequisite (blocking) issues in the issue body when they exist.",
464
+ "Use a **Dependencies** section or `**Depends on:** #123`."
465
+ ].join("\n"),
466
+ tags: ["workflow"]
467
+ }
468
+ ]
469
+ };
470
+
471
+ // src/agent/bundles/jest.ts
472
+ var jestBundle = {
473
+ name: "jest",
474
+ description: "Jest testing conventions and patterns",
475
+ appliesWhen: (project) => hasDep(project, "jest"),
476
+ rules: [
477
+ {
478
+ name: "jest-testing",
479
+ description: "Jest testing conventions and patterns",
480
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
481
+ filePatterns: ["**/*.test.ts", "**/*.spec.ts"],
482
+ content: [
483
+ "# Jest Testing Patterns",
484
+ "",
485
+ "## Mandatory Requirements",
486
+ "",
487
+ "- **Tests MUST be created or updated whenever code functionality is added or changed**",
488
+ "- This applies to all code changes, including:",
489
+ " - New features and functionality",
490
+ " - Bug fixes",
491
+ " - Refactoring that changes behavior",
492
+ " - API changes",
493
+ " - Configuration changes that affect functionality",
494
+ "- Tests should be written or updated as part of the same change that modifies the code",
495
+ "",
496
+ "## Test Structure",
497
+ "",
498
+ "- Use **Jest** with SWC for fast compilation",
499
+ "- Test files: `.spec.ts` or `.test.ts` (co-located with source)",
500
+ "- Use descriptive test names: `describe('ClassName', () => { it('should do something specific', () => {}) })`",
501
+ "- Prefer snapshot tests for complex object structures",
502
+ "- Mock external dependencies appropriately",
503
+ "",
504
+ "## Test Organization",
505
+ "",
506
+ "- Co-locate test files with source files",
507
+ "- Test files can use dev dependencies (ESLint rule override)",
508
+ "- Use `@swc/jest` for fast compilation",
509
+ "- Configure Jest in `jest.config.json` (not in package.json)",
510
+ "",
511
+ "## Example Test Structure",
512
+ "",
513
+ "```typescript",
514
+ "import { MyClass } from './my-class';",
515
+ "",
516
+ "describe('MyClass', () => {",
517
+ " it('should do something specific', () => {",
518
+ " // Test implementation",
519
+ " });",
520
+ "});",
521
+ "```"
522
+ ].join("\n"),
523
+ tags: ["testing"]
524
+ }
525
+ ]
526
+ };
527
+
528
+ // src/pnpm/pnpm-workspace.ts
529
+ import { relative } from "path";
530
+ import { Component, YamlFile } from "projen";
531
+ var MIMIMUM_RELEASE_AGE = {
532
+ ZERO_DAYS: 0,
533
+ ONE_HOUR: 60,
534
+ SIX_HOURS: 360,
535
+ TWELVE_HOURS: 720,
536
+ ONE_DAY: 1440,
537
+ TWO_DAYS: 2880,
538
+ THREE_DAYS: 4320,
539
+ FOUR_DAYS: 5760,
540
+ FIVE_DAYS: 7200,
541
+ SIX_DAYS: 8640,
542
+ ONE_WEEK: 10080
543
+ };
544
+ var PnpmWorkspace = class _PnpmWorkspace extends Component {
545
+ /**
546
+ * Get the pnpm workspace component of a project. If it does not exist,
547
+ * return undefined.
548
+ *
549
+ * @param project
550
+ * @returns
551
+ */
552
+ static of(project) {
553
+ const isDefined = (c) => c instanceof _PnpmWorkspace;
554
+ return project.root.components.find(isDefined);
555
+ }
556
+ constructor(project, options = {}) {
557
+ super(project);
558
+ project.tryFindObjectFile("package.json")?.addDeletionOverride("pnpm");
559
+ this.fileName = options.fileName ?? "pnpm-workspace.yaml";
560
+ this.minimumReleaseAge = options.minimumReleaseAge ?? MIMIMUM_RELEASE_AGE.ONE_DAY;
561
+ this.minimumReleaseAgeExclude = options.minimumReleaseAgeExclude ? ["@codedrifters/*", ...options.minimumReleaseAgeExclude] : ["@codedrifters/*"];
562
+ this.onlyBuiltDependencies = options.onlyBuiltDependencies ? options.onlyBuiltDependencies : [];
563
+ this.ignoredBuiltDependencies = options.ignoredBuiltDependencies ? options.ignoredBuiltDependencies : [];
564
+ this.subprojects = options.subprojects ?? [];
565
+ this.defaultCatalog = options.defaultCatalog;
566
+ this.namedCatalogs = options.namedCatalogs;
567
+ project.addPackageIgnore(this.fileName);
568
+ new YamlFile(this.project, this.fileName, {
569
+ obj: () => {
570
+ const pnpmConfig = {};
571
+ const packages = new Array();
572
+ for (const subproject of project.subprojects) {
573
+ packages.push(relative(this.project.outdir, subproject.outdir));
574
+ }
575
+ const packageSet = new Set(packages);
576
+ for (const subprojectPath of this.subprojects) {
577
+ packageSet.add(subprojectPath);
578
+ }
579
+ if (this.subprojects.length > 0) {
580
+ packages.length = 0;
581
+ packages.push(...packageSet);
582
+ }
583
+ pnpmConfig.minimumReleaseAge = this.minimumReleaseAge;
584
+ if (this.minimumReleaseAgeExclude.length > 0) {
585
+ pnpmConfig.minimumReleaseAgeExclude = this.minimumReleaseAgeExclude;
586
+ }
587
+ if (this.onlyBuiltDependencies.length > 0) {
588
+ pnpmConfig.onlyBuiltDependencies = this.onlyBuiltDependencies;
589
+ }
590
+ if (this.ignoredBuiltDependencies.length > 0) {
591
+ pnpmConfig.ignoreBuiltDependencies = this.ignoredBuiltDependencies;
592
+ }
593
+ if (this.defaultCatalog && Object.keys(this.defaultCatalog).length > 0) {
594
+ pnpmConfig.catalog = this.defaultCatalog;
595
+ }
596
+ if (this.namedCatalogs && Object.keys(this.namedCatalogs).length > 0) {
597
+ pnpmConfig.namedCatalogs = this.namedCatalogs;
598
+ }
599
+ return {
600
+ ...packages.length > 0 ? { packages } : {},
601
+ ...pnpmConfig ? { ...pnpmConfig } : {}
602
+ };
603
+ }
604
+ });
605
+ }
606
+ };
607
+
608
+ // src/agent/bundles/pnpm.ts
609
+ var pnpmBundle = {
610
+ name: "pnpm",
611
+ description: "PNPM workspace rules and dependency management conventions",
612
+ appliesWhen: (project) => hasComponent(project, PnpmWorkspace),
613
+ rules: [
614
+ {
615
+ name: "pnpm-workspace",
616
+ description: "PNPM workspace and dependency management conventions",
617
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
618
+ filePatterns: ["package.json", "pnpm-workspace.yaml", "pnpm-lock.yaml"],
619
+ content: [
620
+ "# PNPM Workspace Conventions",
621
+ "",
622
+ "## Package Management",
623
+ "",
624
+ "- Use **PNPM** for package management",
625
+ "- Workspace configuration in `pnpm-workspace.yaml`",
626
+ "- Dependencies managed through Projen, not directly in `package.json`",
627
+ "- **Always use workspace dependencies** (`workspace:*` or `workspace:^`) for packages within this monorepo",
628
+ "- Never use published NPM versions of internal packages; always reference the local workspace version",
629
+ "",
630
+ "## Dependency Management",
631
+ "",
632
+ "**DO:**",
633
+ "- Configure dependencies in Projen configuration files (`.projenrc.ts` or `projenrc/*.ts`)",
634
+ "- Add dependencies to `deps`, `devDeps`, or `peerDeps` arrays in project configuration",
635
+ '- Use catalog dependencies when available (e.g., `"aws-cdk-lib@catalog:"`)',
636
+ "- Ask the user to run `npx projen` and `pnpm install` after updating dependency configuration",
637
+ "",
638
+ "**DO NOT:**",
639
+ "- Run `npm install some-package`",
640
+ "- Run `pnpm add some-package`",
641
+ "- Run `yarn add some-package`",
642
+ "- Manually edit `package.json` dependencies",
643
+ "",
644
+ "Manual package installation creates conflicts with Projen-managed files."
645
+ ].join("\n"),
646
+ tags: ["workflow"]
647
+ }
648
+ ]
649
+ };
650
+
651
+ // src/agent/bundles/projen.ts
652
+ var projenBundle = {
653
+ name: "projen",
654
+ description: "Projen conventions, synthesis workflow, .projenrc.ts patterns",
655
+ appliesWhen: (project) => hasDep(project, "projen"),
656
+ rules: [
657
+ {
658
+ name: "projen-conventions",
659
+ description: "Projen configuration patterns and best practices",
660
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
661
+ filePatterns: ["projenrc/**/*.ts", ".projenrc.ts"],
662
+ content: [
663
+ "# Projen Conventions",
664
+ "",
665
+ "## Configuration Management",
666
+ "",
667
+ "- **Never edit generated files** (`package.json`, `tsconfig.json`, etc. marked with `// ~~ Generated by projen`)",
668
+ "- Edit Projen configuration in:",
669
+ " - `.projenrc.ts` (root)",
670
+ " - `projenrc/*.ts` (package-specific)",
671
+ "- After making Projen changes, ask the user to run `npx projen` locally (agent must not run it)",
672
+ "",
673
+ "## Custom Projen Components",
674
+ "",
675
+ "All custom components must extend `Component` from Projen and use a static `.of()` factory for discovery:",
676
+ "",
677
+ "```typescript",
678
+ "export class MyComponent extends Component {",
679
+ " public static of(project: Project): MyComponent | undefined {",
680
+ " const isDefined = (c: Component): c is MyComponent =>",
681
+ " c instanceof MyComponent;",
682
+ " return project.components.find(isDefined);",
683
+ " }",
684
+ "",
685
+ " constructor(project: Project, options: MyComponentOptions) {",
686
+ " super(project);",
687
+ " // Implementation",
688
+ " }",
689
+ "}",
690
+ "```",
691
+ "",
692
+ "## Component Authoring",
693
+ "",
694
+ "- Export project types and utilities from `index.ts`",
695
+ "- Follow Projen's component lifecycle patterns",
696
+ "- Use `merge` from `ts-deepmerge` for deep merging configuration objects",
697
+ "- Components should be reusable and configurable; use TypeScript interfaces for component options",
698
+ "- Provide sensible defaults while allowing customization",
699
+ "- Document public APIs with minimal JSDoc; put extended documentation in markdown",
700
+ "",
701
+ "## Dependencies",
702
+ "",
703
+ "- Dependencies managed through Projen configuration, not directly in `package.json`",
704
+ '- Use catalog dependencies when available (e.g., `"projen": "catalog:"`)',
705
+ "- Peer dependencies for shared libraries (e.g., `constructs`, `projen`, `aws-cdk-lib`)",
706
+ '- Use workspace protocol for internal packages: `"@org/pkg@workspace:*"`'
707
+ ].join("\n"),
708
+ tags: ["workflow"]
709
+ }
710
+ ]
711
+ };
712
+
713
+ // src/turbo/turbo-repo.ts
714
+ import { Component as Component3, FileBase, JsonFile } from "projen/lib";
715
+ import { JobPermission } from "projen/lib/github/workflows-model";
716
+
717
+ // src/turbo/turbo-repo-task.ts
718
+ import { Component as Component2 } from "projen/lib";
719
+ var TurboRepoTask = class extends Component2 {
720
+ constructor(project, options) {
721
+ super(project);
722
+ this.project = project;
723
+ this.name = options.name;
724
+ this.dependsOn = options.dependsOn ?? [];
725
+ this.env = options.env ?? [];
726
+ this.passThroughEnv = options.passThroughEnv ?? [];
727
+ this.outputs = options.outputs ?? [];
728
+ this.cache = options.cache ?? true;
729
+ this.inputs = [
730
+ ...options.inputs ?? [],
731
+ // rerun if projen config changes
732
+ ".projen/**",
733
+ // ignore mac files
734
+ "!.DS_Store",
735
+ "!**/.DS_Store"
736
+ ];
737
+ this.outputLogs = options.outputLogs ?? "new-only";
738
+ this.persistent = options.persistent ?? false;
739
+ this.interactive = options.interactive ?? false;
740
+ this.isActive = true;
741
+ }
742
+ taskConfig() {
743
+ return {
744
+ dependsOn: this.dependsOn,
745
+ env: this.env,
746
+ passThroughEnv: this.passThroughEnv,
747
+ outputs: this.outputs,
748
+ cache: this.cache,
749
+ inputs: this.inputs,
750
+ outputLogs: this.outputLogs,
751
+ persistent: this.persistent,
752
+ interactive: this.interactive
753
+ };
754
+ }
755
+ };
756
+
757
+ // src/turbo/turbo-repo.ts
758
+ var ROOT_TURBO_TASK_NAME = "turbo:build";
759
+ var ROOT_CI_TASK_NAME = "build:all";
760
+ var _TurboRepo = class _TurboRepo extends Component3 {
761
+ constructor(project, options = {}) {
762
+ super(project);
763
+ this.project = project;
764
+ /**
765
+ * Sub-Tasks to run
766
+ */
767
+ this.tasks = [];
768
+ this.turboVersion = options.turboVersion ?? "catalog:";
769
+ this.isRootProject = project === project.root;
770
+ if (this.isRootProject) {
771
+ project.addDevDeps(`turbo@${this.turboVersion}`);
772
+ }
773
+ project.gitignore.addPatterns("/.turbo");
774
+ project.npmignore?.addPatterns("/.turbo/");
775
+ this.extends = options.extends ?? (this.isRootProject ? [] : ["//"]);
776
+ this.globalDependencies = options.globalDependencies ?? [];
777
+ this.globalEnv = options.globalEnv ?? [];
778
+ this.globalPassThroughEnv = options.globalPassThroughEnv ?? [];
779
+ this.ui = options.ui ?? "stream";
780
+ this.dangerouslyDisablePackageManagerCheck = options.dangerouslyDisablePackageManagerCheck ?? false;
781
+ this.cacheDir = options.cacheDir ?? ".turbo/cache";
782
+ this.daemon = options.daemon ?? true;
783
+ this.envMode = options.envMode ?? "strict";
784
+ this.remoteCacheOptions = options.remoteCacheOptions;
785
+ this.buildAllTaskEnvVars = options.buildAllTaskEnvVars ?? {};
786
+ const rootGeneratedFiles = this.isRootProject ? this.project.components.filter((c) => c instanceof FileBase).map((c) => c.path) : [];
787
+ this.buildTask = new TurboRepoTask(this.project, {
788
+ name: ROOT_TURBO_TASK_NAME,
789
+ dependsOn: this.isRootProject ? [`^${ROOT_TURBO_TASK_NAME}`] : [],
790
+ ...rootGeneratedFiles.length > 0 && { inputs: rootGeneratedFiles }
791
+ });
792
+ if (this.isRootProject) {
793
+ this.buildAllTask = this.project.tasks.addTask(ROOT_CI_TASK_NAME, {
794
+ description: "Root build followed by sub-project builds. Mimics the CI build process in one step."
795
+ });
796
+ this.buildAllTask.exec("turbo telemetry disable");
797
+ if (this.buildAllTaskEnvVars) {
798
+ Object.entries(this.buildAllTaskEnvVars).forEach(([name, value]) => {
799
+ this.addGlobalEnvVar(name, value);
800
+ });
801
+ }
802
+ if (!this.remoteCacheOptions) {
803
+ this.buildAllTask.exec(
804
+ `turbo ${ROOT_TURBO_TASK_NAME} --summarize --concurrency=10`
805
+ );
806
+ } else {
807
+ this.buildAllTask.exec(
808
+ `turbo turbo:build --summarize --concurrency=10 --cache=remote:rw --api=$TURBO_ENDPOINT --token=$TURBO_TOKEN --team=${this.remoteCacheOptions.teamName}`,
809
+ {
810
+ condition: '[ ! -n "$CI" ]',
811
+ env: {
812
+ TURBO_ENDPOINT: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.endpointParamName} --query Parameter.Value --output text --profile ${this.remoteCacheOptions.profileName})`,
813
+ TURBO_TOKEN: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.tokenParamName} --query Parameter.Value --output text --profile ${this.remoteCacheOptions.profileName})`
814
+ }
815
+ }
816
+ );
817
+ this.buildAllTask.exec(
818
+ `turbo turbo:build --summarize --concurrency=10 --cache=remote:rw --api=$TURBO_ENDPOINT --token=$TURBO_TOKEN --team=${this.remoteCacheOptions.teamName}`,
819
+ {
820
+ condition: '[ -n "$CI" ]',
821
+ env: {
822
+ TURBO_ENDPOINT: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.endpointParamName} --query Parameter.Value --output text)`,
823
+ TURBO_TOKEN: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.tokenParamName} --query Parameter.Value --output text)`
824
+ }
825
+ }
826
+ );
827
+ }
828
+ }
829
+ if (!this.isRootProject) {
830
+ const generatedFiles = this.project.components.filter((c) => c instanceof FileBase).map((c) => c.path);
831
+ this.preCompileTask = new TurboRepoTask(project, {
832
+ name: options.preCompileTask?.name ?? "pre-compile",
833
+ inputs: ["src/**", ...generatedFiles]
834
+ });
835
+ this.compileTask = new TurboRepoTask(project, {
836
+ name: options.compileTask?.name ?? "compile",
837
+ inputs: ["src/**", ...generatedFiles]
838
+ });
839
+ this.postCompileTask = new TurboRepoTask(project, {
840
+ name: options.postCompileTask?.name ?? "post-compile",
841
+ inputs: ["src/**", ...generatedFiles]
842
+ });
843
+ this.testTask = new TurboRepoTask(project, {
844
+ name: options.testTask?.name ?? "test"
845
+ });
846
+ this.packageTask = new TurboRepoTask(project, {
847
+ name: options.packageTask?.name ?? "package",
848
+ inputs: [".npmignore"]
849
+ });
850
+ this.tasks.push(
851
+ this.preCompileTask,
852
+ this.compileTask,
853
+ this.postCompileTask,
854
+ this.testTask,
855
+ this.packageTask
856
+ );
857
+ }
858
+ }
859
+ /**
860
+ * Static method to discovert turbo in a project.
861
+ */
862
+ static of(project) {
863
+ const isDefined = (c) => c instanceof _TurboRepo;
864
+ return project.components.find(isDefined);
865
+ }
866
+ /**
867
+ * Add an env var to the global env vars for all tasks.
868
+ * This will also become an input for the build:all task cache at the root.
869
+ */
870
+ addGlobalEnvVar(name, value) {
871
+ this.buildAllTask?.env(name, value);
872
+ if (this.isRootProject) {
873
+ this.globalEnv.push(name);
874
+ }
875
+ }
876
+ /**
877
+ * Sets GIT_BRANCH_NAME so root tasks (e.g. build:all) pass the current branch.
878
+ * Value must be exactly $(...) so Projen's task runtime expands it; the shell
879
+ * then expands ${GIT_BRANCH_NAME:-$(git rev-parse --abbrev-ref HEAD)}.
880
+ */
881
+ activateBranchNameEnvVar() {
882
+ this.addGlobalEnvVar(
883
+ "GIT_BRANCH_NAME",
884
+ '$(echo "${GIT_BRANCH_NAME:-$(git rev-parse --abbrev-ref HEAD)}")'
885
+ );
886
+ }
887
+ preSynthesize() {
888
+ let nextDependsOn = this.project.deps.all.filter((d) => d.version === "workspace:*").map((d) => [d.name, ROOT_TURBO_TASK_NAME].join("#"));
889
+ if (!this.isRootProject) {
890
+ [
891
+ [this.project.preCompileTask, this.preCompileTask],
892
+ [this.project.compileTask, this.compileTask],
893
+ [this.project.postCompileTask, this.postCompileTask],
894
+ [this.project.testTask, this.testTask],
895
+ [this.project.packageTask, this.packageTask]
896
+ ].forEach(([pjTask, turboTask]) => {
897
+ if (pjTask && turboTask && pjTask.steps.length > 0) {
898
+ if (nextDependsOn.length > 0) {
899
+ turboTask.dependsOn.push(...nextDependsOn);
900
+ }
901
+ nextDependsOn = [turboTask.name];
902
+ } else {
903
+ turboTask.isActive = false;
904
+ }
905
+ });
906
+ this.buildTask.dependsOn.push(...nextDependsOn);
907
+ }
908
+ const fileName = "turbo.json";
909
+ this.project.addPackageIgnore(fileName);
910
+ new JsonFile(this.project, fileName, {
911
+ obj: {
912
+ extends: this.extends.length ? this.extends : void 0,
913
+ globalDependencies: this.isRootProject && this.globalDependencies.length ? this.globalDependencies : void 0,
914
+ globalEnv: this.isRootProject && this.globalEnv.length ? this.globalEnv : void 0,
915
+ globalPassThroughEnv: this.isRootProject && this.globalPassThroughEnv.length ? this.globalPassThroughEnv : void 0,
916
+ ui: this.isRootProject ? this.ui : void 0,
917
+ dangerouslyDisablePackageManagerCheck: this.isRootProject ? this.dangerouslyDisablePackageManagerCheck : void 0,
918
+ cacheDir: this.isRootProject ? this.cacheDir : void 0,
919
+ daemon: this.isRootProject ? this.daemon : void 0,
920
+ envMode: this.isRootProject ? this.envMode : void 0,
921
+ /**
922
+ * All tasks
923
+ */
924
+ tasks: this.tasks.filter((task) => task.isActive).reduce(
925
+ (acc, task) => {
926
+ acc[task.name] = {
927
+ ...task.taskConfig()
928
+ };
929
+ return acc;
930
+ },
931
+ {
932
+ [this.buildTask.name]: { ...this.buildTask.taskConfig() }
933
+ }
934
+ )
935
+ }
936
+ });
937
+ super.preSynthesize();
938
+ }
939
+ };
940
+ _TurboRepo.buildWorkflowOptions = (remoteCacheOptions) => {
941
+ return {
942
+ env: {
943
+ GIT_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
944
+ },
945
+ permissions: {
946
+ contents: JobPermission.WRITE,
947
+ idToken: JobPermission.WRITE
948
+ },
949
+ preBuildSteps: [
950
+ {
951
+ name: "AWS Creds for SSM",
952
+ uses: "aws-actions/configure-aws-credentials@v4",
953
+ with: {
954
+ ["role-to-assume"]: remoteCacheOptions.oidcRole,
955
+ ["aws-region"]: "us-east-1",
956
+ ["role-duration-seconds"]: "900"
957
+ }
958
+ }
959
+ ]
960
+ };
961
+ };
962
+ var TurboRepo = _TurboRepo;
963
+
964
+ // src/agent/bundles/turborepo.ts
965
+ var turborepoBundle = {
966
+ name: "turborepo",
967
+ description: "Turborepo workspace rules and task pipeline conventions",
968
+ appliesWhen: (project) => hasComponent(project, TurboRepo),
969
+ rules: [
970
+ {
971
+ name: "turborepo-conventions",
972
+ description: "Turborepo build system and task pipeline conventions",
973
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
974
+ filePatterns: ["turbo.json", "package.json"],
975
+ content: [
976
+ "# Turborepo Conventions",
977
+ "",
978
+ "## Build System",
979
+ "",
980
+ "- **Build**: `pnpm build:all` (uses Turborepo)",
981
+ "- **Test**: `pnpm test` or `pnpm test:watch`",
982
+ "- **Lint**: `pnpm eslint`",
983
+ "",
984
+ "## Task Pipeline",
985
+ "",
986
+ "- Uses remote caching (requires AWS credentials)",
987
+ "- Only rebuilds changed packages",
988
+ "- Cache key based on file hashes and dependency graph",
989
+ "- Configured in `turbo.json`",
990
+ "",
991
+ "## Workspace Rules",
992
+ "",
993
+ "- Source files: `src/` directory",
994
+ "- Tests: Co-located with source files (`.spec.ts` or `.test.ts`)",
995
+ "- Exports: Use `index.ts` files for clean public APIs",
996
+ "- Configuration: Managed by Projen (edit `.projenrc.ts` or `projenrc/*.ts`)"
997
+ ].join("\n"),
998
+ tags: ["workflow"]
999
+ }
1000
+ ]
1001
+ };
1002
+
1003
+ // src/agent/bundles/typescript.ts
1004
+ var typescriptBundle = {
1005
+ name: "typescript",
1006
+ description: "TypeScript conventions, type safety, naming, JSDoc, member ordering",
1007
+ appliesWhen: (project) => hasFile(project, "tsconfig.json"),
1008
+ rules: [
1009
+ {
1010
+ name: "typescript-conventions",
1011
+ description: "TypeScript coding conventions and style guide",
1012
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
1013
+ filePatterns: ["**/*.ts", "**/*.tsx"],
1014
+ content: [
1015
+ "# TypeScript Conventions",
1016
+ "",
1017
+ "## Type Safety",
1018
+ "",
1019
+ "- Use **strict TypeScript** with all strict checks enabled",
1020
+ "- Always use explicit types; avoid `any` unless absolutely necessary",
1021
+ "- Use `readonly` for immutable properties",
1022
+ "- Prefer interfaces over types for object shapes",
1023
+ "- Use proper TypeScript types from libraries",
1024
+ "",
1025
+ "## Code Organization",
1026
+ "",
1027
+ "- Follow the member ordering rule:",
1028
+ " 1. Public static fields/methods",
1029
+ " 2. Protected static fields/methods",
1030
+ " 3. Private static fields/methods",
1031
+ " 4. Instance fields",
1032
+ " 5. Constructor",
1033
+ " 6. Methods",
1034
+ "",
1035
+ "## Documentation",
1036
+ "",
1037
+ "- **Keep JSDoc minimal** so that the code remains human-readable. Use brief summaries only.",
1038
+ "- Use JSDoc for:",
1039
+ " - Public classes and interfaces (short description)",
1040
+ " - Public methods and properties (brief purpose; parameter and return when not obvious)",
1041
+ " - Configuration options (one-line description)",
1042
+ "- **Extended documentation** belongs in **markdown** files. Link from JSDoc via `@see` or an inline link.",
1043
+ "",
1044
+ "## Naming Conventions",
1045
+ "",
1046
+ "- **Classes**: PascalCase (e.g., `TypeScriptProject`, `StaticHosting`)",
1047
+ "- **Interfaces**: PascalCase, often with `Props` suffix for configuration (e.g., `StaticHostingProps`)",
1048
+ "- **Functions/Methods**: camelCase (e.g., `configureProject`)",
1049
+ "- **Constants**: UPPER_SNAKE_CASE (e.g., `VERSION`, `AWS_STAGE_TYPE`)",
1050
+ "- **Files**: kebab-case for multi-word files (e.g., `aws-deployment-config.ts`)",
1051
+ "- **Private members**: No prefix needed (TypeScript handles visibility)"
1052
+ ].join("\n"),
1053
+ tags: ["coding"]
1054
+ }
1055
+ ]
1056
+ };
1057
+
1058
+ // src/vitest/vitest-component.ts
1059
+ import { Component as Component4 } from "projen";
1060
+ import { Jest } from "projen/lib/javascript";
1061
+ import { TextFile } from "projen/lib/textfile";
1062
+
1063
+ // src/versions.ts
1064
+ var VERSION = {
1065
+ /**
1066
+ * CDK CLI for workflows and command line operations.
1067
+ *
1068
+ * CLI and lib are versioned separately, so this is the CLI version.
1069
+ */
1070
+ AWS_CDK_CLI_VERSION: "2.1117.0",
1071
+ /**
1072
+ * CDK Version to use for construct projects.
1073
+ *
1074
+ * CLI and lib are versioned separately, so this is the lib version.
1075
+ */
1076
+ AWS_CDK_LIB_VERSION: "2.248.0",
1077
+ /**
1078
+ * Version of the AWS Constructs library to use.
1079
+ */
1080
+ AWS_CONSTRUCTS_VERSION: "10.6.0",
1081
+ /**
1082
+ * Version of Node.js to use in CI workflows at github actions.
1083
+ */
1084
+ NODE_WORKFLOWS: "24",
1085
+ /**
1086
+ * Version of PNPM to use in workflows at github actions.
1087
+ */
1088
+ PNPM_VERSION: "10.33.0",
1089
+ /**
1090
+ * Version of Projen to use.
1091
+ */
1092
+ PROJEN_VERSION: "0.99.34",
1093
+ /**
1094
+ * What version of the turborepo library should we use?
1095
+ */
1096
+ TURBO_VERSION: "2.9.4",
1097
+ /**
1098
+ * What version of Vite to use (pnpm override). Pinned to 5.x so Vitest 4.x
1099
+ * can load config (Vite 6+/7+ are ESM-only; see issue #142). Remove override
1100
+ * when moving to ESM-only test setup.
1101
+ */
1102
+ VITE_VERSION: "5.4.11",
1103
+ /**
1104
+ * What version of Vitest to use when testRunner is 'vitest'.
1105
+ * Pinned to 3.x so it works with Vite 5 (Vitest 4 requires Vite 6). See issue #142.
1106
+ */
1107
+ VITEST_VERSION: "3.2.4"
1108
+ };
1109
+
1110
+ // src/vitest/vitest-component.ts
1111
+ var Vitest = class _Vitest extends Component4 {
1112
+ constructor(project, options = {}) {
1113
+ super(project);
1114
+ this.project = project;
1115
+ this.configFilePath = options.configFilePath ?? "vitest.config.ts";
1116
+ const config = options.config ?? {};
1117
+ this.include = config.include ?? ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
1118
+ this.exclude = config.exclude ?? [
1119
+ "**/node_modules/**",
1120
+ "**/dist/**",
1121
+ "**/lib/**",
1122
+ "**/.?*"
1123
+ ];
1124
+ this.environment = config.environment ?? "node";
1125
+ this.passWithNoTests = config.passWithNoTests ?? true;
1126
+ this.coverageEnabled = config.coverageEnabled ?? true;
1127
+ this.coverageDirectory = config.coverageDirectory ?? "coverage";
1128
+ this.coverageReporters = config.coverageReporters ?? ["text", "lcov"];
1129
+ this.version = options.vitestVersion ?? VERSION.VITEST_VERSION;
1130
+ project.addDevDeps(`vitest@${this.version}`);
1131
+ if (this.coverageEnabled) {
1132
+ project.addDevDeps(`@vitest/coverage-v8@${this.version}`);
1133
+ }
1134
+ const coveragePath = `/${this.coverageDirectory}/`;
1135
+ project.gitignore.addPatterns(coveragePath);
1136
+ project.npmignore?.exclude(coveragePath);
1137
+ this.addTestTasks();
1138
+ this.synthesizeConfig();
1139
+ }
1140
+ /**
1141
+ * Find the Vitest component on a project.
1142
+ */
1143
+ static of(project) {
1144
+ const isVitest = (c) => c instanceof _Vitest;
1145
+ return project.components.find(isVitest);
1146
+ }
1147
+ preSynthesize() {
1148
+ super.preSynthesize();
1149
+ for (const component of this.project.components) {
1150
+ if (component instanceof Jest) {
1151
+ throw new Error("Vitest cannot be used together with Jest");
1152
+ }
1153
+ }
1154
+ }
1155
+ addTestTasks() {
1156
+ this.project.testTask.exec("vitest run --update");
1157
+ if (!this.project.tasks.tryFind("test:watch")) {
1158
+ this.project.addTask("test:watch", {
1159
+ description: "Run tests in watch mode",
1160
+ exec: "vitest watch",
1161
+ receiveArgs: true
1162
+ });
1163
+ }
1164
+ }
1165
+ synthesizeConfig() {
1166
+ this.project.tryRemoveFile(this.configFilePath);
1167
+ new TextFile(this, this.configFilePath, {
1168
+ lines: this.renderConfig()
1169
+ });
1170
+ }
1171
+ renderConfig() {
1172
+ return [
1173
+ 'import { defineConfig } from "vitest/config";',
1174
+ "",
1175
+ "export default defineConfig({",
1176
+ " test: {",
1177
+ ` include: ${JSON.stringify(this.include)},`,
1178
+ ` exclude: ${JSON.stringify(this.exclude)},`,
1179
+ ` environment: "${this.environment}",`,
1180
+ ` passWithNoTests: ${this.passWithNoTests},`,
1181
+ " coverage: {",
1182
+ ` enabled: ${this.coverageEnabled},`,
1183
+ ` provider: "v8",`,
1184
+ ` reportsDirectory: "${this.coverageDirectory}",`,
1185
+ ` reporter: ${JSON.stringify(this.coverageReporters)},`,
1186
+ " },",
1187
+ " },",
1188
+ "});"
1189
+ ];
1190
+ }
1191
+ };
1192
+
1193
+ // src/agent/bundles/vitest.ts
1194
+ var vitestBundle = {
1195
+ name: "vitest",
1196
+ description: "Vitest testing conventions, patterns, and file scoping",
1197
+ appliesWhen: (project) => hasComponent(project, Vitest),
1198
+ rules: [
1199
+ {
1200
+ name: "vitest-testing",
1201
+ description: "Vitest testing conventions and patterns",
1202
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
1203
+ filePatterns: ["**/*.test.ts", "**/*.spec.ts"],
1204
+ content: [
1205
+ "# Vitest Testing Patterns",
1206
+ "",
1207
+ "## Mandatory Requirements",
1208
+ "",
1209
+ "- **Tests MUST be created or updated whenever code functionality is added or changed**",
1210
+ "- This applies to all code changes, including:",
1211
+ " - New features and functionality",
1212
+ " - Bug fixes",
1213
+ " - Refactoring that changes behavior",
1214
+ " - API changes",
1215
+ " - Configuration changes that affect functionality",
1216
+ "- Tests should be written or updated as part of the same change that modifies the code",
1217
+ "",
1218
+ "## Test Structure",
1219
+ "",
1220
+ "- Use **Vitest** as the test runner",
1221
+ "- Test files: `.spec.ts` or `.test.ts` (co-located with source)",
1222
+ "- Use descriptive test names: `describe('ClassName', () => { it('should do something specific', () => {}) })`",
1223
+ "- Prefer snapshot tests for complex object structures",
1224
+ "- Mock external dependencies appropriately",
1225
+ "",
1226
+ "## Test Organization",
1227
+ "",
1228
+ "- Co-locate test files with source files",
1229
+ "- Test files can use dev dependencies (ESLint rule override)",
1230
+ "- Import from `vitest`: `import { describe, expect, it } from 'vitest'`",
1231
+ "",
1232
+ "## Example Test Structure",
1233
+ "",
1234
+ "```typescript",
1235
+ "import { describe, expect, it } from 'vitest';",
1236
+ "import { MyClass } from './my-class';",
1237
+ "",
1238
+ "describe('MyClass', () => {",
1239
+ " it('should do something specific', () => {",
1240
+ " // Test implementation",
1241
+ " });",
1242
+ "});",
1243
+ "```"
1244
+ ].join("\n"),
1245
+ tags: ["testing"]
1246
+ }
1247
+ ]
1248
+ };
1249
+
1250
+ // src/agent/bundles/index.ts
1251
+ var BUILT_IN_BUNDLES = [
1252
+ baseBundle,
1253
+ typescriptBundle,
1254
+ vitestBundle,
1255
+ jestBundle,
1256
+ turborepoBundle,
1257
+ pnpmBundle,
1258
+ awsCdkBundle,
1259
+ projenBundle
1260
+ ];
1261
+
1262
+ // src/projects/project-metadata.ts
1263
+ import { Component as Component5 } from "projen";
1264
+ import { NodeProject as NodeProject2 } from "projen/lib/javascript";
1265
+ var GITHUB_HTTPS_RE = /(?:https?:\/\/|git\+https:\/\/)github\.com\/([^/]+)\/([^/.]+)(?:\.git)?/;
1266
+ var GITHUB_SSH_RE = /git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?/;
1267
+ function parseGitHubUrl(url) {
1268
+ const httpsMatch = url.match(GITHUB_HTTPS_RE);
1269
+ if (httpsMatch) {
1270
+ return { owner: httpsMatch[1], name: httpsMatch[2] };
1271
+ }
1272
+ const sshMatch = url.match(GITHUB_SSH_RE);
1273
+ if (sshMatch) {
1274
+ return { owner: sshMatch[1], name: sshMatch[2] };
1275
+ }
1276
+ return { owner: void 0, name: void 0 };
1277
+ }
1278
+ var ProjectMetadata = class _ProjectMetadata extends Component5 {
1279
+ /**
1280
+ * Returns the ProjectMetadata instance for a project, or undefined
1281
+ * if the component has not been added.
1282
+ */
1283
+ static of(project) {
1284
+ const isProjectMetadata = (c) => c instanceof _ProjectMetadata;
1285
+ return project.components.find(isProjectMetadata);
1286
+ }
1287
+ constructor(project, options = {}) {
1288
+ super(project);
1289
+ this.metadata = this.resolveMetadata(options);
1290
+ }
1291
+ /**
1292
+ * Merges explicit options with auto-detected values.
1293
+ * Auto-detection reads the repository URL from package.json
1294
+ * (via Projen's NodePackage manifest) and parses GitHub owner/name.
1295
+ * Explicit options always take precedence over auto-detected values.
1296
+ */
1297
+ resolveMetadata(options) {
1298
+ const autoDetected = this.autoDetectRepository();
1299
+ return {
1300
+ repository: {
1301
+ owner: options.repository?.owner ?? autoDetected.owner,
1302
+ name: options.repository?.name ?? autoDetected.name,
1303
+ defaultBranch: options.repository?.defaultBranch ?? "main"
1304
+ },
1305
+ githubProject: options.githubProject,
1306
+ organization: options.organization,
1307
+ slack: options.slack,
1308
+ labels: options.labels,
1309
+ milestones: options.milestones,
1310
+ docsPath: options.docsPath,
1311
+ deployment: options.deployment
1312
+ };
1313
+ }
1314
+ /**
1315
+ * Attempts to auto-detect repository owner and name from the Projen
1316
+ * project's package.json repository field.
1317
+ */
1318
+ autoDetectRepository() {
1319
+ if (!(this.project instanceof NodeProject2)) {
1320
+ return { owner: void 0, name: void 0 };
1321
+ }
1322
+ const manifest = this.project.package.manifest;
1323
+ const repoField = manifest.repository;
1324
+ if (!repoField) {
1325
+ return { owner: void 0, name: void 0 };
1326
+ }
1327
+ const url = typeof repoField === "string" ? repoField : repoField.url ?? "";
1328
+ return parseGitHubUrl(url);
1329
+ }
1330
+ };
1331
+
213
1332
  // src/agent/renderers/claude-renderer.ts
1333
+ import { JsonFile as JsonFile2 } from "projen";
1334
+ import { TextFile as TextFile2 } from "projen/lib/textfile";
214
1335
  var GENERATED_MARKER = "<!-- ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~ -->";
215
1336
  var ClaudeRenderer = class _ClaudeRenderer {
216
1337
  /**
@@ -235,7 +1356,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
235
1356
  if (i > 0) lines.push("", "---", "");
236
1357
  lines.push(...claudeMdRules[i].content.split("\n"));
237
1358
  }
238
- new TextFile(component, "CLAUDE.md", { lines });
1359
+ new TextFile2(component, "CLAUDE.md", { lines });
239
1360
  }
240
1361
  static renderScopedRules(component, rules) {
241
1362
  const scopedRules = rules.filter((r) => {
@@ -255,7 +1376,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
255
1376
  lines.push("");
256
1377
  }
257
1378
  lines.push(...rule.content.split("\n"));
258
- new TextFile(component, `.claude/rules/${rule.name}.md`, { lines });
1379
+ new TextFile2(component, `.claude/rules/${rule.name}.md`, { lines });
259
1380
  }
260
1381
  }
261
1382
  static renderSettings(component, mcpServers, settings) {
@@ -370,7 +1491,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
370
1491
  hasContent = true;
371
1492
  }
372
1493
  if (!hasContent) return;
373
- new JsonFile(component, ".claude/settings.json", { obj });
1494
+ new JsonFile2(component, ".claude/settings.json", { obj });
374
1495
  }
375
1496
  static buildSandboxObj(sandbox) {
376
1497
  const obj = {};
@@ -446,7 +1567,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
446
1567
  lines.push("---");
447
1568
  lines.push("");
448
1569
  lines.push(...skill.instructions.split("\n"));
449
- new TextFile(component, `.claude/skills/${skill.name}/SKILL.md`, {
1570
+ new TextFile2(component, `.claude/skills/${skill.name}/SKILL.md`, {
450
1571
  lines
451
1572
  });
452
1573
  }
@@ -483,7 +1604,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
483
1604
  lines.push("---");
484
1605
  lines.push("");
485
1606
  lines.push(...agent.prompt.split("\n"));
486
- new TextFile(component, `.claude/agents/${agent.name}.md`, { lines });
1607
+ new TextFile2(component, `.claude/agents/${agent.name}.md`, { lines });
487
1608
  }
488
1609
  }
489
1610
  /**
@@ -508,8 +1629,8 @@ var CopilotRenderer = class {
508
1629
  };
509
1630
 
510
1631
  // src/agent/renderers/cursor-renderer.ts
511
- import { JsonFile as JsonFile2 } from "projen";
512
- import { TextFile as TextFile2 } from "projen/lib/textfile";
1632
+ import { JsonFile as JsonFile3 } from "projen";
1633
+ import { TextFile as TextFile3 } from "projen/lib/textfile";
513
1634
  var GENERATED_MARKER2 = "# ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~";
514
1635
  var CursorRenderer = class _CursorRenderer {
515
1636
  /**
@@ -538,7 +1659,7 @@ var CursorRenderer = class _CursorRenderer {
538
1659
  lines.push("---");
539
1660
  lines.push("");
540
1661
  lines.push(...rule.content.split("\n"));
541
- new TextFile2(component, `.cursor/rules/${rule.name}.mdc`, { lines });
1662
+ new TextFile3(component, `.cursor/rules/${rule.name}.mdc`, { lines });
542
1663
  }
543
1664
  }
544
1665
  static renderSkills(component, skills) {
@@ -562,7 +1683,7 @@ var CursorRenderer = class _CursorRenderer {
562
1683
  lines.push("---");
563
1684
  lines.push("");
564
1685
  lines.push(...skill.instructions.split("\n"));
565
- new TextFile2(component, `.cursor/skills/${skill.name}/SKILL.md`, {
1686
+ new TextFile3(component, `.cursor/skills/${skill.name}/SKILL.md`, {
566
1687
  lines
567
1688
  });
568
1689
  }
@@ -587,7 +1708,7 @@ var CursorRenderer = class _CursorRenderer {
587
1708
  lines.push("---");
588
1709
  lines.push("");
589
1710
  lines.push(...agent.prompt.split("\n"));
590
- new TextFile2(component, `.cursor/agents/${agent.name}.md`, { lines });
1711
+ new TextFile3(component, `.cursor/agents/${agent.name}.md`, { lines });
591
1712
  }
592
1713
  }
593
1714
  static renderMcpServers(component, mcpServers) {
@@ -603,7 +1724,7 @@ var CursorRenderer = class _CursorRenderer {
603
1724
  if (config.env) server.env = { ...config.env };
604
1725
  servers[name] = server;
605
1726
  }
606
- new JsonFile2(component, ".cursor/mcp.json", { obj });
1727
+ new JsonFile3(component, ".cursor/mcp.json", { obj });
607
1728
  }
608
1729
  static renderHooks(component, settings) {
609
1730
  if (!settings?.hooks) return;
@@ -641,26 +1762,75 @@ var CursorRenderer = class _CursorRenderer {
641
1762
  }
642
1763
  if (stop?.length) hooks.stop = stop.map((h) => ({ command: h.command }));
643
1764
  if (Object.keys(hooks).length === 0) return;
644
- new JsonFile2(component, ".cursor/hooks.json", {
1765
+ new JsonFile3(component, ".cursor/hooks.json", {
645
1766
  obj: { version: 1, hooks }
646
1767
  });
647
1768
  }
648
1769
  static renderIgnoreFiles(component, settings) {
649
1770
  if (settings?.ignorePatterns && settings.ignorePatterns.length > 0) {
650
- new TextFile2(component, ".cursorignore", {
1771
+ new TextFile3(component, ".cursorignore", {
651
1772
  lines: [GENERATED_MARKER2, "", ...settings.ignorePatterns]
652
1773
  });
653
1774
  }
654
1775
  if (settings?.indexingIgnorePatterns && settings.indexingIgnorePatterns.length > 0) {
655
- new TextFile2(component, ".cursorindexingignore", {
1776
+ new TextFile3(component, ".cursorindexingignore", {
656
1777
  lines: [GENERATED_MARKER2, "", ...settings.indexingIgnorePatterns]
657
1778
  });
658
1779
  }
659
1780
  }
660
1781
  };
661
1782
 
1783
+ // src/agent/template-resolver.ts
1784
+ var FALLBACKS = {
1785
+ "repository.owner": "<owner>",
1786
+ "repository.name": "<repo>",
1787
+ "repository.defaultBranch": "main",
1788
+ "organization.name": "<organization>",
1789
+ "organization.githubOrg": "<org>",
1790
+ "githubProject.name": "<project-name>",
1791
+ "githubProject.number": "<project-number>",
1792
+ "githubProject.nodeId": "<project-node-id>",
1793
+ docsPath: "<docs-path>"
1794
+ };
1795
+ var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
1796
+ function getNestedValue(obj, path) {
1797
+ const parts = path.split(".");
1798
+ let current = obj;
1799
+ for (const part of parts) {
1800
+ if (current == null || typeof current !== "object") {
1801
+ return void 0;
1802
+ }
1803
+ current = current[part];
1804
+ }
1805
+ if (current == null) {
1806
+ return void 0;
1807
+ }
1808
+ return String(current);
1809
+ }
1810
+ function resolveTemplateVariables(template, metadata) {
1811
+ if (!TEMPLATE_RE.test(template)) {
1812
+ return { resolved: template, unresolvedKeys: [] };
1813
+ }
1814
+ const unresolvedKeys = [];
1815
+ TEMPLATE_RE.lastIndex = 0;
1816
+ const resolved = template.replace(TEMPLATE_RE, (_match, key) => {
1817
+ if (metadata) {
1818
+ const value = getNestedValue(
1819
+ metadata,
1820
+ key
1821
+ );
1822
+ if (value !== void 0) {
1823
+ return value;
1824
+ }
1825
+ }
1826
+ unresolvedKeys.push(key);
1827
+ return FALLBACKS[key] ?? `<${key}>`;
1828
+ });
1829
+ return { resolved, unresolvedKeys };
1830
+ }
1831
+
662
1832
  // src/agent/agent-config.ts
663
- var AgentConfig = class _AgentConfig extends Component3 {
1833
+ var AgentConfig = class _AgentConfig extends Component8 {
664
1834
  /**
665
1835
  * Find the AgentConfig component on a project.
666
1836
  */
@@ -679,12 +1849,20 @@ var AgentConfig = class _AgentConfig extends Component3 {
679
1849
  const skills = this.resolveSkills();
680
1850
  const subAgents = this.resolveSubAgents();
681
1851
  const mcpServers = this.options.mcpServers ?? {};
1852
+ const projectMetadata = ProjectMetadata.of(this.project);
1853
+ const metadata = projectMetadata?.metadata;
1854
+ const resolvedRules = this.resolveTemplates(rules, metadata);
1855
+ const resolvedSkills = this.resolveSkillTemplates(skills, metadata);
1856
+ const resolvedSubAgents = this.resolveSubAgentTemplates(
1857
+ subAgents,
1858
+ metadata
1859
+ );
682
1860
  if (platforms.includes(AGENT_PLATFORM.CURSOR)) {
683
1861
  CursorRenderer.render(
684
1862
  this,
685
- rules,
686
- skills,
687
- subAgents,
1863
+ resolvedRules,
1864
+ resolvedSkills,
1865
+ resolvedSubAgents,
688
1866
  mcpServers,
689
1867
  this.options.cursorSettings
690
1868
  );
@@ -692,18 +1870,28 @@ var AgentConfig = class _AgentConfig extends Component3 {
692
1870
  if (platforms.includes(AGENT_PLATFORM.CLAUDE)) {
693
1871
  ClaudeRenderer.render(
694
1872
  this,
695
- rules,
696
- skills,
697
- subAgents,
1873
+ resolvedRules,
1874
+ resolvedSkills,
1875
+ resolvedSubAgents,
698
1876
  mcpServers,
699
1877
  this.options.claudeSettings
700
1878
  );
701
1879
  }
702
1880
  if (platforms.includes(AGENT_PLATFORM.CODEX)) {
703
- CodexRenderer.render(this, rules, skills, subAgents);
1881
+ CodexRenderer.render(
1882
+ this,
1883
+ resolvedRules,
1884
+ resolvedSkills,
1885
+ resolvedSubAgents
1886
+ );
704
1887
  }
705
1888
  if (platforms.includes(AGENT_PLATFORM.COPILOT)) {
706
- CopilotRenderer.render(this, rules, skills, subAgents);
1889
+ CopilotRenderer.render(
1890
+ this,
1891
+ resolvedRules,
1892
+ resolvedSkills,
1893
+ resolvedSubAgents
1894
+ );
707
1895
  }
708
1896
  }
709
1897
  resolvePlatforms() {
@@ -714,6 +1902,9 @@ var AgentConfig = class _AgentConfig extends Component3 {
714
1902
  if (this.options.autoDetectBundles !== false) {
715
1903
  for (const bundle of BUILT_IN_BUNDLES) {
716
1904
  if (this.options.excludeBundles?.includes(bundle.name)) continue;
1905
+ if (bundle.name === "base" && this.options.includeBaseRules === false) {
1906
+ continue;
1907
+ }
717
1908
  if (bundle.appliesWhen(this.project)) {
718
1909
  for (const rule of bundle.rules) {
719
1910
  ruleMap.set(rule.name, rule);
@@ -741,6 +1932,21 @@ var AgentConfig = class _AgentConfig extends Component3 {
741
1932
  ruleMap.delete(name);
742
1933
  }
743
1934
  }
1935
+ if (this.options.ruleExtensions) {
1936
+ for (const [name, extra] of Object.entries(this.options.ruleExtensions)) {
1937
+ const existing = ruleMap.get(name);
1938
+ if (existing) {
1939
+ ruleMap.set(name, {
1940
+ ...existing,
1941
+ content: `${existing.content}
1942
+
1943
+ ---
1944
+
1945
+ ${extra}`
1946
+ });
1947
+ }
1948
+ }
1949
+ }
744
1950
  return [...ruleMap.values()].sort((a, b) => {
745
1951
  if (a.name === "project-overview") return -1;
746
1952
  if (b.name === "project-overview") return 1;
@@ -788,264 +1994,65 @@ var AgentConfig = class _AgentConfig extends Component3 {
788
1994
  }
789
1995
  return [...agentMap.values()];
790
1996
  }
791
- };
792
-
793
- // src/aws/aws-deployment-config.ts
794
- var import_utils = __toESM(require_lib());
795
- import { join, relative } from "path";
796
- import { Component as Component6 } from "projen";
797
-
798
- // src/turbo/turbo-repo-task.ts
799
- import { Component as Component4 } from "projen/lib";
800
- var TurboRepoTask = class extends Component4 {
801
- constructor(project, options) {
802
- super(project);
803
- this.project = project;
804
- this.name = options.name;
805
- this.dependsOn = options.dependsOn ?? [];
806
- this.env = options.env ?? [];
807
- this.passThroughEnv = options.passThroughEnv ?? [];
808
- this.outputs = options.outputs ?? [];
809
- this.cache = options.cache ?? true;
810
- this.inputs = [
811
- ...options.inputs ?? [],
812
- // rerun if projen config changes
813
- ".projen/**",
814
- // ignore mac files
815
- "!.DS_Store",
816
- "!**/.DS_Store"
817
- ];
818
- this.outputLogs = options.outputLogs ?? "new-only";
819
- this.persistent = options.persistent ?? false;
820
- this.interactive = options.interactive ?? false;
821
- this.isActive = true;
822
- }
823
- taskConfig() {
824
- return {
825
- dependsOn: this.dependsOn,
826
- env: this.env,
827
- passThroughEnv: this.passThroughEnv,
828
- outputs: this.outputs,
829
- cache: this.cache,
830
- inputs: this.inputs,
831
- outputLogs: this.outputLogs,
832
- persistent: this.persistent,
833
- interactive: this.interactive
834
- };
835
- }
836
- };
837
-
838
- // src/turbo/turbo-repo.ts
839
- import { Component as Component5, FileBase, JsonFile as JsonFile3 } from "projen/lib";
840
- import { JobPermission } from "projen/lib/github/workflows-model";
841
- var ROOT_TURBO_TASK_NAME = "turbo:build";
842
- var ROOT_CI_TASK_NAME = "build:all";
843
- var _TurboRepo = class _TurboRepo extends Component5 {
844
- constructor(project, options = {}) {
845
- super(project);
846
- this.project = project;
847
- /**
848
- * Sub-Tasks to run
849
- */
850
- this.tasks = [];
851
- this.turboVersion = options.turboVersion ?? "catalog:";
852
- this.isRootProject = project === project.root;
853
- if (this.isRootProject) {
854
- project.addDevDeps(`turbo@${this.turboVersion}`);
855
- }
856
- project.gitignore.addPatterns("/.turbo");
857
- project.npmignore?.addPatterns("/.turbo/");
858
- this.extends = options.extends ?? (this.isRootProject ? [] : ["//"]);
859
- this.globalDependencies = options.globalDependencies ?? [];
860
- this.globalEnv = options.globalEnv ?? [];
861
- this.globalPassThroughEnv = options.globalPassThroughEnv ?? [];
862
- this.ui = options.ui ?? "stream";
863
- this.dangerouslyDisablePackageManagerCheck = options.dangerouslyDisablePackageManagerCheck ?? false;
864
- this.cacheDir = options.cacheDir ?? ".turbo/cache";
865
- this.daemon = options.daemon ?? true;
866
- this.envMode = options.envMode ?? "strict";
867
- this.remoteCacheOptions = options.remoteCacheOptions;
868
- this.buildAllTaskEnvVars = options.buildAllTaskEnvVars ?? {};
869
- const rootGeneratedFiles = this.isRootProject ? this.project.components.filter((c) => c instanceof FileBase).map((c) => c.path) : [];
870
- this.buildTask = new TurboRepoTask(this.project, {
871
- name: ROOT_TURBO_TASK_NAME,
872
- dependsOn: this.isRootProject ? [`^${ROOT_TURBO_TASK_NAME}`] : [],
873
- ...rootGeneratedFiles.length > 0 && { inputs: rootGeneratedFiles }
874
- });
875
- if (this.isRootProject) {
876
- this.buildAllTask = this.project.tasks.addTask(ROOT_CI_TASK_NAME, {
877
- description: "Root build followed by sub-project builds. Mimics the CI build process in one step."
878
- });
879
- this.buildAllTask.exec("turbo telemetry disable");
880
- if (this.buildAllTaskEnvVars) {
881
- Object.entries(this.buildAllTaskEnvVars).forEach(([name, value]) => {
882
- this.addGlobalEnvVar(name, value);
883
- });
884
- }
885
- if (!this.remoteCacheOptions) {
886
- this.buildAllTask.exec(
887
- `turbo ${ROOT_TURBO_TASK_NAME} --summarize --concurrency=10`
888
- );
889
- } else {
890
- this.buildAllTask.exec(
891
- `turbo turbo:build --summarize --concurrency=10 --cache=remote:rw --api=$TURBO_ENDPOINT --token=$TURBO_TOKEN --team=${this.remoteCacheOptions.teamName}`,
892
- {
893
- condition: '[ ! -n "$CI" ]',
894
- env: {
895
- TURBO_ENDPOINT: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.endpointParamName} --query Parameter.Value --output text --profile ${this.remoteCacheOptions.profileName})`,
896
- TURBO_TOKEN: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.tokenParamName} --query Parameter.Value --output text --profile ${this.remoteCacheOptions.profileName})`
897
- }
898
- }
899
- );
900
- this.buildAllTask.exec(
901
- `turbo turbo:build --summarize --concurrency=10 --cache=remote:rw --api=$TURBO_ENDPOINT --token=$TURBO_TOKEN --team=${this.remoteCacheOptions.teamName}`,
902
- {
903
- condition: '[ -n "$CI" ]',
904
- env: {
905
- TURBO_ENDPOINT: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.endpointParamName} --query Parameter.Value --output text)`,
906
- TURBO_TOKEN: `$(aws ssm get-parameter --name ${this.remoteCacheOptions.tokenParamName} --query Parameter.Value --output text)`
907
- }
908
- }
909
- );
910
- }
911
- }
912
- if (!this.isRootProject) {
913
- const generatedFiles = this.project.components.filter((c) => c instanceof FileBase).map((c) => c.path);
914
- this.preCompileTask = new TurboRepoTask(project, {
915
- name: options.preCompileTask?.name ?? "pre-compile",
916
- inputs: ["src/**", ...generatedFiles]
917
- });
918
- this.compileTask = new TurboRepoTask(project, {
919
- name: options.compileTask?.name ?? "compile",
920
- inputs: ["src/**", ...generatedFiles]
921
- });
922
- this.postCompileTask = new TurboRepoTask(project, {
923
- name: options.postCompileTask?.name ?? "post-compile",
924
- inputs: ["src/**", ...generatedFiles]
925
- });
926
- this.testTask = new TurboRepoTask(project, {
927
- name: options.testTask?.name ?? "test"
928
- });
929
- this.packageTask = new TurboRepoTask(project, {
930
- name: options.packageTask?.name ?? "package",
931
- inputs: [".npmignore"]
932
- });
933
- this.tasks.push(
934
- this.preCompileTask,
935
- this.compileTask,
936
- this.postCompileTask,
937
- this.testTask,
938
- this.packageTask
939
- );
940
- }
941
- }
942
- /**
943
- * Static method to discovert turbo in a project.
944
- */
945
- static of(project) {
946
- const isDefined = (c) => c instanceof _TurboRepo;
947
- return project.components.find(isDefined);
948
- }
949
- /**
950
- * Add an env var to the global env vars for all tasks.
951
- * This will also become an input for the build:all task cache at the root.
952
- */
953
- addGlobalEnvVar(name, value) {
954
- this.buildAllTask?.env(name, value);
955
- if (this.isRootProject) {
956
- this.globalEnv.push(name);
957
- }
958
- }
959
- /**
960
- * Sets GIT_BRANCH_NAME so root tasks (e.g. build:all) pass the current branch.
961
- * Value must be exactly $(...) so Projen's task runtime expands it; the shell
962
- * then expands ${GIT_BRANCH_NAME:-$(git rev-parse --abbrev-ref HEAD)}.
963
- */
964
- activateBranchNameEnvVar() {
965
- this.addGlobalEnvVar(
966
- "GIT_BRANCH_NAME",
967
- '$(echo "${GIT_BRANCH_NAME:-$(git rev-parse --abbrev-ref HEAD)}")'
968
- );
969
- }
970
- preSynthesize() {
971
- let nextDependsOn = this.project.deps.all.filter((d) => d.version === "workspace:*").map((d) => [d.name, ROOT_TURBO_TASK_NAME].join("#"));
972
- if (!this.isRootProject) {
973
- [
974
- [this.project.preCompileTask, this.preCompileTask],
975
- [this.project.compileTask, this.compileTask],
976
- [this.project.postCompileTask, this.postCompileTask],
977
- [this.project.testTask, this.testTask],
978
- [this.project.packageTask, this.packageTask]
979
- ].forEach(([pjTask, turboTask]) => {
980
- if (pjTask && turboTask && pjTask.steps.length > 0) {
981
- if (nextDependsOn.length > 0) {
982
- turboTask.dependsOn.push(...nextDependsOn);
983
- }
984
- nextDependsOn = [turboTask.name];
985
- } else {
986
- turboTask.isActive = false;
987
- }
988
- });
989
- this.buildTask.dependsOn.push(...nextDependsOn);
990
- }
991
- const fileName = "turbo.json";
992
- this.project.addPackageIgnore(fileName);
993
- new JsonFile3(this.project, fileName, {
994
- obj: {
995
- extends: this.extends.length ? this.extends : void 0,
996
- globalDependencies: this.isRootProject && this.globalDependencies.length ? this.globalDependencies : void 0,
997
- globalEnv: this.isRootProject && this.globalEnv.length ? this.globalEnv : void 0,
998
- globalPassThroughEnv: this.isRootProject && this.globalPassThroughEnv.length ? this.globalPassThroughEnv : void 0,
999
- ui: this.isRootProject ? this.ui : void 0,
1000
- dangerouslyDisablePackageManagerCheck: this.isRootProject ? this.dangerouslyDisablePackageManagerCheck : void 0,
1001
- cacheDir: this.isRootProject ? this.cacheDir : void 0,
1002
- daemon: this.isRootProject ? this.daemon : void 0,
1003
- envMode: this.isRootProject ? this.envMode : void 0,
1004
- /**
1005
- * All tasks
1006
- */
1007
- tasks: this.tasks.filter((task) => task.isActive).reduce(
1008
- (acc, task) => {
1009
- acc[task.name] = {
1010
- ...task.taskConfig()
1011
- };
1012
- return acc;
1013
- },
1014
- {
1015
- [this.buildTask.name]: { ...this.buildTask.taskConfig() }
1016
- }
1017
- )
1997
+ /**
1998
+ * Resolves template variables in rule content using project metadata.
1999
+ * Emits synthesis warnings for rules with unresolved variables.
2000
+ */
2001
+ resolveTemplates(rules, metadata) {
2002
+ return rules.map((rule) => {
2003
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2004
+ rule.content,
2005
+ metadata
2006
+ );
2007
+ if (unresolvedKeys.length > 0) {
2008
+ this.project.logger.warn(
2009
+ `AgentConfig: ProjectMetadata not found; rule '${rule.name}' using default values`
2010
+ );
1018
2011
  }
2012
+ return resolved !== rule.content ? { ...rule, content: resolved } : rule;
1019
2013
  });
1020
- super.preSynthesize();
1021
2014
  }
1022
- };
1023
- _TurboRepo.buildWorkflowOptions = (remoteCacheOptions) => {
1024
- return {
1025
- env: {
1026
- GIT_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
1027
- },
1028
- permissions: {
1029
- contents: JobPermission.WRITE,
1030
- idToken: JobPermission.WRITE
1031
- },
1032
- preBuildSteps: [
1033
- {
1034
- name: "AWS Creds for SSM",
1035
- uses: "aws-actions/configure-aws-credentials@v4",
1036
- with: {
1037
- ["role-to-assume"]: remoteCacheOptions.oidcRole,
1038
- ["aws-region"]: "us-east-1",
1039
- ["role-duration-seconds"]: "900"
1040
- }
2015
+ /**
2016
+ * Resolves template variables in skill instructions using project metadata.
2017
+ */
2018
+ resolveSkillTemplates(skills, metadata) {
2019
+ return skills.map((skill) => {
2020
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2021
+ skill.instructions,
2022
+ metadata
2023
+ );
2024
+ if (unresolvedKeys.length > 0) {
2025
+ this.project.logger.warn(
2026
+ `AgentConfig: ProjectMetadata not found; skill '${skill.name}' using default values`
2027
+ );
1041
2028
  }
1042
- ]
1043
- };
2029
+ return resolved !== skill.instructions ? { ...skill, instructions: resolved } : skill;
2030
+ });
2031
+ }
2032
+ /**
2033
+ * Resolves template variables in sub-agent prompts using project metadata.
2034
+ */
2035
+ resolveSubAgentTemplates(subAgents, metadata) {
2036
+ return subAgents.map((agent) => {
2037
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2038
+ agent.prompt,
2039
+ metadata
2040
+ );
2041
+ if (unresolvedKeys.length > 0) {
2042
+ this.project.logger.warn(
2043
+ `AgentConfig: ProjectMetadata not found; sub-agent '${agent.name}' using default values`
2044
+ );
2045
+ }
2046
+ return resolved !== agent.prompt ? { ...agent, prompt: resolved } : agent;
2047
+ });
2048
+ }
1044
2049
  };
1045
- var TurboRepo = _TurboRepo;
1046
2050
 
1047
2051
  // src/aws/aws-deployment-config.ts
1048
- var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
2052
+ var import_utils8 = __toESM(require_lib());
2053
+ import { join, relative as relative2 } from "path";
2054
+ import { Component as Component9 } from "projen";
2055
+ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
1049
2056
  constructor(project) {
1050
2057
  super(project);
1051
2058
  /**
@@ -1069,8 +2076,8 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
1069
2076
  this.env = {
1070
2077
  GIT_BRANCH_NAME: '$(echo "${GIT_BRANCH_NAME:-$(git branch --show-current)}")'
1071
2078
  };
1072
- this.projectPath = relative(project.root.outdir, project.outdir);
1073
- this.rootPath = relative(project.outdir, project.root.outdir);
2079
+ this.projectPath = relative2(project.root.outdir, project.outdir);
2080
+ this.rootPath = relative2(project.outdir, project.root.outdir);
1074
2081
  this.rootCdkOut = join("dist", this.projectPath, "cdk.out");
1075
2082
  this.cdkOut = join(this.rootPath, "dist", this.projectPath, "cdk.out");
1076
2083
  ["deploy", "watch"].forEach((taskName) => {
@@ -1100,17 +2107,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
1100
2107
  */
1101
2108
  get prodTargets() {
1102
2109
  return this.awsDeploymentTargets.filter(
1103
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.PROD
2110
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD
1104
2111
  );
1105
2112
  }
1106
2113
  get prodTargetsForCI() {
1107
2114
  return this.awsDeploymentTargets.filter(
1108
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.PROD && target.ciDeployment
2115
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.ciDeployment
1109
2116
  );
1110
2117
  }
1111
2118
  get prodTargetsForLocal() {
1112
2119
  return this.awsDeploymentTargets.filter(
1113
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.PROD && target.localDeployment
2120
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.localDeployment
1114
2121
  );
1115
2122
  }
1116
2123
  /**
@@ -1119,17 +2126,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
1119
2126
  */
1120
2127
  get stageTargets() {
1121
2128
  return this.awsDeploymentTargets.filter(
1122
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.STAGE
2129
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE
1123
2130
  );
1124
2131
  }
1125
2132
  get stageTargetsForCI() {
1126
2133
  return this.awsDeploymentTargets.filter(
1127
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.STAGE && target.ciDeployment
2134
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.ciDeployment
1128
2135
  );
1129
2136
  }
1130
2137
  get stageTargetsForLocal() {
1131
2138
  return this.awsDeploymentTargets.filter(
1132
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.STAGE && target.localDeployment
2139
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.localDeployment
1133
2140
  );
1134
2141
  }
1135
2142
  /**
@@ -1138,17 +2145,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
1138
2145
  */
1139
2146
  get devTargets() {
1140
2147
  return this.awsDeploymentTargets.filter(
1141
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.DEV
2148
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV
1142
2149
  );
1143
2150
  }
1144
2151
  get devTargetsForCI() {
1145
2152
  return this.awsDeploymentTargets.filter(
1146
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.DEV && target.ciDeployment
2153
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.ciDeployment
1147
2154
  );
1148
2155
  }
1149
2156
  get devTargetsForLocal() {
1150
2157
  return this.awsDeploymentTargets.filter(
1151
- (target) => target.awsStageType === import_utils.AWS_STAGE_TYPE.DEV && target.localDeployment
2158
+ (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.localDeployment
1152
2159
  );
1153
2160
  }
1154
2161
  preSynthesize() {
@@ -1161,9 +2168,9 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component6 {
1161
2168
  };
1162
2169
 
1163
2170
  // src/aws/aws-deployment-target.ts
1164
- var import_utils2 = __toESM(require_lib());
1165
- import { Component as Component7 } from "projen";
1166
- var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component7 {
2171
+ var import_utils9 = __toESM(require_lib());
2172
+ import { Component as Component10 } from "projen";
2173
+ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component10 {
1167
2174
  constructor(project, options) {
1168
2175
  super(project);
1169
2176
  /**
@@ -1228,11 +2235,11 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component7 {
1228
2235
  };
1229
2236
  this.account = options.account;
1230
2237
  this.region = options.region;
1231
- this.awsStageType = options.awsStageType || import_utils2.AWS_STAGE_TYPE.DEV;
1232
- const role = options.deploymentTargetRole ?? options.awsEnvironmentType ?? import_utils2.DEPLOYMENT_TARGET_ROLE.PRIMARY;
2238
+ this.awsStageType = options.awsStageType || import_utils9.AWS_STAGE_TYPE.DEV;
2239
+ const role = options.deploymentTargetRole ?? options.awsEnvironmentType ?? import_utils9.DEPLOYMENT_TARGET_ROLE.PRIMARY;
1233
2240
  this.deploymentTargetRole = role;
1234
2241
  this.awsEnvironmentType = role;
1235
- this.branches = options.branches ?? (this.awsStageType === import_utils2.AWS_STAGE_TYPE.PROD ? [
2242
+ this.branches = options.branches ?? (this.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD ? [
1236
2243
  {
1237
2244
  branch: "main"
1238
2245
  }
@@ -1241,7 +2248,7 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component7 {
1241
2248
  branch: "feature/*"
1242
2249
  }
1243
2250
  ]);
1244
- this.localDeployment = options.localDeployment ?? this.awsStageType === import_utils2.AWS_STAGE_TYPE.DEV;
2251
+ this.localDeployment = options.localDeployment ?? this.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV;
1245
2252
  if (this.localDeployment) {
1246
2253
  const roleName = options.localDeploymentConfig?.roleName?.toLowerCase() || "poweruseraccess";
1247
2254
  const profile = options.localDeploymentConfig?.profile || `${roleName}-${this.awsStageType}-${this.account}-${this.region}`;
@@ -1376,61 +2383,14 @@ var VERSION_KEYS_SKIP = [
1376
2383
  // Pinned to 3.x for Vite 5 compatibility; skip until ESM-only (issue #142)
1377
2384
  ];
1378
2385
 
1379
- // src/versions.ts
1380
- var VERSION = {
1381
- /**
1382
- * CDK CLI for workflows and command line operations.
1383
- *
1384
- * CLI and lib are versioned separately, so this is the CLI version.
1385
- */
1386
- AWS_CDK_CLI_VERSION: "2.1117.0",
1387
- /**
1388
- * CDK Version to use for construct projects.
1389
- *
1390
- * CLI and lib are versioned separately, so this is the lib version.
1391
- */
1392
- AWS_CDK_LIB_VERSION: "2.248.0",
1393
- /**
1394
- * Version of the AWS Constructs library to use.
1395
- */
1396
- AWS_CONSTRUCTS_VERSION: "10.6.0",
1397
- /**
1398
- * Version of Node.js to use in CI workflows at github actions.
1399
- */
1400
- NODE_WORKFLOWS: "24",
1401
- /**
1402
- * Version of PNPM to use in workflows at github actions.
1403
- */
1404
- PNPM_VERSION: "10.33.0",
1405
- /**
1406
- * Version of Projen to use.
1407
- */
1408
- PROJEN_VERSION: "0.99.34",
1409
- /**
1410
- * What version of the turborepo library should we use?
1411
- */
1412
- TURBO_VERSION: "2.9.4",
1413
- /**
1414
- * What version of Vite to use (pnpm override). Pinned to 5.x so Vitest 4.x
1415
- * can load config (Vite 6+/7+ are ESM-only; see issue #142). Remove override
1416
- * when moving to ESM-only test setup.
1417
- */
1418
- VITE_VERSION: "5.4.11",
1419
- /**
1420
- * What version of Vitest to use when testRunner is 'vitest'.
1421
- * Pinned to 3.x so it works with Vite 5 (Vitest 4 requires Vite 6). See issue #142.
1422
- */
1423
- VITEST_VERSION: "3.2.4"
1424
- };
1425
-
1426
2386
  // src/jsii/jsii-faker.ts
1427
2387
  import * as spec from "@jsii/spec";
1428
- import { Component as Component8, JsonFile as JsonFile4 } from "projen";
2388
+ import { Component as Component11, JsonFile as JsonFile4 } from "projen";
1429
2389
  var ProjenBaseFqn = {
1430
2390
  TYPESCRIPT_PROJECT: "projen.typescript.TypeScriptProject",
1431
2391
  TYPESCRIPT_PROJECT_OPTIONS: "projen.typescript.TypeScriptProjectOptions"
1432
2392
  };
1433
- var JsiiFaker = class _JsiiFaker extends Component8 {
2393
+ var JsiiFaker = class _JsiiFaker extends Component11 {
1434
2394
  constructor(project) {
1435
2395
  super(project);
1436
2396
  this.project = project;
@@ -1478,86 +2438,6 @@ var JsiiFaker = class _JsiiFaker extends Component8 {
1478
2438
  }
1479
2439
  };
1480
2440
 
1481
- // src/pnpm/pnpm-workspace.ts
1482
- import { relative as relative2 } from "path";
1483
- import { Component as Component9, YamlFile } from "projen";
1484
- var MIMIMUM_RELEASE_AGE = {
1485
- ZERO_DAYS: 0,
1486
- ONE_HOUR: 60,
1487
- SIX_HOURS: 360,
1488
- TWELVE_HOURS: 720,
1489
- ONE_DAY: 1440,
1490
- TWO_DAYS: 2880,
1491
- THREE_DAYS: 4320,
1492
- FOUR_DAYS: 5760,
1493
- FIVE_DAYS: 7200,
1494
- SIX_DAYS: 8640,
1495
- ONE_WEEK: 10080
1496
- };
1497
- var PnpmWorkspace = class _PnpmWorkspace extends Component9 {
1498
- /**
1499
- * Get the pnpm workspace component of a project. If it does not exist,
1500
- * return undefined.
1501
- *
1502
- * @param project
1503
- * @returns
1504
- */
1505
- static of(project) {
1506
- const isDefined = (c) => c instanceof _PnpmWorkspace;
1507
- return project.root.components.find(isDefined);
1508
- }
1509
- constructor(project, options = {}) {
1510
- super(project);
1511
- project.tryFindObjectFile("package.json")?.addDeletionOverride("pnpm");
1512
- this.fileName = options.fileName ?? "pnpm-workspace.yaml";
1513
- this.minimumReleaseAge = options.minimumReleaseAge ?? MIMIMUM_RELEASE_AGE.ONE_DAY;
1514
- this.minimumReleaseAgeExclude = options.minimumReleaseAgeExclude ? ["@codedrifters/*", ...options.minimumReleaseAgeExclude] : ["@codedrifters/*"];
1515
- this.onlyBuiltDependencies = options.onlyBuiltDependencies ? options.onlyBuiltDependencies : [];
1516
- this.ignoredBuiltDependencies = options.ignoredBuiltDependencies ? options.ignoredBuiltDependencies : [];
1517
- this.subprojects = options.subprojects ?? [];
1518
- this.defaultCatalog = options.defaultCatalog;
1519
- this.namedCatalogs = options.namedCatalogs;
1520
- project.addPackageIgnore(this.fileName);
1521
- new YamlFile(this.project, this.fileName, {
1522
- obj: () => {
1523
- const pnpmConfig = {};
1524
- const packages = new Array();
1525
- for (const subproject of project.subprojects) {
1526
- packages.push(relative2(this.project.outdir, subproject.outdir));
1527
- }
1528
- const packageSet = new Set(packages);
1529
- for (const subprojectPath of this.subprojects) {
1530
- packageSet.add(subprojectPath);
1531
- }
1532
- if (this.subprojects.length > 0) {
1533
- packages.length = 0;
1534
- packages.push(...packageSet);
1535
- }
1536
- pnpmConfig.minimumReleaseAge = this.minimumReleaseAge;
1537
- if (this.minimumReleaseAgeExclude.length > 0) {
1538
- pnpmConfig.minimumReleaseAgeExclude = this.minimumReleaseAgeExclude;
1539
- }
1540
- if (this.onlyBuiltDependencies.length > 0) {
1541
- pnpmConfig.onlyBuiltDependencies = this.onlyBuiltDependencies;
1542
- }
1543
- if (this.ignoredBuiltDependencies.length > 0) {
1544
- pnpmConfig.ignoreBuiltDependencies = this.ignoredBuiltDependencies;
1545
- }
1546
- if (this.defaultCatalog && Object.keys(this.defaultCatalog).length > 0) {
1547
- pnpmConfig.catalog = this.defaultCatalog;
1548
- }
1549
- if (this.namedCatalogs && Object.keys(this.namedCatalogs).length > 0) {
1550
- pnpmConfig.namedCatalogs = this.namedCatalogs;
1551
- }
1552
- return {
1553
- ...packages.length > 0 ? { packages } : {},
1554
- ...pnpmConfig ? { ...pnpmConfig } : {}
1555
- };
1556
- }
1557
- });
1558
- }
1559
- };
1560
-
1561
2441
  // src/projects/monorepo-project.ts
1562
2442
  import {
1563
2443
  NodePackageManager as NodePackageManager2,
@@ -1569,7 +2449,7 @@ import {
1569
2449
  import { merge as merge2 } from "ts-deepmerge";
1570
2450
 
1571
2451
  // src/tasks/reset-task.ts
1572
- import { Component as Component11 } from "projen";
2452
+ import { Component as Component12 } from "projen";
1573
2453
 
1574
2454
  // src/projects/typescript-project.ts
1575
2455
  import { typescript } from "projen";
@@ -1580,94 +2460,6 @@ import {
1580
2460
  } from "projen/lib/javascript";
1581
2461
  import { ReleaseTrigger } from "projen/lib/release";
1582
2462
  import { merge } from "ts-deepmerge";
1583
-
1584
- // src/vitest/vitest-component.ts
1585
- import { Component as Component10 } from "projen";
1586
- import { Jest } from "projen/lib/javascript";
1587
- import { TextFile as TextFile3 } from "projen/lib/textfile";
1588
- var Vitest = class _Vitest extends Component10 {
1589
- constructor(project, options = {}) {
1590
- super(project);
1591
- this.project = project;
1592
- this.configFilePath = options.configFilePath ?? "vitest.config.ts";
1593
- const config = options.config ?? {};
1594
- this.include = config.include ?? ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
1595
- this.exclude = config.exclude ?? [
1596
- "**/node_modules/**",
1597
- "**/dist/**",
1598
- "**/lib/**",
1599
- "**/.?*"
1600
- ];
1601
- this.environment = config.environment ?? "node";
1602
- this.passWithNoTests = config.passWithNoTests ?? true;
1603
- this.coverageEnabled = config.coverageEnabled ?? true;
1604
- this.coverageDirectory = config.coverageDirectory ?? "coverage";
1605
- this.coverageReporters = config.coverageReporters ?? ["text", "lcov"];
1606
- this.version = options.vitestVersion ?? VERSION.VITEST_VERSION;
1607
- project.addDevDeps(`vitest@${this.version}`);
1608
- if (this.coverageEnabled) {
1609
- project.addDevDeps(`@vitest/coverage-v8@${this.version}`);
1610
- }
1611
- const coveragePath = `/${this.coverageDirectory}/`;
1612
- project.gitignore.addPatterns(coveragePath);
1613
- project.npmignore?.exclude(coveragePath);
1614
- this.addTestTasks();
1615
- this.synthesizeConfig();
1616
- }
1617
- /**
1618
- * Find the Vitest component on a project.
1619
- */
1620
- static of(project) {
1621
- const isVitest = (c) => c instanceof _Vitest;
1622
- return project.components.find(isVitest);
1623
- }
1624
- preSynthesize() {
1625
- super.preSynthesize();
1626
- for (const component of this.project.components) {
1627
- if (component instanceof Jest) {
1628
- throw new Error("Vitest cannot be used together with Jest");
1629
- }
1630
- }
1631
- }
1632
- addTestTasks() {
1633
- this.project.testTask.exec("vitest run --update");
1634
- if (!this.project.tasks.tryFind("test:watch")) {
1635
- this.project.addTask("test:watch", {
1636
- description: "Run tests in watch mode",
1637
- exec: "vitest watch",
1638
- receiveArgs: true
1639
- });
1640
- }
1641
- }
1642
- synthesizeConfig() {
1643
- this.project.tryRemoveFile(this.configFilePath);
1644
- new TextFile3(this, this.configFilePath, {
1645
- lines: this.renderConfig()
1646
- });
1647
- }
1648
- renderConfig() {
1649
- return [
1650
- 'import { defineConfig } from "vitest/config";',
1651
- "",
1652
- "export default defineConfig({",
1653
- " test: {",
1654
- ` include: ${JSON.stringify(this.include)},`,
1655
- ` exclude: ${JSON.stringify(this.exclude)},`,
1656
- ` environment: "${this.environment}",`,
1657
- ` passWithNoTests: ${this.passWithNoTests},`,
1658
- " coverage: {",
1659
- ` enabled: ${this.coverageEnabled},`,
1660
- ` provider: "v8",`,
1661
- ` reportsDirectory: "${this.coverageDirectory}",`,
1662
- ` reporter: ${JSON.stringify(this.coverageReporters)},`,
1663
- " },",
1664
- " },",
1665
- "});"
1666
- ];
1667
- }
1668
- };
1669
-
1670
- // src/projects/typescript-project.ts
1671
2463
  var TestRunner = {
1672
2464
  JEST: "jest",
1673
2465
  VITEST: "vitest"
@@ -1832,7 +2624,7 @@ var TypeScriptProject = class extends typescript.TypeScriptProject {
1832
2624
  };
1833
2625
 
1834
2626
  // src/tasks/reset-task.ts
1835
- var ResetTask = class _ResetTask extends Component11 {
2627
+ var ResetTask = class _ResetTask extends Component12 {
1836
2628
  constructor(project, options = {}) {
1837
2629
  super(project);
1838
2630
  this.project = project;
@@ -1905,8 +2697,8 @@ var ResetTask = class _ResetTask extends Component11 {
1905
2697
  };
1906
2698
 
1907
2699
  // src/vscode/vscode.ts
1908
- import { Component as Component12, vscode } from "projen";
1909
- var VSCodeConfig = class extends Component12 {
2700
+ import { Component as Component13, vscode } from "projen";
2701
+ var VSCodeConfig = class extends Component13 {
1910
2702
  constructor(project) {
1911
2703
  super(project);
1912
2704
  const vsConfig = new vscode.VsCode(project);
@@ -2282,6 +3074,15 @@ var MonorepoProject = class extends TypeScriptAppProject {
2282
3074
  if (options.approveMergeUpgradeOptions) {
2283
3075
  addApproveMergeUpgradeWorkflow(this, options.approveMergeUpgradeOptions);
2284
3076
  }
3077
+ if (options.projectMetadata !== false) {
3078
+ new ProjectMetadata(
3079
+ this,
3080
+ typeof options.projectMetadata === "object" ? options.projectMetadata : {}
3081
+ );
3082
+ }
3083
+ if (options.agentConfig) {
3084
+ new AgentConfig(this, options.agentConfigOptions);
3085
+ }
2285
3086
  if (this.buildWorkflow) {
2286
3087
  addBuildCompleteJob(this.buildWorkflow);
2287
3088
  }
@@ -2340,9 +3141,9 @@ var MonorepoProject = class extends TypeScriptAppProject {
2340
3141
 
2341
3142
  // src/typescript/typescript-config.ts
2342
3143
  import { relative as relative3 } from "path";
2343
- import { Component as Component13 } from "projen";
3144
+ import { Component as Component14 } from "projen";
2344
3145
  import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
2345
- var TypeScriptConfig = class extends Component13 {
3146
+ var TypeScriptConfig = class extends Component14 {
2346
3147
  constructor(project) {
2347
3148
  super(project);
2348
3149
  let tsPaths = {};
@@ -2369,13 +3170,13 @@ var TypeScriptConfig = class extends Component13 {
2369
3170
  };
2370
3171
 
2371
3172
  // src/workflows/aws-deploy-workflow.ts
2372
- var import_utils3 = __toESM(require_lib());
2373
- import { Component as Component14 } from "projen";
3173
+ var import_utils10 = __toESM(require_lib());
3174
+ import { Component as Component15 } from "projen";
2374
3175
  import { BuildWorkflow } from "projen/lib/build";
2375
3176
  import { GitHub } from "projen/lib/github";
2376
3177
  import { JobPermission as JobPermission4 } from "projen/lib/github/workflows-model";
2377
3178
  var PROD_DEPLOY_NAME = "prod-deploy";
2378
- var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component14 {
3179
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
2379
3180
  constructor(project, options = {}) {
2380
3181
  super(project);
2381
3182
  this.project = project;
@@ -2386,7 +3187,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component14 {
2386
3187
  * @deprecated Use deployment target role terminology elsewhere. This property is maintained for backward compatibility.
2387
3188
  * @default 'primary' (this is the only type supported currently)
2388
3189
  */
2389
- this.awsEnvironmentType = import_utils3.DEPLOYMENT_TARGET_ROLE.PRIMARY;
3190
+ this.awsEnvironmentType = import_utils10.DEPLOYMENT_TARGET_ROLE.PRIMARY;
2390
3191
  this.setupNode = () => {
2391
3192
  return [
2392
3193
  {
@@ -2496,7 +3297,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component14 {
2496
3297
  }
2497
3298
  const turbo = TurboRepo.of(this.rootProject);
2498
3299
  const buildWorkflowOptions = turbo?.remoteCacheOptions ? TurboRepo.buildWorkflowOptions(turbo.remoteCacheOptions) : {};
2499
- this.awsStageType = options.awsStageType ?? import_utils3.AWS_STAGE_TYPE.DEV;
3300
+ this.awsStageType = options.awsStageType ?? import_utils10.AWS_STAGE_TYPE.DEV;
2500
3301
  this.awsDeploymentTargets = options.awsDeploymentTargets ?? AwsDeploymentConfig.of(project)?.awsDeploymentTargets.filter(
2501
3302
  (target) => target.awsStageType === this.awsStageType && target.ciDeployment
2502
3303
  ) ?? [];
@@ -2656,6 +3457,7 @@ export {
2656
3457
  MonorepoProject,
2657
3458
  PROD_DEPLOY_NAME,
2658
3459
  PnpmWorkspace,
3460
+ ProjectMetadata,
2659
3461
  ROOT_CI_TASK_NAME,
2660
3462
  ROOT_TURBO_TASK_NAME,
2661
3463
  ResetTask,
@@ -2671,6 +3473,15 @@ export {
2671
3473
  Vitest,
2672
3474
  addApproveMergeUpgradeWorkflow,
2673
3475
  addBuildCompleteJob,
2674
- getLatestEligibleVersion
3476
+ awsCdkBundle,
3477
+ baseBundle,
3478
+ getLatestEligibleVersion,
3479
+ jestBundle,
3480
+ pnpmBundle,
3481
+ projenBundle,
3482
+ resolveTemplateVariables,
3483
+ turborepoBundle,
3484
+ typescriptBundle,
3485
+ vitestBundle
2675
3486
  };
2676
3487
  //# sourceMappingURL=index.mjs.map