@intentius/chant-lexicon-aws 0.0.22 → 0.1.0

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/src/plugin.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import { createRequire } from "module";
2
- import type { LexiconPlugin, IntrinsicDef, SkillDefinition, ResourceMetadata } from "@intentius/chant/lexicon";
2
+ import type { LexiconPlugin, IntrinsicDef, ResourceMetadata } from "@intentius/chant/lexicon";
3
3
  const require = createRequire(import.meta.url);
4
4
  import type { LintRule } from "@intentius/chant/lint/rule";
5
- import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
6
5
  import type { TemplateParser } from "@intentius/chant/import/parser";
7
6
  import type { TypeScriptGenerator } from "@intentius/chant/import/generator";
8
7
  import type { CompletionContext, CompletionItem, HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
9
- import type { McpToolContribution, McpResourceContribution } from "@intentius/chant/mcp/types";
8
+ import { discoverLintRules, discoverPostSynthChecks } from "@intentius/chant/lint/discover";
9
+ import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
10
+ import { readFileSync } from "fs";
11
+ import { join, dirname } from "path";
12
+ import { fileURLToPath } from "url";
10
13
  import { awsSerializer } from "./serializer";
11
14
 
12
15
  /**
@@ -20,11 +23,8 @@ export const awsPlugin: LexiconPlugin = {
20
23
  serializer: awsSerializer,
21
24
 
22
25
  lintRules(): LintRule[] {
23
- // Lazy-load to avoid pulling in rules unless needed
24
- const { hardcodedRegionRule } = require("./lint/rules/hardcoded-region");
25
- const { s3EncryptionRule } = require("./lint/rules/s3-encryption");
26
- const { iamWildcardRule } = require("./lint/rules/iam-wildcard");
27
- return [hardcodedRegionRule, s3EncryptionRule, iamWildcardRule];
26
+ const rulesDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "rules");
27
+ return discoverLintRules(rulesDir, import.meta.url);
28
28
  },
29
29
 
30
30
  intrinsics(): IntrinsicDef[] {
@@ -261,36 +261,9 @@ export const logsBucket = new Bucket({
261
261
  return new CFGenerator();
262
262
  },
263
263
 
264
- postSynthChecks(): PostSynthCheck[] {
265
- // Lazy-load to avoid pulling in checks unless needed
266
- const { waw010 } = require("./lint/post-synth/waw010");
267
- const { waw011 } = require("./lint/post-synth/waw011");
268
- const { cor020 } = require("./lint/post-synth/cor020");
269
- const { ext001 } = require("./lint/post-synth/ext001");
270
- const { waw013 } = require("./lint/post-synth/waw013");
271
- const { waw014 } = require("./lint/post-synth/waw014");
272
- const { waw015 } = require("./lint/post-synth/waw015");
273
- const { waw016 } = require("./lint/post-synth/waw016");
274
- const { waw017 } = require("./lint/post-synth/waw017");
275
- const { waw018 } = require("./lint/post-synth/waw018");
276
- const { waw019 } = require("./lint/post-synth/waw019");
277
- const { waw020 } = require("./lint/post-synth/waw020");
278
- const { waw021 } = require("./lint/post-synth/waw021");
279
- const { waw022 } = require("./lint/post-synth/waw022");
280
- const { waw023 } = require("./lint/post-synth/waw023");
281
- const { waw024 } = require("./lint/post-synth/waw024");
282
- const { waw025 } = require("./lint/post-synth/waw025");
283
- const { waw026 } = require("./lint/post-synth/waw026");
284
- const { waw027 } = require("./lint/post-synth/waw027");
285
- const { waw028 } = require("./lint/post-synth/waw028");
286
- const { waw029 } = require("./lint/post-synth/waw029");
287
- const { waw030 } = require("./lint/post-synth/waw030");
288
- const { waw031 } = require("./lint/post-synth/waw031");
289
- return [
290
- waw010, waw011, cor020, ext001, waw013, waw014, waw015, waw016, waw017,
291
- waw018, waw019, waw020, waw021, waw022, waw023, waw024, waw025,
292
- waw026, waw027, waw028, waw029, waw030, waw031,
293
- ];
264
+ postSynthChecks() {
265
+ const postSynthDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "post-synth");
266
+ return discoverPostSynthChecks(postSynthDir, import.meta.url);
294
267
  },
295
268
 
296
269
  async generate(options?: { verbose?: boolean }): Promise<void> {
@@ -377,473 +350,42 @@ export const logsBucket = new Bucket({
377
350
  await generateDocs(options);
378
351
  },
379
352
 
380
- skills(): SkillDefinition[] {
381
- return [
382
- {
383
- name: "chant-aws",
384
- description: "AWS CloudFormation lifecycle — build, diff, deploy, rollback, and troubleshoot from a chant project",
385
- content: `---
386
- skill: chant-aws
387
- description: Build, validate, and deploy CloudFormation templates from a chant project
388
- user-invocable: true
389
- ---
390
-
391
- # AWS CloudFormation Operational Playbook
392
-
393
- ## How chant and CloudFormation relate
394
-
395
- chant is a **synthesis compiler** — it compiles TypeScript source files into CloudFormation JSON (or YAML). \`chant build\` does not call AWS APIs; synthesis is pure and deterministic. The optional \`chant state snapshot\` command queries AWS APIs to capture deployment metadata (physical IDs, status, outputs) for observability. Your job as an agent is to bridge synthesis and deployment:
396
-
397
- - Use **chant** for: build, lint, diff (local template comparison)
398
- - Use **AWS CLI** for: validate-template, deploy, change sets, rollback, drift detection, and all stack operations
399
-
400
- The source of truth for infrastructure is the TypeScript in \`src/\`. The generated template (\`stack.json\`) is an intermediate artifact.
401
-
402
- ## Build and validate
403
-
404
- ### Build the template
405
-
406
- \`\`\`bash
407
- chant build src/ --output stack.json
408
- \`\`\`
409
-
410
- Options:
411
- - \`--format yaml\` — emit YAML instead of JSON
412
- - \`--watch\` rebuild on source changes
413
-
414
- ### Lint the source
415
-
416
- \`\`\`bash
417
- chant lint src/
418
- \`\`\`
419
-
420
- Options:
421
- - \`--fix\` — auto-fix violations where possible
422
- - \`--format sarif\` — SARIF output for CI integration
423
- - \`--watch\` — re-lint on changes
424
-
425
- ### Validate with CloudFormation
426
-
427
- \`\`\`bash
428
- aws cloudformation validate-template --template-body file://stack.json
429
- \`\`\`
430
-
431
- ### What each step catches
432
-
433
- | Step | Catches | When to run |
434
- |------|---------|-------------|
435
- | \`chant lint\` | Best-practice violations, security anti-patterns, naming issues | Every edit |
436
- | \`chant build\` | TypeScript errors, missing properties, type mismatches | Before deploy |
437
- | \`validate-template\` | CloudFormation schema errors, invalid intrinsic usage | Before deploy |
438
-
439
- Always run all three before deploying. Lint catches things validate-template cannot (and vice versa).
440
-
441
- ## Diffing and change preview
442
-
443
- This is the most critical section for production safety. **Never deploy to production without previewing changes.**
444
-
445
- ### Local diff
446
-
447
- Compare your proposed template against what is currently deployed:
448
-
449
- \`\`\`bash
450
- # Get the currently deployed template
451
- aws cloudformation get-template --stack-name <stack-name> --query TemplateBody --output json > deployed.json
452
-
453
- # Build the proposed template
454
- chant build src/ --output proposed.json
455
-
456
- # Diff them
457
- diff deployed.json proposed.json
458
- \`\`\`
459
-
460
- ### Change sets (recommended for production)
461
-
462
- Change sets let you preview exactly what CloudFormation will do before it does it.
463
-
464
- \`\`\`bash
465
- # 1. Create the change set
466
- aws cloudformation create-change-set \\
467
- --stack-name <stack-name> \\
468
- --template-body file://stack.json \\
469
- --change-set-name review-$(date +%s) \\
470
- --capabilities CAPABILITY_NAMED_IAM
471
-
472
- # 2. Wait for it to compute
473
- aws cloudformation wait change-set-create-complete \\
474
- --stack-name <stack-name> \\
475
- --change-set-name review-<id>
476
-
477
- # 3. Review the changes
478
- aws cloudformation describe-change-set \\
479
- --stack-name <stack-name> \\
480
- --change-set-name review-<id>
481
-
482
- # 4a. Execute if changes look safe
483
- aws cloudformation execute-change-set \\
484
- --stack-name <stack-name> \\
485
- --change-set-name review-<id>
486
-
487
- # 4b. Or delete if you want to abort
488
- aws cloudformation delete-change-set \\
489
- --stack-name <stack-name> \\
490
- --change-set-name review-<id>
491
- \`\`\`
492
-
493
- ### Interpreting change set results
494
-
495
- Each resource change has an **Action** and a **Replacement** value. Read them together:
496
-
497
- | Action | Replacement | Risk | Meaning |
498
- |--------|-------------|------|---------|
499
- | Add | — | Low | New resource will be created |
500
- | Modify | False | Low | In-place update, no disruption |
501
- | Modify | Conditional | **MEDIUM** | May replace depending on property — investigate further |
502
- | Modify | True | **HIGH** | Resource will be DESTROYED and recreated — **data loss risk** |
503
- | Remove | — | **HIGH** | Resource will be deleted |
504
-
505
- ### Properties that always cause replacement
506
-
507
- These property changes ALWAYS destroy and recreate the resource:
508
- - \`BucketName\` on S3 buckets
509
- - \`TableName\` on DynamoDB tables
510
- - \`DBInstanceIdentifier\` on RDS instances
511
- - \`FunctionName\` on Lambda functions
512
- - \`CidrBlock\` on VPCs and subnets
513
- - \`ClusterIdentifier\` on Redshift clusters
514
- - \`DomainName\` on Elasticsearch/OpenSearch domains
515
- - \`TopicName\` on SNS topics
516
- - \`QueueName\` on SQS queues
517
-
518
- **CRITICAL**: When you see \`Replacement: True\` on any stateful resource (databases, S3 buckets, queues with messages, DynamoDB tables), ALWAYS flag this to the user and get explicit confirmation before executing. This will destroy the existing resource and all its data.
519
-
520
- ## Deploying a new stack
521
-
522
- \`\`\`bash
523
- aws cloudformation deploy \\
524
- --template-file stack.json \\
525
- --stack-name <stack-name> \\
526
- --capabilities CAPABILITY_NAMED_IAM \\
527
- --parameter-overrides Env=prod Version=1.0 \\
528
- --tags Project=myapp Environment=prod
529
- \`\`\`
530
-
531
- ### Capabilities
532
-
533
- | Capability | When needed |
534
- |------------|-------------|
535
- | \`CAPABILITY_IAM\` | Template creates IAM resources with auto-generated names |
536
- | \`CAPABILITY_NAMED_IAM\` | Template creates IAM resources with custom names (use this by default — it's a superset) |
537
- | \`CAPABILITY_AUTO_EXPAND\` | Template uses macros or nested stacks with transforms |
538
-
539
- **Recommendation**: Default to \`CAPABILITY_NAMED_IAM\` unless the template also uses macros, in which case use \`--capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND\`.
540
-
541
- ### Monitoring deployment
542
-
543
- \`\`\`bash
544
- # Wait for completion (blocks until done)
545
- aws cloudformation wait stack-create-complete --stack-name <stack-name>
546
-
547
- # Or poll events in real-time
548
- watch -n 5 "aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 10 --query 'StackEvents[].{Time:Timestamp,Resource:LogicalResourceId,Status:ResourceStatus,Reason:ResourceStatusReason}' --output table"
549
- \`\`\`
550
-
551
- ### Getting outputs
552
-
553
- \`\`\`bash
554
- aws cloudformation describe-stacks \\
555
- --stack-name <stack-name> \\
556
- --query 'Stacks[0].Outputs'
557
- \`\`\`
558
-
559
- ## Updating an existing stack
560
-
561
- ### Safe path — change set workflow (production / stateful stacks)
562
-
563
- 1. Build: \`chant build src/ --output stack.json\`
564
- 2. Create change set (see Diffing section above)
565
- 3. Review every resource change — pay special attention to Replacement values
566
- 4. Get user confirmation for any destructive changes
567
- 5. Execute the change set
568
- 6. Monitor: \`aws cloudformation wait stack-update-complete --stack-name <stack-name>\`
569
-
570
- ### Fast path — direct deploy (dev / stateless stacks)
571
-
572
- \`\`\`bash
573
- aws cloudformation deploy \\
574
- --template-file stack.json \\
575
- --stack-name <stack-name> \\
576
- --capabilities CAPABILITY_NAMED_IAM \\
577
- --no-fail-on-empty-changeset
578
- \`\`\`
579
-
580
- The \`--no-fail-on-empty-changeset\` flag prevents a non-zero exit code when there are no changes (useful in CI).
581
-
582
- ### Updating parameters only (no template change)
583
-
584
- \`\`\`bash
585
- aws cloudformation deploy \\
586
- --stack-name <stack-name> \\
587
- --use-previous-template \\
588
- --capabilities CAPABILITY_NAMED_IAM \\
589
- --parameter-overrides Env=staging
590
- \`\`\`
591
-
592
- ### Which path to use
593
-
594
- | Scenario | Path |
595
- |----------|------|
596
- | Production stack with databases/storage | Safe path (change set) |
597
- | Any stack with \`Replacement: True\` changes | Safe path (change set) |
598
- | Dev/test stack, stateless resources only | Fast path (direct deploy) |
599
- | CI/CD automated pipeline with approval gate | Safe path (change set with manual approval) |
600
- | Parameter-only change, no template diff | Fast path with \`--use-previous-template\` |
601
-
602
- ## Rollback and recovery
603
-
604
- ### Stack states reference
605
-
606
- | State | Meaning | Action |
607
- |-------|---------|--------|
608
- | \`CREATE_COMPLETE\` | Stack created successfully | None — healthy |
609
- | \`UPDATE_COMPLETE\` | Update succeeded | None — healthy |
610
- | \`DELETE_COMPLETE\` | Stack deleted | Gone — recreate if needed |
611
- | \`CREATE_IN_PROGRESS\` | Creation underway | Wait |
612
- | \`UPDATE_IN_PROGRESS\` | Update underway | Wait |
613
- | \`DELETE_IN_PROGRESS\` | Deletion underway | Wait |
614
- | \`ROLLBACK_IN_PROGRESS\` | Create failed, rolling back | Wait |
615
- | \`UPDATE_ROLLBACK_IN_PROGRESS\` | Update failed, rolling back | Wait |
616
- | \`CREATE_FAILED\` | Creation failed (rare) | Check events, delete stack |
617
- | \`ROLLBACK_COMPLETE\` | Create failed, rollback finished | **Must delete and recreate** — cannot update |
618
- | \`ROLLBACK_FAILED\` | Create rollback failed | Check events, may need manual cleanup |
619
- | \`UPDATE_ROLLBACK_COMPLETE\` | Update failed, rolled back to previous | Healthy — fix template and try again |
620
- | \`UPDATE_ROLLBACK_FAILED\` | Update rollback itself failed | **See recovery steps below** |
621
- | \`DELETE_FAILED\` | Deletion failed | Check events, retry or use retain |
622
-
623
- ### Recovering from UPDATE_ROLLBACK_FAILED
624
-
625
- This is the most common "stuck" state. A resource that CloudFormation tried to roll back could not be restored.
626
-
627
- **Step 1**: Identify the stuck resource:
628
-
629
- \`\`\`bash
630
- aws cloudformation describe-stack-events \\
631
- --stack-name <stack-name> \\
632
- --query "StackEvents[?ResourceStatus=='UPDATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" \\
633
- --output table
634
- \`\`\`
635
-
636
- **Step 2a** — Try continuing the rollback:
637
-
638
- \`\`\`bash
639
- aws cloudformation continue-update-rollback --stack-name <stack-name>
640
- aws cloudformation wait stack-update-complete --stack-name <stack-name>
641
- \`\`\`
642
-
643
- **Step 2b** — If that fails, skip the stuck resources:
644
-
645
- \`\`\`bash
646
- aws cloudformation continue-update-rollback \\
647
- --stack-name <stack-name> \\
648
- --resources-to-skip LogicalResourceId1 LogicalResourceId2
649
- \`\`\`
650
-
651
- **WARNING**: Skipping resources causes state divergence — CloudFormation's view of the stack will no longer match reality. You may need to manually clean up skipped resources or import them back later.
652
-
653
- ### Recovering from ROLLBACK_COMPLETE
654
-
655
- A stack in \`ROLLBACK_COMPLETE\` cannot be updated. You must delete it and create a new one:
656
-
657
- \`\`\`bash
658
- aws cloudformation delete-stack --stack-name <stack-name>
659
- aws cloudformation wait stack-delete-complete --stack-name <stack-name>
660
- # Then deploy fresh
661
- aws cloudformation deploy --template-file stack.json --stack-name <stack-name> --capabilities CAPABILITY_NAMED_IAM
662
- \`\`\`
663
-
664
- ## Stack lifecycle operations
665
-
666
- ### Delete a stack
667
-
668
- \`\`\`bash
669
- aws cloudformation delete-stack --stack-name <stack-name>
670
- aws cloudformation wait stack-delete-complete --stack-name <stack-name>
671
- \`\`\`
672
-
673
- If deletion fails because a resource cannot be deleted (e.g., non-empty S3 bucket), use retain:
674
-
675
- \`\`\`bash
676
- aws cloudformation delete-stack \\
677
- --stack-name <stack-name> \\
678
- --retain-resources BucketLogicalId
679
- \`\`\`
680
-
681
- To protect a stack from accidental deletion:
682
-
683
- \`\`\`bash
684
- aws cloudformation update-termination-protection \\
685
- --enable-termination-protection \\
686
- --stack-name <stack-name>
687
- \`\`\`
688
-
689
- ### Drift detection
690
-
691
- Detect whether resources have been modified outside of CloudFormation:
692
-
693
- \`\`\`bash
694
- # Start detection
695
- DRIFT_ID=$(aws cloudformation detect-stack-drift --stack-name <stack-name> --query StackDriftDetectionId --output text)
696
-
697
- # Check status
698
- aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id $DRIFT_ID
699
-
700
- # View drifted resources
701
- aws cloudformation describe-stack-resource-drifts \\
702
- --stack-name <stack-name> \\
703
- --stack-resource-drift-status-filters MODIFIED DELETED
704
- \`\`\`
705
-
706
- ### Import existing resources
707
-
708
- Bring resources that were created outside CloudFormation under stack management:
709
-
710
- \`\`\`bash
711
- aws cloudformation create-change-set \\
712
- --stack-name <stack-name> \\
713
- --template-body file://stack.json \\
714
- --change-set-name import-resources \\
715
- --change-set-type IMPORT \\
716
- --resources-to-import '[{"ResourceType":"AWS::S3::Bucket","LogicalResourceId":"MyBucket","ResourceIdentifier":{"BucketName":"existing-bucket-name"}}]'
717
- \`\`\`
718
-
719
- ## Troubleshooting decision tree
720
-
721
- When a deployment fails, follow this diagnostic flow:
722
-
723
- ### Step 1: Check the stack status
724
-
725
- \`\`\`bash
726
- aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].StackStatus' --output text
727
- \`\`\`
728
-
729
- ### Step 2: Branch on status
730
-
731
- - **\`*_IN_PROGRESS\`** → Wait. Do not take action while an operation is in progress.
732
- - **\`*_FAILED\` or \`*_ROLLBACK_*\`** → Read the events (Step 3).
733
- - **\`*_COMPLETE\`** → Stack is stable. If behavior is wrong, check resource configuration.
734
-
735
- ### Step 3: Read the failure events
736
-
737
- \`\`\`bash
738
- aws cloudformation describe-stack-events \\
739
- --stack-name <stack-name> \\
740
- --query "StackEvents[?contains(ResourceStatus, 'FAILED')].[LogicalResourceId,ResourceStatusReason]" \\
741
- --output table
742
- \`\`\`
743
-
744
- ### Step 4: Diagnose by error pattern
745
-
746
- | Error pattern | Likely cause | Fix |
747
- |---------------|-------------|-----|
748
- | "already exists" | Resource name collision — another stack or manual creation owns this name | Use dynamic names: \`Sub\\\`\\\${AWS.StackName}-myresource\\\`\` |
749
- | "not authorized" or "AccessDenied" | Missing IAM permissions, SCP restriction, or wrong \`--capabilities\` | Check IAM policy, add \`--capabilities CAPABILITY_NAMED_IAM\` |
750
- | "limit exceeded" or "LimitExceededException" | AWS service quota hit | Request quota increase or reduce resource count |
751
- | "Template error" or "Template format error" | Invalid template syntax | Run \`aws cloudformation validate-template\` and \`chant lint src/\` |
752
- | "Circular dependency" | Two resources reference each other | Break the cycle — extract one reference to an output or parameter |
753
- | "is in UPDATE_ROLLBACK_FAILED state and can not be updated" | Stuck rollback | See UPDATE_ROLLBACK_FAILED recovery above |
754
- | "is in ROLLBACK_COMPLETE state and can not be updated" | Failed creation, rolled back | Delete the stack and recreate |
755
- | "No updates are to be performed" | Template unchanged | Use \`--no-fail-on-empty-changeset\` or verify your changes are in the built template |
756
- | "Resource is not in the state" | Resource was modified outside CF | Run drift detection, then update or import |
757
- | "Maximum number of addresses has been reached" | EIP limit (default 5) | Request EIP quota increase |
758
-
759
- ## Quick reference
760
-
761
- ### Stack info commands
762
-
763
- \`\`\`bash
764
- # List all stacks
765
- aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
766
-
767
- # Describe a stack (status, params, outputs, tags)
768
- aws cloudformation describe-stacks --stack-name <stack-name>
769
-
770
- # List resources in a stack
771
- aws cloudformation list-stack-resources --stack-name <stack-name>
772
-
773
- # Get outputs only
774
- aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].Outputs'
775
-
776
- # Recent events
777
- aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 20
778
-
779
- # Get deployed template
780
- aws cloudformation get-template --stack-name <stack-name> --query TemplateBody
781
- \`\`\`
782
-
783
- ### Full build-to-deploy pipeline
784
-
785
- \`\`\`bash
786
- # 1. Lint
787
- chant lint src/
788
-
789
- # 2. Build
790
- chant build src/ --output stack.json
791
-
792
- # 3. Validate
793
- aws cloudformation validate-template --template-body file://stack.json
794
-
795
- # 4. Create change set
796
- aws cloudformation create-change-set \\
797
- --stack-name <stack-name> \\
798
- --template-body file://stack.json \\
799
- --change-set-name deploy-$(date +%s) \\
800
- --capabilities CAPABILITY_NAMED_IAM
801
-
802
- # 5. Review changes
803
- aws cloudformation describe-change-set \\
804
- --stack-name <stack-name> \\
805
- --change-set-name deploy-<id>
806
-
807
- # 6. Execute (after user confirms)
808
- aws cloudformation execute-change-set \\
809
- --stack-name <stack-name> \\
810
- --change-set-name deploy-<id>
811
-
812
- # 7. Wait for completion
813
- aws cloudformation wait stack-update-complete --stack-name <stack-name>
814
- \`\`\`
815
- `,
816
- triggers: [
817
- { type: "file-pattern", value: "**/*.aws.ts" },
818
- { type: "file-pattern", value: "**/stack.json" },
819
- { type: "file-pattern", value: "**/template.yaml" },
820
- { type: "context", value: "aws" },
821
- { type: "context", value: "cloudformation" },
822
- { type: "context", value: "deploy" },
823
- ],
824
- preConditions: [
825
- "AWS CLI is installed and configured (aws sts get-caller-identity succeeds)",
826
- "chant CLI is installed (chant --version succeeds)",
827
- "Project has chant source files in src/",
828
- ],
829
- postConditions: [
830
- "Stack is in a stable state (*_COMPLETE)",
831
- "No failed resources in stack events",
832
- ],
833
- parameters: [
834
- {
835
- name: "resourceType",
836
- description: "AWS CloudFormation resource type (e.g. AWS::S3::Bucket)",
837
- type: "string",
838
- required: false,
839
- },
840
- ],
841
- examples: [
842
- {
843
- title: "S3 Bucket with encryption",
844
- description: "Create an S3 bucket with server-side encryption enabled",
845
- input: "Create an encrypted S3 bucket",
846
- output: `new Bucket("MyBucket", {
353
+ skills: createSkillsLoader(import.meta.url, [
354
+ {
355
+ file: "chant-aws.md",
356
+ name: "chant-aws",
357
+ description: "AWS CloudFormation lifecycle — build, diff, deploy, rollback, and troubleshoot from a chant project",
358
+ triggers: [
359
+ { type: "file-pattern", value: "**/*.aws.ts" },
360
+ { type: "file-pattern", value: "**/stack.json" },
361
+ { type: "file-pattern", value: "**/template.yaml" },
362
+ { type: "context", value: "aws" },
363
+ { type: "context", value: "cloudformation" },
364
+ { type: "context", value: "deploy" },
365
+ ],
366
+ preConditions: [
367
+ "AWS CLI is installed and configured (aws sts get-caller-identity succeeds)",
368
+ "chant CLI is installed (chant --version succeeds)",
369
+ "Project has chant source files in src/",
370
+ ],
371
+ postConditions: [
372
+ "Stack is in a stable state (*_COMPLETE)",
373
+ "No failed resources in stack events",
374
+ ],
375
+ parameters: [
376
+ {
377
+ name: "resourceType",
378
+ description: "AWS CloudFormation resource type (e.g. AWS::S3::Bucket)",
379
+ type: "string",
380
+ required: false,
381
+ },
382
+ ],
383
+ examples: [
384
+ {
385
+ title: "S3 Bucket with encryption",
386
+ description: "Create an S3 bucket with server-side encryption enabled",
387
+ input: "Create an encrypted S3 bucket",
388
+ output: `new Bucket("MyBucket", {
847
389
  BucketEncryption: {
848
390
  ServerSideEncryptionConfiguration: [
849
391
  { ServerSideEncryptionByDefault: { SSEAlgorithm: "aws:kms" } }
@@ -856,24 +398,24 @@ aws cloudformation wait stack-update-complete --stack-name <stack-name>
856
398
  RestrictPublicBuckets: true,
857
399
  },
858
400
  })`,
859
- },
860
- {
861
- title: "Deploy a new stack",
862
- description: "Build a chant project and deploy it as a new CloudFormation stack",
863
- input: "Deploy this project as a new stack called my-app-prod",
864
- output: `chant lint src/
401
+ },
402
+ {
403
+ title: "Deploy a new stack",
404
+ description: "Build a chant project and deploy it as a new CloudFormation stack",
405
+ input: "Deploy this project as a new stack called my-app-prod",
406
+ output: `chant lint src/
865
407
  chant build src/ --output stack.json
866
408
  aws cloudformation validate-template --template-body file://stack.json
867
409
  aws cloudformation deploy \\
868
410
  --template-file stack.json \\
869
411
  --stack-name my-app-prod \\
870
412
  --capabilities CAPABILITY_NAMED_IAM`,
871
- },
872
- {
873
- title: "Preview changes before updating",
874
- description: "Create a change set to review what will change before applying an update",
875
- input: "Show me what will change if I deploy this update to my-app-prod",
876
- output: `chant build src/ --output stack.json
413
+ },
414
+ {
415
+ title: "Preview changes before updating",
416
+ description: "Create a change set to review what will change before applying an update",
417
+ input: "Show me what will change if I deploy this update to my-app-prod",
418
+ output: `chant build src/ --output stack.json
877
419
  aws cloudformation create-change-set \\
878
420
  --stack-name my-app-prod \\
879
421
  --template-body file://stack.json \\
@@ -883,12 +425,12 @@ aws cloudformation create-change-set \\
883
425
  aws cloudformation describe-change-set \\
884
426
  --stack-name my-app-prod \\
885
427
  --change-set-name review-<id>`,
886
- },
887
- {
888
- title: "Fix a stuck rollback",
889
- description: "Recover a stack stuck in UPDATE_ROLLBACK_FAILED state",
890
- input: "My stack my-app-prod is stuck in UPDATE_ROLLBACK_FAILED, help me fix it",
891
- output: `# Identify the stuck resource
428
+ },
429
+ {
430
+ title: "Fix a stuck rollback",
431
+ description: "Recover a stack stuck in UPDATE_ROLLBACK_FAILED state",
432
+ input: "My stack my-app-prod is stuck in UPDATE_ROLLBACK_FAILED, help me fix it",
433
+ output: `# Identify the stuck resource
892
434
  aws cloudformation describe-stack-events \\
893
435
  --stack-name my-app-prod \\
894
436
  --query "StackEvents[?ResourceStatus=='UPDATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" \\
@@ -896,54 +438,28 @@ aws cloudformation describe-stack-events \\
896
438
  # Attempt to continue the rollback
897
439
  aws cloudformation continue-update-rollback --stack-name my-app-prod
898
440
  aws cloudformation wait stack-update-complete --stack-name my-app-prod`,
899
- },
900
- ],
901
- },
902
- ];
903
-
904
- // Load file-based skills from src/skills/
905
- const { readFileSync } = require("fs");
906
- const { join, dirname } = require("path");
907
- const { fileURLToPath } = require("url");
908
- const dir = dirname(fileURLToPath(import.meta.url));
909
-
910
- const skillFiles = [
911
- {
912
- file: "chant-eks.md",
913
- name: "chant-eks",
914
- description: "EKS end-to-end workflow — provision cluster, configure kubectl, deploy K8s workloads",
915
- triggers: [
916
- { type: "context", value: "eks" },
917
- { type: "context", value: "kubernetes" },
918
- { type: "context", value: "k8s-workloads" },
919
- ],
920
- parameters: [],
921
- examples: [
922
- {
923
- title: "Full EKS deployment",
924
- input: "Set up a complete EKS environment with my API",
925
- output: "chant build src/infra/ --output infra.json && aws cloudformation deploy --template-file infra.json --stack-name my-eks --capabilities CAPABILITY_NAMED_IAM",
926
- },
927
- ],
928
- },
929
- ];
930
-
931
- for (const skill of skillFiles) {
932
- try {
933
- const content = readFileSync(join(dir, "skills", skill.file), "utf-8");
934
- skills.push({
935
- name: skill.name,
936
- description: skill.description,
937
- content,
938
- triggers: skill.triggers,
939
- parameters: skill.parameters,
940
- examples: skill.examples,
941
- });
942
- } catch { /* skip missing skills */ }
943
- }
944
-
945
- return skills;
946
- },
441
+ },
442
+ ],
443
+ },
444
+ {
445
+ file: "chant-aws-eks.md",
446
+ name: "chant-aws-eks",
447
+ description: "EKS end-to-end workflow provision cluster, configure kubectl, deploy K8s workloads",
448
+ triggers: [
449
+ { type: "context", value: "eks" },
450
+ { type: "context", value: "kubernetes" },
451
+ { type: "context", value: "k8s-workloads" },
452
+ ],
453
+ parameters: [],
454
+ examples: [
455
+ {
456
+ title: "Full EKS deployment",
457
+ input: "Set up a complete EKS environment with my API",
458
+ output: "chant build src/infra/ --output infra.json && aws cloudformation deploy --template-file infra.json --stack-name my-eks --capabilities CAPABILITY_NAMED_IAM",
459
+ },
460
+ ],
461
+ },
462
+ ]),
947
463
 
948
464
  completionProvider(ctx: CompletionContext): CompletionItem[] {
949
465
  const { awsCompletions } = require("./lsp/completions");
@@ -1040,13 +556,13 @@ aws cloudformation wait stack-update-complete --stack-name my-app-prod`,
1040
556
  return resources;
1041
557
  },
1042
558
 
1043
- mcpTools(): McpToolContribution[] {
559
+ mcpTools() {
1044
560
  return [
1045
561
  {
1046
562
  name: "diff",
1047
563
  description: "Compare current build output against previous output for AWS CloudFormation",
1048
564
  inputSchema: {
1049
- type: "object",
565
+ type: "object" as const,
1050
566
  properties: {
1051
567
  path: {
1052
568
  type: "string",
@@ -1072,7 +588,7 @@ aws cloudformation wait stack-update-complete --stack-name my-app-prod`,
1072
588
  ];
1073
589
  },
1074
590
 
1075
- mcpResources(): McpResourceContribution[] {
591
+ mcpResources() {
1076
592
  return [
1077
593
  {
1078
594
  uri: "resource-catalog",