Skip to content

cli commands#99

Merged
Kitenite merged 7 commits intomainfrom
cli-commands
Nov 19, 2025
Merged

cli commands#99
Kitenite merged 7 commits intomainfrom
cli-commands

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Nov 18, 2025

Summary by CodeRabbit

  • New Features

    • New multi-command CLI with commands to list/get/create/delete environments, workspaces, agents, and changes.
    • Added agent stop and stop-all operations.
    • Terminal table UI for clearer output and consistent loading/success/error messaging.
    • Local CLI database initialization for persistent CLI state.
  • Chores

    • Switched build/start to a Bun-based workflow and added required runtime UI and devtools dependencies.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 18, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Replaces the Meow single-command CLI with a Commander-based multi-command CLI (env, workspace, agent, change), adds Ink-based command UIs and a Table component, introduces a DB singleton, normalizes import paths (remove .js), switches build/start to Bun, and updates dependencies.

Changes

Cohort / File(s) Summary
Build & Runtime
apps/cli/package.json, apps/cli/src/server.ts, package.json
Switched build to bun build and start to bun src/server.ts; added runtime deps (commander, ink-select-input, ink-table), adjusted bun-types devVersion, and added react-devtools-core to root deps. Server now spawns bun to run compiled CLI.
CLI Entry
apps/cli/src/cli.tsx
Replaced Meow with Commander; added hierarchical subcommands (env, workspace, agent, change), input validation, and per-command Ink rendering and exit handling. Exposes many command handler exports.
Command Modules
apps/cli/src/commands/*
apps/cli/src/commands/{agent,change,env,workspace}.tsx, apps/cli/src/commands/index.ts
New Ink-based command components for CRUD operations (list/get/create/delete) plus agent stop/stopAll; consolidated re-exports in index.ts. Components call orchestrators and support onComplete callbacks.
UI Component
apps/cli/src/components/Table.tsx
New Ink Table component that renders arrays of objects as vertical rows of key/value pairs; returns null for empty data.
Database Singleton
apps/cli/src/lib/db.ts
New getDb() that ensures ~/.superset exists, constructs db.json path, lazily creates and caches a LowdbAdapter instance.
Orchestrators — Imports
apps/cli/src/lib/orchestrators/*
.../change-orchestrator.ts, .../environment-orchestrator.ts, .../process-orchestrator.ts, .../workspace-orchestrator.ts, .../index.ts
Normalized module specifiers by removing .js extensions in imports/exports; minor formatting changes only, no behavior changes.
Storage Layer
apps/cli/src/lib/storage/*
.../adapter.ts, .../lowdb-adapter.ts, .../types.ts, .../index.ts
Removed .js extensions from internal imports/re-exports; updated type import paths. No API changes.
Tests — Import paths
apps/cli/src/lib/orchestrators/__tests__/*, apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts
Updated tests to use extensionless imports and minor assertion formatting; test logic unchanged.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Bun as Bun (runtime)
    participant CLI as Commander (cli.tsx)
    participant Cmd as Command Module
    participant Ink as Ink Renderer
    participant Orch as Orchestrator
    participant DB as Lowdb (getDb)

    User->>Bun: run CLI (bun src/cli.tsx ...)
    Bun->>CLI: deliver argv
    CLI->>Cmd: route subcommand (env|workspace|agent|change)
    Cmd->>Ink: render component (e.g., EnvList)
    Ink->>Orch: call <orchestrator>.list/get/create/...
    Orch->>DB: read/write db.json via getDb()
    DB-->>Orch: return result
    Orch-->>Ink: return data/result
    Ink-->>User: render table / message
    Ink->>CLI: onComplete -> exit process
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus review on: apps/cli/src/cli.tsx (command routing, argument validation, exit behavior), apps/cli/src/lib/db.ts (filesystem handling, caching), and new command components (apps/cli/src/commands/*) for consistent error/loading flows and orchestrator interactions.

Possibly related PRs

Poem

🐰 I hopped through lines of code today,

Commands now march in tidy array.
Bun bakes the build, Ink paints the view,
Tables and DBs — a carrot or two.
I twitch my nose and run away — hooray!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is a series of bullet points (add change types, orchestrator implementation, add commands, clean up imports, working commands, update bun build, save) that lack proper organization and detail, failing to follow the provided template with structured sections for Description, Related Issues, Type of Change, Testing, and Additional Notes. Rewrite the description following the template: provide a clear summary of the CLI architecture overhaul, link related issues, specify the type of change (Refactor/New Feature), describe testing approach, and add any additional context about the migration from Meow to Commander.
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'cli commands' is generic and vague, failing to convey meaningful information about the substantial changes in the changeset, which include a major CLI architecture overhaul from Meow to Commander, new command modules, and build system changes. Revise the title to be more specific and descriptive, such as 'Refactor CLI to use Commander-based architecture with command modules' or 'Migrate CLI from Meow to Commander and add modular command structure'.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c0ed08 and b41e66f.

📒 Files selected for processing (8)
  • apps/cli/src/cli.tsx (1 hunks)
  • apps/cli/src/commands/agent.tsx (1 hunks)
  • apps/cli/src/commands/change.tsx (1 hunks)
  • apps/cli/src/commands/env.tsx (1 hunks)
  • apps/cli/src/commands/index.ts (1 hunks)
  • apps/cli/src/commands/workspace.tsx (1 hunks)
  • apps/cli/src/components/Table.tsx (1 hunks)
  • apps/cli/src/lib/db.ts (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (7)
apps/cli/src/components/Table.tsx (1)

4-6: Consider improving type safety.

The use of Record<string, any>[] reduces type safety. Consider using a generic type parameter to maintain type information:

-interface TableProps {
-	data: Record<string, any>[];
+interface TableProps<T extends Record<string, unknown>> {
+	data: T[];
 }

Then update the component signature:

-export default function Table({ data }: TableProps) {
+export default function Table<T extends Record<string, unknown>>({ data }: TableProps<T>) {

This preserves type information while allowing flexible data structures.

apps/cli/src/lib/db.ts (1)

17-21: Consider using async filesystem operations.

Using synchronous existsSync and mkdirSync blocks the event loop during CLI initialization. While the impact is minimal for a CLI tool, async operations would be more idiomatic in a Node.js context.

Apply this diff to use async filesystem operations:

-import { mkdirSync, existsSync } from "node:fs";
+import { mkdir, access } from "node:fs/promises";
+import { constants } from "node:fs";

-export function getDb(): LowdbAdapter {
+export async function getDb(): Promise<LowdbAdapter> {
 	if (adapter) {
 		return adapter;
 	}

 	// Create ~/.superset directory if it doesn't exist
 	const supersetDir = join(homedir(), ".superset");
-	if (!existsSync(supersetDir)) {
-		mkdirSync(supersetDir, { recursive: true });
-	}
+	try {
+		await access(supersetDir, constants.F_OK);
+	} catch {
+		await mkdir(supersetDir, { recursive: true });
+	}

 	const dbPath = join(supersetDir, "db.json");
 	adapter = new LowdbAdapter(dbPath);

 	return adapter;
 }

Note: This would require updating all getDb() call sites to await getDb().

apps/cli/src/cli.tsx (1)

93-99: Consider extracting type validation logic.

The type validation pattern is repeated across WorkspaceType, ProcessType, and AgentType. Extracting this into a helper function would improve maintainability.

Add a helper function at the top of the file:

function validateEnum<T extends Record<string, string>>(
	value: string,
	enumObj: T,
	typeName: string
): value is T[keyof T] {
	if (!Object.values(enumObj).includes(value as T[keyof T])) {
		console.error(
			`Invalid ${typeName}: ${value}. Must be one of: ${Object.values(enumObj).join(", ")}`
		);
		process.exit(1);
	}
	return true;
}

Then simplify the validation calls:

// Workspace validation
validateEnum(type, WorkspaceType, "workspace type");

// Process validation
validateEnum(type, ProcessType, "process type");

// Agent validation (conditional)
if (options.agentType) {
	validateEnum(options.agentType, AgentType, "agent type");
	agentType = options.agentType as AgentType;
}

Also applies to: 148-154, 157-166

apps/cli/src/commands/workspace.tsx (1)

13-68: Consider using proper types instead of any.

The state variables use any types, reducing type safety. Consider importing and using the proper Workspace type from your type definitions.

Apply this pattern:

+import type { Workspace } from "../types/workspace";

 export function WorkspaceList({ environmentId, onComplete }: WorkspaceListProps) {
-	const [workspaces, setWorkspaces] = React.useState<any[]>([]);
+	const [workspaces, setWorkspaces] = React.useState<Workspace[]>([]);

Apply similar changes to WorkspaceGet and WorkspaceCreate components.

apps/cli/src/commands/change.tsx (1)

27-31: Consider extracting date formatting logic.

The date formatting pattern is duplicated. A helper function would improve maintainability.

Add a helper at the top of the file:

function formatChangeDate(change: any) {
	return {
		...change,
		createdAt: new Date(change.createdAt).toLocaleString(),
	};
}

Then use it in both components:

// In ChangeList
const formatted = chgs.map(formatChangeDate);

// In ChangeCreate
const formatted = formatChangeDate(chg);

Also applies to: 92-96

apps/cli/src/commands/env.tsx (2)

11-57: Replace any types with proper Environment type.

All state variables use any types. Using the proper Environment type would improve type safety and align with TypeScript best practices.

Apply this pattern across all four components:

+import type { Environment } from "../types/environment";

 export function EnvList({ onComplete }: EnvListProps) {
-	const [environments, setEnvironments] = React.useState<any[]>([]);
+	const [environments, setEnvironments] = React.useState<Environment[]>([]);

 export function EnvGet({ id, onComplete }: EnvGetProps) {
-	const [environment, setEnvironment] = React.useState<any | null>(null);
+	const [environment, setEnvironment] = React.useState<Environment | null>(null);

 export function EnvCreate({ onComplete }: EnvCreateProps) {
-	const [environment, setEnvironment] = React.useState<any | null>(null);
+	const [environment, setEnvironment] = React.useState<Environment | null>(null);

Also applies to: 64-101, 107-144, 151-195


1-195: Consider abstracting the common command component pattern.

The env, workspace, agent, and change command files share nearly identical structure for list/get/create/delete operations. Consider creating a higher-order component or custom hook to reduce duplication.

Example approach with a custom hook:

// hooks/useCommand.ts
function useCommand<T>(
	operation: () => Promise<T>,
	deps: any[]
) {
	const [data, setData] = React.useState<T | null>(null);
	const [error, setError] = React.useState<string | null>(null);
	const [loading, setLoading] = React.useState(true);

	React.useEffect(() => {
		const execute = async () => {
			try {
				const result = await operation();
				setData(result);
			} catch (err) {
				setError(err instanceof Error ? err.message : "Unknown error");
			} finally {
				setLoading(false);
			}
		};
		execute();
	}, deps);

	return { data, error, loading };
}

This would eliminate ~70% of the duplicated code across command files while maintaining the same behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89561af and 98023b1.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (25)
  • apps/cli/package.json (3 hunks)
  • apps/cli/src/cli.tsx (1 hunks)
  • apps/cli/src/commands/agent.tsx (1 hunks)
  • apps/cli/src/commands/change.tsx (1 hunks)
  • apps/cli/src/commands/env.tsx (1 hunks)
  • apps/cli/src/commands/index.ts (1 hunks)
  • apps/cli/src/commands/workspace.tsx (1 hunks)
  • apps/cli/src/components/Table.tsx (1 hunks)
  • apps/cli/src/lib/db.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/change-orchestrator.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/index.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/process-orchestrator.ts (1 hunks)
  • apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1 hunks)
  • apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (1 hunks)
  • apps/cli/src/lib/storage/adapter.ts (1 hunks)
  • apps/cli/src/lib/storage/index.ts (1 hunks)
  • apps/cli/src/lib/storage/lowdb-adapter.ts (1 hunks)
  • apps/cli/src/lib/storage/types.ts (1 hunks)
  • apps/cli/src/server.ts (1 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
apps/cli/src/commands/agent.tsx (4)
apps/cli/src/commands/index.ts (6)
  • AgentList (9-9)
  • AgentGet (10-10)
  • AgentCreate (11-11)
  • AgentStop (12-12)
  • AgentStopAll (13-13)
  • AgentDelete (14-14)
apps/cli/src/lib/db.ts (1)
  • getDb (12-27)
apps/cli/src/components/Table.tsx (1)
  • Table (8-28)
apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
  • stopAll (99-119)
apps/cli/src/cli.tsx (5)
apps/cli/src/commands/env.tsx (4)
  • EnvList (11-57)
  • EnvGet (64-101)
  • EnvCreate (107-144)
  • EnvDelete (151-195)
apps/cli/src/commands/index.ts (17)
  • EnvList (1-1)
  • EnvGet (1-1)
  • EnvCreate (1-1)
  • EnvDelete (1-1)
  • WorkspaceList (3-3)
  • WorkspaceGet (4-4)
  • WorkspaceCreate (5-5)
  • WorkspaceDelete (6-6)
  • AgentList (9-9)
  • AgentGet (10-10)
  • AgentCreate (11-11)
  • AgentStop (12-12)
  • AgentStopAll (13-13)
  • AgentDelete (14-14)
  • ChangeList (16-16)
  • ChangeCreate (16-16)
  • ChangeDelete (16-16)
apps/cli/src/commands/workspace.tsx (4)
  • WorkspaceList (13-68)
  • WorkspaceGet (75-112)
  • WorkspaceCreate (121-163)
  • WorkspaceDelete (170-212)
apps/cli/src/commands/agent.tsx (6)
  • AgentList (14-74)
  • AgentGet (81-127)
  • AgentCreate (136-188)
  • AgentStop (195-236)
  • AgentStopAll (242-278)
  • AgentDelete (285-327)
apps/cli/src/commands/change.tsx (3)
  • ChangeList (12-68)
  • ChangeCreate (76-124)
  • ChangeDelete (131-173)
apps/cli/src/commands/workspace.tsx (4)
apps/cli/src/commands/index.ts (4)
  • WorkspaceList (3-3)
  • WorkspaceGet (4-4)
  • WorkspaceCreate (5-5)
  • WorkspaceDelete (6-6)
apps/cli/src/lib/db.ts (1)
  • getDb (12-27)
apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
  • WorkspaceOrchestrator (14-115)
apps/cli/src/components/Table.tsx (1)
  • Table (8-28)
apps/cli/src/commands/change.tsx (2)
apps/cli/src/lib/db.ts (1)
  • getDb (12-27)
apps/cli/src/components/Table.tsx (1)
  • Table (8-28)
apps/cli/src/commands/env.tsx (3)
apps/cli/src/lib/db.ts (1)
  • getDb (12-27)
apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
  • EnvironmentOrchestrator (12-110)
apps/cli/src/components/Table.tsx (1)
  • Table (8-28)
🪛 GitHub Actions: CI
package.json

[warning] 1-1: Diagnostics indicate 182 errors, 185 warnings from lint checks; consider increasing max diagnostics or addressing root causes in codebase.

apps/cli/src/commands/agent.tsx

[warning] 15-15: lint/suspicious/noExplicitAny: Unexpected any. Consider providing a specific type for state.

apps/cli/src/cli.tsx

[warning] 4-4: lint/correctness/noUnusedImports: Unused import 'React'.


[warning] 29-33: Formatter would have printed changes (formatting suggestions) for code in this file.

apps/cli/src/commands/change.tsx

[warning] 13-13: lint/suspicious/noExplicitAny: Unexpected any. Consider providing a specific type for state.

apps/cli/package.json

[warning] 1-1: Diagnostics indicate 182 errors, 185 warnings from lint checks; consider increasing max diagnostics or addressing root causes in codebase.

🔇 Additional comments (23)
apps/cli/src/lib/storage/types.ts (1)

9-9: LGTM! Import path normalized to extensionless specifier.

The removal of the .js extension aligns with the project-wide migration to extensionless TypeScript module resolution.

apps/cli/package.json (3)

11-11: Bun-based bundling aligns with PR objectives.

The switch to Bun bundling is consistent with the CLI modernization described in the PR summary.


25-28: New dependencies align with Commander-based CLI architecture.

The addition of commander, ink-select-input, and ink-table supports the multi-command CLI structure described in the PR summary.


38-38: Verify intentional downgrade of bun-types.

The bun-types version was changed from ^1.3.2 to ^1.3.1, which is a downgrade. This could be intentional for compatibility reasons, but please confirm this was not an accidental change.

apps/cli/src/components/Table.tsx (1)

13-13: Non-null assertion is safe here.

The use of data[0]! is protected by the length check on line 9, making the non-null assertion safe.

apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)

9-11: LGTM! Import paths normalized to extensionless specifiers.

The removal of .js extensions aligns with the project-wide migration to extensionless TypeScript module resolution.

apps/cli/src/lib/storage/lowdb-adapter.ts (1)

5-11: LGTM! Import paths normalized to extensionless specifiers.

The removal of .js extensions from local module imports aligns with the project-wide migration to extensionless TypeScript module resolution.

apps/cli/src/lib/storage/adapter.ts (1)

1-1: LGTM! Import path normalized to extensionless specifier.

The removal of the .js extension aligns with the project-wide migration to extensionless TypeScript module resolution.

apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (1)

5-11: LGTM! Test imports normalized to extensionless specifiers.

The removal of .js extensions aligns with the project-wide migration to extensionless TypeScript module resolution.

apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)

7-8: LGTM: Consistent import path normalization.

The removal of .js extensions aligns with the project-wide migration to Bun-based module resolution.

apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (1)

5-10: LGTM: Test imports updated consistently.

Import paths normalized to extensionless specifiers, matching the broader module resolution changes across tests and orchestrators.

apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (1)

6-8: LGTM: Storage test imports normalized.

Import paths updated to extensionless specifiers, consistent with the storage layer module resolution changes.

apps/cli/src/server.ts (1)

13-13: Runtime switched to Bun.

The watch server now spawns the CLI using Bun instead of Node, aligning with the PR's migration to Bun-based bundling. Ensure that the build output in dist/cli.js is compatible with Bun's runtime and that Bun is available in all development environments.

apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)

5-6: LGTM: Import paths normalized.

Extensionless import specifiers applied consistently with other orchestrator modules.

apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts (1)

5-9: LGTM: Test imports updated.

Import paths normalized to extensionless specifiers, matching the pattern across all orchestrator tests.

apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)

5-6: LGTM: Imports normalized.

Extensionless import paths applied consistently with the change orchestrator's dependencies.

apps/cli/src/lib/storage/index.ts (1)

1-5: LGTM: Storage exports normalized.

All re-export paths updated to extensionless specifiers, maintaining the same public API while aligning with module resolution changes.

apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (1)

5-10: LGTM!

The import path normalization to extensionless module specifiers is consistent with the broader PR changes and TypeScript/Bun conventions.

apps/cli/src/lib/orchestrators/index.ts (1)

1-4: LGTM!

Export path normalization aligns with the extensionless module resolution pattern used throughout the PR.

apps/cli/src/commands/index.ts (1)

1-16: LGTM!

The centralized command export structure provides a clean public API surface for the CLI module system.

apps/cli/src/commands/agent.tsx (3)

1-7: LGTM!

The imports are well-organized and appropriate for the agent management CLI commands.


38-39: Consistent error handling looks good.

The error handling pattern is consistent across all components, properly checking instanceof Error before accessing the message property, with a fallback to "Unknown error". This provides a good user experience for CLI error messages.

Also applies to: 102-103, 163-164, 207-208, 254-255, 297-298


47-47: Callback dependency pattern is correct for one-time CLI execution.

The review comment raises a valid dependency array principle, but it doesn't apply here. All parent components pass onComplete={() => process.exit(0)} as inline functions (see cli.tsx lines 127, 135, 169-173, 183, 190, 198). These components execute once, call onComplete?.() in the finally block, which terminates the process. There's no subsequent re-rendering or effect re-runs. Including onComplete in the dependency array is the correct React pattern—it ensures the effect captures the dependency. This is idiomatic for CLI applications that run single operations and exit.

Likely an incorrect or invalid review comment.

Comment thread apps/cli/src/cli.tsx
import { render } from "ink";
import meow from "meow";
import { Command } from "commander";
import React from "react";
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.

⚠️ Potential issue | 🟡 Minor

Remove unused React import.

The React import is not needed as the JSX transform handles it automatically.

Apply this diff:

-import React from "react";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React from "react";
🧰 Tools
🪛 GitHub Actions: CI

[warning] 4-4: lint/correctness/noUnusedImports: Unused import 'React'.

🤖 Prompt for AI Agents
In apps/cli/src/cli.tsx at line 4, remove the unused import "import React from
\"react\";" since the project uses the automatic JSX transform; delete that
import line and ensure no other references to React remain in the file.

Comment thread apps/cli/src/cli.tsx
Comment thread apps/cli/src/commands/agent.tsx Outdated
Comment on lines +30 to +35
const formatted = processes.map((p) => ({
...p,
createdAt: new Date(p.createdAt).toLocaleString(),
updatedAt: new Date(p.updatedAt).toLocaleString(),
endedAt: p.endedAt ? new Date(p.endedAt).toLocaleString() : "—",
}));
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.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicate date formatting logic into a helper function.

The same date formatting logic is repeated across three components (lines 30-35, 94-99, 155-160), violating the DRY principle. Consider extracting this into a reusable helper function.

Create a new utility file apps/cli/src/lib/format-utils.ts:

export function formatProcessDates<T extends { createdAt: Date | string; updatedAt: Date | string; endedAt?: Date | string | null }>(
	process: T
): T & { createdAt: string; updatedAt: string; endedAt: string } {
	return {
		...process,
		createdAt: new Date(process.createdAt).toLocaleString(),
		updatedAt: new Date(process.updatedAt).toLocaleString(),
		endedAt: process.endedAt ? new Date(process.endedAt).toLocaleString() : "—",
	};
}

Then import and use it in each component:

+import { formatProcessDates } from "../lib/format-utils";

-				const formatted = processes.map((p) => ({
-					...p,
-					createdAt: new Date(p.createdAt).toLocaleString(),
-					updatedAt: new Date(p.updatedAt).toLocaleString(),
-					endedAt: p.endedAt ? new Date(p.endedAt).toLocaleString() : "—",
-				}));
+				const formatted = processes.map(formatProcessDates);

Also applies to: 94-99, 155-160

🤖 Prompt for AI Agents
In apps/cli/src/commands/agent.tsx around lines 30-35 (and similarly at 94-99
and 155-160), duplicate date-formatting logic is used to convert
createdAt/updatedAt/endedAt to locale strings; extract this into a reusable
helper by adding apps/cli/src/lib/format-utils.ts with a generic
formatProcessDates function that accepts a process object and returns the same
object with createdAt and updatedAt as toLocaleString() and endedAt as
toLocaleString() or "—", then import that function into this file (and the other
two locations) and replace the inline mapping with a call to formatProcessDates
for each process item.

Comment thread apps/cli/src/commands/agent.tsx Outdated
: "No agents/processes found";
return (
<Text dimColor>
{message}. Create one with: superset agent create &lt;workspace-id&gt; &lt;type&gt;
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.

⚠️ Potential issue | 🟡 Minor

Use JSX syntax instead of HTML entity.

The HTML entity &lt; should be replaced with the actual < character in JSX. React handles escaping automatically.

Apply this diff:

-				{message}. Create one with: superset agent create &lt;workspace-id&gt; &lt;type&gt;
+				{message}. Create one with: superset agent create <workspace-id> <type>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{message}. Create one with: superset agent create &lt;workspace-id&gt; &lt;type&gt;
{message}. Create one with: superset agent create <workspace-id> <type>
🤖 Prompt for AI Agents
In apps/cli/src/commands/agent.tsx around line 63, the help/message text
currently uses HTML entities "&lt;" and "&gt;"; replace them with the literal
characters "<" and ">" so the JSX renders "<workspace-id>" and "<type>"
correctly (React will escape them automatically). Update the string to: Create
one with: superset agent create <workspace-id> <type> (using the literal < and >
characters in the JSX).

Comment on lines +12 to +15
export function ChangeList({ workspaceId, onComplete }: ChangeListProps) {
const [changes, setChanges] = React.useState<any[]>([]);
const [error, setError] = React.useState<string | null>(null);
const [loading, setLoading] = React.useState(true);
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.

⚠️ Potential issue | 🟡 Minor

Replace any type with proper Change type.

The pipeline warning flags the use of any for state. Using the proper Change type would improve type safety.

Apply this diff:

+import type { Change } from "../types/change";

 export function ChangeList({ workspaceId, onComplete }: ChangeListProps) {
-	const [changes, setChanges] = React.useState<any[]>([]);
+	const [changes, setChanges] = React.useState<Change[]>([]);

Also update ChangeCreate (line 77) similarly:

-	const [change, setChange] = React.useState<any | null>(null);
+	const [change, setChange] = React.useState<Change | null>(null);

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: CI

[warning] 13-13: lint/suspicious/noExplicitAny: Unexpected any. Consider providing a specific type for state.

🤖 Prompt for AI Agents
In apps/cli/src/commands/change.tsx around lines 12 to 15, replace the useState
any[] with the proper Change[] type and change the error state to use the
correct error type; update the state declarations to use the imported or locally
defined Change type (e.g., React.useState<Change[]>([])) and
React.useState<string | null>(null) as needed, and apply the same replacement
for the ChangeCreate state at line 77 so no use of any remains in component
state.

Comment thread package.json
Comment on lines +42 to 44
"dependencies": {
"react-devtools-core": "^7.0.1"
}
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the necessity and placement of react-devtools-core.

Adding react-devtools-core as a production dependency at the root level is unusual. Typically, dev tools dependencies are:

  • Placed in specific workspace packages that use them
  • Added as devDependencies rather than dependencies

🏁 Script executed:

#!/bin/bash
# Search for usage of react-devtools-core in the codebase
rg -n "react-devtools-core" --type-add 'code:*.{ts,tsx,js,jsx}' --type code -C3

Length of output: 0


I need to perform broader verification to understand whether react-devtools-core is actually used in the codebase.


---

🏁 Script executed:

```shell
#!/bin/bash
# Broader search without type restrictions
echo "=== Searching for react-devtools-core in all files ==="
rg -n "react-devtools-core" -C3

echo ""
echo "=== Searching for react-devtools substring ==="
rg -n "react-devtools" -C3

echo ""
echo "=== Checking package.json for workspace/workspaces config ==="
cat package.json | head -60

Length of output: 7394


I need to verify if any workspace packages use the ink package (which declares react-devtools-core as a peerDependency) or if react-devtools-core is needed at all.


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for ink usage in workspace packages
echo "=== Searching for ink imports/usage ==="
rg -n "from.*ink|import.*ink|require.*ink" --type-add 'code:*.{ts,tsx,js,jsx}' --type code

echo ""
echo "=== Checking workspace package.json files for ink dependency ==="
find . -path "*/node_modules" -prune -o -path "*/.git" -prune -o -type f -name "package.json" -print | xargs grep -l "ink" 2>/dev/null

echo ""
echo "=== Checking if any package.json has react-devtools-core ==="
find . -path "*/node_modules" -prune -o -path "*/.git" -prune -o -type f -name "package.json" -print | xargs grep -l "react-devtools-core" 2>/dev/null

Length of output: 1764


Let me verify the workspace package.json files to confirm the final placement issue:


---

🏁 Script executed:

```shell
#!/bin/bash
# Check workspace package.json files
echo "=== apps/cli/package.json ==="
cat apps/cli/package.json | grep -A 20 -B 5 "ink\|react-devtools"

echo ""
echo "=== apps/desktop/package.json ==="
cat apps/desktop/package.json | grep -A 10 -B 5 "ink\|react-devtools"

Length of output: 1437


Move react-devtools-core to apps/cli/package.json as a devDependency.

The dependency is currently placed at the root level in production dependencies to satisfy an optional peerDependency of ink (which apps/cli uses). However, this placement is incorrect:

  • react-devtools-core should reside in apps/cli/package.json where ink@^6.5.0 is actually declared
  • It should be a devDependency (React debugging is a development tool)
  • The version mismatch should be resolved: root declares ^7.0.1 while ink expects ^6.1.2

Remove the entry from root package.json and add it to apps/cli/package.json devDependencies with a compatible version.

🤖 Prompt for AI Agents
In package.json around lines 42 to 44, the production dependency
"react-devtools-core": "^7.0.1" is misplaced and version-mismatched for the CLI:
remove this entry from the root dependencies and add "react-devtools-core" as a
devDependency in apps/cli/package.json (where ink@^6.5.0 is declared) using a
version compatible with ink (e.g. ^6.1.2); ensure you run install/update
lockfile after the change so the workspace lockfile remains consistent.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (1)

56-60: Async .rejects assertions must be awaited in all tests

The test file has multiple assertions using .rejects without await. According to bun:test documentation, these assertions must be awaited or they won't execute and tests can pass even if errors aren't thrown.

Affected locations:

  • Lines 56–60 ("get" test): expect(orchestrator.get("non-existent")).rejects.toThrow(...)
  • Lines 94–96 ("update" test): expect(orchestrator.update("non-existent", {})).rejects.toThrow()
  • Lines 137, 147, 159, 163–164 ("delete" tests): multiple .rejects.toThrow() calls

Add await before each expect() call:

- expect(orchestrator.get("non-existent")).rejects.toThrow(
+ await expect(orchestrator.get("non-existent")).rejects.toThrow(
♻️ Duplicate comments (1)
apps/cli/src/cli.tsx (1)

3-25: Remove unused React import to satisfy lint and keep imports minimal.

React is not referenced in this file (JSX is used only via Ink components and the modern JSX transform), so the import at Line 4 is unused and flagged by lint. You can safely drop it.

-import React from "react";

Please re-run your linter/CI after removing this to confirm there are no JSX runtime configuration issues.

🧹 Nitpick comments (1)
apps/cli/src/cli.tsx (1)

27-35: Consider deriving CLI version from package.json to avoid drift.

Hard‑coding "0.1.0" in .version() can get out of sync with the actual package version. Consider reading the version from your root/package metadata instead so the CLI stays consistent across releases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98023b1 and 4c0ed08.

📒 Files selected for processing (9)
  • apps/cli/src/cli.tsx (1 hunks)
  • apps/cli/src/commands/agent.tsx (1 hunks)
  • apps/cli/src/commands/change.tsx (1 hunks)
  • apps/cli/src/commands/env.tsx (1 hunks)
  • apps/cli/src/commands/workspace.tsx (1 hunks)
  • apps/cli/src/components/Table.tsx (1 hunks)
  • apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (2 hunks)
  • apps/cli/src/lib/orchestrators/change-orchestrator.ts (2 hunks)
  • apps/cli/src/lib/orchestrators/process-orchestrator.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/cli/src/commands/workspace.tsx
  • apps/cli/src/lib/orchestrators/process-orchestrator.ts
  • apps/cli/src/commands/change.tsx
  • apps/cli/src/commands/agent.tsx
  • apps/cli/src/commands/env.tsx
  • apps/cli/src/components/Table.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/cli/src/cli.tsx (5)
apps/cli/test.js (2)
  • render (8-8)
  • render (14-14)
apps/cli/src/commands/env.tsx (4)
  • EnvList (11-61)
  • EnvGet (68-105)
  • EnvCreate (111-148)
  • EnvDelete (155-200)
apps/cli/src/commands/workspace.tsx (4)
  • WorkspaceList (13-72)
  • WorkspaceGet (79-116)
  • WorkspaceCreate (125-167)
  • WorkspaceDelete (174-218)
apps/cli/src/commands/agent.tsx (6)
  • AgentList (14-77)
  • AgentGet (84-132)
  • AgentCreate (141-204)
  • AgentStop (211-252)
  • AgentStopAll (258-296)
  • AgentDelete (303-347)
apps/cli/src/commands/change.tsx (3)
  • ChangeList (12-68)
  • ChangeCreate (76-128)
  • ChangeDelete (135-177)
🔇 Additional comments (5)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (1)

5-10: Import path normalization to extensionless modules looks consistent

The updated imports without .js extensions are consistent with the rest of the codebase and should work well with modern TS/bundler resolution; no issues from this change alone.

apps/cli/src/cli.tsx (2)

74-129: Workspace command wiring and type validation look solid.

The workspace subcommands (list, get, create, delete) are wired cleanly into the Ink components, and the runtime validation of WorkspaceType in create with clear error messaging + process.exit(1) is a good pattern that matches the help text.


224-263: Change commands and entrypoint wiring look consistent and clear.

The change subcommands (list, create, delete) follow the same pattern as the other groups and route cleanly into the Ink components, and program.parse() at the end ensures the CLI is invoked on execution. No issues from a wiring/structure perspective.

apps/cli/src/lib/orchestrators/change-orchestrator.ts (2)

40-45: LGTM: Improved readability with explicit field destructuring.

The reformatted destructuring makes each immutable field (id, workspaceId, createdAt) more visible and explicit. The logic remains unchanged—these fields are still correctly filtered out to prevent desynchronization during updates.


5-6: No issues found — the build configuration already supports extensionless imports.

The TypeScript configuration chain is properly set up: apps/cli/tsconfig.json extends @superset/typescript/react-library.json, which extends packages/typescript-config/base.json. The base configuration includes "moduleResolution": "bundler" and references Bun TypeScript documentation, confirming this is the intended Bun setup. Extensionless imports are already present in the file and working correctly. The PR changes are consistent with the existing, properly configured build system.

Comment thread apps/cli/src/cli.tsx
Comment on lines +36 to +69
// Environment commands
const env = program.command("env").description("Manage environments");

env
.command("list")
.description("List all environments")
.action(() => {
render(<EnvList onComplete={() => process.exit(0)} />);
});

env
.command("get")
.description("Get environment by ID")
.argument("<id>", "Environment ID")
.action((id: string) => {
render(<EnvGet id={id} onComplete={() => process.exit(0)} />);
});

env
.command("create")
.description("Create a new environment")
.action(() => {
render(<EnvCreate onComplete={() => process.exit(0)} />);
});

env
.command("delete")
.description(
"Delete an environment (cascades to workspaces, processes, changes)",
)
.argument("<id>", "Environment ID")
.action((id: string) => {
render(<EnvDelete id={id} onComplete={() => process.exit(0)} />);
});
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.

⚠️ Potential issue | 🟡 Minor

Exit code is always 0 even when commands encounter runtime errors.

All env commands render components with onComplete={() => process.exit(0)}, and the corresponding components (EnvList, EnvGet, etc. in apps/cli/src/commands/env.tsx) always call onComplete in a finally block, regardless of whether an error occurred. That means the CLI will typically exit with code 0 even if the orchestrator throws and the user sees an Error: message.

For automation/scripts, it’s usually preferable to return a non‑zero exit code when the command fails. Consider threading a success/error flag through onComplete (or similar) so the CLI can process.exit(1) on failures.

🤖 Prompt for AI Agents
In apps/cli/src/cli.tsx around lines 36 to 69, the env subcommands always call
onComplete with process.exit(0) which forces a zero exit code even when the
underlying component encountered an error; change the pattern so onComplete
accepts a boolean or numeric status (e.g., onComplete(success: boolean)) and
propagate success/failure from the Env* components (they should call
onComplete(false) on error and onComplete(true) on success), then update each
CLI command to call process.exit(0) on success and process.exit(1) on failure
based on that status.

Comment thread apps/cli/src/cli.tsx
Comment on lines +157 to +199
agent
.command("create")
.description("Create a new agent/process")
.argument("<workspaceId>", "Workspace ID")
.argument("<type>", `Process type (${Object.values(ProcessType).join(", ")})`)
.option(
"--agent-type <agentType>",
`Agent type (${Object.values(AgentType).join(", ")}) - required if type is 'agent'`,
)
.action(
(workspaceId: string, type: string, options: { agentType?: string }) => {
// Validate process type
if (!Object.values(ProcessType).includes(type as ProcessType)) {
console.error(
`Invalid process type: ${type}. Must be one of: ${Object.values(ProcessType).join(", ")}`,
);
process.exit(1);
}

// Validate agent type if provided
let agentType: AgentType | undefined;
if (options.agentType) {
if (
!Object.values(AgentType).includes(options.agentType as AgentType)
) {
console.error(
`Invalid agent type: ${options.agentType}. Must be one of: ${Object.values(AgentType).join(", ")}`,
);
process.exit(1);
}
agentType = options.agentType as AgentType;
}

render(
<AgentCreate
workspaceId={workspaceId}
type={type as ProcessType}
agentType={agentType}
onComplete={() => process.exit(0)}
/>,
);
},
},
);
);
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.

⚠️ Potential issue | 🟠 Major

Enforce --agent-type when creating an agent process.

The help text says --agent-type is “required if type is 'agent', but the implementation only validates agentTypewhen it is provided and otherwise proceeds withagentTypeasundefined. This can lead to confusing behavior or downstream errors in ProcessOrchestrator.create`.

You can enforce the requirement directly in this action handler, e.g.:

   // Validate process type
   if (!Object.values(ProcessType).includes(type as ProcessType)) {
     console.error(
       `Invalid process type: ${type}. Must be one of: ${Object.values(ProcessType).join(", ")}`,
     );
     process.exit(1);
   }

+  // If creating an agent process, require an explicit agent type
+  if (type === ProcessType.AGENT && !options.agentType) {
+    console.error(
+      "Missing required option: --agent-type <agentType> when type is 'agent'.",
+    );
+    process.exit(1);
+  }
+
   // Validate agent type if provided
   let agentType: AgentType | undefined;

(Adjust the ProcessType.AGENT member name to match your actual enum.)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
agent
.command("create")
.description("Create a new agent/process")
.argument("<workspaceId>", "Workspace ID")
.argument("<type>", `Process type (${Object.values(ProcessType).join(", ")})`)
.option(
"--agent-type <agentType>",
`Agent type (${Object.values(AgentType).join(", ")}) - required if type is 'agent'`,
)
.action(
(workspaceId: string, type: string, options: { agentType?: string }) => {
// Validate process type
if (!Object.values(ProcessType).includes(type as ProcessType)) {
console.error(
`Invalid process type: ${type}. Must be one of: ${Object.values(ProcessType).join(", ")}`,
);
process.exit(1);
}
// Validate agent type if provided
let agentType: AgentType | undefined;
if (options.agentType) {
if (
!Object.values(AgentType).includes(options.agentType as AgentType)
) {
console.error(
`Invalid agent type: ${options.agentType}. Must be one of: ${Object.values(AgentType).join(", ")}`,
);
process.exit(1);
}
agentType = options.agentType as AgentType;
}
render(
<AgentCreate
workspaceId={workspaceId}
type={type as ProcessType}
agentType={agentType}
onComplete={() => process.exit(0)}
/>,
);
},
},
);
);
agent
.command("create")
.description("Create a new agent/process")
.argument("<workspaceId>", "Workspace ID")
.argument("<type>", `Process type (${Object.values(ProcessType).join(", ")})`)
.option(
"--agent-type <agentType>",
`Agent type (${Object.values(AgentType).join(", ")}) - required if type is 'agent'`,
)
.action(
(workspaceId: string, type: string, options: { agentType?: string }) => {
// Validate process type
if (!Object.values(ProcessType).includes(type as ProcessType)) {
console.error(
`Invalid process type: ${type}. Must be one of: ${Object.values(ProcessType).join(", ")}`,
);
process.exit(1);
}
// If creating an agent process, require an explicit agent type
if (type === ProcessType.AGENT && !options.agentType) {
console.error(
"Missing required option: --agent-type <agentType> when type is 'agent'.",
);
process.exit(1);
}
// Validate agent type if provided
let agentType: AgentType | undefined;
if (options.agentType) {
if (
!Object.values(AgentType).includes(options.agentType as AgentType)
) {
console.error(
`Invalid agent type: ${options.agentType}. Must be one of: ${Object.values(AgentType).join(", ")}`,
);
process.exit(1);
}
agentType = options.agentType as AgentType;
}
render(
<AgentCreate
workspaceId={workspaceId}
type={type as ProcessType}
agentType={agentType}
onComplete={() => process.exit(0)}
/>,
);
},
);
🤖 Prompt for AI Agents
In apps/cli/src/cli.tsx around lines 157 to 199, the action handler does not
enforce that --agent-type is provided when the selected process type is the
agent variant; update the handler to check if type === ProcessType.AGENT (or the
correct enum member) and if so, ensure options.agentType is present — if
missing, print a clear error listing valid AgentType values and exit(1); keep
the existing validation for provided agentType and then set agentType
accordingly before calling render.

@Kitenite Kitenite merged commit 12bb571 into main Nov 19, 2025
0 of 5 checks passed
@Kitenite Kitenite deleted the cli-commands branch November 19, 2025 00:42
@coderabbitai coderabbitai Bot mentioned this pull request Nov 20, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Nov 21, 2025
5 tasks
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.

1 participant