Skip to content
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ clix ios-setup

**Note:** Some steps require manual action in Xcode and Apple Developer Portal.

### `clix update`

Check for and install CLI updates.

```bash
# Check for updates and install with confirmation
clix update

# Preview update without executing
clix update --dry-run

# Skip confirmation prompt
clix update --force
```

**Options:**

- `--dry-run` - Preview update without executing
- `--force` - Skip confirmation prompt

### Interactive Skills (Chat Mode Only)

The following skills require step-by-step guidance and are only available in chat mode. Run `clix` to start interactive chat, then use `/<skill>` commands.
Expand Down
36 changes: 31 additions & 5 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,32 @@ clix debug "Push notifications not working on iOS"
3. Provides root cause analysis
4. Gives recommended fixes with file paths

### `clix update`

Check for and install CLI updates.

```bash
# Check for updates and install with confirmation
clix update

# Preview update without executing
clix update --dry-run

# Skip confirmation prompt
clix update --force
```

**Options:**
- `--dry-run` - Preview update without executing
- `--force` - Skip confirmation prompt

**What it does:**
1. Checks npm registry for latest version
2. Detects installation method (npm, yarn, pnpm, bun, homebrew, binary)
3. Shows update plan with current and latest version
4. Prompts for confirmation (unless --force)
5. Executes appropriate update command

### `clix install-mcp [agent]`

Install Clix MCP Server for enhanced AI assistance.
Expand Down Expand Up @@ -659,16 +685,16 @@ Restart Claude to activate.

**Aliases:** `/upgrade`

**What it does:** Checks for available updates to Clix CLI and provides update instructions.
**What it does:** In interactive mode, displays CLI update instructions. For actual updates, use `clix update` from the terminal with options like `--dry-run` and `--force`.

**Example:**
```
> /update
Checking for updates...
✓ New version available: 1.2.0 (current: 1.1.0)
To update Clix CLI, run `clix update` from the terminal.

To update:
npm update -g @clix-so/clix-cli
Available options:
--dry-run Preview update without executing
--force Skip confirmation prompt
```

#### `/exit` - Exit Chat
Expand Down
5 changes: 4 additions & 1 deletion src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ async function main() {

case 'update':
case 'upgrade':
await updateCommand();
await updateCommand({
dryRun: cli.flags.dryRun,
force: cli.flags.force,
});
break;

case 'uninstall':
Expand Down
136 changes: 116 additions & 20 deletions src/commands/update.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,141 @@
/**
* CLI update command - checks for and displays update information.
* CLI update command - checks for and executes updates.
*
* @module commands/update
*/

import readline from 'node:readline/promises';
import {
checkForUpdate,
detectInstallationMethod,
getUpdateCommand,
executeUpdate,
planUpdate,
type UpdateOptions,
type UpdatePlan,
} from '../lib/services/update-service';

/**
* Check for updates and display information.
* Display the update plan to the user.
*/
export async function updateCommand(): Promise<void> {
function displayUpdatePlan(plan: UpdatePlan): void {
console.log('\n=== Update Plan ===\n');

console.log(`Installation method: ${plan.installMethod}`);
console.log(`Current version: ${plan.currentVersion}`);
console.log(`Latest version: ${plan.latestVersion}`);

if (!plan.hasUpdate) {
console.log('\nYou are already on the latest version.');
return;
}

console.log(`\nUpdate command: ${plan.updateCommand}`);

if (!plan.canAutoUpdate) {
console.log('\nNote: Auto-update is not supported for this installation method.');
console.log(`Please run manually:\n ${plan.updateCommand}`);
}

console.log('');
}

/**
* Display the update result.
*/
function displayUpdateResult(
result: Awaited<ReturnType<typeof executeUpdate>>,
plan: UpdatePlan,
): void {
console.log('\n=== Update Result ===\n');

if (result.success) {
console.log(`✓ ${result.message}`);
} else {
console.log(`✗ ${result.message}`);
if (!plan.canAutoUpdate) {
console.log(`\nTo update manually, run:\n ${plan.updateCommand}`);
}
}
console.log('');
}

/**
* Prompt user for confirmation.
*/
async function promptConfirmation(message: string): Promise<boolean> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

try {
const answer = await rl.question(`${message} [y/N] `);
return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
} finally {
rl.close();
}
}

/**
* Execute the update command.
*/
export async function updateCommand(
options: UpdateOptions = { dryRun: false, force: false },
): Promise<void> {
console.log('Checking for updates...\n');

try {
const [updateResult, installInfo] = await Promise.all([
checkForUpdate(5000), // Use a longer timeout for CLI
detectInstallationMethod(),
]);
// Plan the update (checks for updates and detects installation method)
const plan = await planUpdate();

if (updateResult.error) {
console.error(`Failed to check for updates: ${updateResult.error}`);
// Check for update-check errors (network/registry failures)
if (plan.error) {
console.error(`Failed to check for updates: ${plan.error}`);
process.exit(1);
}

if (!updateResult.hasUpdate) {
console.log(`You're on the latest version (${updateResult.currentVersion})`);
// No update available
if (!plan.hasUpdate) {
console.log(`You're on the latest version (${plan.currentVersion})`);
return;
}

// Display the plan
displayUpdatePlan(plan);

// Can't auto-update - stop here
if (!plan.canAutoUpdate) {
return;
}

const updateCmd = getUpdateCommand(installInfo);
console.log(
`Update available: ${updateResult.currentVersion} -> ${updateResult.latestVersion}`,
);
console.log(`\nTo update, run:\n ${updateCmd}\n`);
// Dry run - stop here
if (options.dryRun) {
console.log('[DRY RUN] No changes were made.\n');
return;
}

// Confirm with user
if (!options.force) {
const confirmed = await promptConfirmation(
`Update from ${plan.currentVersion} to ${plan.latestVersion}?`,
);
if (!confirmed) {
console.log('\nUpdate cancelled.\n');
return;
}
}

// Execute the update
console.log('\nUpdating...\n');
const result = await executeUpdate(plan, options);

// Display the result
displayUpdateResult(result, plan);

if (!result.success) {
process.exit(1);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.error(`Failed to check for updates: ${errorMessage}`);
console.error(`\nFailed to update: ${errorMessage}\n`);
process.exit(1);
}
}
17 changes: 11 additions & 6 deletions src/lib/commands/update.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Update command - checks for and displays update information.
* Update command - checks for and executes updates.
*
* @module commands/update
*/
Expand All @@ -8,12 +8,12 @@ import type { Command } from './types';

/**
* Update command implementation.
* Checks for available updates and displays update instructions.
* Directs user to CLI for actual update execution.
*/
export const updateCommand: Command = {
type: 'local',
name: 'update',
description: 'Check for available updates',
description: 'Check for and apply available updates',
isEnabled: true,
isHidden: false,
aliases: ['upgrade'],
Expand All @@ -23,8 +23,13 @@ export const updateCommand: Command = {
},

async call() {
// The actual update check is handled by the command handler in useCommandHandler
// This command signals the intent to check for updates
return { success: true, message: 'Checking for updates...' };
return {
success: true,
message:
'To update Clix CLI, run `clix update` from the terminal.\n\n' +
'Available options:\n' +
' --dry-run Preview update without executing\n' +
' --force Skip confirmation prompt',
};
},
};
Loading