aws-cli-agent 0.6.0 → 0.6.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to this project are documented here.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
5
5
  versioning follows [SemVer](https://semver.org/).
6
6
 
7
+ ## [0.6.1] - 2026-05-31
8
+
9
+ ### Added
10
+
11
+ - **AWS commands are highlighted in the bash script approval preview.** When the agent generates a script for execution, AWS CLI invocations now stand out from the rest of the script body with inverse-color "highlighter strips" so the mutating calls are easy to spot in a long script:
12
+ - Read-only AWS calls (`describe-*`, `list-*`, `get-*`, `s3 ls`, `sts get-caller-identity`, etc.) render as a **blue strip**.
13
+ - Mutating AWS calls (`delete-*`, `terminate-*`, `create-*`, `put-*`, `s3 rm`, `s3 cp`, etc.) render as a **yellow strip**.
14
+ - Everything else — shell control flow, comments, args, flags, pipelines — stays in the existing green script body color.
15
+
16
+ Detection is pattern-based, not a full shell parser: `aws <service> <verb>` is matched wherever it appears in a line (start of line, after a pipe, after `time` or `env VAR=val`, inside a `$(...)` substitution). Read-only vs. mutating classification uses the same `READ_ONLY_VERBS` / `READ_ONLY_FULL` lists that drive the per-command auto-approve decision — single source of truth, no drift between the two features. Adding a verb to either list affects both behaviors.
17
+
18
+ False positives (e.g. an `aws ec2 describe-instances` substring inside an `echo "..."` literal) get highlighted too. Accepted limitation: a real shell tokenizer would catch these, but the surrounding `echo` and quotes make them visually distinguishable in context.
19
+
7
20
  ## [0.6.0] - 2026-05-31
8
21
 
9
22
  ### Breaking
@@ -55,7 +68,6 @@ versioning follows [SemVer](https://semver.org/).
55
68
 
56
69
  - **Ctrl-C inside an SSM session no longer prints "Cannot perform start session: read /dev/stdin: input/output error".** Previously, Ctrl-C delivered SIGINT to both the AWS CLI subprocess AND aca; aca's process tore down the shared stdin before the AWS CLI's own cleanup completed, producing the I/O-error message. The fix: `aca` installs a no-op SIGINT handler for the lifetime of any interactive AWS CLI subprocess, leaving the signal exclusively to the child. The AWS CLI now performs its normal clean shutdown and exits with code 0 or 130, which aca recognizes as a clean termination.
57
70
 
58
- ## [0.5.0] - 2026-05-19
59
71
  ### Added
60
72
 
61
73
  - **Graceful error handling for AWS CLI failures.** AWS CLI exit codes
@@ -85,8 +97,6 @@ versioning follows [SemVer](https://semver.org/).
85
97
  With verbose off, nothing aca generates reaches the terminal — only
86
98
  the AWS CLI's verbatim output does, matching the README's promise.
87
99
 
88
- ## [0.4.0] - 2026-05-18
89
-
90
100
  ### Changed
91
101
 
92
102
  - **Dependency upgrades.** Vercel AI SDK v4 → v6, zod v3 → v4, TypeScript
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import { History } from './history.js';
9
9
  import { runAgent } from './agent.js';
10
10
  import { FILES, PATHS, DEFAULT_SCRIPT_FOLDER } from './paths.js';
11
11
  import { UserCancelledError } from './errors.js';
12
- const VERSION = '0.6.0';
12
+ const VERSION = '0.6.1';
13
13
  /**
14
14
  * Apply CLI flags on top of the loaded config. Flags only override; they
15
15
  * never widen or compose with each other implicitly.
@@ -1,5 +1,20 @@
1
1
  import type { Logger } from '../logger.js';
2
2
  import type { Config } from '../config.js';
3
+ /**
4
+ * Read-only verb patterns. Used both to decide whether an AWS CLI call may
5
+ * auto-approve, and (since 0.7.0) to syntax-highlight bash scripts in the
6
+ * approval display — read-only verbs render in light blue, mutating in yellow.
7
+ *
8
+ * Exported so bash.ts can apply the same classification consistently. If
9
+ * you add a verb here, the highlighter picks it up automatically.
10
+ */
11
+ export declare const READ_ONLY_VERBS: RegExp[];
12
+ /**
13
+ * Full-command patterns that are read-only but don't match a verb prefix
14
+ * (e.g. `s3 ls`, `sts get-caller-identity` where the resource type isn't a
15
+ * standard verb). Same usage as READ_ONLY_VERBS.
16
+ */
17
+ export declare const READ_ONLY_FULL: RegExp[];
3
18
  export declare function awsCliTool(opts: {
4
19
  logger: Logger;
5
20
  config: Config;
@@ -4,7 +4,15 @@ import { z } from 'zod';
4
4
  import { confirm } from '@inquirer/prompts';
5
5
  import chalk from 'chalk';
6
6
  import { FATAL_AWS_EXIT_CODES, FatalAwsCliError, UserCancelledError, wrapPrompt } from '../errors.js';
7
- const READ_ONLY_VERBS = [
7
+ /**
8
+ * Read-only verb patterns. Used both to decide whether an AWS CLI call may
9
+ * auto-approve, and (since 0.7.0) to syntax-highlight bash scripts in the
10
+ * approval display — read-only verbs render in light blue, mutating in yellow.
11
+ *
12
+ * Exported so bash.ts can apply the same classification consistently. If
13
+ * you add a verb here, the highlighter picks it up automatically.
14
+ */
15
+ export const READ_ONLY_VERBS = [
8
16
  /^describe-/,
9
17
  /^list-/,
10
18
  /^get-/,
@@ -15,7 +23,12 @@ const READ_ONLY_VERBS = [
15
23
  /^scan$/,
16
24
  /^query$/,
17
25
  ];
18
- const READ_ONLY_FULL = [
26
+ /**
27
+ * Full-command patterns that are read-only but don't match a verb prefix
28
+ * (e.g. `s3 ls`, `sts get-caller-identity` where the resource type isn't a
29
+ * standard verb). Same usage as READ_ONLY_VERBS.
30
+ */
31
+ export const READ_ONLY_FULL = [
19
32
  /^s3\s+ls(\s|$)/,
20
33
  ];
21
34
  /**
@@ -8,6 +8,7 @@ import { select } from '@inquirer/prompts';
8
8
  import chalk from 'chalk';
9
9
  import { DEFAULT_SCRIPT_FOLDER } from '../paths.js';
10
10
  import { wrapPrompt } from '../errors.js';
11
+ import { READ_ONLY_FULL, READ_ONLY_VERBS } from './aws-cli.js';
11
12
  function runProcess(cmd, args) {
12
13
  return new Promise((resolve, reject) => {
13
14
  const proc = spawn(cmd, args, { env: process.env });
@@ -32,6 +33,109 @@ function indent(s, prefix) {
32
33
  .map((l) => prefix + l)
33
34
  .join('\n');
34
35
  }
36
+ /**
37
+ * Match an `aws <service> <verb> ...` invocation anywhere within a line.
38
+ * The lookbehind is intentionally permissive — it matches `aws` at the
39
+ * start of the line, after a pipe (`| aws ...`), after `time aws ...`,
40
+ * after `env VAR=val aws ...`, etc. We don't try to be a full shell
41
+ * parser: that's overkill for a syntax-highlight feature. False positives
42
+ * (e.g. the literal string `"use aws ec2 ..."` inside an echo) get
43
+ * highlighted too, but that's preferable to false negatives — and the
44
+ * surrounding context usually makes them visually distinguishable.
45
+ *
46
+ * Captures three groups: service (group 1), verb (group 2), and the rest
47
+ * of the args up to end of line or a shell metacharacter that ends a
48
+ * command (group 3). We stop the arg span at `|`, `;`, `&`, `)`, and `>` /
49
+ * `<` redirections so we don't highlight half of the next pipeline stage.
50
+ */
51
+ const AWS_CALL_RE = /\baws\s+([a-z][a-z0-9-]*)\s+([a-z][a-z0-9-]*)((?:\s+[^|;&)<>\n]*)?)/g;
52
+ /**
53
+ * Decide whether an `aws <service> <verb>` invocation is read-only. Uses
54
+ * the same classification as the per-command auto-approve path so the
55
+ * highlighting matches the runtime behavior: if the highlight is light
56
+ * blue, the command would auto-approve with `autoApprove.readOnly: true`
57
+ * if it were a standalone tool call.
58
+ */
59
+ function isAwsCallReadOnly(service, verb, restOfArgs) {
60
+ // READ_ONLY_FULL patterns match against `service verb [args...]`.
61
+ // We pass the same shape to mirror runtime behavior.
62
+ const full = `${service} ${verb}${restOfArgs}`;
63
+ if (READ_ONLY_FULL.some((re) => re.test(full)))
64
+ return true;
65
+ if (READ_ONLY_VERBS.some((re) => re.test(verb)))
66
+ return true;
67
+ return false;
68
+ }
69
+ /**
70
+ * Color the AWS CLI invocations inside a script. Read-only calls
71
+ * (describe-, list-, get-, etc.) render in light blue; mutating calls
72
+ * (delete-, terminate-, create-, etc.) render in yellow. Non-AWS portions
73
+ * of each line stay in the script body's default green wrap.
74
+ *
75
+ * Implementation note: chalk colors don't nest the way you'd hope. If we
76
+ * wrapped the whole script in `chalk.green()` and then embedded blue/yellow
77
+ * spans inside it, the inner spans' closing reset (\u001b[39m) would
78
+ * disable the green for the remainder of the string. To avoid that, we
79
+ * render each line piece by piece, explicitly re-applying green to the
80
+ * non-highlighted spans.
81
+ */
82
+ function highlightAwsCalls(script) {
83
+ return script
84
+ .split('\n')
85
+ .map((line) => {
86
+ // Walk the line in pieces, emitting green for plain text and a
87
+ // distinct color for each AWS invocation. We use the regex's
88
+ // exec-loop position tracking to splice the line.
89
+ AWS_CALL_RE.lastIndex = 0;
90
+ let out = '';
91
+ let cursor = 0;
92
+ let m;
93
+ while ((m = AWS_CALL_RE.exec(line)) !== null) {
94
+ // The `aws` keyword itself isn't in the capture groups — find it
95
+ // by scanning backward from the service position.
96
+ const matchStart = m.index;
97
+ const service = m[1];
98
+ const verb = m[2];
99
+ const restOfArgs = m[3] ?? '';
100
+ const readOnly = isAwsCallReadOnly(service, verb, restOfArgs);
101
+ // Pre-`aws` text (e.g. `| `, `time `, or empty for start-of-line)
102
+ // stays green.
103
+ if (matchStart > cursor) {
104
+ out += chalk.green(line.slice(cursor, matchStart));
105
+ }
106
+ // The `aws <service> <verb>` triple gets the distinct color.
107
+ // restOfArgs (the flags and values) stays green — flags are the
108
+ // boring part, the verb is what the user needs to verify.
109
+ // Inverse (swaps fg/bg) produces a "highlighter strip" look: the
110
+ // background takes the color and the text shows through in the
111
+ // terminal's default foreground. Much more visible against the
112
+ // green script body than colored text alone would be — the eye
113
+ // snaps to a block of color faster than to a colored word.
114
+ // Green strip for read-only (matches the safe/discovery feel of
115
+ // the surrounding green script body); red strip for mutating
116
+ // (the classic "stop and look" signal).
117
+ const highlight = readOnly
118
+ ? chalk.inverse.blueBright
119
+ : chalk.inverse.yellowBright;
120
+ out += highlight(`aws ${service} ${verb}`);
121
+ if (restOfArgs.length > 0) {
122
+ out += chalk.green(restOfArgs);
123
+ }
124
+ cursor = matchStart + m[0].length;
125
+ }
126
+ // Trailing text after the last match stays green.
127
+ if (cursor < line.length) {
128
+ out += chalk.green(line.slice(cursor));
129
+ }
130
+ // If no AWS call was found in the line, fall through with the whole
131
+ // line in green — same as the old uniform-green behavior.
132
+ if (out === '') {
133
+ out = chalk.green(line);
134
+ }
135
+ return out;
136
+ })
137
+ .join('\n');
138
+ }
35
139
  /**
36
140
  * Compute a filesystem-friendly filename for a saved script.
37
141
  * Combines a timestamp (so files sort chronologically) with a short slug
@@ -64,7 +168,11 @@ export function bashScriptTool(opts) {
64
168
  process.stderr.write('\n');
65
169
  process.stderr.write(`${chalk.bold(' Reason: ')}${purpose}\n`);
66
170
  process.stderr.write(`${chalk.bold(' Script:')}\n`);
67
- process.stderr.write(chalk.green(indent(script, ' ')) + '\n');
171
+ // Render the script with AWS calls highlighted. Read-only calls
172
+ // (describe-, list-, get-, etc.) render in light blue; mutating calls
173
+ // (delete-, terminate-, create-, etc.) render in yellow. Everything
174
+ // else stays green. See highlightAwsCalls for the parser caveats.
175
+ process.stderr.write(highlightAwsCalls(indent(script, ' ')) + '\n');
68
176
  // The save-to-disk option respects the configured folder, or falls back
69
177
  // to the XDG default. Compute the would-be path *before* prompting so
70
178
  // the user can see exactly where it'll land.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-cli-agent",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Agentic AI assistant that turns natural-language requests into AWS CLI commands and runs them locally.",
5
5
  "type": "module",
6
6
  "bin": {