@jaybeeuu/agent-uplink 1.0.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.
@@ -0,0 +1,129 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ pull_request:
7
+ branches: ["**"]
8
+
9
+ jobs:
10
+ install-and-build:
11
+ name: Install & Build
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: pnpm/action-setup@v4
20
+
21
+ - uses: actions/setup-node@v4
22
+ with:
23
+ node-version: 22
24
+ cache: pnpm
25
+
26
+ - name: Install dependencies
27
+ run: pnpm install --frozen-lockfile
28
+
29
+ - name: Cache node_modules
30
+ uses: actions/cache/save@v4
31
+ with:
32
+ path: node_modules
33
+ key: node_modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
34
+
35
+ - name: Build
36
+ run: pnpm build
37
+
38
+ - name: Cache build artifacts
39
+ uses: actions/cache/save@v4
40
+ with:
41
+ path: dist/
42
+ key: dist-${{ runner.os }}-${{ github.sha }}
43
+
44
+ lint:
45
+ name: Lint
46
+ runs-on: ubuntu-latest
47
+ needs: install-and-build
48
+ permissions:
49
+ contents: read
50
+
51
+ steps:
52
+ - uses: actions/checkout@v4
53
+
54
+ - uses: pnpm/action-setup@v4
55
+
56
+ - uses: actions/setup-node@v4
57
+ with:
58
+ node-version: 22
59
+
60
+ - name: Restore node_modules
61
+ uses: actions/cache/restore@v4
62
+ with:
63
+ path: node_modules
64
+ key: node_modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
65
+
66
+ - name: Lint
67
+ run: pnpm lint
68
+
69
+ typecheck:
70
+ name: Typecheck
71
+ runs-on: ubuntu-latest
72
+ needs: install-and-build
73
+ permissions:
74
+ contents: read
75
+
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+
79
+ - uses: pnpm/action-setup@v4
80
+
81
+ - uses: actions/setup-node@v4
82
+ with:
83
+ node-version: 22
84
+
85
+ - name: Restore node_modules
86
+ uses: actions/cache/restore@v4
87
+ with:
88
+ path: node_modules
89
+ key: node_modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
90
+
91
+ - name: Restore build artifacts
92
+ uses: actions/cache/restore@v4
93
+ with:
94
+ path: dist/
95
+ key: dist-${{ runner.os }}-${{ github.sha }}
96
+
97
+ - name: Typecheck
98
+ run: pnpm typecheck
99
+
100
+ test:
101
+ name: Test
102
+ runs-on: ubuntu-latest
103
+ needs: install-and-build
104
+ permissions:
105
+ contents: read
106
+
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+
110
+ - uses: pnpm/action-setup@v4
111
+
112
+ - uses: actions/setup-node@v4
113
+ with:
114
+ node-version: 22
115
+
116
+ - name: Restore node_modules
117
+ uses: actions/cache/restore@v4
118
+ with:
119
+ path: node_modules
120
+ key: node_modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
121
+
122
+ - name: Restore build artifacts
123
+ uses: actions/cache/restore@v4
124
+ with:
125
+ path: dist/
126
+ key: dist-${{ runner.os }}-${{ github.sha }}
127
+
128
+ - name: Test
129
+ run: pnpm test
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Josh Bickley-Wallace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # agent-uplink
2
+
3
+ A CLI and MCP server for managing AI capabilities — skills, agents, and instructions — for use with GitHub Copilot and other AI tooling.
4
+
5
+ Uses standard file structures (`.agents/`) rather than tool-specific directories, and supports MCP (Model Context Protocol) so AI agents can manage capabilities directly.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add -g @jaybeeuu/agent-uplink
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```sh
16
+ # Create a skill
17
+ uplink skill create my-skill
18
+
19
+ # Edit an existing instruction
20
+ uplink instruction edit coding-style
21
+
22
+ # List all agents
23
+ uplink agent list
24
+
25
+ # Sync changes to git remote
26
+ uplink sync
27
+
28
+ # Install VS Code integration (user-level settings.json + mcp.json)
29
+ uplink install vscode
30
+ ```
31
+
32
+ ## Commands
33
+
34
+ ### Resource management
35
+
36
+ Each resource type (`skill`, `agent`, `instruction`) supports the same subcommands:
37
+
38
+ | Command | Description |
39
+ |---------|-------------|
40
+ | `uplink <type> create <name>` | Create a new resource and open it in your editor |
41
+ | `uplink <type> edit <name>` | Open an existing resource in your editor |
42
+ | `uplink <type> list` | List all resources of this type |
43
+ | `uplink <type> show <name>` | Print the content of a resource |
44
+ | `uplink <type> delete <name>` | Delete a resource (prompts for confirmation) |
45
+
46
+ ### Sync
47
+
48
+ ```sh
49
+ uplink sync # Commit and push all changes
50
+ uplink sync --dry-run # Show what would be synced
51
+ ```
52
+
53
+ Commits all changes in the capabilities directory with a descriptive message and pushes to the configured remote.
54
+
55
+ ### Configuration
56
+
57
+ ```sh
58
+ uplink config list # Show all settings
59
+ uplink config get capabilitiesDir # Get a specific setting
60
+ uplink config set capabilitiesDir ~/caps # Set the capabilities directory
61
+ uplink config set editor "code --wait" # Set your preferred editor
62
+ uplink config set gitRemote upstream # Set git remote name
63
+ uplink config edit # Open config file in your editor
64
+ ```
65
+
66
+ Configuration is stored at `~/.agent-uplink/config.json`.
67
+
68
+ | Key | Default | Description |
69
+ |-----|---------|-------------|
70
+ | `capabilitiesDir` | `~/.agent-uplink/capabilities` | Directory where capability files are stored |
71
+ | `editor` | `$VISUAL` / `$EDITOR` / `vi` | Editor command to use |
72
+ | `gitRemote` | `origin` | Git remote to push to |
73
+
74
+ ### Install / Uninstall integrations
75
+
76
+ ```sh
77
+ uplink install vscode # Configure user-level VS Code / GitHub Copilot
78
+ uplink uninstall vscode # Remove the VS Code integration
79
+ ```
80
+
81
+ **VS Code** (`uplink install vscode`): Updates `~/.config/Code/User/settings.json` (Linux) with:
82
+ - `chat.agentSkillsLocations` — points at your `skills/` directory
83
+ - `chat.instructionsFilesLocations` — points at your `instructions/` directory
84
+ - `chat.agentFilesLocations` — points at your `agents/` directory
85
+
86
+ Also writes `~/.config/Code/User/mcp.json` to register the uplink MCP server so GitHub Copilot can use it without any further setup.
87
+
88
+ ### MCP server
89
+
90
+ ```sh
91
+ uplink mcp start # Start the MCP server (stdio mode)
92
+ ```
93
+
94
+ The MCP server exposes the following tools to AI agents:
95
+
96
+ | Tool | Description |
97
+ |------|-------------|
98
+ | `list_capabilities` | List capabilities of a given type |
99
+ | `get_capability` | Read the content of a capability |
100
+ | `create_capability` | Create a new capability |
101
+ | `update_capability` | Update an existing capability |
102
+ | `delete_capability` | Delete a capability |
103
+ | `sync_capabilities` | Commit and push all changes |
104
+
105
+ After `uplink install vscode`, the MCP server is automatically configured at user level so GitHub Copilot can use it without any further setup.
106
+
107
+ ## Capabilities directory structure
108
+
109
+ ```
110
+ <capabilitiesDir>/
111
+ ├── skills/
112
+ │ └── <name>.md
113
+ ├── agents/
114
+ │ └── <name>.md
115
+ └── instructions/
116
+ └── <name>.md
117
+ ```
118
+
119
+ The capabilities directory can be a git repository, enabling `uplink sync` to commit and push changes across machines.
120
+
121
+ ## Debug logging
122
+
123
+ Set the `DEBUG` environment variable to enable verbose output:
124
+
125
+ ```sh
126
+ DEBUG=agent-uplink:* # Enable all modules
127
+ DEBUG=agent-uplink:config # Enable config module only
128
+ DEBUG=agent-uplink:install # Enable install module only
129
+ ```
130
+
131
+ ## Development
132
+
133
+ ```sh
134
+ pnpm install # Install dependencies
135
+ pnpm build # Compile TypeScript
136
+ pnpm test # Run tests
137
+ pnpm dev -- <args> # Run CLI without building (uses tsx)
138
+ ```
@@ -0,0 +1,15 @@
1
+ // @ts-check
2
+ import { base } from "@jaybeeuu/eslint-config/base";
3
+ import globals from "globals";
4
+
5
+ export default [
6
+ { ignores: ["dist/**", "node_modules/**"] },
7
+ ...base,
8
+ {
9
+ languageOptions: {
10
+ globals: {
11
+ ...globals.node,
12
+ },
13
+ },
14
+ },
15
+ ];
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@jaybeeuu/agent-uplink",
3
+ "version": "1.0.0",
4
+ "description": "CLI and MCP for managing skills, agents & instructions for AI tooling",
5
+ "type": "module",
6
+ "bin": {
7
+ "uplink": "./dist/cli.js"
8
+ },
9
+ "main": "./dist/cli.js",
10
+ "scripts": {
11
+ "build": "tsc --project tsconfig.build.json",
12
+ "dev": "tsx src/cli.ts",
13
+ "start": "node dist/cli.js",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "typecheck": "tsc --noEmit",
17
+ "lint": "eslint ."
18
+ },
19
+ "keywords": [
20
+ "cli",
21
+ "ai",
22
+ "copilot",
23
+ "agents",
24
+ "skills",
25
+ "mcp"
26
+ ],
27
+ "author": {
28
+ "name": "Josh Bickley-Wallace",
29
+ "url": "https://jaybeeuu.dev"
30
+ },
31
+ "license": "MIT",
32
+ "packageManager": "pnpm@10.32.1",
33
+ "pnpm": {
34
+ "onlyBuiltDependencies": [
35
+ "esbuild"
36
+ ]
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.27.1",
40
+ "chalk": "^5.6.2",
41
+ "commander": "^14.0.3",
42
+ "debug": "^4.4.3",
43
+ "enquirer": "^2.4.1",
44
+ "open": "^10.2.0",
45
+ "simple-git": "^3.33.0",
46
+ "zod": "^4.3.6"
47
+ },
48
+ "devDependencies": {
49
+ "@jaybeeuu/eslint-config": "^5.0.0",
50
+ "@types/debug": "^4.1.13",
51
+ "@types/node": "^25.5.0",
52
+ "eslint": "^10.0.3",
53
+ "globals": "^17.4.0",
54
+ "tsx": "^4.21.0",
55
+ "typescript": "^5.9.3",
56
+ "vitest": "^4.1.0"
57
+ }
58
+ }
@@ -0,0 +1,139 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdirSync, rmSync, existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { tmpdir } from "os";
5
+ import {
6
+ createCapability,
7
+ readCapability,
8
+ updateCapability,
9
+ deleteCapability,
10
+ listCapabilities,
11
+ capabilityExists,
12
+ getCapabilityPath,
13
+ } from "./capabilities.js";
14
+
15
+ const TMP = join(tmpdir(), `uplink-capabilities-test-${String(process.pid)}`);
16
+
17
+ beforeEach(() => {
18
+ mkdirSync(TMP, { recursive: true });
19
+ });
20
+
21
+ afterEach(() => {
22
+ rmSync(TMP, { recursive: true, force: true });
23
+ });
24
+
25
+ describe("createCapability", () => {
26
+ it("creates a skill file with a default template", () => {
27
+ const cap = createCapability(TMP, "skill", "my-skill");
28
+ expect(cap.name).toBe("my-skill");
29
+ expect(cap.type).toBe("skill");
30
+ expect(existsSync(cap.path)).toBe(true);
31
+ expect(cap.content).toContain("name: my-skill");
32
+ expect(cap.content).toContain("description: Describe this skill here.");
33
+ expect(cap.content).toContain("---");
34
+ expect(cap.content).toContain("# my-skill");
35
+ });
36
+
37
+ it("creates a capability with custom content", () => {
38
+ const cap = createCapability(TMP, "skill", "custom", "custom content");
39
+ expect(readFileSync(cap.path, "utf-8")).toBe("custom content");
40
+ });
41
+
42
+ it("creates an agent file with the correct extension", () => {
43
+ const cap = createCapability(TMP, "agent", "my-agent");
44
+ expect(cap.path).toMatch(/\.md$/);
45
+ expect(cap.content).toContain("name: my-agent");
46
+ });
47
+
48
+ it("throws if a capability already exists", () => {
49
+ createCapability(TMP, "skill", "dup");
50
+ expect(() => createCapability(TMP, "skill", "dup")).toThrow(
51
+ /already exists/
52
+ );
53
+ });
54
+ });
55
+
56
+ describe("readCapability", () => {
57
+ it("reads an existing capability", () => {
58
+ createCapability(TMP, "skill", "readable", "hello world");
59
+ const cap = readCapability(TMP, "skill", "readable");
60
+ expect(cap.content).toBe("hello world");
61
+ expect(cap.name).toBe("readable");
62
+ });
63
+
64
+ it("throws if capability does not exist", () => {
65
+ expect(() => readCapability(TMP, "skill", "missing")).toThrow(/not found/);
66
+ });
67
+ });
68
+
69
+ describe("updateCapability", () => {
70
+ it("updates the content of an existing capability", () => {
71
+ createCapability(TMP, "instruction", "my-inst", "old content");
72
+ const cap = updateCapability(TMP, "instruction", "my-inst", "new content");
73
+ expect(cap.content).toBe("new content");
74
+ expect(readFileSync(cap.path, "utf-8")).toBe("new content");
75
+ });
76
+
77
+ it("throws if capability does not exist", () => {
78
+ expect(() =>
79
+ updateCapability(TMP, "skill", "missing", "content")
80
+ ).toThrow(/not found/);
81
+ });
82
+ });
83
+
84
+ describe("deleteCapability", () => {
85
+ it("deletes an existing capability", () => {
86
+ const cap = createCapability(TMP, "skill", "deletable", "bye");
87
+ deleteCapability(TMP, "skill", "deletable");
88
+ expect(existsSync(cap.path)).toBe(false);
89
+ });
90
+
91
+ it("throws if capability does not exist", () => {
92
+ expect(() => {
93
+ deleteCapability(TMP, "skill", "missing");
94
+ }).toThrow(/not found/);
95
+ });
96
+ });
97
+
98
+ describe("listCapabilities", () => {
99
+ it("returns empty array when no capabilities exist", () => {
100
+ expect(listCapabilities(TMP, "skill")).toEqual([]);
101
+ });
102
+
103
+ it("lists capabilities sorted alphabetically", () => {
104
+ createCapability(TMP, "skill", "zebra", "z");
105
+ createCapability(TMP, "skill", "alpha", "a");
106
+ createCapability(TMP, "skill", "beta", "b");
107
+ expect(listCapabilities(TMP, "skill")).toEqual(["alpha", "beta", "zebra"]);
108
+ });
109
+
110
+ it("only lists capabilities of the correct type", () => {
111
+ createCapability(TMP, "skill", "a-skill", "");
112
+ createCapability(TMP, "agent", "an-agent", "");
113
+ expect(listCapabilities(TMP, "skill")).toEqual(["a-skill"]);
114
+ expect(listCapabilities(TMP, "agent")).toEqual(["an-agent"]);
115
+ });
116
+ });
117
+
118
+ describe("capabilityExists", () => {
119
+ it("returns true for an existing capability", () => {
120
+ createCapability(TMP, "skill", "exists", "");
121
+ expect(capabilityExists(TMP, "skill", "exists")).toBe(true);
122
+ });
123
+
124
+ it("returns false for a non-existent capability", () => {
125
+ expect(capabilityExists(TMP, "skill", "nope")).toBe(false);
126
+ });
127
+ });
128
+
129
+ describe("getCapabilityPath", () => {
130
+ it("returns the correct path for a skill", () => {
131
+ const path = getCapabilityPath(TMP, "skill", "my-skill");
132
+ expect(path).toBe(join(TMP, "skills", "my-skill.md"));
133
+ });
134
+
135
+ it("returns the correct path for an agent", () => {
136
+ const path = getCapabilityPath(TMP, "agent", "my-agent");
137
+ expect(path).toBe(join(TMP, "agents", "my-agent.md"));
138
+ });
139
+ });
@@ -0,0 +1,141 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readdirSync,
5
+ readFileSync,
6
+ writeFileSync,
7
+ unlinkSync,
8
+ } from "fs";
9
+ import { join, extname, basename } from "path";
10
+ import {
11
+ CAPABILITY_DIRS,
12
+ CAPABILITY_EXTENSIONS,
13
+ type Capability,
14
+ type CapabilityType,
15
+ } from "./types.js";
16
+
17
+ const SKILL_TEMPLATE = (name: string): string =>
18
+ `---\nname: ${name}\ndescription: Describe this skill here.\n---\n\n# ${name}\n\n## Steps\n\n1. Step one\n`;
19
+
20
+ const AGENT_TEMPLATE = (name: string): string =>
21
+ `---\nname: ${name}\ndescription: Describe this agent here.\ntools: []\n---\n\n# ${name}\n\nAgent instructions go here.\n`;
22
+
23
+ const INSTRUCTION_TEMPLATE = (name: string): string =>
24
+ `# ${name}\n\nInstruction content goes here.\n`;
25
+
26
+ const TEMPLATES = {
27
+ skill: SKILL_TEMPLATE,
28
+ agent: AGENT_TEMPLATE,
29
+ instruction: INSTRUCTION_TEMPLATE,
30
+ };
31
+
32
+ export function getCapabilityDir(capabilitiesDir: string, type: CapabilityType): string {
33
+ return join(capabilitiesDir, CAPABILITY_DIRS[type]);
34
+ }
35
+
36
+ export function getCapabilityPath(
37
+ capabilitiesDir: string,
38
+ type: CapabilityType,
39
+ name: string
40
+ ): string {
41
+ const dir = getCapabilityDir(capabilitiesDir, type);
42
+ return join(dir, name + CAPABILITY_EXTENSIONS[type]);
43
+ }
44
+
45
+ export function ensureCapabilitiesDir(capabilitiesDir: string): void {
46
+ for (const dir of Object.values(CAPABILITY_DIRS)) {
47
+ const fullPath = join(capabilitiesDir, dir);
48
+ if (!existsSync(fullPath)) {
49
+ mkdirSync(fullPath, { recursive: true });
50
+ }
51
+ }
52
+ }
53
+
54
+ export function createCapability(
55
+ capabilitiesDir: string,
56
+ type: CapabilityType,
57
+ name: string,
58
+ content?: string
59
+ ): Capability {
60
+ ensureCapabilitiesDir(capabilitiesDir);
61
+ const path = getCapabilityPath(capabilitiesDir, type, name);
62
+
63
+ if (existsSync(path)) {
64
+ throw new Error(`${type} '${name}' already exists at ${path}`);
65
+ }
66
+
67
+ const fileContent = content ?? TEMPLATES[type](name);
68
+ writeFileSync(path, fileContent, "utf-8");
69
+
70
+ return { name, type, path, content: fileContent };
71
+ }
72
+
73
+ export function readCapability(
74
+ capabilitiesDir: string,
75
+ type: CapabilityType,
76
+ name: string
77
+ ): Capability {
78
+ const path = getCapabilityPath(capabilitiesDir, type, name);
79
+
80
+ if (!existsSync(path)) {
81
+ throw new Error(`${type} '${name}' not found`);
82
+ }
83
+
84
+ const content = readFileSync(path, "utf-8");
85
+ return { name, type, path, content };
86
+ }
87
+
88
+ export function updateCapability(
89
+ capabilitiesDir: string,
90
+ type: CapabilityType,
91
+ name: string,
92
+ content: string
93
+ ): Capability {
94
+ const path = getCapabilityPath(capabilitiesDir, type, name);
95
+
96
+ if (!existsSync(path)) {
97
+ throw new Error(`${type} '${name}' not found`);
98
+ }
99
+
100
+ writeFileSync(path, content, "utf-8");
101
+ return { name, type, path, content };
102
+ }
103
+
104
+ export function deleteCapability(
105
+ capabilitiesDir: string,
106
+ type: CapabilityType,
107
+ name: string
108
+ ): void {
109
+ const path = getCapabilityPath(capabilitiesDir, type, name);
110
+
111
+ if (!existsSync(path)) {
112
+ throw new Error(`${type} '${name}' not found`);
113
+ }
114
+
115
+ unlinkSync(path);
116
+ }
117
+
118
+ export function listCapabilities(
119
+ capabilitiesDir: string,
120
+ type: CapabilityType
121
+ ): string[] {
122
+ const dir = getCapabilityDir(capabilitiesDir, type);
123
+
124
+ if (!existsSync(dir)) {
125
+ return [];
126
+ }
127
+
128
+ const ext = CAPABILITY_EXTENSIONS[type];
129
+ return readdirSync(dir)
130
+ .filter((f) => f.endsWith(ext))
131
+ .map((f) => basename(f, extname(f)))
132
+ .sort();
133
+ }
134
+
135
+ export function capabilityExists(
136
+ capabilitiesDir: string,
137
+ type: CapabilityType,
138
+ name: string
139
+ ): boolean {
140
+ return existsSync(getCapabilityPath(capabilitiesDir, type, name));
141
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { makeCapabilityCommand } from "./commands/capability.js";
4
+ import { makeSyncCommand } from "./commands/sync.js";
5
+ import { makeConfigCommand } from "./commands/config.js";
6
+ import { makeInstallCommand, makeUninstallCommand } from "./commands/install.js";
7
+ import { makeMcpCommand } from "./commands/mcp.js";
8
+ import { rootLogger } from "./logger.js";
9
+
10
+ const program = new Command();
11
+
12
+ program
13
+ .name("uplink")
14
+ .description(
15
+ "CLI and MCP for managing AI capabilities: skills, agents, and instructions"
16
+ )
17
+ .version("1.0.0");
18
+
19
+ // Resource management commands
20
+ program.addCommand(makeCapabilityCommand("skill"));
21
+ program.addCommand(makeCapabilityCommand("agent"));
22
+ program.addCommand(makeCapabilityCommand("instruction"));
23
+
24
+ // Utility commands
25
+ program.addCommand(makeSyncCommand());
26
+ program.addCommand(makeConfigCommand());
27
+ program.addCommand(makeInstallCommand());
28
+ program.addCommand(makeUninstallCommand());
29
+ program.addCommand(makeMcpCommand());
30
+
31
+ // Parse and execute
32
+ program.parseAsync(process.argv).catch((err: unknown) => {
33
+ rootLogger.error(err);
34
+ process.exit(1);
35
+ });