@michaelhartmayer/agentctl 1.1.3 → 1.1.4
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 +136 -17
- package/dist/ctl.js +6 -3
- package/dist/index.js +124 -73
- package/dist/logic/ctl.js +2 -1
- package/dist/logic/index.js +38 -23
- package/dist/logic/install.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,14 +90,31 @@ Teach your AI agent how to use `agentctl` by installing a skill file.
|
|
|
90
90
|
agentctl ctl --install-skill cursor
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
###
|
|
94
|
-
|
|
93
|
+
### Provide the proper help copy
|
|
94
|
+
When creating commands, make sure the `manifest.json` properly scopes its information:
|
|
95
|
+
- `description`: A short summary of the command, displayed when viewing the list of available commands.
|
|
96
|
+
- `help`: A longer set of instructions/usage string. For `group` types, this is displayed when calling the group without a subcommand. For `scaffold` types, this is currently for reference only; your command script must manually handle an `--help` flag for detailed usage.
|
|
97
|
+
|
|
98
|
+
#### Scaffold Schema
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"name": "<command_folder_name>",
|
|
102
|
+
"description": "<insert command summary here>",
|
|
103
|
+
"help": "<insert longer usage/help instructions here>",
|
|
104
|
+
"type": "scaffold",
|
|
105
|
+
"run": "./command.cmd"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
95
108
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
#### Group Schema
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"name": "<group_folder_name>",
|
|
113
|
+
"description": "<insert group summary here>",
|
|
114
|
+
"help": "<insert longer group description/instructions here>",
|
|
115
|
+
"type": "group"
|
|
116
|
+
}
|
|
99
117
|
```
|
|
100
|
-
*(Requires `gemini-cli` installed and authenticated)*
|
|
101
118
|
|
|
102
119
|
---
|
|
103
120
|
|
|
@@ -105,17 +122,119 @@ agentctl agent headless-gemini . "refactor the formatting in src/"
|
|
|
105
122
|
|
|
106
123
|
The `ctl` subcommand is the meta-layer for managing `agentctl` itself.
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
### `agentctl ctl scaffold <path>`
|
|
126
|
+
Create a new script-based command. This generates a folder containing a `manifest.json` and a starter script (`command.cmd`/`command.sh`) for your logic.
|
|
127
|
+
- **Why it's great:** Automates the boilerplate of creating a new CLI tool.
|
|
128
|
+
- **When to use it:** When you have a complex script or an arbitrary executable (Node, Python, Go) you want to expose easily via the CLI.
|
|
129
|
+
```bash
|
|
130
|
+
agentctl ctl scaffold build:front
|
|
131
|
+
agentctl ctl scaffold "build server" # Creates group 'build' and subcommand 'server'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `agentctl ctl group <path>`
|
|
135
|
+
Create a new namespace to organize subcommands.
|
|
136
|
+
- **Why it's great:** Keeps your CLI clean by grouping related tools without forcing you to write a parent CLI router.
|
|
137
|
+
- **When to use it:** When you have a collection of similar commands (e.g., `db migrate`, `db reset`) and want an organized help menu grouping them under `db`.
|
|
138
|
+
```bash
|
|
139
|
+
agentctl ctl group data # Creates 'data' group
|
|
140
|
+
agentctl ctl group "cloud aws" # Creates nested 'cloud/aws' groups
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `agentctl ctl alias <path> <target>`
|
|
144
|
+
Create a command that simply runs an existing binary, string, or target.
|
|
145
|
+
- **Why it's great:** Replaces bash aliases with structured, documentable commands that work locally or globally across environments and OSes.
|
|
146
|
+
- **When to use it:** When you have a long, frequently used string (`docker compose run --rm node npm install`) and want a short name (`agentctl npm install`) without needing a whole generated shell script.
|
|
147
|
+
```bash
|
|
148
|
+
agentctl ctl alias "tools gh" gh
|
|
149
|
+
agentctl ctl alias list-files "ls -la"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `agentctl ctl rm [options] <path>`
|
|
153
|
+
Permanently remove a command or entire group from your workspace.
|
|
154
|
+
- **Why it's great:** Quickly clean up old tooling right from the CLI without needing to manually delete `.agentctl` directories.
|
|
155
|
+
- **When to use it:** When retiring a script or cleaning up an obsolete group.
|
|
156
|
+
|
|
157
|
+
**Options:**
|
|
158
|
+
- `-g, --global`: Remove from global scope instead of local
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
agentctl ctl rm build:front # Removes local 'build front' command
|
|
162
|
+
agentctl ctl rm mytool --global # Removes global 'mytool' command
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `agentctl ctl mv [options] <src> <dest>`
|
|
166
|
+
Rename a command or move it to a new group/namespace.
|
|
167
|
+
- **Why it's great:** Refactor your CLI commands like you'd refactor code, moving logic around namespaces without breaking the underlying executed scripts.
|
|
168
|
+
- **When to use it:** When restructuring your toolbelt from a flat list to a nested categorization.
|
|
169
|
+
|
|
170
|
+
**Options:**
|
|
171
|
+
- `-g, --global`: Operate in global scope
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
agentctl ctl mv mytool my-new-tool
|
|
175
|
+
agentctl ctl mv "tools ping" "network ping"
|
|
176
|
+
agentctl ctl mv "tools ping" "network ping" --global # Moves in global scope
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `agentctl ctl list [options]`
|
|
180
|
+
List all currently available commands across both your local and global scopes.
|
|
181
|
+
- **Why it's great:** Generates an easy-to-read JSON summary of everything your agent can do.
|
|
182
|
+
- **When to use it:** When programming an agent or exploring what tooling is available in a scoped project.
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
agentctl ctl list
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `agentctl ctl inspect [options] <path>`
|
|
189
|
+
Dump the resolved manifest and location of a given command path.
|
|
190
|
+
- **Why it's great:** Eliminates the "where did this command come from?" problem when debugging complex workspaces.
|
|
191
|
+
- **When to use it:** When you have local/global conflicts or want to quickly see the JSON source manifest for a command.
|
|
192
|
+
```bash
|
|
193
|
+
agentctl ctl inspect dev:start
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `agentctl ctl global [options] <path>`
|
|
197
|
+
Promote a local command or group to your globally available toolbelt.
|
|
198
|
+
- **Why it's great:** Eject highly useful, generic project abstractions directly into your personal universal toolbelt.
|
|
199
|
+
- **When to use it:** You wrote a script specific to a project and realized "I want this CLI tool in every repo I touch."
|
|
200
|
+
|
|
201
|
+
**Options:**
|
|
202
|
+
- `-c, --copy`: Copy the command (keep local version, default)
|
|
203
|
+
- `-m, --move`: Move the command (delete local after copying)
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
agentctl ctl global "dev toolkit" # Copies the command to global scope
|
|
207
|
+
agentctl ctl global "dev toolkit" --move # Moves it instead of copy
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `agentctl ctl local [options] <path>`
|
|
211
|
+
Bring a global command down into the current local project environment.
|
|
212
|
+
- **Why it's great:** Promotes collaboration seamlessly. You can take your personal script, localize it, and commit it to git for your entire team.
|
|
213
|
+
- **When to use it:** You have a generic tool that is suddenly very relevant to a specific repository, and you want CI or your coworkers to access it.
|
|
214
|
+
|
|
215
|
+
**Options:**
|
|
216
|
+
- `-c, --copy`: Copy the command (keep global version, default)
|
|
217
|
+
- `-m, --move`: Move the command (delete global after pulling)
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
agentctl ctl local "my global-tool"
|
|
221
|
+
agentctl ctl local "my global-tool" --move
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### `agentctl ctl install [options] <repo-url> [path...]`
|
|
225
|
+
Install commands or groups directly from a remote Git repository's `.agentctl` folder into your local or global scope.
|
|
226
|
+
- **Why it's great:** Provides dependency-free tool sharing! Distribute entire suites of scripts/commands without using a package manager.
|
|
227
|
+
- **When to use it:** When you want to pull down shared utility commands that your team maintains in a central repository, or distribute your own tools to others.
|
|
228
|
+
|
|
229
|
+
**Options:**
|
|
230
|
+
- `-g, --global`: Install globally instead of locally
|
|
231
|
+
- `--allow-collisions`: Allow overwriting existing commands or merging into groups
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
agentctl ctl install https://github.com/my-org/tools
|
|
235
|
+
agentctl ctl install https://github.com/my-org/tools --global
|
|
236
|
+
agentctl ctl install https://github.com/my-org/tools deploy --allow-collisions
|
|
237
|
+
```
|
|
119
238
|
|
|
120
239
|
---
|
|
121
240
|
|
package/dist/ctl.js
CHANGED
|
@@ -181,7 +181,8 @@ async function install(repoUrl, pathArgs, options = {}) {
|
|
|
181
181
|
pathParts: pathArgs,
|
|
182
182
|
global: !!options.global,
|
|
183
183
|
allowCollisions: !!options.allowCollisions,
|
|
184
|
-
localRoot: ctx.localRoot,
|
|
184
|
+
localRoot: ctx.localRoot || ctx.cwd,
|
|
185
|
+
isNewLocalRoot: !options.global && !ctx.localRoot,
|
|
185
186
|
globalRoot: ctx.globalRoot,
|
|
186
187
|
osTmpdir: os_1.default.tmpdir()
|
|
187
188
|
};
|
|
@@ -238,9 +239,11 @@ async function list(options = {}) {
|
|
|
238
239
|
else if (manifest.type)
|
|
239
240
|
type = manifest.type;
|
|
240
241
|
}
|
|
241
|
-
const item = { path: cmdPath, type, scope, description: manifest?.description || '' };
|
|
242
242
|
if (!commands.has(cmdPath)) {
|
|
243
|
-
|
|
243
|
+
if (manifest) {
|
|
244
|
+
const item = { path: cmdPath, type, scope, description: manifest.description || '' };
|
|
245
|
+
commands.set(cmdPath, item);
|
|
246
|
+
}
|
|
244
247
|
const effectiveManifest = manifest || { name: file, type: 'group' };
|
|
245
248
|
if (!(0, manifest_1.isCappedManifest)(effectiveManifest))
|
|
246
249
|
await walk(filePath, cmdPathParts, scope);
|
package/dist/index.js
CHANGED
|
@@ -15,13 +15,44 @@ const path_1 = __importDefault(require("path"));
|
|
|
15
15
|
const pkgPath = path_1.default.join(__dirname, '../package.json');
|
|
16
16
|
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
17
17
|
const program = new commander_1.Command();
|
|
18
|
+
program.addHelpCommand(false);
|
|
18
19
|
program
|
|
19
20
|
.name('agentctl')
|
|
20
21
|
.description('Agent Controller CLI - Unified control plane for humans and agents')
|
|
21
22
|
.version(pkg.version);
|
|
22
23
|
// --- Subcommand: ctl ---
|
|
23
24
|
const ctl = program.command('ctl')
|
|
24
|
-
.description('Agent Controller Management - Create, organize, and manage commands')
|
|
25
|
+
.description('Agent Controller Management - Create, organize, and manage commands')
|
|
26
|
+
.addHelpCommand(false)
|
|
27
|
+
.configureHelp({ visibleCommands: () => [] })
|
|
28
|
+
.addHelpText('after', `
|
|
29
|
+
${chalk_1.default.bold('Agentctl Paradigm:')}
|
|
30
|
+
Agentctl acts as a unified control plane allowing both Humans and AI Agents
|
|
31
|
+
to create, discover, and execute local shell commands. By running \`agentctl ctl scaffold <name>\`
|
|
32
|
+
you create a directory in the \`.agentctl\` folder containing a \`manifest.json\`
|
|
33
|
+
and a run script. This dynamically creates a new \`agentctl <name>\` command that
|
|
34
|
+
is easily callable by agents and globally executable on your machine.
|
|
35
|
+
|
|
36
|
+
Commands:
|
|
37
|
+
|
|
38
|
+
${chalk_1.default.bold('Creation')}
|
|
39
|
+
scaffold [path...] create a new command
|
|
40
|
+
alias [args...] create a shell alias
|
|
41
|
+
group [path...] create a namespace group
|
|
42
|
+
|
|
43
|
+
${chalk_1.default.bold('Organize & Scope')}
|
|
44
|
+
rm [options] [path...] delete a command
|
|
45
|
+
mv [options] [src] [dest] rename/move a command
|
|
46
|
+
global [options] [path...] make a command global
|
|
47
|
+
local [options] [path...] make a command local
|
|
48
|
+
|
|
49
|
+
${chalk_1.default.bold('Information')}
|
|
50
|
+
list list all commands
|
|
51
|
+
inspect [path...] inspect command details
|
|
52
|
+
|
|
53
|
+
${chalk_1.default.bold('Integration')}
|
|
54
|
+
install [options] [repoUrl] [pathParts...] install remote command group
|
|
55
|
+
`);
|
|
25
56
|
const withErrorHandling = (fn) => {
|
|
26
57
|
return async (...args) => {
|
|
27
58
|
try {
|
|
@@ -38,6 +69,7 @@ const withErrorHandling = (fn) => {
|
|
|
38
69
|
}
|
|
39
70
|
};
|
|
40
71
|
};
|
|
72
|
+
const normalizePath = (parts) => parts.flatMap(p => p.split(/[\s/\\:]+/).filter(Boolean));
|
|
41
73
|
ctl.command('scaffold')
|
|
42
74
|
.description('Scaffold a new command directory with a manifest and starter script.')
|
|
43
75
|
.argument('[path...]', 'Hierarchical path for the new command (e.g., "dev start" or "utils/cleanup")')
|
|
@@ -45,20 +77,34 @@ ctl.command('scaffold')
|
|
|
45
77
|
.addHelpText('after', `
|
|
46
78
|
Additional Info:
|
|
47
79
|
This command creates a folder in your local .agentctl directory.
|
|
48
|
-
Inside, it generates:
|
|
49
|
-
- manifest.json: Metadata
|
|
50
|
-
- command.sh/cmd: A starter script for your logic.
|
|
80
|
+
Inside this newly created folder, it generates:
|
|
81
|
+
- manifest.json: Metadata config to edit for your command.
|
|
82
|
+
- command.sh/cmd: A starter script to edit for your actual logic.
|
|
83
|
+
|
|
84
|
+
Manifest Schema (manifest.json) to edit:
|
|
85
|
+
{
|
|
86
|
+
"name": "<command_folder_name>",
|
|
87
|
+
"description": "<insert command summary here>",
|
|
88
|
+
"help": "<insert longer usage/help instructions here>",
|
|
89
|
+
"type": "scaffold", // do not change!
|
|
90
|
+
"run": "./command.cmd" // points to the script to execute
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Note:
|
|
94
|
+
- The "description" is displayed when you view this command in a list.
|
|
95
|
+
- Commands must provide their own help implementation (e.g. by handling --help inside your script).
|
|
51
96
|
|
|
52
97
|
Examples:
|
|
53
|
-
$ agentctl ctl scaffold build
|
|
54
|
-
$ agentctl ctl scaffold
|
|
98
|
+
$ agentctl ctl scaffold build:front
|
|
99
|
+
$ agentctl ctl scaffold "build front" # Creates group 'build' and subcommand 'front'
|
|
55
100
|
`)
|
|
56
101
|
.action(withErrorHandling(async (pathParts, opts, command) => {
|
|
57
|
-
|
|
102
|
+
const normalized = normalizePath(pathParts);
|
|
103
|
+
if (normalized.length === 0) {
|
|
58
104
|
command.help();
|
|
59
105
|
return;
|
|
60
106
|
}
|
|
61
|
-
await (0, ctl_1.scaffold)(
|
|
107
|
+
await (0, ctl_1.scaffold)(normalized);
|
|
62
108
|
}));
|
|
63
109
|
ctl.command('alias')
|
|
64
110
|
.description('Create a command that executes a raw shell string.')
|
|
@@ -75,7 +121,11 @@ Examples:
|
|
|
75
121
|
return;
|
|
76
122
|
}
|
|
77
123
|
const target = args.pop();
|
|
78
|
-
const name = args;
|
|
124
|
+
const name = normalizePath(args);
|
|
125
|
+
if (name.length === 0) {
|
|
126
|
+
command.help();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
79
129
|
await (0, ctl_1.alias)(name, target);
|
|
80
130
|
}));
|
|
81
131
|
ctl.command('group')
|
|
@@ -86,17 +136,33 @@ ctl.command('group')
|
|
|
86
136
|
Additional Info:
|
|
87
137
|
Groups allow you to categorize commands. Running a group command without
|
|
88
138
|
subcommands will list all direct subcommands within that group.
|
|
139
|
+
|
|
140
|
+
This command creates a folder in your local .agentctl directory.
|
|
141
|
+
Inside this newly created folder, it generates a manifest.json.
|
|
142
|
+
|
|
143
|
+
Group Schema (manifest.json) to edit:
|
|
144
|
+
{
|
|
145
|
+
"name": "<group_folder_name>",
|
|
146
|
+
"description": "<insert group summary here>",
|
|
147
|
+
"help": "<insert longer group description/instructions here>",
|
|
148
|
+
"type": "group" // do not change!
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Note:
|
|
152
|
+
- The "description" is displayed when you view this group in a list.
|
|
153
|
+
- The "help" is displayed when you call this group without a subcommand.
|
|
89
154
|
|
|
90
155
|
Examples:
|
|
91
156
|
$ agentctl ctl group dev
|
|
92
|
-
$ agentctl ctl group data
|
|
157
|
+
$ agentctl ctl group "data pipelines" # Creates group 'data' and subgroup 'pipelines'
|
|
93
158
|
`)
|
|
94
159
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
95
|
-
|
|
160
|
+
const normalized = normalizePath(parts);
|
|
161
|
+
if (normalized.length === 0) {
|
|
96
162
|
command.help();
|
|
97
163
|
return;
|
|
98
164
|
}
|
|
99
|
-
await (0, ctl_1.group)(
|
|
165
|
+
await (0, ctl_1.group)(normalized);
|
|
100
166
|
}));
|
|
101
167
|
ctl.command('rm')
|
|
102
168
|
.description('Permanently remove a command or group.')
|
|
@@ -105,15 +171,16 @@ ctl.command('rm')
|
|
|
105
171
|
.summary('delete a command')
|
|
106
172
|
.addHelpText('after', `
|
|
107
173
|
Examples:
|
|
108
|
-
|
|
109
|
-
$ agentctl ctl rm utils
|
|
110
|
-
`)
|
|
174
|
+
$ agentctl ctl rm dev start
|
|
175
|
+
$ agentctl ctl rm utils--global
|
|
176
|
+
`)
|
|
111
177
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
112
|
-
|
|
178
|
+
const normalized = normalizePath(parts);
|
|
179
|
+
if (normalized.length === 0) {
|
|
113
180
|
command.help();
|
|
114
181
|
return;
|
|
115
182
|
}
|
|
116
|
-
await (0, ctl_1.rm)(
|
|
183
|
+
await (0, ctl_1.rm)(normalized, { global: opts.global });
|
|
117
184
|
}));
|
|
118
185
|
ctl.command('mv')
|
|
119
186
|
.description('Move or rename a command/group within its current scope.')
|
|
@@ -123,26 +190,28 @@ ctl.command('mv')
|
|
|
123
190
|
.summary('rename/move a command')
|
|
124
191
|
.addHelpText('after', `
|
|
125
192
|
Examples:
|
|
126
|
-
|
|
127
|
-
$ agentctl ctl mv utils scripts
|
|
128
|
-
`)
|
|
193
|
+
$ agentctl ctl mv "dev start" "dev begin"
|
|
194
|
+
$ agentctl ctl mv utils scripts--global
|
|
195
|
+
`)
|
|
129
196
|
.action(withErrorHandling(async (src, dest, opts, command) => {
|
|
130
|
-
|
|
197
|
+
const normalizedSrc = normalizePath(src ? [src] : []);
|
|
198
|
+
const normalizedDest = normalizePath(dest ? [dest] : []);
|
|
199
|
+
if (normalizedSrc.length === 0 || normalizedDest.length === 0) {
|
|
131
200
|
command.help();
|
|
132
201
|
return;
|
|
133
202
|
}
|
|
134
|
-
await (0, ctl_1.mv)(
|
|
203
|
+
await (0, ctl_1.mv)(normalizedSrc, normalizedDest, { global: opts.global });
|
|
135
204
|
}));
|
|
136
205
|
ctl.command('list')
|
|
137
206
|
.description('List all available commands across local and global scopes.')
|
|
138
207
|
.summary('list all commands')
|
|
139
208
|
.addHelpText('after', `
|
|
140
209
|
Output Columns:
|
|
141
|
-
|
|
142
|
-
SCOPE
|
|
143
|
-
COMMAND
|
|
210
|
+
TYPE - scaffold, alias, or group
|
|
211
|
+
SCOPE - local(project - specific) or global(user - wide)
|
|
212
|
+
COMMAND - The path used to invoke the command
|
|
144
213
|
DESCRIPTION - Brief text from the command's manifest
|
|
145
|
-
`)
|
|
214
|
+
`)
|
|
146
215
|
.action(withErrorHandling(async () => {
|
|
147
216
|
const items = await (0, ctl_1.list)();
|
|
148
217
|
console.log(chalk_1.default.bold('TYPE SCOPE COMMAND DESCRIPTION'));
|
|
@@ -158,14 +227,15 @@ ctl.command('inspect')
|
|
|
158
227
|
.summary('inspect command details')
|
|
159
228
|
.addHelpText('after', `
|
|
160
229
|
Examples:
|
|
161
|
-
|
|
162
|
-
`)
|
|
230
|
+
$ agentctl ctl inspect dev start
|
|
231
|
+
`)
|
|
163
232
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
164
|
-
|
|
233
|
+
const normalized = normalizePath(parts);
|
|
234
|
+
if (normalized.length === 0) {
|
|
165
235
|
command.help();
|
|
166
236
|
return;
|
|
167
237
|
}
|
|
168
|
-
const info = await (0, ctl_1.inspect)(
|
|
238
|
+
const info = await (0, ctl_1.inspect)(normalized);
|
|
169
239
|
if (info) {
|
|
170
240
|
console.log(JSON.stringify(info, null, 2));
|
|
171
241
|
}
|
|
@@ -182,18 +252,19 @@ ctl.command('global')
|
|
|
182
252
|
.summary('make a command global')
|
|
183
253
|
.addHelpText('after', `
|
|
184
254
|
Additional Info:
|
|
185
|
-
|
|
255
|
+
Global commands are stored in your home directory and are available in any project.
|
|
186
256
|
|
|
187
|
-
Examples:
|
|
188
|
-
|
|
189
|
-
$ agentctl ctl global dev/deploy
|
|
190
|
-
`)
|
|
257
|
+
Examples:
|
|
258
|
+
$ agentctl ctl global utils / cleanup
|
|
259
|
+
$ agentctl ctl global dev / deploy--move
|
|
260
|
+
`)
|
|
191
261
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
192
|
-
|
|
262
|
+
const normalized = normalizePath(parts);
|
|
263
|
+
if (normalized.length === 0) {
|
|
193
264
|
command.help();
|
|
194
265
|
return;
|
|
195
266
|
}
|
|
196
|
-
await (0, ctl_1.pushGlobal)(
|
|
267
|
+
await (0, ctl_1.pushGlobal)(normalized, { move: opts.move, copy: opts.copy || !opts.move });
|
|
197
268
|
}));
|
|
198
269
|
ctl.command('local')
|
|
199
270
|
.description('Pull a global command into the current local project.')
|
|
@@ -203,38 +274,16 @@ ctl.command('local')
|
|
|
203
274
|
.summary('make a command local')
|
|
204
275
|
.addHelpText('after', `
|
|
205
276
|
Examples:
|
|
206
|
-
|
|
207
|
-
$ agentctl ctl local snippets/js
|
|
208
|
-
`)
|
|
277
|
+
$ agentctl ctl local utils / shared
|
|
278
|
+
$ agentctl ctl local snippets / js--move
|
|
279
|
+
`)
|
|
209
280
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
210
|
-
|
|
281
|
+
const normalized = normalizePath(parts);
|
|
282
|
+
if (normalized.length === 0) {
|
|
211
283
|
command.help();
|
|
212
284
|
return;
|
|
213
285
|
}
|
|
214
|
-
await (0, ctl_1.pullLocal)(
|
|
215
|
-
}));
|
|
216
|
-
ctl.command('install-skill')
|
|
217
|
-
.description('Configure a supported AI agent (like Cursor or Gemini) to natively use Agentctl.')
|
|
218
|
-
.argument('[agent]', 'Agent name (cursor, antigravity, agentsmd, gemini)')
|
|
219
|
-
.option('-g, --global', 'Install globally for the agent (if supported)')
|
|
220
|
-
.summary('configure AI agent integration')
|
|
221
|
-
.addHelpText('after', `
|
|
222
|
-
Supported Agents:
|
|
223
|
-
- cursor (Installs to .cursor/skills)
|
|
224
|
-
- antigravity (Installs to .agent/skills or ~/.gemini/antigravity)
|
|
225
|
-
- agentsmd (Installs to .agents/skills)
|
|
226
|
-
- gemini (Installs to .gemini/skills or ~/.gemini/skills)
|
|
227
|
-
|
|
228
|
-
Examples:
|
|
229
|
-
$ agentctl ctl install-skill cursor
|
|
230
|
-
$ agentctl ctl install-skill antigravity --global
|
|
231
|
-
`)
|
|
232
|
-
.action(withErrorHandling(async (agent, opts, command) => {
|
|
233
|
-
if (!agent) {
|
|
234
|
-
command.help();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
await (0, ctl_1.installSkill)(agent, { global: opts.global });
|
|
286
|
+
await (0, ctl_1.pullLocal)(normalized, { move: opts.move, copy: opts.copy || !opts.move });
|
|
238
287
|
}));
|
|
239
288
|
ctl.command('install')
|
|
240
289
|
.description('Install a command group from a remote Git repository.')
|
|
@@ -245,19 +294,20 @@ ctl.command('install')
|
|
|
245
294
|
.summary('install remote command group')
|
|
246
295
|
.addHelpText('after', `
|
|
247
296
|
Additional Info:
|
|
248
|
-
|
|
297
|
+
Fetches the.agentctl folder from the remote repository and installs it into
|
|
249
298
|
your local or global agentctl environment.
|
|
250
299
|
|
|
251
|
-
Examples:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
`)
|
|
300
|
+
Examples:
|
|
301
|
+
$ agentctl ctl install https://github.com/org/repo-tools
|
|
302
|
+
$ agentctl ctl install https://github.com/org/deploy-scripts deploy --global
|
|
303
|
+
`)
|
|
255
304
|
.action(withErrorHandling(async (repoUrl, pathParts, opts, command) => {
|
|
256
305
|
if (!repoUrl) {
|
|
257
306
|
command.help();
|
|
258
307
|
return;
|
|
259
308
|
}
|
|
260
|
-
|
|
309
|
+
const normalized = normalizePath(pathParts || []);
|
|
310
|
+
await (0, ctl_1.install)(repoUrl, normalized, { global: opts.global, allowCollisions: opts.allowCollisions });
|
|
261
311
|
}));
|
|
262
312
|
// --- Dynamic Command Logic ---
|
|
263
313
|
async function handleDynamicCommand(args) {
|
|
@@ -283,7 +333,7 @@ async function handleDynamicCommand(args) {
|
|
|
283
333
|
return e;
|
|
284
334
|
if (e.message.startsWith(' '))
|
|
285
335
|
return e;
|
|
286
|
-
if (e.message === '
|
|
336
|
+
if (e.message === 'Command group containing subcommands')
|
|
287
337
|
return { ...e, message: chalk_1.default.dim(e.message) };
|
|
288
338
|
}
|
|
289
339
|
return e;
|
|
@@ -309,7 +359,8 @@ async function handleDynamicCommand(args) {
|
|
|
309
359
|
const lines = [''];
|
|
310
360
|
lines.push(chalk_1.default.bold('User Commands:'));
|
|
311
361
|
for (const cmd of topLevel) {
|
|
312
|
-
|
|
362
|
+
const desc = cmd.description || 'Command group containing subcommands';
|
|
363
|
+
lines.push(` ${chalk_1.default.yellow(cmd.path.padEnd(27))}${desc}`);
|
|
313
364
|
}
|
|
314
365
|
lines.push('');
|
|
315
366
|
program.addHelpText('after', lines.join('\n'));
|
package/dist/logic/ctl.js
CHANGED
package/dist/logic/index.js
CHANGED
|
@@ -11,6 +11,15 @@ exports.AppLogic = {
|
|
|
11
11
|
return [{ type: 'log', message: `Command '${args.join(' ')}' not found. Run 'agentctl list' to see available commands.` }];
|
|
12
12
|
}
|
|
13
13
|
const { manifest, args: remainingArgs, scope, manifestPath, cmdPath } = result;
|
|
14
|
+
const effects = [];
|
|
15
|
+
const allowedKeys = new Set(['name', 'description', 'help', 'type', 'run', 'flags']);
|
|
16
|
+
const unsupportedKeys = Object.keys(manifest).filter(k => !allowedKeys.has(k));
|
|
17
|
+
if (unsupportedKeys.length > 0) {
|
|
18
|
+
effects.push({
|
|
19
|
+
type: 'log',
|
|
20
|
+
message: `[WARNING] The manifest at ${manifestPath} contains unsupported keys: ${unsupportedKeys.join(', ')}. Agentctl will ignore them.`
|
|
21
|
+
});
|
|
22
|
+
}
|
|
14
23
|
if (manifest.run) {
|
|
15
24
|
const cmdDir = path_1.default.dirname(manifestPath);
|
|
16
25
|
let runCmd = manifest.run;
|
|
@@ -19,33 +28,38 @@ exports.AppLogic = {
|
|
|
19
28
|
}
|
|
20
29
|
runCmd = runCmd.replace(/{{DIR}}/g, cmdDir);
|
|
21
30
|
const fullCommand = `${runCmd} ${remainingArgs.join(' ')}`;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
onExit: (code) => {
|
|
34
|
-
process.exit(code || 0);
|
|
35
|
-
}
|
|
31
|
+
effects.push({ type: 'log', message: `[${scope}] Running: ${fullCommand}` }, {
|
|
32
|
+
type: 'spawn',
|
|
33
|
+
command: fullCommand,
|
|
34
|
+
options: {
|
|
35
|
+
cwd: process.cwd(),
|
|
36
|
+
shell: true,
|
|
37
|
+
stdio: 'inherit',
|
|
38
|
+
env: { ...process.env, AGENTCTL_SCOPE: scope }
|
|
39
|
+
},
|
|
40
|
+
onExit: (code) => {
|
|
41
|
+
process.exit(code || 0);
|
|
36
42
|
}
|
|
37
|
-
|
|
43
|
+
});
|
|
44
|
+
return effects;
|
|
38
45
|
}
|
|
39
46
|
else {
|
|
40
|
-
return exports.AppLogic.planGroupList(manifest, cmdPath, []);
|
|
47
|
+
return [...effects, ...exports.AppLogic.planGroupList(manifest, cmdPath, [], manifestPath)];
|
|
41
48
|
}
|
|
42
49
|
},
|
|
43
|
-
planGroupList(manifest, cmdPath, allCommands) {
|
|
44
|
-
const effects = [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
planGroupList(manifest, cmdPath, allCommands, manifestPath) {
|
|
51
|
+
const effects = [];
|
|
52
|
+
if (manifestPath) {
|
|
53
|
+
const allowedKeys = new Set(['name', 'description', 'help', 'type', 'run', 'flags']);
|
|
54
|
+
const unsupportedKeys = Object.keys(manifest).filter(k => !allowedKeys.has(k));
|
|
55
|
+
if (unsupportedKeys.length > 0) {
|
|
56
|
+
effects.push({
|
|
57
|
+
type: 'log',
|
|
58
|
+
message: `[WARNING] The manifest at ${manifestPath} contains unsupported keys: ${unsupportedKeys.join(', ')}. Agentctl will ignore them.`
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
effects.push({ type: 'log', message: manifest.name }, { type: 'log', message: manifest.help || manifest.description || 'Command group containing subcommands' }, { type: 'log', message: '\nSubcommands:' });
|
|
49
63
|
const prefix = cmdPath + ' ';
|
|
50
64
|
const depth = cmdPath.split(' ').length;
|
|
51
65
|
const children = allCommands.filter(c => c.path.startsWith(prefix) && c.path !== cmdPath);
|
|
@@ -56,7 +70,8 @@ exports.AppLogic = {
|
|
|
56
70
|
else {
|
|
57
71
|
for (const child of direct) {
|
|
58
72
|
const name = child.path.split(' ').pop();
|
|
59
|
-
|
|
73
|
+
const desc = child.description || 'Command group containing subcommands';
|
|
74
|
+
effects.push({ type: 'log', message: ` ${name}\t${desc}` });
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
return effects;
|
package/dist/logic/install.js
CHANGED
|
@@ -43,6 +43,9 @@ function planInstallCopy(ctx, deps) {
|
|
|
43
43
|
}
|
|
44
44
|
// Ensure target path exists
|
|
45
45
|
effects.push({ type: 'mkdir', path: targetDir });
|
|
46
|
+
if (!ctx.global && ctx.isNewLocalRoot) {
|
|
47
|
+
effects.push({ type: 'log', message: `Initialized new .agentctl folder at ${agentctlDir}` });
|
|
48
|
+
}
|
|
46
49
|
// Copy contents
|
|
47
50
|
effects.push({
|
|
48
51
|
type: 'copy',
|