Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c6e0232
Update dependencies and enhance CLI functionality: bump @opentui/core…
vacom Mar 21, 2026
ec6b3f9
Refactor CLI authentication mechanism: replace 'token' with 'api-key'…
vacom Mar 24, 2026
25bf688
Update dependencies and enhance CLI functionality: bump @biomejs/biom…
vacom Mar 25, 2026
11c406a
Update API key in Vite React example configuration: replace existing …
vacom Mar 25, 2026
a28c7be
Refactor getProjectName function to prioritize git repository name: u…
vacom Mar 25, 2026
bc0ba9d
Add repository URL to code references output: enhance the runCodeRefs…
vacom Mar 25, 2026
2aaae59
Enhance user interface and submission feedback: update submission mes…
vacom Mar 25, 2026
5c552db
Update dependencies in bun.lock and package.json: bump @opentui/core …
vacom Mar 27, 2026
e733521
Update dependencies and configuration files: bump @biomejs/biome to v…
vacom Apr 6, 2026
f1242a7
Update dependencies and configuration: bump @opentui/core and @opentu…
vacom Apr 9, 2026
c67fba7
Update version to 0.0.3 and enhance flag reference scanning: expand s…
vacom Apr 9, 2026
a1466f7
Update dependencies: bump vitest to version 4.1.3 and bun to version …
vacom Apr 10, 2026
fcf1f5b
add github action workflow
vacom Apr 10, 2026
1181948
Update devDependencies: add @types/react version 19.2.14 to package.j…
vacom Apr 10, 2026
9a1a0c2
Update version to 0.0.4, modify example configuration with new projec…
vacom Apr 10, 2026
f5ccc2c
Update dependencies and configuration: bump @biomejs/biome to version…
vacom Apr 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
package:
name: Package Quality
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 24.14.0

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.12

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Lint and format check
run: bun run lint

- name: Typecheck
run: bun run typecheck

- name: Run tests
run: bun run test

- name: Build package
run: bun run build

- name: Smoke test built CLI
run: bun dist/bin.js code-refs --help

example:
name: Example Build
runs-on: ubuntu-latest
needs: package
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 24.14.0

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.12

- name: Install package dependencies
run: bun install --frozen-lockfile

- name: Build package
run: bun run build

- name: Install example dependencies
working-directory: examples/vite-react
run: bun install --frozen-lockfile

- name: Build example app
working-directory: examples/vite-react
run: bun run build
114 changes: 88 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ CLI tool for [Basestack](https://basestack.co) Feature Flags. Scan your codebase
## Features

- **Code Refs** — Scan your codebase for `useFlag("slug")` hooks and `<Feature slug="slug">` components
- Collects file paths, line numbers, and code context for each flag reference
- Collects file paths, line numbers, and surrounding code context (2 lines before and after) for each flag reference
- Filters out references with empty or invalid flag slugs
- Groups references by flag slug
- Detects git branch and project name automatically
- Submits references to the Basestack API
Expand Down Expand Up @@ -33,30 +34,41 @@ The CLI reads configuration from three sources (in order of priority):
### 1. CLI flags (highest priority)

```bash
flags-cli code-refs --project-id=proj_xxx --token=sk_xxx
flags-cli code-refs --project-id=proj_xxx --environment-key=env_xxx --api-key=sk_xxx
```

### 2. Environment variables
### 2. Config file

```bash
export BASESTACK_PROJECT_ID=proj_xxx
export BASESTACK_SECRET_TOKEN=sk_xxx
export BASESTACK_API_URL=https://api.basestack.co # optional
```

### 3. Config file

Create a `.basestackrc` file in your project root:
Create a `.flagsrc` file in your project root. JSON is supported:

```json
{
"projectId": "proj_xxx",
"secretToken": "sk_xxx",
"apiUrl": "https://api.basestack.co"
"projectKey": "proj_xxx",
"environmentKey": "env_xxx",
"apiKey": "sk_xxx",
"apiUrl": "https://flags-api.basestack.co/v1"
}
```

> **Important:** Add `.basestackrc` to your `.gitignore` to avoid committing secrets.
You can also use env-style entries in `.flagsrc`:

```bash
projectKey=proj_xxx
environmentKey=env_xxx
apiKey=sk_xxx
apiUrl=https://flags-api.basestack.co/v1
```

### 3. Environment variables

```bash
export BASESTACK_FLAGS_PROJECT_KEY=proj_xxx
export BASESTACK_FLAGS_ENVIRONMENT_KEY=env_xxx
export BASESTACK_FLAGS_API_KEY=sk_xxx
export BASESTACK_FLAGS_API_URL=https://flags-api.basestack.co/v1 # optional
```

> **Important:** Add `.flagsrc` to your `.gitignore` to avoid committing secrets.

## Usage

Expand All @@ -71,33 +83,64 @@ flags-cli code-refs
With options:

```bash
flags-cli code-refs --project-id=proj_xxx --token=sk_xxx --dir=./src
flags-cli code-refs --project-id=proj_xxx --environment-key=env_xxx --api-key=sk_xxx --dir=./src
```

When run in an interactive terminal, `code-refs` opens an OpenTUI interface with progress, a scrollable results pane, and keyboard shortcuts. In CI or other non-interactive environments, it automatically falls back to plain text output.

### All options

| Option | Description | Default |
|---|---|---|
| `--project-id` | Basestack project ID | `BASESTACK_PROJECT_ID` env var |
| `--token` | Secret token for API auth | `BASESTACK_SECRET_TOKEN` env var |
| `--api-url` | API base URL | `https://api.basestack.co` |
| `--project-id` | Basestack project key (required) | `BASESTACK_FLAGS_PROJECT_KEY` env var |
| `--environment-key` | Basestack environment key (required) | `BASESTACK_FLAGS_ENVIRONMENT_KEY` env var |
| `--api-key` | API key for authentication (required) | `BASESTACK_FLAGS_API_KEY` env var |
| `--api-url` | API base URL | `https://flags-api.basestack.co/v1` |
| `--dir` | Directory to scan | Current directory |
| `-h, --help` | Show help | — |

### What it scans for

The scanner looks for two patterns in `.ts`, `.tsx`, `.js`, and `.jsx` files:
The scanner looks for common flag references in `.ts`, `.tsx`, `.js`, `.jsx`, `.html`, and `.htm` files:

```tsx
// Hook usage
const isEnabled = useFlag("my-feature");
const flag = useFlag<{ variant?: string }>("my-feature");
const enabled = useBooleanFlagValue("my-feature", false);
const config = useObjectFlagValue("checkout-config", {});

// Component usage
<Feature slug="my-feature">
<MyComponent />
</Feature>

// SDK usage
const flag = await fetchFlag("my-feature", flagsConfig);
const value = await client.getFlag("my-feature");
client.clearFlagCache("my-feature");
client.getBooleanValue("my-feature", false);
client.getObjectValue("checkout-config", {});
client.getBooleanDetails("my-feature", false);

// Modal usage
const { openFeedbackModal } = useFlag("my-feature");
openFeedbackModal({ featureName: "My Feature" });

const { openFeedbackModal } = useFeatureFlagModals();
openFeedbackModal("my-feature", { featureName: "My Feature" });

// Preload usage
const sdkOptions = {
preloadFlags: ["header", "footer"],
};

// Web component usage
<feature-flag-feedback-modal flag-key="my-feature" />
```

For simple dynamic cases, the scanner also resolves identifier arguments when they are backed by a nearby string literal, such as `fetchFlag(slug, ...)` with `slug = "header"` or `const slug = "header"`.

It automatically skips `node_modules`, `dist`, `build`, `.git`, `coverage`, `.next`, `.turbo`, and `out` directories.

### CI/CD Integration
Expand All @@ -109,13 +152,14 @@ Add to your CI pipeline to keep flag references up-to-date:
- name: Update flag references
run: bunx @basestack/flags-cli code-refs
env:
BASESTACK_PROJECT_ID: ${{ secrets.BASESTACK_PROJECT_ID }}
BASESTACK_SECRET_TOKEN: ${{ secrets.BASESTACK_SECRET_TOKEN }}
BASESTACK_FLAGS_PROJECT_KEY: ${{ secrets.BASESTACK_FLAGS_PROJECT_KEY }}
BASESTACK_FLAGS_ENVIRONMENT_KEY: ${{ secrets.BASESTACK_FLAGS_ENVIRONMENT_KEY }}
BASESTACK_FLAGS_API_KEY: ${{ secrets.BASESTACK_FLAGS_API_KEY }}
```

## API Contract

The CLI submits flag references via `POST /v1/projects/{projectId}/code-refs` with the following payload:
The CLI submits flag references via `POST {baseUrl}/flags/code-refs` with the following payload:

```json
{
Expand All @@ -126,14 +170,32 @@ The CLI submits flag references via `POST /v1/projects/{projectId}/code-refs` wi
{
"filePath": "src/App.tsx",
"lineNumber": 12,
"lineContent": "const isEnabled = useFlag(\"my-feature\");"
"lineContent": "function App() {\n const isEnabled = useFlag(\"my-feature\");\n const showBanner = useFlag(\"banner\");\n\n return ("
}
]
}
}
```

Authentication is via `Authorization: Bearer <secret-token>` header.
> **Note:** `lineContent` includes up to 2 lines before and 2 lines after the matched line for surrounding context.

### Headers

| Header | Description |
|---|---|
| `x-api-key` | API key (required) |
| `x-project-key` | Project key (required) |
| `x-environment-key` | Environment key (required) |

### Response

The API returns a JSON response with status `201`:

```json
{
"success": true
}
```

## Development

Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
Loading
Loading