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.
Files changed (3) hide show
  1. package/README.md +51 -0
  2. package/dist/cli.js +92 -0
  3. 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
+ }