aiupdate 0.1.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 +51 -0
- package/dist/cli.js +92 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# aiupdate
|
|
2
|
+
|
|
3
|
+
Update all your AI agent CLIs and skills concurrently.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx aiupdate # update everything (tools + skills)
|
|
9
|
+
npx aiupdate claude # update one tool (skills skipped)
|
|
10
|
+
npx aiupdate claude codex opencode # update specific tools (skills skipped)
|
|
11
|
+
npx aiupdate skills # update skills only
|
|
12
|
+
npx aiupdate claude skills # update one tool + skills
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install globally:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g aiupdate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each tool runs concurrently. After updating, aiupdate shows the version change (or "up to date") for each tool.
|
|
22
|
+
|
|
23
|
+
## What it updates
|
|
24
|
+
|
|
25
|
+
| Tool | Update command |
|
|
26
|
+
|------|---------------|
|
|
27
|
+
| [Claude Code](https://claude.ai/code) | `claude update` |
|
|
28
|
+
| [OpenAI Codex](https://github.com/openai/codex) | `codex update` |
|
|
29
|
+
| [OpenCode](https://opencode.ai) | `opencode upgrade` |
|
|
30
|
+
| [Cursor Agent](https://cursor.com) | `cursor-agent update` |
|
|
31
|
+
| [GitHub Copilot CLI](https://githubnext.com/projects/copilot-cli) | `copilot update` |
|
|
32
|
+
| [skills](https://github.com/anthropics/skills) | `npx skills update -g -p -y` |
|
|
33
|
+
|
|
34
|
+
Tools not installed on your machine are silently skipped.
|
|
35
|
+
|
|
36
|
+
## Contributing
|
|
37
|
+
|
|
38
|
+
To add a new tool, use the `/add-agent` skill in Claude Code — it walks through verification, adds the entry to `AI_TOOLS`, builds, and commits.
|
|
39
|
+
|
|
40
|
+
If adding manually, open `src/cli.ts` and append one line to `AI_TOOLS`:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
{ name: 'mytool', command: 'mytool', args: ['update'], versioned: true },
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then build and test:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm run build
|
|
50
|
+
aiupdate mytool
|
|
51
|
+
```
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { realpathSync } from "fs";
|
|
6
|
+
import { Listr } from "listr2";
|
|
7
|
+
import { execa } from "execa";
|
|
8
|
+
var AI_TOOLS = [
|
|
9
|
+
{ name: "claude", command: "claude", args: ["update"], versioned: true },
|
|
10
|
+
{ name: "codex", command: "codex", args: ["update"], versioned: true },
|
|
11
|
+
{ name: "opencode", command: "opencode", args: ["upgrade"], versioned: true },
|
|
12
|
+
{ name: "cursor-agent", command: "cursor-agent", args: ["update"], versioned: true },
|
|
13
|
+
{ name: "copilot", command: "copilot", args: ["update"], versioned: true }
|
|
14
|
+
];
|
|
15
|
+
var SKILLS_TASK = {
|
|
16
|
+
name: "skills",
|
|
17
|
+
command: "npx",
|
|
18
|
+
args: ["--yes", "skills", "update", "-g", "-p", "-y"]
|
|
19
|
+
};
|
|
20
|
+
async function defaultChecker(command) {
|
|
21
|
+
try {
|
|
22
|
+
await execa("which", [command]);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function getVersion(command) {
|
|
29
|
+
try {
|
|
30
|
+
const { stdout } = await execa(command, ["--version"]);
|
|
31
|
+
const match = stdout.match(/\d+\.\d+\.\d+/);
|
|
32
|
+
return match ? match[0] : null;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function formatTitle(name, before, after) {
|
|
38
|
+
if (!before) return name;
|
|
39
|
+
return after && after !== before ? `${name} ${before} \u2192 ${after}` : `${name} up to date (${after ?? before})`;
|
|
40
|
+
}
|
|
41
|
+
function selectTools(targetArgs) {
|
|
42
|
+
const skillsOnly = targetArgs.length === 1 && targetArgs[0] === "skills";
|
|
43
|
+
const includeSkills = targetArgs.length === 0 || targetArgs.includes("skills");
|
|
44
|
+
const aiToolTargets = targetArgs.filter((a) => a !== "skills");
|
|
45
|
+
const selectedAITools = skillsOnly ? [] : aiToolTargets.length > 0 ? AI_TOOLS.filter((t) => aiToolTargets.includes(t.name)) : AI_TOOLS;
|
|
46
|
+
return { selectedAITools, includeSkills };
|
|
47
|
+
}
|
|
48
|
+
async function run(targetArgs, executor = async (cmd, args) => {
|
|
49
|
+
await execa(cmd, args);
|
|
50
|
+
}, checker = defaultChecker, versioner = getVersion) {
|
|
51
|
+
const { selectedAITools, includeSkills } = selectTools(targetArgs);
|
|
52
|
+
const checks = await Promise.all(
|
|
53
|
+
selectedAITools.map(async (t) => ({ tool: t, installed: await checker(t.command) }))
|
|
54
|
+
);
|
|
55
|
+
const availableTools = checks.filter((c) => c.installed).map((c) => c.tool);
|
|
56
|
+
const allTools = includeSkills ? [...availableTools, SKILLS_TASK] : availableTools;
|
|
57
|
+
let hasFailures = false;
|
|
58
|
+
const runner = new Listr(
|
|
59
|
+
allTools.map((tool) => ({
|
|
60
|
+
title: tool.name,
|
|
61
|
+
task: async (_, task) => {
|
|
62
|
+
const before = tool.versioned ? await versioner(tool.command) : null;
|
|
63
|
+
try {
|
|
64
|
+
await executor(tool.command, tool.args);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
hasFailures = true;
|
|
67
|
+
const e = err;
|
|
68
|
+
throw new Error(e.stderr || e.shortMessage || String(e));
|
|
69
|
+
}
|
|
70
|
+
if (before) {
|
|
71
|
+
const after = await versioner(tool.command);
|
|
72
|
+
task.title = formatTitle(tool.name, before, after);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})),
|
|
76
|
+
{ concurrent: true, exitOnError: false }
|
|
77
|
+
);
|
|
78
|
+
await runner.run().catch(() => {
|
|
79
|
+
});
|
|
80
|
+
return !hasFailures;
|
|
81
|
+
}
|
|
82
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
83
|
+
const success = await run(process.argv.slice(2));
|
|
84
|
+
if (!success) process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
AI_TOOLS,
|
|
88
|
+
SKILLS_TASK,
|
|
89
|
+
formatTitle,
|
|
90
|
+
run,
|
|
91
|
+
selectTools
|
|
92
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aiupdate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Update all your AI agent CLIs and skills concurrently",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aiupdate": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/cli.ts --format esm --target node18 --clean && chmod +x dist/cli.js",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"dev": "tsx src/cli.ts",
|
|
16
|
+
"test": "vitest run"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"execa": "^9.0.0",
|
|
20
|
+
"listr2": "^10.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"tsx": "^4.0.0",
|
|
25
|
+
"typescript": "^5.0.0",
|
|
26
|
+
"vitest": "^4.1.7"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"ai",
|
|
33
|
+
"cli",
|
|
34
|
+
"update",
|
|
35
|
+
"claude",
|
|
36
|
+
"codex",
|
|
37
|
+
"opencode"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|