Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Use the `/add-agent` skill — see `.claude/skills/add-agent/skill.md` for the f
| opencode | `opencode` | `upgrade` |
| cursor-agent | `cursor-agent` | `update` |
| copilot | `copilot` | `update` |
| pi | `pi` | `update pi` |

## Key files

Expand All @@ -34,4 +35,4 @@ Use the `/add-agent` skill — see `.claude/skills/add-agent/skill.md` for the f

## Versioning

`getVersion` extracts the first `/\d+\.\d+\.\d+/` match from `<tool> --version` stdout. If the output format doesn't match, set `versioned: false`.
`getVersion` extracts the first `/\d+\.\d+\.\d+/` match from `<tool> --version` stdout or stderr. If the output format doesn't match, set `versioned: false`.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Each tool runs concurrently. After updating, aiupdate shows the version change (
| [OpenCode](https://opencode.ai) | `opencode upgrade` |
| [Cursor Agent](https://cursor.com) | `cursor-agent update` |
| [GitHub Copilot CLI](https://githubnext.com/projects/copilot-cli) | `copilot update` |
| [pi](https://pi.dev/) | `pi update pi` |
| [skills](https://github.com/anthropics/skills) | `npx skills update -g -p -y` |

Tools not installed on your machine are silently skipped.
Expand Down
21 changes: 20 additions & 1 deletion src/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { mkdtemp, writeFile, chmod, rm } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { describe, it, expect } from 'vitest';
import { selectTools, run, formatTitle, AI_TOOLS, SKILLS_TASK } from './cli.js';
import { selectTools, run, formatTitle, getVersion, AI_TOOLS, SKILLS_TASK } from './cli.js';

const allInstalled: (cmd: string) => Promise<boolean> = async () => true;
const noneInstalled: (cmd: string) => Promise<boolean> = async () => false;
Expand All @@ -26,6 +29,22 @@ describe('formatTitle', () => {
});
});

describe('getVersion', () => {
it('extracts versions from stderr', async () => {
const dir = await mkdtemp(join(tmpdir(), 'aiupdate-'));
const command = join(dir, 'version-stderr');

try {
await writeFile(command, "#!/usr/bin/env node\nprocess.stderr.write('fake 9.8.7\\n');\n");
await chmod(command, 0o755);

await expect(getVersion(command)).resolves.toBe('9.8.7');
} finally {
await rm(dir, { recursive: true, force: true });
}
});
});

describe('selectTools', () => {
it('returns all AI tools + skills when no args', () => {
const { selectedAITools, includeSkills } = selectTools([]);
Expand Down
7 changes: 4 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const AI_TOOLS: Tool[] = [
{ name: 'opencode', command: 'opencode', args: ['upgrade'], versioned: true },
{ name: 'cursor-agent', command: 'cursor-agent', args: ['update'], versioned: true },
{ name: 'copilot', command: 'copilot', args: ['update'], versioned: true },
{ name: 'pi', command: 'pi', args: ['update', 'pi'], versioned: true },
];

export const SKILLS_TASK: Tool = {
Expand All @@ -38,10 +39,10 @@ async function defaultChecker(command: string): Promise<boolean> {
}
}

async function getVersion(command: string): Promise<string | null> {
export async function getVersion(command: string): Promise<string | null> {
try {
const { stdout } = await execa(command, ['--version']);
const match = stdout.match(/\d+\.\d+\.\d+/);
const { stdout, stderr } = await execa(command, ['--version']);
const match = `${stdout}\n${stderr}`.match(/\d+\.\d+\.\d+/);
return match ? match[0] : null;
} catch {
return null;
Expand Down