Skip to main content
Skip to main content

Watch Mode

Live feedback for CDK static analysis. Re-runs `cdk synth` plus the rule pack on every save and reprints the findings table โ€” synth only, never deploys. Reuses the same `watch` block in cdk.json that `cdk watch` reads, so a single config drives both tools.

Why Watch Mode?

Tight feedback while editing infrastructure

See findings on save, not after a CI run. The same dev-loop you expect from prettier/eslint/biome, but for CloudFormation-level checks across IAM, S3, KMS, Lambda, and 30+ other AWS services.

Doesn't deploy โ€” unlike `cdk watch`

`cdk watch` is a shortcut for `deploy --watch` and pushes real infra to AWS on every save. `cdk-insights scan --watch` shells out to `cdk synth` only and runs the static rule pack against the templates. Zero AWS API calls beyond what `cdk synth` already does.

Reuses your existing cdk.json watch config

If you've already configured `watch.include` / `watch.exclude` for `cdk watch`, the same block drives the cdk-insights loop โ€” one configuration, two tools.

AI off by default โ€” credits stay safe

AI analysis is forced off in watch mode so a fresh cache miss on every keystroke doesnโ€™t silently drain your monthly credits. Run `cdk-insights scan` manually when you want AI on a known-good intermediate state.

How it works

1

Start the watch loop

Run from your CDK app root. The first iteration does the full auth, license, and synth pipeline. Subsequent runs reuse the resolved license context โ€” the loop never re-authenticates.

# direct invocation:
npx cdk-insights scan --watch

# or, after `npx cdk-insights init` (1.29.0+):
npm run cdk-insights:watch
๐Ÿ” CDK Insights (free tier)
โ„น๏ธ ๐Ÿ‘€ Watch mode โ€” watching 1 include pattern(s)
   AI analysis disabled in watch mode. Press Ctrl+C to exit.
โ„น๏ธ ๐Ÿ”„ Initial scan
2

Edit a file in your CDK app

Save any file inside your watch globs. The loop debounces for 300 ms (so a burst of saves triggers one re-run, not five), then re-runs `cdk synth` and re-evaluates the rule pack. The terminal clears between runs so the latest result is in focus.

// edit lib/api-stack.ts
const api = new apigateway.RestApi(this, "Api");
๐Ÿ”„ change lib/api-stack.ts
โ„น๏ธ โšก Synthesizing all CDK stacks...
โœ… Synthesis complete. Found 1 stack to analyze.
โœ“ Done at 14:22:08
3

Synth failures preserve last good results

If `cdk synth` fails (TypeScript compile error, missing context lookup), the watcher reports the error and keeps the last good results visible. Fix and save โ€” the loop picks up where it left off.

// introduce a TS error
const x: number = "not a number";
๐Ÿ”„ change lib/api-stack.ts
โŒ Synth failed โ€” keeping last good results. Fix the error and save again.
4

Stop with Ctrl+C

SIGINT (and SIGTERM) cleanly close the chokidar watcher before exiting. Nothing is left running.

^C
โ„น๏ธ ๐Ÿ‘‹ Stopping watcher.

Reuses your cdk.json watch block

The watcher reads the same `watch.include` / `watch.exclude` block CDK uses for `cdk watch`. One config, two tools.

{
  "app": "node bin/my-app.js",
  "watch": {
    "include": ["lib/**/*.ts", "bin/**/*.ts"],
    "exclude": ["**/*.test.ts", "**/handlers/**"]
  }
}
include (default)

When `watch.include` is unset, defaults to the project root โ€” i.e. watch everything. Matches `cdk watch` exactly so a single config drives both tools.

exclude (mandatory)

Always appended to user-provided excludes (never replaces them): `**/.*`, `**/.*/**`, `**/node_modules/**`, plus the cloud-assembly output dir (`output` in cdk.json, default `cdk.out`). The output dir is excluded only when it resolves under the project root as a relative path.

exclude (user-provided)

Use this to narrow scope for a static-analysis loop โ€” e.g. `**/*.test.ts`, `**/handlers/**` to skip Lambda runtime code that does not affect synth output.

Watch-safe defaults

A few flags are forced or rejected because they donโ€™t make sense in a re-rendering live loop. The watcher chooses these regardless of what you pass on the command line:

AI analysisโ†’Forced off (`--local`)

Cache-miss = 1 credit per resource. Running on every save would burn credits with each edit. Run `cdk-insights scan` manually for AI feedback on a known-good intermediate state.

--writeBaselineโ†’Rejected with error

Would silently overwrite the baseline file on every save, baking in whatever findings happen to exist mid-edit.

--diffโ†’Rejected with error

Filters findings against a moving target; meaningless when re-running continuously.

--github / --withIssue / --prCommentโ†’Forced off

Would otherwise create a GitHub issue or post a PR comment on every save.

failOnCriticalโ†’Forced off

Would kill the watcher on the first critical finding.

--output json / sarif / markdown / github-actionsโ†’Coerced to `table` (one-line warning)

Machine formats don't make sense for a re-rendering live loop.

Caveats

  • Synth is whole-app, not per-file. CDKโ€™s synth output is a global function of the construct tree โ€” thereโ€™s no per-file CFN generation. The watch loop runs the full synth on every save. On small apps thatโ€™s typically 1โ€“3 seconds; on large multi-stack apps it can be 5+. Match your cdk.json app command to the fastest TS runner you can (e.g. tsx or precompiled JS) to keep the loop snappy.
  • Default include is the whole project root. Matches cdk watch behaviour: every file change triggers a re-synth, including handler code that doesnโ€™t affect CFN output. Add an exclude block in cdk.json to narrow scope (e.g. **/handlers/**, **/*.test.ts) โ€” the same config benefits cdk watch too.
  • AI is intentionally off. For AI-powered analysis, run cdk-insights scan manually on a known-good intermediate state. Watch mode is the fast static-analysis loop; AI is the deeper one-shot tool.

Pair with Diff Mode

Watch mode is for the dev loop; diff mode is for the CI gate. Use them together: watch while youโ€™re editing, then a one-shot --diff scan in CI fails only when a PR introduces a new finding.

Read the Diff Mode docs