postman-cli 1.63.0 → 1.65.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/README.md CHANGED
@@ -20,6 +20,18 @@ The Postman CLI brings the power of Postman’s API platform directly to your te
20
20
  npm install -g postman-cli
21
21
  ```
22
22
 
23
+ After a global npm installation, the package checks for existing Claude Code and Codex
24
+ configuration directories. When found, it adds a managed Postman CLI availability note to
25
+ the agent's global instructions without replacing existing content. This step is idempotent
26
+ and non-blocking, so an inaccessible agent configuration does not fail the CLI installation.
27
+
28
+ npm hides successful lifecycle-script output by default. To see the agent configuration
29
+ summary and Claude Code plugin recommendation during installation, use:
30
+
31
+ ```bash
32
+ npm install -g postman-cli --foreground-scripts
33
+ ```
34
+
23
35
  ### Alternative Installation Methods
24
36
 
25
37
  For direct binary downloads, platform-specific installers, or other installation methods, see the **[Installation Guide](https://learning.postman.com/docs/postman-cli/postman-cli-installation/)**.
package/man/postman.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH POSTMAN 1 "2026-09-23" "v1.63.0" "Postman CLI Manual"
1
+ .TH POSTMAN 1 "2026-09-25" "v1.65.0" "Postman CLI Manual"
2
2
  .SH NAME
3
3
  postman \- Command\-line companion utility for Postman
4
4
  .SH SYNOPSIS
@@ -293,6 +293,12 @@ Score a Postman collection for AI readiness by ID, local file path, or local\-mo
293
293
  .B collection get
294
294
  Fetch a Postman collection in the V3 format and print it.
295
295
  .TP
296
+ .B collection generate
297
+ Generate artifacts from a collection.
298
+ .TP
299
+ .B collection sync
300
+ Sync artifacts generated from a collection.
301
+ .TP
296
302
  .B collection request
297
303
  Add, update, or remove requests in a local v3 collection.
298
304
  .TP
@@ -376,6 +382,9 @@ Score a Postman collection for AI readiness by ID, local file path, or local\-mo
376
382
  .B \-o, \-\-output <value>
377
383
  Output format for the results. [choices: "cli", "json", "html"]
378
384
  .TP
385
+ .B \-\-export <path>
386
+ Write the report to this file instead of stdout. A directory receives the report under a name derived from the collection. Requires \-\-output html.
387
+ .TP
379
388
  .B \-\-min\-score <n>
380
389
  Exit with a non\-zero code if the overall score is below this threshold (0\-100).
381
390
 
@@ -385,6 +394,7 @@ Examples:
385
394
  $ postman collection ai\-readiness ./postman/collections/My\e API
386
395
  $ postman collection ai\-readiness ./my\-collection.json
387
396
  $ postman collection ai\-readiness 631643\-f695cab7\-... \-\-output json
397
+ $ postman collection ai\-readiness ./my\-collection.json \-\-output html > report.html
388
398
  $ postman collection ai\-readiness ./my\-collection.json \-\-min\-score 70
389
399
 
390
400
  Resolving a collection by ID requires authentication. Use `postman login` before running this command with a UID.
@@ -410,6 +420,80 @@ Eg. postman collection get 12345\-33823532ab9e41c9b6fd12d0fd459b8b
410
420
  postman collection get 0123456789abcdef01234567 \-\-json
411
421
 
412
422
 
423
+ .SS "collection generate"
424
+ Generate artifacts from a collection.
425
+
426
+ .B Usage:
427
+ [options] [command]
428
+
429
+ .B Subcommands:
430
+ .TP
431
+ .B collection generate spec
432
+ Generate an OpenAPI specification from a Postman collection.
433
+
434
+ .SS "collection generate spec"
435
+ Generate an OpenAPI specification from a Postman collection.
436
+
437
+ .B Usage:
438
+ [options] <collection>
439
+
440
+ .B Options:
441
+ .TP
442
+ .B \-n, \-\-name <name>
443
+ Specification name / title
444
+ .TP
445
+ .B \-\-spec\-version <ver>
446
+ Target OpenAPI version: 3.1, 3.0, or 2.0 (default: 3.1) (default: 3.1)
447
+ .TP
448
+ .B \-f, \-\-format <fmt>
449
+ Output format: yaml or json (default: yaml) (default: yaml)
450
+ .TP
451
+ .B \-\-api\-key <key>
452
+ Postman API key
453
+
454
+ .TP Examples:
455
+
456
+ Examples:
457
+ postman collection generate spec ./postman/collections/My\e API \-n "My API Spec"
458
+ postman collection generate spec ./postman/collections/My\e API \-n "My Spec" \-\-spec\-version 3.0
459
+ postman collection generate spec ./collection.json \-n "My Spec" \-f json
460
+ postman collection generate spec 12345678\-abcd\-1234\-abcd\-1234567890ab \-n "My Spec"
461
+
462
+
463
+
464
+ .SS "collection sync"
465
+ Sync artifacts generated from a collection.
466
+
467
+ .B Usage:
468
+ [options] [command]
469
+
470
+ .B Subcommands:
471
+ .TP
472
+ .B collection sync spec
473
+ Sync a specification from a linked collection (local path or cloud UUID).
474
+
475
+ .SS "collection sync spec"
476
+ Sync a specification from a linked collection (local path or cloud UUID).
477
+
478
+ .B Usage:
479
+ [options] <collection>
480
+
481
+ .B Options:
482
+ .TP
483
+ .B \-\-spec\-id <id>
484
+ Specification ID to sync (required for cloud mode)
485
+ .TP
486
+ .B \-\-api\-key <key>
487
+ Postman API key
488
+
489
+ .TP Examples:
490
+
491
+ Examples:
492
+ postman collection sync spec ./postman/collections/My\e API
493
+ postman collection sync spec 12345\-col\-id \-\-spec\-id 67890\-spec\-id
494
+
495
+
496
+
413
497
  .SS "collection request"
414
498
  Add, update, or remove requests in a local v3 collection.
415
499
 
@@ -872,6 +956,15 @@ Redirect requests for a URL or {{variable}} to a mock during the run. Format: "<
872
956
  .B \-\-simulate <path>
873
957
  Start mock servers with fault\-injection scenarios from a .sim.yaml file
874
958
  .TP
959
+ .B \-\-simulation <id>
960
+ Simulation id to record start history against when using \-\-simulate.
961
+ .TP
962
+ .B \-\-simulate\-workspace <id>
963
+ Workspace that owns the \-\-simulate run, for recording its start history.
964
+ .TP
965
+ .B \-\-no\-simulate\-history
966
+ Do not record the \-\-simulate run in the simulation's start history.
967
+ .TP
875
968
  .B \-\-report\-events
876
969
  Upload results for git\-native v3 collection runs. Analytics are sent by default
877
970
  .TP
@@ -1350,6 +1443,9 @@ Create a new spec in a workspace, or (with path) scaffold a local spec file.
1350
1443
  .TP
1351
1444
  .B spec generate
1352
1445
  Generate artifacts from a specification.
1446
+ .TP
1447
+ .B spec sync
1448
+ Sync a generated collection with its source specification.
1353
1449
 
1354
1450
  .SS "spec lint"
1355
1451
  Run linting on the given specification by ID or local file path.
@@ -1387,6 +1483,9 @@ Score an OpenAPI specification for AI readiness by ID or local file path.
1387
1483
  .B \-o, \-\-output <value>
1388
1484
  Output format for the results. [choices: "cli", "json", "html"]
1389
1485
  .TP
1486
+ .B \-\-export <path>
1487
+ Write the report to this file instead of stdout. A directory receives the report under a name derived from the specification. Requires \-\-output html.
1488
+ .TP
1390
1489
  .B \-\-min\-score <n>
1391
1490
  Exit with a non\-zero code if the overall score is below this threshold (0\-100).
1392
1491
 
@@ -1395,6 +1494,8 @@ Exit with a non\-zero code if the overall score is below this threshold (0\-100)
1395
1494
  Examples:
1396
1495
  $ postman spec ai\-readiness ./openapi.yaml
1397
1496
  $ postman spec ai\-readiness 6e2e5b3e\-... \-\-output json
1497
+ $ postman spec ai\-readiness ./openapi.yaml \-\-output html > report.html
1498
+ $ postman spec ai\-readiness ./openapi.yaml \-\-output html \-\-export ./report.html
1398
1499
  $ postman spec ai\-readiness ./openapi.yaml \-\-min\-score 70
1399
1500
 
1400
1501
 
@@ -1617,6 +1718,39 @@ Examples:
1617
1718
 
1618
1719
 
1619
1720
 
1721
+ .SS "spec sync"
1722
+ Sync a generated collection with its source specification.
1723
+
1724
+ .B Usage:
1725
+ [options] [command]
1726
+
1727
+ .B Subcommands:
1728
+ .TP
1729
+ .B spec sync collection
1730
+ Sync a collection from a linked specification (local path or cloud UUID).
1731
+
1732
+ .SS "spec sync collection"
1733
+ Sync a collection from a linked specification (local path or cloud UUID).
1734
+
1735
+ .B Usage:
1736
+ [options] <spec>
1737
+
1738
+ .B Options:
1739
+ .TP
1740
+ .B \-\-collection\-id <id>
1741
+ Collection ID to sync (required for cloud mode)
1742
+ .TP
1743
+ .B \-\-api\-key <key>
1744
+ Postman API key
1745
+
1746
+ .TP Examples:
1747
+
1748
+ Examples:
1749
+ postman spec sync collection ./postman/specs/My\e API/index.yaml
1750
+ postman spec sync collection 12345\-spec\-id \-\-collection\-id 67890\-col\-id
1751
+
1752
+
1753
+
1620
1754
  .SS "monitor"
1621
1755
  Run and manage Postman monitors.
1622
1756
 
@@ -2139,12 +2273,18 @@ Read a workspace's metadata, and optionally the ids of what it holds.
2139
2273
  .B workspace create
2140
2274
  Create a Postman workspace and bind it to this git repository.
2141
2275
  .TP
2276
+ .B workspace delete
2277
+ Permanently delete a workspace and everything in it. Prompts for confirmation unless \-\-yes is passed.
2278
+ .TP
2142
2279
  .B workspace pull
2143
2280
  Pull workspace entities from a Postman workspace into the local git\-native folder.
2144
2281
  .TP
2145
2282
  .B workspace connect-git
2146
2283
  Connect a Postman workspace to a local git repository.
2147
2284
  .TP
2285
+ .B workspace disconnect-git
2286
+ Disconnect a Postman workspace from its local git repository.
2287
+ .TP
2148
2288
  .B workspace diff
2149
2289
  Preview local\-vs\-cloud drift before pushing. Read\-only: nothing is created, updated or deleted.
2150
2290
 
@@ -2315,6 +2455,38 @@ Examples:
2315
2455
  $ postman workspace create \-\-visibility personal \-\-no\-connect
2316
2456
 
2317
2457
 
2458
+ .SS "workspace delete"
2459
+ Permanently delete a workspace and everything in it. Prompts for confirmation unless \-\-yes is passed.
2460
+
2461
+ .B Usage:
2462
+ [options] [workspaceId]
2463
+
2464
+ .B Options:
2465
+ .TP
2466
+ .B \-y, \-\-yes
2467
+ Skip the confirmation prompt
2468
+ .TP
2469
+ .B \-\-json
2470
+ Print the outcome as machine\-readable JSON
2471
+ .TP
2472
+ .B \-\-verbose
2473
+ Show detailed logging
2474
+ .TP
2475
+ .B \-\-timeout <ms>
2476
+ Abort the run after this many milliseconds. Defaults to 30000.
2477
+
2478
+ .TP Examples:
2479
+
2480
+ Examples:
2481
+ postman workspace delete 12345678\-90ab\-cdef\-1234\-567890abcdef
2482
+ Confirm, then delete
2483
+ postman workspace delete 12345678\-90ab\-cdef\-1234\-567890abcdef \-\-yes
2484
+ Delete without confirmation (for scripts and CI)
2485
+ postman workspace delete 12345678\-90ab\-cdef\-1234\-567890abcdef \-\-yes \-\-json
2486
+ Machine\-readable outcome
2487
+
2488
+
2489
+
2318
2490
  .SS "workspace pull"
2319
2491
  Pull workspace entities from a Postman workspace into the local git\-native folder.
2320
2492
 
@@ -2368,6 +2540,36 @@ Examples:
2368
2540
 
2369
2541
 
2370
2542
 
2543
+ .SS "workspace disconnect\-git"
2544
+ Disconnect a Postman workspace from its local git repository.
2545
+
2546
+ .B Usage:
2547
+ [options] [workspaceId]
2548
+
2549
+ .B Options:
2550
+ .TP
2551
+ .B \-y, \-\-yes
2552
+ Skip all confirmation prompts
2553
+ .TP
2554
+ .B \-\-verbose
2555
+ Show detailed logging
2556
+
2557
+ .TP Examples:
2558
+
2559
+ Examples:
2560
+ postman workspace disconnect\-git
2561
+ Disconnect the workspace this folder is connected to. Disconnects it in
2562
+ Postman and clears the connection in .postman/resources.yaml. No files
2563
+ are deleted.
2564
+ postman workspace disconnect\-git <workspaceId>
2565
+ Disconnect a specific workspace, whatever workspace this folder is
2566
+ connected to. Use this to clear an existing workspace connection. No files
2567
+ are deleted.
2568
+ postman workspace disconnect\-git \-\-yes
2569
+ CI: skip the confirmation prompt.
2570
+
2571
+
2572
+
2371
2573
  .SS "workspace diff"
2372
2574
  Preview local\-vs\-cloud drift before pushing. Read\-only: nothing is created, updated or deleted.
2373
2575
 
@@ -2386,7 +2588,7 @@ Skip content comparison. Faster, but updates are listed without checking whether
2386
2588
  Print the diff as machine\-readable JSON.
2387
2589
  .TP
2388
2590
  .B \-\-exit\-code
2389
- Exit with code 1 when drift is found (for CI gates).
2591
+ Exit with code 1 when a verified difference is found (for CI gates). Entity types with no comparison path are reported but do not fail the gate.
2390
2592
  .TP
2391
2593
  .B \-\-verbose
2392
2594
  Show detailed logging
@@ -2489,10 +2691,10 @@ Specify an Id to a Postman Environment
2489
2691
  Specify an Id to a Postman Globals
2490
2692
  .TP
2491
2693
  .B \-\-setup\-collection <id>
2492
- Collection UID to run before the Postman Cloud performance test
2694
+ Collection UID to run before the performance test
2493
2695
  .TP
2494
2696
  .B \-\-teardown\-collection <id>
2495
- Collection UID to run after the Postman Cloud performance test
2697
+ Collection UID to run after the performance test
2496
2698
  .TP
2497
2699
  .B \-\-vu\-count <count>
2498
2700
  Number of virtual users (default: 20)
@@ -2506,6 +2708,24 @@ Load profile type: fixed, ramp\-up, spike, peak (default: ramp\-up)
2506
2708
  .B \-\-data\-file <path>
2507
2709
  Path to a JSON or CSV data file to use with the collection
2508
2710
  .TP
2711
+ .B \-\-ssl\-client\-cert\-list <path>
2712
+ Path to a client certificates configuration file (JSON). Local runner only.
2713
+ .TP
2714
+ .B \-\-ssl\-client\-cert <path>
2715
+ Path to a client certificate (PEM) for mTLS targets. Local runner only.
2716
+ .TP
2717
+ .B \-\-ssl\-client\-key <path>
2718
+ Path to the client certificate private key. Local runner only.
2719
+ .TP
2720
+ .B \-\-ssl\-client\-passphrase <passphrase>
2721
+ Client certificate passphrase (for a protected key). Local runner only.
2722
+ .TP
2723
+ .B \-k, \-\-insecure
2724
+ Disable SSL certificate verification for the target. Local runner only.
2725
+ .TP
2726
+ .B \-\-ssl\-extra\-ca\-certs <path>
2727
+ Path to additionally trusted CA certificates (PEM) for the target. Local runner only.
2728
+ .TP
2509
2729
  .B \-\-dataset\-id <id>
2510
2730
  Use a Postman Dataset as iteration data (with \-\-dataset\-view\-id)
2511
2731
  .TP
@@ -2539,6 +2759,7 @@ Format: "<{{var}}|host> mock|mock\-server:<path|id> [scenario]" (default: )
2539
2759
  Examples:
2540
2760
  postman performance run 123456\-45159473\-1e45\-1f34\-5678\-1234567890ab \-\-vu\-count 50 \-\-duration 15
2541
2761
  postman performance run 123456\-45159473\-1e45\-1f34\-5678\-1234567890ab \-\-pass\-if "less_than(p95, 500)"
2762
+ postman performance run 123456\-45159473\-1e45\-1f34\-5678\-1234567890ab \-\-runner local \-\-setup\-collection 123456\-11111111\-1111\-4111\-8111\-111111111111 \-\-teardown\-collection 123456\-22222222\-2222\-4222\-8222\-222222222222
2542
2763
  postman performance run 123456\-45159473\-1e45\-1f34\-5678\-1234567890ab \-\-runner postman\-cloud \-\-setup\-collection 123456\-11111111\-1111\-4111\-8111\-111111111111 \-\-teardown\-collection 123456\-22222222\-2222\-4222\-8222\-222222222222
2543
2764
  postman performance run 123456\-45159473\-1e45\-1f34\-5678\-1234567890ab \-\-runner postman\-cloud\-static\-ip
2544
2765
 
@@ -3713,6 +3934,12 @@ Port to run on (e.g. 4010), or "auto" for a free one. Defaults to the mock's con
3713
3934
  .TP
3714
3935
  .B \-\-api\-key <key>
3715
3936
  Postman API key, used with a Postman cloud id (defaults to your `postman login` session)
3937
+ .TP
3938
+ .B \-w, \-\-workspace <id>
3939
+ Workspace that owns the run, for recording its start history. Auto\-resolved for a Postman cloud id; required to record history for a path.
3940
+ .TP
3941
+ .B \-\-no\-history
3942
+ Do not record this run in the mock's start history.
3716
3943
 
3717
3944
  .TP Examples:
3718
3945
 
@@ -3721,6 +3948,13 @@ Eg. postman mock run 12345678\-90ab\-cdef\-1234\-567890abcdef # by id, from Pos
3721
3948
  postman mock run ./postman/mocks/orders \-\-environment ./postman/environments/dev.environment.yaml
3722
3949
  postman mock run ./postman/mocks/orders \-\-port auto # pick any free port
3723
3950
  postman mock run ./postman/mocks/orders \-\-port 4600 # use port 4600 (fails if it is in use)
3951
+ postman mock run ./postman/mocks/orders \-\-workspace <id> # record start history for a path run
3952
+
3953
+ Eligible runs are recorded in the mock's start history (source: cli) so they appear under
3954
+ "Previous starts" in Postman. Cloud ids record automatically; a manifest\-backed path run needs
3955
+ \-\-workspace. A raw handler with no mock id cannot be attributed and is not recorded.
3956
+ In CI the pipeline/branch is captured automatically. Best\-effort and never blocks the server;
3957
+ use \-\-no\-history to opt out.
3724
3958
 
3725
3959
  Paths can be relative (e.g. ./postman/mocks/orders) or absolute (e.g. /Users/me/postman/mocks/orders).
3726
3960
 
@@ -4083,6 +4317,28 @@ Start mocks with fault\-injection scenarios defined in a .sim.yaml file
4083
4317
  .B Usage:
4084
4318
  [options] <filepath>
4085
4319
 
4320
+ .B Options:
4321
+ .TP
4322
+ .B \-w, \-\-workspace <id>
4323
+ Workspace that owns the run, for recording its simulation start history.
4324
+ .TP
4325
+ .B \-\-simulation <id>
4326
+ Simulation id to record start history against (a local .sim.yaml has only a name). Required, with \-\-workspace, to record history.
4327
+ .TP
4328
+ .B \-\-api\-key <key>
4329
+ Postman API key for recording start history (defaults to your `postman login` session).
4330
+ .TP
4331
+ .B \-\-no\-history
4332
+ Do not record this run in the simulation's start history.
4333
+
4334
+ .TP Examples:
4335
+
4336
+ Eligible runs are recorded in the simulation's start history (source: cli) so they appear
4337
+ under "Previous starts" in Postman. A parent start is recorded with one child start per member
4338
+ mock; pass \-\-workspace and \-\-simulation to enable it. In CI the pipeline/branch is captured
4339
+ automatically. Best\-effort and never blocks the servers; use \-\-no\-history to opt out.
4340
+
4341
+
4086
4342
  .SS "describe"
4087
4343
  [Beta] Get API context for AI coding agents directly from the command line.
4088
4344
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postman-cli",
3
- "version": "1.63.0",
3
+ "version": "1.65.0",
4
4
  "description": "Official Postman CLI - Command-line companion for API development, testing, and automation",
5
5
  "keywords": [
6
6
  "postman",
@@ -49,19 +49,23 @@
49
49
  "bin": {
50
50
  "postman": "bin/postman.js"
51
51
  },
52
+ "scripts": {
53
+ "postinstall": "node ./scripts/post-npm-installation.js"
54
+ },
52
55
  "man": "./man/postman.1",
53
56
  "files": [
54
57
  "bin/",
55
58
  "index.js",
56
59
  "package.json",
57
60
  "README.md",
58
- "man/"
61
+ "man/",
62
+ "scripts/"
59
63
  ],
60
64
  "optionalDependencies": {
61
- "@postman/pm-bin-macos-arm64": "1.63.0",
62
- "@postman/pm-bin-macos-x64": "1.63.0",
63
- "@postman/pm-bin-linux-x64": "1.63.0",
64
- "@postman/pm-bin-linux-arm64": "1.63.0",
65
- "@postman/pm-bin-windows-x64": "1.63.0"
65
+ "@postman/pm-bin-macos-arm64": "1.65.0",
66
+ "@postman/pm-bin-macos-x64": "1.65.0",
67
+ "@postman/pm-bin-linux-x64": "1.65.0",
68
+ "@postman/pm-bin-linux-arm64": "1.65.0",
69
+ "@postman/pm-bin-windows-x64": "1.65.0"
66
70
  }
67
71
  }
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env node
2
+
3
+ /* eslint-disable no-console */
4
+ /* eslint-disable one-var */
5
+
6
+ const fileSystem = require('node:fs');
7
+ const operatingSystem = require('node:os');
8
+ const path = require('node:path');
9
+
10
+ const POSTMAN_INSTRUCTION =
11
+ 'Postman CLI is installed on this device, and can be used for API Engineering work.',
12
+ POSTMAN_INSTRUCTION_START = '<!-- postman-cli:installation:start -->',
13
+ POSTMAN_INSTRUCTION_END = '<!-- postman-cli:installation:end -->',
14
+ POSTMAN_INSTRUCTION_BLOCK = [
15
+ POSTMAN_INSTRUCTION_START,
16
+ POSTMAN_INSTRUCTION,
17
+ POSTMAN_INSTRUCTION_END
18
+ ].join('\n'),
19
+ CLAUDE_PLUGIN_MESSAGE = [
20
+ '',
21
+ 'Claude Code users and agents can use the Postman Plugin for Claude Code for full API lifecycle',
22
+ 'management including documentation, collections, workspaces, environments, mocks, monitors right',
23
+ 'from your repository.',
24
+ '',
25
+ 'Install it with:',
26
+ ' claude plugin install postman@claude-plugins-official',
27
+ '',
28
+ 'Then start Claude Code and run:',
29
+ ' /postman:setup'
30
+ ].join('\n');
31
+
32
+ /**
33
+ * @typedef {Object} AgentConfiguration
34
+ * @property {string} name - User-facing agent name.
35
+ * @property {string} configurationEnvironmentVariable - Environment override for the configuration root.
36
+ * @property {string} fallbackDirectory - Directory beneath the current user's home directory.
37
+ * @property {string[]} instructionFileNames - Ordered instruction files; the last is the fallback.
38
+ * @property {(context: AgentPostConfigurationContext) => void} [postConfiguration] - Agent follow-up.
39
+ */
40
+
41
+ /**
42
+ * Selects the first existing instruction file, or the final configured fallback.
43
+ *
44
+ * @param {AgentConfiguration} agentConfiguration - Agent path conventions.
45
+ * @param {string} configurationRoot - Resolved agent configuration root.
46
+ * @returns {string} Absolute instruction-file path.
47
+ */
48
+ function resolveInstructionFile (agentConfiguration, configurationRoot) {
49
+ const existingInstructionFileName = agentConfiguration.instructionFileNames.find(
50
+ (instructionFileName) => {
51
+ return fileSystem.existsSync(path.join(configurationRoot, instructionFileName));
52
+ }
53
+ );
54
+
55
+ return path.join(
56
+ configurationRoot,
57
+ existingInstructionFileName || agentConfiguration.instructionFileNames[
58
+ agentConfiguration.instructionFileNames.length - 1
59
+ ]
60
+ );
61
+ }
62
+
63
+ /** @type {AgentConfiguration[]} */
64
+ const AGENT_CONFIGURATIONS = [
65
+ {
66
+ name: 'Claude Code',
67
+ configurationEnvironmentVariable: 'CLAUDE_CONFIG_DIR',
68
+ fallbackDirectory: '.claude',
69
+ instructionFileNames: ['CLAUDE.md'],
70
+ postConfiguration: ({ configurationResult, invocationDirectory }) => {
71
+ const projectConfigurationDetected = invocationDirectory ?
72
+ isDirectory(path.join(invocationDirectory, '.claude')) :
73
+ false;
74
+
75
+ if (configurationResult.detected || projectConfigurationDetected) {
76
+ console.log(CLAUDE_PLUGIN_MESSAGE);
77
+ }
78
+ }
79
+ },
80
+ {
81
+ name: 'Codex',
82
+ configurationEnvironmentVariable: 'CODEX_HOME',
83
+ fallbackDirectory: '.codex',
84
+ instructionFileNames: ['AGENTS.override.md', 'AGENTS.md']
85
+ }
86
+ ];
87
+
88
+ /**
89
+ * @param {NodeJS.ProcessEnv} environment - Installation environment.
90
+ * @returns {boolean} Whether npm is installing the package globally.
91
+ */
92
+ function isGlobalInstallation (environment) {
93
+ const globalSetting = String(environment.npm_config_global || '').toLowerCase();
94
+
95
+ return globalSetting === 'true' || globalSetting === '1';
96
+ }
97
+
98
+ /**
99
+ * @param {string} directory - Directory to inspect.
100
+ * @returns {boolean} Whether the path is an accessible directory.
101
+ */
102
+ function isDirectory (directory) {
103
+ try {
104
+ return fileSystem.statSync(directory).isDirectory();
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * @param {unknown} error - Caught failure.
113
+ * @returns {string} Sanitized failure classification.
114
+ */
115
+ function getErrorCode (error) {
116
+ try {
117
+ return error && typeof error.code === 'string' ? error.code : 'UNKNOWN';
118
+ }
119
+ catch {
120
+ return 'UNKNOWN';
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Reporting must not turn this best-effort hook into a failed npm installation.
126
+ *
127
+ * @param {string} message - Sanitized warning to print.
128
+ * @returns {void}
129
+ */
130
+ function printNonBlockingWarning (message) {
131
+ try {
132
+ console.warn(message);
133
+ }
134
+ catch {
135
+ // The post-install configuration remains non-critical when output is unavailable.
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @param {NodeJS.ProcessEnv} environment - Installation environment.
141
+ * @param {string} userHome - Current user's home directory.
142
+ * @returns {string|null} Directory from which the user invoked npm.
143
+ */
144
+ function resolveInvocationDirectory (environment, userHome) {
145
+ const initialDirectory = environment.INIT_CWD && environment.INIT_CWD.trim();
146
+
147
+ return initialDirectory ? path.resolve(userHome, initialDirectory) : null;
148
+ }
149
+
150
+ /**
151
+ * @param {AgentConfiguration} agentConfiguration - Agent path conventions.
152
+ * @param {NodeJS.ProcessEnv} environment - Installation environment.
153
+ * @param {string} userHome - Current user's home directory.
154
+ * @param {string|null} invocationDirectory - Directory from which npm was invoked.
155
+ * @returns {string} Absolute agent configuration root.
156
+ */
157
+ function resolveAgentConfigurationRoot (
158
+ agentConfiguration,
159
+ environment,
160
+ userHome,
161
+ invocationDirectory
162
+ ) {
163
+ const configuredRoot = environment[agentConfiguration.configurationEnvironmentVariable];
164
+
165
+ if (configuredRoot && configuredRoot.trim()) {
166
+ return path.resolve(invocationDirectory || userHome, configuredRoot.trim());
167
+ }
168
+
169
+ return path.join(userHome, agentConfiguration.fallbackDirectory);
170
+ }
171
+
172
+ /**
173
+ * Adds the Postman installation block without rewriting existing instructions.
174
+ *
175
+ * @param {string} instructionFile - Instruction file to update.
176
+ * @returns {'added'|'existing'} Update result.
177
+ * @throws {NodeJS.ErrnoException} When the instruction file cannot be read or appended.
178
+ */
179
+ function addPostmanInstruction (instructionFile) {
180
+ let existingInstructions = '';
181
+
182
+ try {
183
+ existingInstructions = fileSystem.readFileSync(instructionFile, 'utf8');
184
+ }
185
+ catch (error) {
186
+ if (error.code !== 'ENOENT') {
187
+ throw error;
188
+ }
189
+ }
190
+
191
+ if (existingInstructions.includes(POSTMAN_INSTRUCTION_START) ||
192
+ existingInstructions.includes(POSTMAN_INSTRUCTION)) {
193
+ return 'existing';
194
+ }
195
+
196
+ const separator = existingInstructions.length === 0 ?
197
+ '' :
198
+ existingInstructions.endsWith('\n') ? '\n' : '\n\n';
199
+
200
+ fileSystem.appendFileSync(
201
+ instructionFile,
202
+ `${separator}${POSTMAN_INSTRUCTION_BLOCK}\n`,
203
+ 'utf8'
204
+ );
205
+
206
+ return 'added';
207
+ }
208
+
209
+ /**
210
+ * @typedef {Object} AgentConfigurationResult
211
+ * @property {string} name - User-facing agent name.
212
+ * @property {boolean} detected - Whether its global configuration root exists.
213
+ * @property {'added'|'existing'|null} instruction - Instruction update outcome.
214
+ * @property {string|null} errorCode - Sanitized failure classification.
215
+ */
216
+
217
+ /**
218
+ * @typedef {Object} AgentPostConfigurationContext
219
+ * @property {AgentConfigurationResult} configurationResult - Agent configuration outcome.
220
+ * @property {string|null} invocationDirectory - Directory from which npm was invoked.
221
+ */
222
+
223
+ /**
224
+ * @param {AgentConfiguration} agentConfiguration - Agent path conventions.
225
+ * @param {NodeJS.ProcessEnv} environment - Installation environment.
226
+ * @param {string} userHome - Current user's home directory.
227
+ * @param {string|null} invocationDirectory - Directory from which npm was invoked.
228
+ * @returns {AgentConfigurationResult} Detection and update result.
229
+ */
230
+ function configureAgent (agentConfiguration, environment, userHome, invocationDirectory) {
231
+ let detected = false;
232
+
233
+ try {
234
+ const configurationRoot = resolveAgentConfigurationRoot(
235
+ agentConfiguration,
236
+ environment,
237
+ userHome,
238
+ invocationDirectory
239
+ );
240
+
241
+ if (!isDirectory(configurationRoot)) {
242
+ return {
243
+ name: agentConfiguration.name,
244
+ detected,
245
+ instruction: null,
246
+ errorCode: null
247
+ };
248
+ }
249
+
250
+ detected = true;
251
+
252
+ return {
253
+ name: agentConfiguration.name,
254
+ detected,
255
+ instruction: addPostmanInstruction(
256
+ resolveInstructionFile(agentConfiguration, configurationRoot)
257
+ ),
258
+ errorCode: null
259
+ };
260
+ }
261
+ catch (error) {
262
+ return {
263
+ name: agentConfiguration.name,
264
+ detected,
265
+ instruction: null,
266
+ errorCode: getErrorCode(error)
267
+ };
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Prints a concise result without exposing user-specific filesystem paths.
273
+ *
274
+ * @param {AgentConfigurationResult} result - Agent configuration result.
275
+ * @returns {void}
276
+ */
277
+ function printAgentResult (result) {
278
+ if (result.instruction === 'added') {
279
+ console.log(`Added Postman CLI availability to ${result.name} global instructions.`);
280
+ }
281
+ else if (result.instruction === 'existing') {
282
+ console.log(`${result.name} global instructions already include Postman CLI.`);
283
+ }
284
+ else if (result.errorCode) {
285
+ printNonBlockingWarning(
286
+ `Could not update ${result.name} global instructions (${result.errorCode}); ` +
287
+ 'the Postman CLI installation is still usable.'
288
+ );
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Runs an agent-specific follow-up without affecting other agents or npm.
294
+ *
295
+ * @param {AgentConfiguration} agentConfiguration - Agent path conventions.
296
+ * @param {AgentConfigurationResult} configurationResult - Agent configuration outcome.
297
+ * @param {string|null} invocationDirectory - Directory from which npm was invoked.
298
+ * @returns {void}
299
+ */
300
+ function runAgentPostConfiguration (
301
+ agentConfiguration,
302
+ configurationResult,
303
+ invocationDirectory
304
+ ) {
305
+ if (!agentConfiguration.postConfiguration) {
306
+ return;
307
+ }
308
+
309
+ try {
310
+ agentConfiguration.postConfiguration({
311
+ configurationResult,
312
+ invocationDirectory
313
+ });
314
+ }
315
+ catch (error) {
316
+ printNonBlockingWarning(
317
+ `Could not complete ${agentConfiguration.name} post-configuration ` +
318
+ `(${getErrorCode(error)}); the Postman CLI installation is still usable.`
319
+ );
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Configures supported agents after a global npm installation.
325
+ *
326
+ * Instruction updates are deliberately non-critical. Installing the Postman CLI
327
+ * must still succeed when an agent configuration is read-only or malformed.
328
+ *
329
+ * @param {NodeJS.ProcessEnv} [environment] - Installation environment.
330
+ * @returns {void}
331
+ */
332
+ function runPostInstallation (environment = process.env) {
333
+ try {
334
+ if (!isGlobalInstallation(environment)) {
335
+ return;
336
+ }
337
+
338
+ const userHome = operatingSystem.homedir(),
339
+ invocationDirectory = resolveInvocationDirectory(environment, userHome),
340
+ configuredAgents = AGENT_CONFIGURATIONS.map((agentConfiguration) => {
341
+ return {
342
+ agentConfiguration,
343
+ configurationResult: configureAgent(
344
+ agentConfiguration,
345
+ environment,
346
+ userHome,
347
+ invocationDirectory
348
+ )
349
+ };
350
+ });
351
+
352
+ console.log('Postman CLI is installed and can be used for API Engineering work.');
353
+ configuredAgents.forEach(({ configurationResult }) => {
354
+ try {
355
+ printAgentResult(configurationResult);
356
+ }
357
+ catch (error) {
358
+ printNonBlockingWarning(
359
+ `Could not report ${configurationResult.name} configuration ` +
360
+ `(${getErrorCode(error)}); the Postman CLI installation is still usable.`
361
+ );
362
+ }
363
+ });
364
+ configuredAgents.forEach(({ agentConfiguration, configurationResult }) => {
365
+ runAgentPostConfiguration(
366
+ agentConfiguration,
367
+ configurationResult,
368
+ invocationDirectory
369
+ );
370
+ });
371
+ }
372
+ catch (error) {
373
+ printNonBlockingWarning(
374
+ `Could not configure coding agents after installing Postman CLI (${getErrorCode(error)}); ` +
375
+ 'the Postman CLI installation is still usable.'
376
+ );
377
+ }
378
+ }
379
+
380
+ if (require.main === module) {
381
+ try {
382
+ runPostInstallation();
383
+ }
384
+ catch (error) {
385
+ printNonBlockingWarning(
386
+ `Could not run Postman CLI post-installation (${getErrorCode(error)}); ` +
387
+ 'the Postman CLI installation is still usable.'
388
+ );
389
+ }
390
+ finally {
391
+ process.exitCode = 0;
392
+ }
393
+ }
394
+
395
+ module.exports = {
396
+ AGENT_CONFIGURATIONS,
397
+ POSTMAN_INSTRUCTION,
398
+ addPostmanInstruction,
399
+ isGlobalInstallation,
400
+ resolveAgentConfigurationRoot,
401
+ runPostInstallation
402
+ };