@configdirector/cli 0.0.0-stage → 0.1.0-beta.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 ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@configdirector/cli` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0-beta.1] - 2026-10-16
8
+
9
+ ### Added
10
+
11
+ - `configdirector login` asks for an API token, checks that it can list your projects, and saves it for later runs where only you can read it. The token is hidden while you paste it. `configdirector login --token <token>` saves a token without asking.
12
+ - `configdirector init --project <slug>` writes `configdirector.json`, linking the current directory to one of your projects. It never overwrites an existing file.
13
+ - Every command accepts `--token`, `--base-url`, `--project` and `--json`. The token comes from `--token`, then `CONFIGDIRECTOR_TOKEN`, then the token saved by `login`. `CONFIGDIRECTOR_BASE_URL` and `CONFIGDIRECTOR_PROJECT` stand in for the other two options, and an empty variable counts as unset.
14
+ - Exit code 0 means the command succeeded, 1 means `validate` found a problem, and 2 means the command could not run: a usage mistake, a rejected token, a suspended service or an unreachable API. Errors go to stderr, so with `--json` the JSON is all that reaches stdout.
15
+ - `configdirector scan [path]` lists every place your code reads a config key through a ConfigDirector SDK or OpenFeature provider, grouped by key, with the file, the line, the SDK and the call, and the in-code default value when the call passes a literal one. It covers every ConfigDirector SDK and OpenFeature provider, including their test clients and the Java SDK's `@*ConfigValue` test annotations. Calls whose key is not a string literal, such as a variable or a key built at runtime, are listed separately as dynamic references. A call is found when its file imports the SDK, or imports a file of the repository that does, one level deep (JavaScript and TypeScript, Python, Dart and Go), so the setup module the SDK docs create is enough; Nuxt's auto-imported composables need no import. A file that imports only a ConfigDirector OpenFeature provider, or a setup module that does, does not count as importing the SDK, so a call there that merely shares a name with an SDK call, such as `preferences.getBoolean("onboarding-done", false)`, is not reported. Nuxt calls in the `server` directory next to `nuxt.config` are reported as the Nuxt server SDK, and other Nuxt calls as the Nuxt client SDK.
16
+ - `scan` reads the current directory, or only `[path]`. It skips what `.gitignore` ignores, the `node_modules`, `vendor`, `build`, `dist` and `target` directories, binary files and files over 1 MB. `include` and `exclude` in `configdirector.json` narrow it further.
17
+ - `scan` uses the `project` in `configdirector.json` when neither `--project` nor `CONFIGDIRECTOR_PROJECT` is set.
18
+ - `scan --json` prints the references for scripts and CI. `schema/scan-output.schema.json` in the package is the JSON Schema of that output.
19
+ - `scan` resolves a call that passes a constant, such as `client.getValue(NEW_CHECKOUT, false)`, to the key the constant holds, when one assignment of a string to that name is found anywhere in the scanned code: `const`, Kotlin `val`, Swift `case`, Go, Python and C# constants, and properties of object literals, enums and maps read as `FLAGS.checkoutV2` or `Flag.newCheckout.rawValue`. The output shows the constant after the call. A name assigned two different strings, an alias of another constant and a key built at runtime stay dynamic references.
20
+ - `scan` finds the project's current and deprecated keys wherever they appear as a quoted string outside SDK calls, such as in a wrapper function, a key list or a test fixture, and lists each as an indirect reference under its key, marked as a test when the file is in a test directory or has a test name. `scan` now needs the `configs:read` permission to list the project's keys. The counts at the end of the output and `--json` carry a `kind`, `direct` or `indirect`, per reference.
21
+ - `scan` finds the calls of a file that imports the client through an `index` file or a barrel library that re-exports it, up to two re-export steps: `export … from` in JavaScript and TypeScript, `export` in Dart, and relative imports in a Python package's `__init__.py`. JavaScript and TypeScript imports through a path alias, such as `@/lib/config-director`, are followed too, using `paths` and `baseUrl` from the nearest `tsconfig.json` or `jsconfig.json`, the configs it extends, or the configs it references, as in Vite's templates. Nuxt's `~` and `@` aliases work once `nuxt prepare`, a dev run or a build has generated the `.nuxt` configs.
22
+ - Swift and Go files use what other files of their Swift target or Go package declare without importing them, so `scan` now finds the calls of a file that uses a client created in another file of the same target or package, as in the Swift SDK docs where `ContentView.swift` reads the `client` that `ConfigDirectorSetup.swift` creates. A Go package is the `.go` files of one directory with the same `package` name, so an external test package such as `package shop_test` counts only through its own imports. A Swift target is a directory under `Sources/` or `Tests/` of the nearest `Package.swift`, the directory holding the nearest Xcode project, or else the file's own directory. A call that only shares a name with an SDK call is still not reported when no file of its target or package imports the SDK; in a target that does, an unrelated `value(for:)` call such as `DateComponents.value(for: .day)` is listed as a dynamic reference, which `validate` ignores.
23
+ - `configdirector validate [path]` checks every config key your code reads through an SDK against the project's configs, and exits 1 when it finds a problem, so CI fails the build before a wrong key ships. It reports a key no config has, the old key of a renamed config with its current key, an in-code default value or typed getter of a type the SDKs do not serve the config as, a client SDK reading a server-only config or a server SDK reading a client-only one, and an in-code default value the enum does not allow. A timespan is served as a number, so it is read with a number, and an enum whose values are numbers can be read with a number too. The output groups the problems by kind with the file, the line and what is wrong; `--json` adds a `findings` array to the scan's JSON, described in `schema/scan-output.schema.json`. Keys listed in `ignoreKeys` in `configdirector.json` are not checked, test client calls are checked for unknown and deprecated keys only, and dynamic and indirect references never fail the check.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ConfigDirector
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is furnished
10
+ to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,90 @@
1
- # Temporary Holding Version
1
+ # ConfigDirector CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `configdirector` is the command line tool for [ConfigDirector](https://www.configdirector.com), the managed configuration and feature flag service. It is in beta: commands and output may change before 1.0. This release signs in, links a repository to a project, lists where the repository's code reads config keys, and checks those keys against the project's configs.
4
+
5
+ The [CLI page of the ConfigDirector docs](https://docs.configdirector.com/integrations/cli) covers the API token, CI setup for GitHub Actions and GitLab, and what each problem `validate` reports means for your build.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install --global @configdirector/cli@beta
11
+ ```
12
+
13
+ It needs Node.js 20 or later.
14
+
15
+ ## Sign in
16
+
17
+ Create an API token in the ConfigDirector dashboard, then run:
18
+
19
+ ```bash
20
+ configdirector login
21
+ ```
22
+
23
+ The token is saved for later runs, where only you can read it. In CI, set `CONFIGDIRECTOR_TOKEN` instead.
24
+
25
+ ## Link a repository to a project
26
+
27
+ From the repository root:
28
+
29
+ ```bash
30
+ configdirector init --project <project-slug>
31
+ ```
32
+
33
+ This writes `configdirector.json`. Commit it so every run and every CI job uses the same project.
34
+
35
+ ## Find where the code reads configs
36
+
37
+ From the repository root:
38
+
39
+ ```bash
40
+ configdirector scan
41
+ ```
42
+
43
+ `scan` lists every place where the code reads a config key through a ConfigDirector SDK or OpenFeature provider, grouped by key, with the file and line of each. Test client calls, such as `setValue` or Java's `@BooleanConfigValue`, are included and marked as tests. `configdirector scan src` scans only `src`.
44
+
45
+ A call that passes a constant instead of a string is reported under the constant's key when one assignment of a string to that name is found anywhere in the scanned code: `const NEW_CHECKOUT = "new-checkout"`, Kotlin `val`, Swift `case newCheckout = "new-checkout"`, Go `NewCheckout = "new-checkout"`, Python `NEW_CHECKOUT = "new-checkout"`, C# `const string`, and properties such as `checkoutV2: "new-checkout"` in object literals, enums and maps. A dotted argument such as `FLAGS.checkoutV2` or `Flag.newCheckout.rawValue` is looked up by its last name. The output shows the constant after the call, as in `getValue(NEW_CHECKOUT)`. A name assigned two different strings, an alias of another constant, a key built at runtime or a key read from configuration cannot be resolved, so those calls are listed under their own heading as dynamic references: the scan cannot tell which config they read.
46
+
47
+ The scan also looks for the project's current and deprecated keys wherever they appear as a quoted string outside those calls, such as in a wrapper function, a key list or a test fixture, and reports each as an indirect reference under its key. An indirect reference in a test directory or a test-named file, such as `test/`, `checkout.test.ts`, `checkout_test.go`, `test_checkout.py` or `CheckoutTests.cs`, is marked as a test, whereas a direct reference is marked as a test only when the call is a test client call. The definition of a constant that an SDK call reads is not repeated as an indirect reference.
48
+
49
+ A call is found when its file imports the SDK, or imports a file of the repository that does, such as the setup module the SDK docs create. In Swift and Go, it is also found when another file of the same Swift target or Go package imports the SDK, since those files use each other's declarations without an import. A `getValue` from another library is not reported unless its file, or its Swift target or Go package, imports one of those. Imports are followed one level deep: relative imports and path aliases in JavaScript and TypeScript, Python modules, Dart files including your own `package:` imports, and Go packages of the same module. When the imported file does not import the SDK itself, the scan follows what it re-exports, up to two steps, so an `index` file or a barrel library in front of the setup module works: `export { client } from "./setup"` and `export * from "./setup"` in JavaScript and TypeScript, `export 'src/setup.dart';` in Dart, and relative imports such as `from .flags import client` in a Python package's `__init__.py`. A file that reaches the client through a second ordinary import, a re-export written as an import followed by a separate `export`, or a CommonJS `module.exports` is not scanned. A Go package is the `.go` files of one directory with the same `package` name, so an external test package such as `package shop_test` counts only through its own imports. A Swift target is found from the nearest `Package.swift` or Xcode project above the file: in a Swift package, each directory under `Sources/` or `Tests/` is a target; an Xcode project's directory is one target; with neither, the file's own directory is. Nuxt's auto-imported composables need no import. A Kotlin or Java file belongs to the Android SDK when the nearest Gradle build file above it is an Android module, and to the Java server SDK otherwise. A Nuxt file belongs to the Nuxt server SDK when it is in the `server` directory next to the nearest `nuxt.config`, and to the Nuxt client SDK otherwise; a Next.js file belongs to the client or the server SDK by the entry point it imports.
50
+
51
+ A path alias such as `@/lib/config-director` is resolved the way TypeScript resolves it, from `compilerOptions.paths` and `baseUrl` in the nearest `tsconfig.json` above the importing file, or `jsconfig.json` when that directory has no `tsconfig.json`. Configs it extends with a relative path count, and when it declares no aliases of its own, the configs listed in its `references` are tried in order, as in Vite's templates. Nuxt's `~` and `@` aliases work once `nuxt prepare`, a dev run or a build has generated the `.nuxt` configs. Aliases set only in bundler configuration, such as `resolve.alias` in `vite.config.ts` or webpack, are not followed. These configs and the files they point to, like the other files of a Swift target or Go package, are read even when the scan would skip them, as long as they are under 1 MB.
52
+
53
+ The scan skips what `.gitignore` ignores, the `node_modules`, `vendor`, `build`, `dist` and `target` directories, binary files and files over 1 MB. In `configdirector.json`, `include` limits the scan to the files matching its globs, and `exclude` skips the files matching its globs.
54
+
55
+ With `--json`, `scan` prints the references for scripts and CI; `schema/scan-output.schema.json` in the package describes that output. Each reference has a `kind`, `direct` or `indirect`, and a direct reference resolved from a constant carries the constant as `argument`.
56
+
57
+ ## Check the keys the code reads
58
+
59
+ From the repository root, or as a CI step:
60
+
61
+ ```bash
62
+ configdirector validate
63
+ ```
64
+
65
+ `validate` runs the same scan, then checks every config key the code reads through an SDK against the project's configs. It exits with 1 when it finds a problem, so the build fails before a wrong key ships:
66
+
67
+ - **Unknown key**: no config in the project has the key, as its current key or a deprecated one, so the SDK always returns the in-code default value. The key of an archived config is unknown too.
68
+ - **Deprecated key**: the config was renamed. The output names its current key.
69
+ - **Type mismatch**: the in-code default value, or a getter whose name fixes the type such as `getBoolean`, reads the config as a type the SDKs do not serve it as, so the SDK returns the in-code default value. A boolean reads a boolean config; an integer reads an integer, float or timespan config; a float reads a float or timespan config; a string reads a string, enum or URL config; an object or array reads a JSON config. A timespan is served as a number in its unit, so read it with a number, not a string such as `"30s"`. An enum whose values are numbers can also be read with a number.
70
+ - **Availability mismatch**: a client SDK reads a server-only config, or a server SDK reads a client-only config, so the SDK never receives it.
71
+ - **Enum value not allowed**: the in-code default value is not one of the enum's allowed values.
72
+
73
+ A test client call, such as `setValue` or `createTestClient`, is checked for unknown and deprecated keys only, since ConfigDirector never serves a test client. Dynamic and indirect references are listed by `scan` but never checked, and a config's lifecycle state is never a problem. To skip a key, such as one another project serves, list it in `ignoreKeys` in `configdirector.json`. `include` and `exclude` narrow the files checked as they do for `scan`.
74
+
75
+ The output lists the problems grouped by kind, with the file, the line, the key and what is wrong, such as the type a timespan is served as, then counts the references checked. With `--json`, `validate` prints the scan's JSON with a `findings` array; `schema/scan-output.schema.json` describes it.
76
+
77
+ ## Options
78
+
79
+ | Option | Environment variable | Meaning |
80
+ | ------------------ | ------------------------- | ----------------------------------------------------------------------------------------------- |
81
+ | `--token <token>` | `CONFIGDIRECTOR_TOKEN` | API token. Without either, the token saved by `login` is used. |
82
+ | `--base-url <url>` | `CONFIGDIRECTOR_BASE_URL` | API host. Defaults to `https://api.configdirector.com`. |
83
+ | `--project <slug>` | `CONFIGDIRECTOR_PROJECT` | Project slug. Without either, `scan` and `validate` use the `project` in `configdirector.json`. |
84
+ | `--json` | | Print JSON on stdout. Errors always go to stderr. |
85
+
86
+ The command exits with 0 when it succeeds, 1 when `validate` found a problem, and 2 when it could not run: a usage mistake, a rejected token, a suspended service or an unreachable API. A CI job can therefore tell a wrong config key from an outage.
87
+
88
+ ## License
89
+
90
+ [MIT](LICENSE)