@nuucognition/cli-core 0.0.1-beta.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 ADDED
@@ -0,0 +1,392 @@
1
+ # @nuucognition/cli-core
2
+
3
+ Shared CLI infrastructure for the NUU ecosystem. Provides feature flags, configuration, authentication, output formatting, and error handling that all NUU CLIs share.
4
+
5
+ ## Architecture
6
+
7
+ All NUU CLI state lives under `~/.nuucognition/`:
8
+
9
+ ```
10
+ ~/.nuucognition/
11
+ ├── auth.json # Production credentials (shared across all CLIs)
12
+ ├── auth.dev.json # Development credentials
13
+ ├── .flint/config.toml # Flint-specific config
14
+ ├── .vessel/config.toml # Vessel-specific config
15
+ └── .nuu/config.toml # nuu CLI config
16
+ ```
17
+
18
+ Auth credentials are shared — log in once, all CLIs are authenticated. Per-CLI config (mode, feature overrides) is scoped under `.<cliname>/`.
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import {
24
+ resolveRuntimeSync,
25
+ createFeatureRegistry,
26
+ isFeatureEnabled,
27
+ createLoginCommand,
28
+ createLogoutCommand,
29
+ createWhoamiCommand,
30
+ CliError,
31
+ error,
32
+ success,
33
+ } from "@nuucognition/cli-core"
34
+ import { Command } from "commander"
35
+
36
+ // 1. Define your features
37
+ const FEATURES = createFeatureRegistry([
38
+ { id: "init", tier: "prod", type: "command", description: "Initialize workspace", requiresAuth: false },
39
+ { id: "sync", tier: "prod", type: "command", description: "Sync configuration", requiresAuth: false },
40
+ { id: "publish", tier: "exp", type: "command", description: "Publish to registry", requiresAuth: true },
41
+ { id: "agent", tier: "dev", type: "command", description: "Agent management", requiresAuth: true },
42
+ ])
43
+
44
+ // 2. Resolve runtime (reads config, checks auth)
45
+ const runtime = resolveRuntimeSync({ cliname: "mycli" })
46
+ const devAvailable = runtime.mode === "dev"
47
+
48
+ // 3. Wire up auth commands
49
+ const authConfig = {
50
+ cliname: "mycli",
51
+ defaultEnv: devAvailable ? "dev" as const : "prod" as const,
52
+ devAvailable,
53
+ }
54
+
55
+ const program = new Command()
56
+ .name("mycli")
57
+ .version("0.1.0")
58
+
59
+ program.addCommand(createLoginCommand(authConfig))
60
+ program.addCommand(createLogoutCommand(authConfig))
61
+ program.addCommand(createWhoamiCommand(authConfig))
62
+
63
+ // 4. Conditionally register feature-gated commands
64
+ if (isFeatureEnabled(FEATURES, "publish", runtime)) {
65
+ program.addCommand(publishCommand)
66
+ }
67
+
68
+ await program.parseAsync(process.argv)
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ ### Feature Flags
74
+
75
+ Three-tier system controlling what's available at runtime.
76
+
77
+ **Tiers:**
78
+
79
+ | Tier | Available when |
80
+ |------|---------------|
81
+ | `prod` | Always |
82
+ | `exp` | In `dev` mode, or when explicitly opted in via config or `--enable` flag |
83
+ | `dev` | Only in `dev` mode |
84
+
85
+ **Registry:**
86
+
87
+ ```typescript
88
+ import { createFeatureRegistry, getFeature, getFeaturesByTier, getCommandFeatures, hasFeature } from "@nuucognition/cli-core"
89
+
90
+ const registry = createFeatureRegistry([
91
+ {
92
+ id: string, // unique identifier
93
+ tier: "prod" | "exp" | "dev",
94
+ type: "command" | "function" | "module",
95
+ description: string,
96
+ requiresAuth: boolean, // gate behind authentication
97
+ since?: string, // version introduced
98
+ graduated?: string, // version promoted to current tier
99
+ previousTier?: "dev" | "exp",
100
+ deprecated?: boolean,
101
+ },
102
+ ])
103
+
104
+ getFeature(registry, "publish") // FeatureDefinition | undefined
105
+ getFeaturesByTier(registry, "exp") // FeatureDefinition[]
106
+ getCommandFeatures(registry) // all command-type features
107
+ hasFeature(registry, "publish") // boolean
108
+ ```
109
+
110
+ **Guards:**
111
+
112
+ ```typescript
113
+ import { isFeatureEnabled, requireFeature, checkFeature } from "@nuucognition/cli-core"
114
+
115
+ // Boolean check — use for conditional registration
116
+ if (isFeatureEnabled(registry, "publish", runtime)) {
117
+ program.addCommand(publishCommand)
118
+ }
119
+
120
+ // Throws FeatureNotAvailableError or AuthRequiredError
121
+ requireFeature(registry, "publish", runtime, "mycli")
122
+
123
+ // Diagnostic — returns detailed availability info
124
+ const check = checkFeature(registry, "publish", runtime)
125
+ // { available, requiredTier, currentMode, experimental, requiresAuth, authenticated, reason? }
126
+ ```
127
+
128
+ ### Runtime Resolution
129
+
130
+ Resolves mode, experimental status, per-feature overrides, and auth state.
131
+
132
+ ```typescript
133
+ import { resolveRuntime, resolveRuntimeSync } from "@nuucognition/cli-core"
134
+
135
+ // Sync (use at startup for command registration)
136
+ const runtime = resolveRuntimeSync({ cliname: "mycli" })
137
+
138
+ // Async
139
+ const runtime = await resolveRuntime({ cliname: "mycli" })
140
+
141
+ // runtime: {
142
+ // mode: "prod" | "dev",
143
+ // experimental: boolean,
144
+ // enabledFeatures: string[], // from [features] config table
145
+ // authenticated: boolean, // usable auth material exists
146
+ // }
147
+ ```
148
+
149
+ **Resolution chain (first match wins):**
150
+
151
+ - **Mode:** config file `mode` field -> `BUILD_MODE` env -> `"dev"` default
152
+ - **Experimental:** config file `experimental` field -> `false` in prod, `true` in dev
153
+ - **Enabled features:** config file `[features]` table
154
+ - **Authenticated:** checks `~/.nuucognition/auth.json` (or `auth.dev.json`) for a valid session or refresh token
155
+
156
+ **Mutators:**
157
+
158
+ ```typescript
159
+ import { setMode, resetMode, setExperimental, resetExperimental, setFeatureEnabled, resetFeatureEnabled } from "@nuucognition/cli-core"
160
+
161
+ const config = { cliname: "mycli" }
162
+
163
+ await setMode(config, "prod") // write mode to config.toml
164
+ await resetMode(config) // remove mode override
165
+ await setExperimental(config, true) // enable all exp features
166
+ await setFeatureEnabled(config, "publish", true) // enable one feature
167
+ await resetFeatureEnabled(config, "publish") // remove override
168
+ ```
169
+
170
+ ### Configuration
171
+
172
+ Reads TOML config from `~/.nuucognition/.<cliname>/config.toml`.
173
+
174
+ ```typescript
175
+ import { resolveConfig, resolveConfigSync, getConfigPath, ensureConfigDir } from "@nuucognition/cli-core"
176
+
177
+ const config = resolveConfigSync({ cliname: "mycli" })
178
+ // Reads ~/.nuucognition/.mycli/config.toml, merges with defaults
179
+
180
+ const config = resolveConfigSync({
181
+ cliname: "mycli",
182
+ defaults: { someField: "fallback" },
183
+ })
184
+
185
+ getConfigPath("mycli") // ~/.nuucognition/.mycli/config.toml
186
+ await ensureConfigDir("mycli") // creates dir if missing, returns path
187
+ ```
188
+
189
+ **Config file format:**
190
+
191
+ ```toml
192
+ mode = "prod"
193
+ experimental = false
194
+
195
+ [features]
196
+ publish = true
197
+ export = true
198
+ ```
199
+
200
+ ### Authentication
201
+
202
+ Browser-based PKCE OAuth flow via Clerk. Credentials stored in `~/.nuucognition/`.
203
+
204
+ **Credential storage:**
205
+
206
+ ```typescript
207
+ import { loadAuth, loadAuthSync, saveAuth, clearAuth, hasAuth, getNuuDir, ensureNuuDir } from "@nuucognition/cli-core"
208
+
209
+ // Load credentials
210
+ const creds = await loadAuth("prod") // NuuCredentials | null
211
+ const creds = loadAuthSync("dev") // sync variant
212
+
213
+ // Save (creates ~/.nuucognition/ if needed)
214
+ await saveAuth(creds, "prod")
215
+
216
+ // Clear (deletes auth file)
217
+ await clearAuth("prod")
218
+
219
+ // Check if usable auth exists (sync, no network)
220
+ hasAuth("prod") // true if session valid or refresh token valid
221
+
222
+ getNuuDir() // ~/.nuucognition/
223
+ await ensureNuuDir() // create if missing
224
+ ```
225
+
226
+ **NuuCredentials shape:**
227
+
228
+ ```typescript
229
+ interface NuuCredentials {
230
+ sessionToken: string // Clerk JWT
231
+ userId: string // Clerk user ID
232
+ email: string
233
+ expiresAt: string // ISO 8601
234
+ refreshToken?: string // long-lived refresh token
235
+ refreshExpiresAt?: string // ISO 8601
236
+ activeOrg?: { // currently selected organisation
237
+ id: string // Convex document ID
238
+ slug: string
239
+ name: string // display name
240
+ }
241
+ }
242
+ ```
243
+
244
+ **Auth validation (load + auto-refresh):**
245
+
246
+ ```typescript
247
+ import { getValidAuth, requireValidAuth } from "@nuucognition/cli-core"
248
+
249
+ // Returns null if no valid auth (won't throw)
250
+ const creds = await getValidAuth({ accountUrl, authEnv: "dev", cliname: "mycli" }, "dev")
251
+
252
+ // Throws if not authenticated
253
+ const creds = await requireValidAuth({ accountUrl, authEnv: "prod", cliname: "mycli" })
254
+ ```
255
+
256
+ **Login flow (PKCE):**
257
+
258
+ ```typescript
259
+ import { startLoginFlow, refreshAuth } from "@nuucognition/cli-core"
260
+
261
+ // Opens browser, waits for callback, exchanges code, saves credentials
262
+ const creds = await startLoginFlow({
263
+ accountUrl: "https://account.nuucognition.com", // or "http://localhost:3000" for dev
264
+ authEnv: "prod",
265
+ callbackPort: 3847, // default
266
+ cliname: "mycli",
267
+ })
268
+
269
+ // Manual refresh
270
+ const refreshed = await refreshAuth(creds, { accountUrl, cliname: "mycli" })
271
+ ```
272
+
273
+ ### Auth Command Factories
274
+
275
+ Pre-built commander commands for login, logout, whoami.
276
+
277
+ ```typescript
278
+ import { createLoginCommand, createLogoutCommand, createWhoamiCommand } from "@nuucognition/cli-core"
279
+
280
+ const authConfig = {
281
+ cliname: "mycli", // used in help text and error messages
282
+ defaultEnv: "prod" as const, // default auth environment
283
+ devAvailable: true, // adds --dev flag to commands
284
+ prodAccountUrl?: string, // override prod account URL
285
+ devAccountUrl?: string, // override dev account URL
286
+ callbackPort?: number, // override callback port (default 3847)
287
+ }
288
+
289
+ program.addCommand(createLoginCommand(authConfig)) // mycli login [--dev] [--verbose]
290
+ program.addCommand(createLogoutCommand(authConfig)) // mycli logout [--dev]
291
+ program.addCommand(createWhoamiCommand(authConfig)) // mycli whoami [--dev]
292
+ ```
293
+
294
+ **whoami output:**
295
+ ```
296
+ logged-in: user@example.com
297
+ selected-org: NUU Cognition
298
+ ```
299
+
300
+ ### Output Helpers
301
+
302
+ Consistent terminal output formatting using picocolors. Respects `NO_COLOR`.
303
+
304
+ ```typescript
305
+ import { success, error, warn, info, dim, bold } from "@nuucognition/cli-core"
306
+
307
+ success("Done") // ✓ Done (stdout, green)
308
+ error("Failed") // ✗ Failed (stderr, red)
309
+ warn("Careful") // ! Careful (stderr, yellow)
310
+ info("Note") // i Note (stdout, blue)
311
+
312
+ dim("muted text") // returns dimmed string
313
+ bold("emphasis") // returns bold string
314
+ ```
315
+
316
+ ### Error Classes
317
+
318
+ Structured errors with exit codes, error codes, and actionable suggestions.
319
+
320
+ ```typescript
321
+ import { CliError, FeatureNotAvailableError, AuthRequiredError } from "@nuucognition/cli-core"
322
+
323
+ // General CLI error
324
+ throw new CliError("Config not found", 1, "CONFIG_NOT_FOUND", undefined, [
325
+ "Run `mycli init` to create a config file.",
326
+ ])
327
+
328
+ // Feature gating errors (thrown by requireFeature)
329
+ throw new FeatureNotAvailableError("publish", "exp", context, "mycli")
330
+ // ✗ Feature 'publish' requires experimental access.
331
+ // Run `mycli --enable publish` for one invocation
332
+ // or set `[features].publish = true` in `~/.nuucognition/.mycli/config.toml`.
333
+
334
+ throw new AuthRequiredError("publish", "mycli")
335
+ // ✗ Feature 'publish' requires authentication.
336
+ // Run `mycli login` or `nuu login`.
337
+ ```
338
+
339
+ ### Environment Variables
340
+
341
+ Utility for reading scoped env vars. Not used by runtime resolution — available for CLI-specific needs.
342
+
343
+ ```typescript
344
+ import { scopedEnvVar, scopedEnvVarTrue } from "@nuucognition/cli-core"
345
+
346
+ scopedEnvVar("mycli", "API_URL") // reads NUU_MYCLI_API_URL
347
+ scopedEnvVarTrue("mycli", "DEBUG") // reads NUU_MYCLI_DEBUG, returns boolean
348
+ ```
349
+
350
+ ## Build Configuration
351
+
352
+ CLIs using cli-core should bundle it via tsup `noExternal` when in the same monorepo:
353
+
354
+ ```typescript
355
+ // tsup.config.ts
356
+ import { defineConfig } from "tsup"
357
+
358
+ export default defineConfig({
359
+ entry: ["src/index.ts"],
360
+ format: ["esm"],
361
+ target: "node20",
362
+ clean: true,
363
+ dts: true,
364
+ define: {
365
+ "process.env.BUILD_MODE": '"prod"',
366
+ },
367
+ noExternal: ["@nuucognition/cli-core"],
368
+ })
369
+ ```
370
+
371
+ ## Types
372
+
373
+ All types are exported for use in consuming CLIs:
374
+
375
+ ```typescript
376
+ import type {
377
+ FeatureDefinition,
378
+ FeatureRegistry,
379
+ FeatureContext,
380
+ FeatureCheck,
381
+ FeatureTier, // "prod" | "exp" | "dev"
382
+ FeatureType, // "command" | "function" | "module"
383
+ RuntimeMode, // "prod" | "dev"
384
+ AuthEnvironment, // "prod" | "dev"
385
+ ResolvedRuntime,
386
+ ModeConfig,
387
+ ConfigOptions,
388
+ NuuCredentials,
389
+ LoginConfig,
390
+ AuthCommandFactoryConfig,
391
+ } from "@nuucognition/cli-core"
392
+ ```
@@ -0,0 +1,150 @@
1
+ import { Command } from 'commander';
2
+
3
+ type FeatureTier = "prod" | "exp" | "dev";
4
+ type FeatureType = "command" | "function" | "module";
5
+ type RuntimeMode = "prod" | "dev";
6
+ type AuthEnvironment = RuntimeMode;
7
+ interface FeatureDefinition {
8
+ id: string;
9
+ tier: FeatureTier;
10
+ type: FeatureType;
11
+ description: string;
12
+ requiresAuth: boolean;
13
+ since?: string;
14
+ graduated?: string;
15
+ previousTier?: "dev" | "exp";
16
+ deprecated?: boolean;
17
+ }
18
+ interface FeatureRegistry {
19
+ readonly features: readonly FeatureDefinition[];
20
+ }
21
+ interface FeatureContext {
22
+ mode: RuntimeMode;
23
+ experimental: boolean;
24
+ enabledFeatures: string[];
25
+ authenticated: boolean;
26
+ }
27
+ interface FeatureCheck {
28
+ available: boolean;
29
+ requiredTier: FeatureTier;
30
+ currentMode: RuntimeMode;
31
+ experimental: boolean;
32
+ requiresAuth: boolean;
33
+ authenticated: boolean;
34
+ reason?: string;
35
+ }
36
+ interface ModeConfig {
37
+ cliname: string;
38
+ }
39
+ interface ResolvedRuntime extends FeatureContext {
40
+ }
41
+ interface ConfigOptions {
42
+ cliname: string;
43
+ defaults?: Record<string, unknown>;
44
+ }
45
+ interface NuuCredentials {
46
+ sessionToken: string;
47
+ userId: string;
48
+ email: string;
49
+ expiresAt: string;
50
+ refreshToken?: string;
51
+ refreshExpiresAt?: string;
52
+ activeOrg?: {
53
+ id: string;
54
+ slug: string;
55
+ name: string;
56
+ };
57
+ }
58
+ interface LoginConfig {
59
+ accountUrl: string;
60
+ authEnv?: AuthEnvironment;
61
+ callbackPort?: number;
62
+ cliname?: string;
63
+ }
64
+ declare const NUU_ACCOUNT_URL_PROD = "https://account.nuucognition.com";
65
+ declare const NUU_ACCOUNT_URL_DEV = "http://localhost:3000";
66
+ interface AuthCommandFactoryConfig {
67
+ cliname?: string;
68
+ prodAccountUrl?: string;
69
+ devAccountUrl?: string;
70
+ defaultEnv?: AuthEnvironment;
71
+ callbackPort?: number;
72
+ devAvailable?: boolean;
73
+ }
74
+ declare const TIER_ALIASES: Record<string, FeatureTier>;
75
+ declare function normalizeTier(input: string): FeatureTier | undefined;
76
+ declare function isValidTier(input: string): boolean;
77
+ declare function normalizeRuntimeMode(input: string): RuntimeMode | undefined;
78
+
79
+ declare function scopedEnvVar(cliname: string, field: string): string | undefined;
80
+ declare function scopedEnvVarTrue(cliname: string, field: string): boolean;
81
+
82
+ declare function getConfigDir(cliname: string): string;
83
+ declare function getConfigPath(cliname: string): string;
84
+ declare function ensureConfigDir(cliname: string): Promise<string>;
85
+ declare function ensureConfigDirSync(cliname: string): string;
86
+ declare function resolveConfig(options: ConfigOptions): Promise<Record<string, unknown>>;
87
+ declare function resolveConfigSync(options: ConfigOptions): Record<string, unknown>;
88
+
89
+ declare function createFeatureRegistry(features: FeatureDefinition[]): FeatureRegistry;
90
+ declare function getFeature(registry: FeatureRegistry, id: string): FeatureDefinition | undefined;
91
+ declare function getFeaturesByTier(registry: FeatureRegistry, tier: FeatureTier): FeatureDefinition[];
92
+ declare function getFeaturesByType(registry: FeatureRegistry, type: FeatureType): FeatureDefinition[];
93
+ declare function getCommandFeatures(registry: FeatureRegistry): FeatureDefinition[];
94
+ declare function hasFeature(registry: FeatureRegistry, id: string): boolean;
95
+
96
+ declare function isTierEnabled(featureTier: "prod" | "exp" | "dev", mode: RuntimeMode): boolean;
97
+ declare function resolveRuntime(config: ModeConfig): Promise<ResolvedRuntime>;
98
+ declare function resolveRuntimeSync(config: ModeConfig): ResolvedRuntime;
99
+ declare function setMode(config: ModeConfig, mode: RuntimeMode): Promise<void>;
100
+ declare function resetMode(config: ModeConfig): Promise<void>;
101
+ declare function setExperimental(config: ModeConfig, enabled: boolean): Promise<void>;
102
+ declare function resetExperimental(config: ModeConfig): Promise<void>;
103
+ declare function setFeatureEnabled(config: ModeConfig, featureId: string, enabled: boolean): Promise<void>;
104
+ declare function resetFeatureEnabled(config: ModeConfig, featureId: string): Promise<void>;
105
+
106
+ declare function isFeatureEnabled(registry: FeatureRegistry, id: string, context: FeatureContext): boolean;
107
+ declare function requireFeature(registry: FeatureRegistry, id: string, context: FeatureContext, cliname: string): void;
108
+ declare function checkFeature(registry: FeatureRegistry, id: string, context: FeatureContext): FeatureCheck;
109
+
110
+ declare function setAuthVerbose(enabled: boolean): void;
111
+ declare function getNuuDir(): string;
112
+ declare function ensureNuuDir(): Promise<string>;
113
+ declare function loadAuth(env?: AuthEnvironment): Promise<NuuCredentials | null>;
114
+ declare function loadAuthSync(env?: AuthEnvironment): NuuCredentials | null;
115
+ declare function saveAuth(creds: NuuCredentials, env?: AuthEnvironment): Promise<void>;
116
+ declare function clearAuth(env?: AuthEnvironment): Promise<void>;
117
+ declare function hasAuth(env?: AuthEnvironment): boolean;
118
+ declare function startLoginFlow(config: LoginConfig): Promise<NuuCredentials>;
119
+ declare function refreshAuth(creds: NuuCredentials, config: LoginConfig): Promise<NuuCredentials>;
120
+ declare function getValidAuth(config: LoginConfig, env?: AuthEnvironment): Promise<NuuCredentials | null>;
121
+ declare function requireValidAuth(config: LoginConfig, env?: AuthEnvironment): Promise<NuuCredentials>;
122
+ declare function isAuthExpired(credentials: NuuCredentials): boolean;
123
+ declare function shouldRefreshAuth(credentials: NuuCredentials): boolean;
124
+
125
+ declare function createLoginCommand(config: AuthCommandFactoryConfig): Command;
126
+ declare function createLogoutCommand(config: AuthCommandFactoryConfig): Command;
127
+ declare function createWhoamiCommand(config: AuthCommandFactoryConfig): Command;
128
+
129
+ declare function success(message: string): void;
130
+ declare function error(message: string): void;
131
+ declare function warn(message: string): void;
132
+ declare function info(message: string): void;
133
+ declare function dim(message: string): string;
134
+ declare function bold(message: string): string;
135
+
136
+ declare class CliError extends Error {
137
+ readonly exitCode: number;
138
+ readonly code?: string;
139
+ readonly ref?: string;
140
+ readonly suggestions?: string[];
141
+ constructor(message: string, exitCode?: number, code?: string, ref?: string, suggestions?: string[]);
142
+ }
143
+ declare class FeatureNotAvailableError extends CliError {
144
+ constructor(featureId: string, requiredTier: FeatureTier, context: FeatureContext, cliname: string);
145
+ }
146
+ declare class AuthRequiredError extends CliError {
147
+ constructor(featureId: string, cliname: string);
148
+ }
149
+
150
+ export { type AuthCommandFactoryConfig, type AuthEnvironment, AuthRequiredError, CliError, type ConfigOptions, type FeatureCheck, type FeatureContext, type FeatureDefinition, FeatureNotAvailableError, type FeatureRegistry, type FeatureTier, type FeatureType, type LoginConfig, type ModeConfig, NUU_ACCOUNT_URL_DEV, NUU_ACCOUNT_URL_PROD, type NuuCredentials, type ResolvedRuntime, type RuntimeMode, TIER_ALIASES, bold, checkFeature, clearAuth, createFeatureRegistry, createLoginCommand, createLogoutCommand, createWhoamiCommand, dim, ensureConfigDir, ensureConfigDirSync, ensureNuuDir, error, getCommandFeatures, getConfigDir, getConfigPath, getFeature, getFeaturesByTier, getFeaturesByType, getNuuDir, getValidAuth, hasAuth, hasFeature, info, isAuthExpired, isFeatureEnabled, isTierEnabled, isValidTier, loadAuth, loadAuthSync, normalizeRuntimeMode, normalizeTier, refreshAuth, requireFeature, requireValidAuth, resetExperimental, resetFeatureEnabled, resetMode, resolveConfig, resolveConfigSync, resolveRuntime, resolveRuntimeSync, saveAuth, scopedEnvVar, scopedEnvVarTrue, setAuthVerbose, setExperimental, setFeatureEnabled, setMode, shouldRefreshAuth, startLoginFlow, success, warn };