Skip to content

feat(cli): add projects support with --project flag and name resolution#147

Open
hiroTamada wants to merge 11 commits intomainfrom
hiro/cli-project-support
Open

feat(cli): add projects support with --project flag and name resolution#147
hiroTamada wants to merge 11 commits intomainfrom
hiro/cli-project-support

Conversation

@hiroTamada
Copy link
Copy Markdown
Contributor

@hiroTamada hiroTamada commented Apr 10, 2026

Summary

  • Adds a global --project persistent flag (and KERNEL_PROJECT_ID env var) that injects the X-Kernel-Project-Id header to scope all API requests to a specific project
  • The flag accepts either a project ID or a project name — names are resolved via the projects list endpoint with case-insensitive matching
  • Adds kernel projects subcommands: list, create, get, delete, get-limits, set-limits
  • Upgrades kernel-go-sdk to v0.48.0 for project endpoint support (also fixes ProxyService.Check signature change)

How it works

When --project is provided:

  1. If the value looks like a cuid2 ID (24 lowercase alphanumeric chars), it's used directly as the project ID header
  2. Otherwise, the CLI authenticates first, calls GET /projects to list all projects, finds a case-insensitive name match, and injects the resolved ID
  3. Clear errors for: no match found, multiple matches (ambiguous name)

Test plan

Tested against staging with a project-scoped API key:

  • --project "cli-test-project" (by name) — resolves correctly
  • --project "CLI-TEST-PROJECT" (case-insensitive) — resolves correctly
  • --project "g9vcya6unur84k70n0u8s9p9" (by ID) — works directly
  • --project "nonexistent-project" — clear error message
  • KERNEL_PROJECT_ID="cli-test-project" env var with name — resolves correctly
  • No --project with scoped API key — auto-scopes via server middleware
  • kernel projects list/create/get/delete/get-limits/set-limits — all functional

Made with Cursor


Note

Medium Risk
Introduces a new global request-scoping mechanism via the X-Kernel-Project-Id header and adds project CRUD/limits commands, which can affect all API calls when enabled. Also bumps the Kernel SDK and adjusts proxy health-check calls to match the new SDK signature.

Overview
Adds first-class project management to the CLI via a new projects command group (list, create, get, delete, plus limits get/set with optional --output json) and validation for limit updates (rejects negative values, treats unset vs set via Int64Flag).

Introduces a global --project flag (or KERNEL_PROJECT env var) that injects X-Kernel-Project-Id into all authenticated requests, plus project name → ID resolution via paginated project listing with clear errors for no/ambiguous matches.

Updates CUID2 validation to require a leading letter, upgrades github.com/kernel/kernel-go-sdk to v0.48.0, and adapts proxy health-check wiring/tests to the SDK’s updated Check signature.

Reviewed by Cursor Bugbot for commit 748bef4. Bugbot is set up for automated code reviews on this repo. Configure here.

Add global --project flag (and KERNEL_PROJECT_ID env var) that injects
X-Kernel-Project-Id header to scope all API requests to a project.
The flag accepts either a project ID or a project name — names are
resolved via the projects list endpoint (case-insensitive).

Also adds `kernel projects` subcommands: list, create, get, delete,
get-limits, set-limits.

Upgrades kernel-go-sdk to v0.48.0 for project endpoint support.

Made-with: Cursor
@firetiger-agent
Copy link
Copy Markdown

Firetiger deploy monitoring skipped

This PR didn't match the auto-monitor filter configured on your GitHub connection:

Any PR that changes the kernel API. Monitor changes to API endpoints (packages/api/cmd/api/) and Temporal workflows (packages/api/lib/temporal) in the kernel repo

Reason: PR modifies CLI functionality (packages/cli) and SDK dependencies, not kernel API endpoints (packages/api/cmd/api/) or Temporal workflows (packages/api/lib/temporal).

To monitor this PR anyway, reply with @firetiger monitor this.

Comment thread cmd/root.go Outdated
Comment thread cmd/proxies/common_test.go
- looksLikeCUID: require first character to be a letter (cuid2 spec)
- FakeProxyService.CheckFunc: update type signature and delegation to
  include body parameter

Made-with: Cursor
Comment thread cmd/root.go Outdated
Reuse cuidRegex from browsers.go in looksLikeCUID and update the regex
to require the first character to be a letter (matching the cuid2 spec).

Made-with: Cursor
Comment thread cmd/projects.go Outdated
Previously negative values were silently discarded, reporting success
without sending the value to the API.

Made-with: Cursor
Comment thread cmd/root.go
get, delete, get-limits, and set-limits now accept either a project ID
or name. Names are resolved via the projects list endpoint, reusing the
same resolveProjectByName helper as the --project global flag.

Made-with: Cursor
The default page size could miss projects beyond the first page.
Request a large limit to cover typical org sizes.

Made-with: Cursor
Comment thread cmd/root.go Outdated
Comment thread cmd/root.go Outdated
Comment thread cmd/projects.go Outdated
Comment thread cmd/projects.go Outdated
Comment thread cmd/projects.go Outdated
Adopt the typed handler pattern for projects while preserving Cobra wiring, add nested limits subcommands with backward-compatible aliases, and standardize limits output behavior. Also support KERNEL_PROJECT precedence and paginate project name resolution to avoid missing matches outside the first page.

Made-with: Cursor
@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 13, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedgolang/​github.com/​kernel/​kernel-go-sdk@​v0.44.1-0.20260323174449-5e56fc5d99a6 ⏵ v0.48.072 +1100100100100

View full report

@hiroTamada hiroTamada requested a review from rgarcia April 13, 2026 16:52
Copy link
Copy Markdown
Contributor

@masnwilliams masnwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm — all review comments addressed. typed handler pattern, nested limits subcommands, human-readable output, and KERNEL_PROJECT precedence all look good.

Remove name resolution from the global --project flag. The flag now
passes the value directly as the X-Kernel-Project-Id header without
attempting to resolve project names.

The resolveProjectByName function is retained for explicit project
subcommands (e.g. 'projects get <name>') where name lookup is expected.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a20844d. Configure here.

Comment thread cmd/root.go

if projectVal != "" {
clientOpts = append(clientOpts, option.WithHeader("X-Kernel-Project-Id", projectVal))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global --project flag skips name-to-ID resolution

High Severity

The --project flag handler in PersistentPreRunE passes the raw projectVal directly into the X-Kernel-Project-Id header without calling resolveProjectArg or resolveProjectByName. When a user supplies a project name instead of an ID, the name string is sent verbatim as the header value rather than being resolved to an actual project ID. The resolveProjectByName function is defined in the same file but never wired into this code path, so the documented name-resolution behavior (CUID2 check, then list-and-match) doesn't occur for the global flag.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a20844d. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems worthy

Comment thread cmd/root.go

if projectVal != "" {
clientOpts = append(clientOpts, option.WithHeader("X-Kernel-Project-Id", projectVal))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems worthy

Projects is a brand new feature — KERNEL_PROJECT_ID was never in
production, so there's no legacy to preserve. Only read KERNEL_PROJECT.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@masnwilliams
Copy link
Copy Markdown
Contributor

Pushed a commit dropping the KERNEL_PROJECT_ID fallback per the docs thread — projects is brand new, there's nothing to preserve.

One other thing worth a quick check before merge: the PR description says the --project global flag resolves names → IDs client-side ("If the value looks like a cuid2 ID… it's used directly as the project ID header. Otherwise, the CLI authenticates first, calls GET /projects… and injects the resolved ID"), but in cmd/root.go the PersistentPreRunE passes projectVal straight into the X-Kernel-Project-Id header with no call to resolveProjectByName:

projectVal, _ := cmd.Flags().GetString("project")
projectVal = resolveProjectSelection(projectVal)

if projectVal != "" {
    clientOpts = append(clientOpts, option.WithHeader("X-Kernel-Project-Id", projectVal))
}

resolveProjectByName is only wired into resolveProjectArg in cmd/projects.go for positional args on projects get/delete/limits.

So either:

  1. The server resolves names from the header too (in which case the description is fine, but the cuid2 regex check in the description is misleading since it doesn't run), or
  2. kernel browsers list --project staging sends staging as the header literal and the API rejects it.

Your test plan shows --project "cli-test-project" passing — is that because the server accepts names in X-Kernel-Project-Id, or was it happening to hit the positional-arg path of a projects subcommand? Worth adding resolveProjectByName in the PersistentPreRunE path if it's #2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants