@codedrifters/configulator 0.0.163 → 0.0.164

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
@@ -262,6 +262,37 @@ var awsCdkBundle = {
262
262
  "}",
263
263
  "```",
264
264
  "",
265
+ "## L2 Over L1 Construct Preference",
266
+ "",
267
+ "- Always prefer L2 constructs (e.g., `s3.Bucket`, `lambda.Function`) over L1 escape hatches (`CfnBucket`, `CfnFunction`)",
268
+ "- Only drop to L1 when the L2 construct is missing the feature you need or does not exist yet",
269
+ "- When using L1, add a comment explaining why the L2 was insufficient",
270
+ "",
271
+ "## Filename / Class Name Matching",
272
+ "",
273
+ "- Kebab-case filenames must mirror PascalCase class names (e.g., `my-construct.ts` \u2192 `MyConstruct`)",
274
+ "- One primary construct per file; supporting types (props interface, enums) live in the same file",
275
+ "- Test files follow the same pattern: `my-construct.spec.ts`",
276
+ "",
277
+ "## Cross-Stack Lookup Patterns",
278
+ "",
279
+ "- Lookups live on **service classes**, not component/construct classes",
280
+ "- Use static methods for cross-stack lookups so consumers don't need an instance:",
281
+ "",
282
+ "```typescript",
283
+ "export class MyService {",
284
+ " /** Look up a resource from another stack via SSM. */",
285
+ " public static lookupArn(scope: Construct, id: string): string {",
286
+ " return StringParameter.valueForStringParameter(",
287
+ " scope,",
288
+ " `/my-app/my-resource-arn`,",
289
+ " );",
290
+ " }",
291
+ "}",
292
+ "```",
293
+ "",
294
+ "- Store outputs in SSM parameters with well-known paths; do not pass values between stacks via props",
295
+ "",
265
296
  "## AWS Best Practices",
266
297
  "",
267
298
  "- Use AWS CDK v2 (`aws-cdk-lib`)",
@@ -275,8 +306,30 @@ var awsCdkBundle = {
275
306
  "",
276
307
  "- Mock `Code.fromAsset` in any test file that synthesizes CDK stacks",
277
308
  "- 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"
309
+ "- Add the mock in `beforeAll` and restore in `afterAll`:",
310
+ "",
311
+ "```typescript",
312
+ "let fromAssetMock: MockInstance;",
313
+ "",
314
+ "beforeAll(() => {",
315
+ ' fromAssetMock = vi.spyOn(Code, "fromAsset").mockReturnValue({',
316
+ " isInline: false,",
317
+ " bind: () => ({",
318
+ " s3Location: {",
319
+ ' bucketName: "mock-assets-bucket",',
320
+ ' objectKey: "mock-asset-key.zip",',
321
+ " },",
322
+ " }),",
323
+ " } as unknown as AssetCode);",
324
+ "});",
325
+ "",
326
+ "afterAll(() => {",
327
+ " fromAssetMock.mockRestore();",
328
+ "});",
329
+ "```",
330
+ "",
331
+ "- Normalize the template before snapshotting when the stack includes asset-based Lambdas",
332
+ "- Use `Template.fromStack(stack)` and assert with `hasResourceProperties` for targeted checks"
280
333
  ].join("\n"),
281
334
  tags: ["infrastructure"]
282
335
  }
@@ -585,6 +638,98 @@ var baseBundle = {
585
638
  ]
586
639
  };
587
640
 
641
+ // src/agent/bundles/github-workflow.ts
642
+ import { GitHub } from "projen/lib/github";
643
+ var githubWorkflowBundle = {
644
+ name: "github-workflow",
645
+ description: "GitHub issue and PR workflow automation patterns",
646
+ appliesWhen: (project) => hasComponent(project, GitHub),
647
+ rules: [
648
+ {
649
+ name: "issue-workflow",
650
+ description: "Automated workflow for starting work on a GitHub issue",
651
+ scope: AGENT_RULE_SCOPE.ALWAYS,
652
+ content: [
653
+ "# Issue Workflow",
654
+ "",
655
+ '## "Work on issue X" Automation',
656
+ "",
657
+ "When the user says **work on issue X** (or similar), follow these steps exactly:",
658
+ "",
659
+ "1. **Fetch issue details** \u2014 use `gh issue view <number>` to get the title, body, and labels",
660
+ "2. **Determine branch type** from the issue title prefix:",
661
+ " - `feat:` / `feature:` \u2192 `feat/`",
662
+ " - `fix:` / `bug:` \u2192 `fix/`",
663
+ " - `docs:` \u2192 `docs/`",
664
+ " - `chore:` / `refactor:` \u2192 `chore/`",
665
+ " - `test:` \u2192 `test/`",
666
+ " - No prefix \u2192 `feat/`",
667
+ "3. **Create a branch** following the naming convention: `<type>/<short-slug>-<issue-number>` (e.g., `feat/add-login-42`)",
668
+ "4. **Checkout the branch** locally",
669
+ "5. **Link the branch to the issue** by posting a comment: `gh issue comment <number> --body 'Branch: \\`<branch-name>\\`'`",
670
+ "6. **Stop and wait** for user instructions \u2014 do **NOT** start implementing",
671
+ "",
672
+ "### Important",
673
+ "",
674
+ "- Never begin implementation without explicit user direction",
675
+ "- If the issue title has no conventional prefix, default to `feat/`",
676
+ "- Keep the slug short (3-5 words max, kebab-case)"
677
+ ].join("\n"),
678
+ tags: ["workflow"]
679
+ },
680
+ {
681
+ name: "pr-workflow",
682
+ description: "Automated workflow for opening a pull request",
683
+ scope: AGENT_RULE_SCOPE.ALWAYS,
684
+ content: [
685
+ "# PR Workflow",
686
+ "",
687
+ '## "Open a PR" Automation',
688
+ "",
689
+ "When the user says **open a PR** (or similar), follow these steps exactly:",
690
+ "",
691
+ "1. **Check for uncommitted changes** \u2014 if any exist, commit them with a conventional commit message",
692
+ "2. **Push the branch** to origin: `git push -u origin <branch>`",
693
+ "3. **Create the PR** using `gh pr create`:",
694
+ " - **Title**: use a conventional commit style title (e.g., `feat(scope): short description`)",
695
+ " - **Body**: include `Closes #<issue-number>` (derived from the branch name) and a brief summary of changes",
696
+ "4. **Display merge dialog text** for the user to copy-paste when merging:",
697
+ "",
698
+ "```",
699
+ "Commit message:",
700
+ "<conventional-commit-title>",
701
+ "",
702
+ "Extended description:",
703
+ "- Bullet points summarizing the changes",
704
+ "- Closes #<issue-number>",
705
+ "```",
706
+ "",
707
+ "### PR Body Template",
708
+ "",
709
+ "```markdown",
710
+ "## Summary",
711
+ "",
712
+ "<1-3 bullet points describing what changed and why>",
713
+ "",
714
+ "Closes #<issue-number>",
715
+ "",
716
+ "## Test Plan",
717
+ "",
718
+ "- [ ] Tests pass locally",
719
+ "- [ ] Relevant changes have been reviewed",
720
+ "```",
721
+ "",
722
+ "### Important",
723
+ "",
724
+ "- Always derive the issue number from the branch name (e.g., `feat/add-login-42` \u2192 `#42`)",
725
+ "- Use conventional commit format for the PR title",
726
+ "- Do not merge the PR \u2014 only create it and display the merge text"
727
+ ].join("\n"),
728
+ tags: ["workflow"]
729
+ }
730
+ ]
731
+ };
732
+
588
733
  // src/agent/bundles/jest.ts
589
734
  var jestBundle = {
590
735
  name: "jest",
@@ -771,6 +916,28 @@ var projenBundle = {
771
916
  description: "Projen conventions, synthesis workflow, .projenrc.ts patterns",
772
917
  appliesWhen: (project) => hasDep(project, "projen"),
773
918
  rules: [
919
+ {
920
+ name: "projen-sandbox-restrictions",
921
+ description: "Commands the agent must never run in Projen-managed projects",
922
+ scope: AGENT_RULE_SCOPE.ALWAYS,
923
+ content: [
924
+ "# Projen Sandbox Restrictions",
925
+ "",
926
+ "## Prohibited Commands",
927
+ "",
928
+ "The agent must **never** run the following commands \u2014 always ask the user to run them instead:",
929
+ "",
930
+ "- `pnpm install` / `pnpm i` \u2014 modifies the lockfile and may trigger synthesis",
931
+ "- `npx projen` \u2014 synthesizes generated files and must be run by the user",
932
+ "",
933
+ "## Rationale",
934
+ "",
935
+ "Projen-managed projects rely on synthesized files (`package.json`, `tsconfig.json`, etc.) that are regenerated from `.projenrc.ts`.",
936
+ "Running install or synthesis in the agent's sandbox can cause lockfile drift, phantom dependency changes, or overwrite user-controlled configuration.",
937
+ "The user must run these commands locally so they can review the resulting changes."
938
+ ].join("\n"),
939
+ tags: ["workflow", "safety"]
940
+ },
774
941
  {
775
942
  name: "projen-conventions",
776
943
  description: "Projen configuration patterns and best practices",
@@ -787,6 +954,41 @@ var projenBundle = {
787
954
  " - `projenrc/*.ts` (package-specific)",
788
955
  "- After making Projen changes, ask the user to run `npx projen` locally (agent must not run it)",
789
956
  "",
957
+ "## DO NOT Use Manual Package Manager Commands",
958
+ "",
959
+ "- **Never** run `pnpm add`, `pnpm remove`, `npm install`, or `yarn add` to modify dependencies",
960
+ "- Dependencies must be added through Projen configuration (e.g., `project.addDeps()`, `project.addDevDeps()`)",
961
+ "- Manual package manager commands bypass Projen's dependency management and will be overwritten on the next synthesis",
962
+ "- Correct workflow: edit `.projenrc.ts` \u2192 ask user to run `npx projen` \u2192 user runs `pnpm install` if needed",
963
+ "",
964
+ "## Workspace Dependencies",
965
+ "",
966
+ "When adding dependencies between packages in a monorepo:",
967
+ "",
968
+ '- Use the workspace protocol: `project.addDeps("@org/sibling-pkg@workspace:*")`',
969
+ "- This ensures the local version is always resolved, not a registry version",
970
+ '- For dev dependencies: `project.addDevDeps("@org/sibling-pkg@workspace:*")`',
971
+ "",
972
+ "## The `configureMyProject` Pattern",
973
+ "",
974
+ "Each package should expose a `configureMyProject(options)` factory function that creates and configures its Projen project:",
975
+ "",
976
+ "```typescript",
977
+ "export function configureMyProject(",
978
+ " options: MyProjectOptions,",
979
+ "): TypeScriptProject {",
980
+ " const project = new TypeScriptProject({",
981
+ " ...options,",
982
+ " // package-specific defaults",
983
+ " });",
984
+ "",
985
+ " // Attach components, configure rules, etc.",
986
+ " return project;",
987
+ "}",
988
+ "```",
989
+ "",
990
+ "This pattern keeps `.projenrc.ts` clean and makes package configuration reusable and testable.",
991
+ "",
790
992
  "## Custom Projen Components",
791
993
  "",
792
994
  "All custom components must extend `Component` from Projen and use a static `.of()` factory for discovery:",
@@ -1393,7 +1595,8 @@ var BUILT_IN_BUNDLES = [
1393
1595
  turborepoBundle,
1394
1596
  pnpmBundle,
1395
1597
  awsCdkBundle,
1396
- projenBundle
1598
+ projenBundle,
1599
+ githubWorkflowBundle
1397
1600
  ];
1398
1601
 
1399
1602
  // src/projects/project-metadata.ts
@@ -2224,7 +2427,7 @@ ${extra}`
2224
2427
  };
2225
2428
 
2226
2429
  // src/aws/aws-deployment-config.ts
2227
- var import_utils8 = __toESM(require_lib());
2430
+ var import_utils9 = __toESM(require_lib());
2228
2431
  import { join, relative as relative2 } from "path";
2229
2432
  import { Component as Component9 } from "projen";
2230
2433
  var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
@@ -2282,17 +2485,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
2282
2485
  */
2283
2486
  get prodTargets() {
2284
2487
  return this.awsDeploymentTargets.filter(
2285
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD
2488
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD
2286
2489
  );
2287
2490
  }
2288
2491
  get prodTargetsForCI() {
2289
2492
  return this.awsDeploymentTargets.filter(
2290
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.ciDeployment
2493
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD && target.ciDeployment
2291
2494
  );
2292
2495
  }
2293
2496
  get prodTargetsForLocal() {
2294
2497
  return this.awsDeploymentTargets.filter(
2295
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.PROD && target.localDeployment
2498
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD && target.localDeployment
2296
2499
  );
2297
2500
  }
2298
2501
  /**
@@ -2301,17 +2504,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
2301
2504
  */
2302
2505
  get stageTargets() {
2303
2506
  return this.awsDeploymentTargets.filter(
2304
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE
2507
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.STAGE
2305
2508
  );
2306
2509
  }
2307
2510
  get stageTargetsForCI() {
2308
2511
  return this.awsDeploymentTargets.filter(
2309
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.ciDeployment
2512
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.STAGE && target.ciDeployment
2310
2513
  );
2311
2514
  }
2312
2515
  get stageTargetsForLocal() {
2313
2516
  return this.awsDeploymentTargets.filter(
2314
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.STAGE && target.localDeployment
2517
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.STAGE && target.localDeployment
2315
2518
  );
2316
2519
  }
2317
2520
  /**
@@ -2320,17 +2523,17 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
2320
2523
  */
2321
2524
  get devTargets() {
2322
2525
  return this.awsDeploymentTargets.filter(
2323
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV
2526
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV
2324
2527
  );
2325
2528
  }
2326
2529
  get devTargetsForCI() {
2327
2530
  return this.awsDeploymentTargets.filter(
2328
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.ciDeployment
2531
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV && target.ciDeployment
2329
2532
  );
2330
2533
  }
2331
2534
  get devTargetsForLocal() {
2332
2535
  return this.awsDeploymentTargets.filter(
2333
- (target) => target.awsStageType === import_utils8.AWS_STAGE_TYPE.DEV && target.localDeployment
2536
+ (target) => target.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV && target.localDeployment
2334
2537
  );
2335
2538
  }
2336
2539
  preSynthesize() {
@@ -2343,7 +2546,7 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
2343
2546
  };
2344
2547
 
2345
2548
  // src/aws/aws-deployment-target.ts
2346
- var import_utils9 = __toESM(require_lib());
2549
+ var import_utils10 = __toESM(require_lib());
2347
2550
  import { Component as Component10 } from "projen";
2348
2551
  var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component10 {
2349
2552
  constructor(project, options) {
@@ -2410,11 +2613,11 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component10 {
2410
2613
  };
2411
2614
  this.account = options.account;
2412
2615
  this.region = options.region;
2413
- this.awsStageType = options.awsStageType || import_utils9.AWS_STAGE_TYPE.DEV;
2414
- const role = options.deploymentTargetRole ?? options.awsEnvironmentType ?? import_utils9.DEPLOYMENT_TARGET_ROLE.PRIMARY;
2616
+ this.awsStageType = options.awsStageType || import_utils10.AWS_STAGE_TYPE.DEV;
2617
+ const role = options.deploymentTargetRole ?? options.awsEnvironmentType ?? import_utils10.DEPLOYMENT_TARGET_ROLE.PRIMARY;
2415
2618
  this.deploymentTargetRole = role;
2416
2619
  this.awsEnvironmentType = role;
2417
- this.branches = options.branches ?? (this.awsStageType === import_utils9.AWS_STAGE_TYPE.PROD ? [
2620
+ this.branches = options.branches ?? (this.awsStageType === import_utils10.AWS_STAGE_TYPE.PROD ? [
2418
2621
  {
2419
2622
  branch: "main"
2420
2623
  }
@@ -2423,7 +2626,7 @@ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component10 {
2423
2626
  branch: "feature/*"
2424
2627
  }
2425
2628
  ]);
2426
- this.localDeployment = options.localDeployment ?? this.awsStageType === import_utils9.AWS_STAGE_TYPE.DEV;
2629
+ this.localDeployment = options.localDeployment ?? this.awsStageType === import_utils10.AWS_STAGE_TYPE.DEV;
2427
2630
  if (this.localDeployment) {
2428
2631
  const roleName = options.localDeploymentConfig?.roleName?.toLowerCase() || "poweruseraccess";
2429
2632
  const profile = options.localDeploymentConfig?.profile || `${roleName}-${this.awsStageType}-${this.account}-${this.region}`;
@@ -3358,10 +3561,10 @@ var TypeScriptConfig = class extends Component14 {
3358
3561
  };
3359
3562
 
3360
3563
  // src/workflows/aws-deploy-workflow.ts
3361
- var import_utils10 = __toESM(require_lib());
3564
+ var import_utils11 = __toESM(require_lib());
3362
3565
  import { Component as Component15 } from "projen";
3363
3566
  import { BuildWorkflow } from "projen/lib/build";
3364
- import { GitHub } from "projen/lib/github";
3567
+ import { GitHub as GitHub2 } from "projen/lib/github";
3365
3568
  import { JobPermission as JobPermission4 } from "projen/lib/github/workflows-model";
3366
3569
  var PROD_DEPLOY_NAME = "prod-deploy";
3367
3570
  var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
@@ -3375,7 +3578,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
3375
3578
  * @deprecated Use deployment target role terminology elsewhere. This property is maintained for backward compatibility.
3376
3579
  * @default 'primary' (this is the only type supported currently)
3377
3580
  */
3378
- this.awsEnvironmentType = import_utils10.DEPLOYMENT_TARGET_ROLE.PRIMARY;
3581
+ this.awsEnvironmentType = import_utils11.DEPLOYMENT_TARGET_ROLE.PRIMARY;
3379
3582
  this.setupNode = () => {
3380
3583
  return [
3381
3584
  {
@@ -3477,7 +3680,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
3477
3680
  );
3478
3681
  }
3479
3682
  this.rootProject = project.root;
3480
- const github = GitHub.of(this.rootProject);
3683
+ const github = GitHub2.of(this.rootProject);
3481
3684
  if (!github) {
3482
3685
  throw new Error(
3483
3686
  "AwsDeployWorkflow requires a GitHub component in the root project"
@@ -3485,7 +3688,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
3485
3688
  }
3486
3689
  const turbo = TurboRepo.of(this.rootProject);
3487
3690
  const buildWorkflowOptions = turbo?.remoteCacheOptions ? TurboRepo.buildWorkflowOptions(turbo.remoteCacheOptions) : {};
3488
- this.awsStageType = options.awsStageType ?? import_utils10.AWS_STAGE_TYPE.DEV;
3691
+ this.awsStageType = options.awsStageType ?? import_utils11.AWS_STAGE_TYPE.DEV;
3489
3692
  this.awsDeploymentTargets = options.awsDeploymentTargets ?? AwsDeploymentConfig.of(project)?.awsDeploymentTargets.filter(
3490
3693
  (target) => target.awsStageType === this.awsStageType && target.ciDeployment
3491
3694
  ) ?? [];
@@ -3664,6 +3867,7 @@ export {
3664
3867
  awsCdkBundle,
3665
3868
  baseBundle,
3666
3869
  getLatestEligibleVersion,
3870
+ githubWorkflowBundle,
3667
3871
  jestBundle,
3668
3872
  pnpmBundle,
3669
3873
  projenBundle,