@michaelhartmayer/agentctl 1.0.3 → 1.1.3
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/dist/ctl.js +217 -308
- package/dist/effects.js +61 -0
- package/dist/fs-utils.js +26 -19
- package/dist/index.js +256 -232
- package/dist/logic/ctl.js +172 -0
- package/dist/logic/index.js +64 -0
- package/dist/logic/install.js +57 -0
- package/dist/logic/manifest.js +8 -0
- package/dist/logic/resolve.js +73 -0
- package/dist/logic/skills.js +20 -0
- package/dist/logic/utils.js +31 -0
- package/dist/manifest.js +23 -18
- package/dist/resolve.js +52 -109
- package/dist/skills.js +16 -39
- package/package.json +1 -1
- package/dist/package.json +0 -60
- package/dist/src/ctl.js +0 -316
- package/dist/src/fs-utils.js +0 -35
- package/dist/src/index.js +0 -305
- package/dist/src/manifest.js +0 -19
- package/dist/src/resolve.js +0 -112
- package/dist/src/skills.js +0 -39
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Logic = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
const skills_1 = require("../skills");
|
|
10
|
+
/**
|
|
11
|
+
* THE BRAIN: Pure functions that calculate "Plans" (Effect arrays).
|
|
12
|
+
* NO IMPURE IMPORTS (no fs, no os, no process).
|
|
13
|
+
*/
|
|
14
|
+
exports.Logic = {
|
|
15
|
+
planScaffold(args, ctx, options) {
|
|
16
|
+
if (args.length === 0)
|
|
17
|
+
throw new Error('No command path provided');
|
|
18
|
+
if (options.exists) {
|
|
19
|
+
throw new Error(`Command ${args.join(' ')} already exists`);
|
|
20
|
+
}
|
|
21
|
+
if (options.cappedAncestor) {
|
|
22
|
+
throw new Error(`Cannot nest command under capped command: ${options.cappedAncestor.relPath}`);
|
|
23
|
+
}
|
|
24
|
+
const localRoot = ctx.localRoot || ctx.cwd;
|
|
25
|
+
const agentctlDir = path_1.default.join(localRoot, '.agentctl');
|
|
26
|
+
const cmdPath = args.join(path_1.default.sep);
|
|
27
|
+
const targetDir = path_1.default.join(agentctlDir, cmdPath);
|
|
28
|
+
const name = args[args.length - 1];
|
|
29
|
+
const type = options.type || 'scaffold';
|
|
30
|
+
const isWin = ctx.platform === 'win32';
|
|
31
|
+
const effects = [{ type: 'mkdir', path: targetDir }];
|
|
32
|
+
const manifest = {
|
|
33
|
+
name,
|
|
34
|
+
description: '',
|
|
35
|
+
type,
|
|
36
|
+
};
|
|
37
|
+
if (type === 'scaffold') {
|
|
38
|
+
const scriptName = isWin ? 'command.cmd' : 'command.sh';
|
|
39
|
+
const scriptPath = path_1.default.join(targetDir, scriptName);
|
|
40
|
+
const scriptContent = isWin
|
|
41
|
+
? '@echo off\r\nREM Add your command logic here\r\necho Not implemented'
|
|
42
|
+
: '#!/usr/bin/env bash\n# Add your command logic here\necho "Not implemented"';
|
|
43
|
+
manifest.run = `./${scriptName}`;
|
|
44
|
+
effects.push({ type: 'writeFile', path: scriptPath, content: scriptContent });
|
|
45
|
+
if (!isWin)
|
|
46
|
+
effects.push({ type: 'chmod', path: scriptPath, mode: 0o755 });
|
|
47
|
+
}
|
|
48
|
+
else if (type === 'alias') {
|
|
49
|
+
manifest.run = options.target;
|
|
50
|
+
}
|
|
51
|
+
effects.push({ type: 'writeJson', path: path_1.default.join(targetDir, 'manifest.json'), content: manifest });
|
|
52
|
+
const logMsg = type === 'scaffold' ? `Scaffolded command: ${args.join(' ')}` :
|
|
53
|
+
type === 'alias' ? `Aliased command: ${args.join(' ')} -> ${options.target}` :
|
|
54
|
+
`Created group: ${args.join(' ')}`;
|
|
55
|
+
effects.push({ type: 'log', message: logMsg });
|
|
56
|
+
return { effects };
|
|
57
|
+
},
|
|
58
|
+
planPushGlobal(args, ctx, options) {
|
|
59
|
+
if (!ctx.localRoot)
|
|
60
|
+
throw new Error('Not in a local context');
|
|
61
|
+
if (!options.existsInLocal)
|
|
62
|
+
throw new Error(`Local command ${args.join(' ')} not found`);
|
|
63
|
+
if (options.existsInGlobal)
|
|
64
|
+
throw new Error(`Global command ${args.join(' ')} already exists`);
|
|
65
|
+
const cmdPathStr = args.join(path_1.default.sep);
|
|
66
|
+
const srcDir = path_1.default.join(ctx.localRoot, '.agentctl', cmdPathStr);
|
|
67
|
+
const destDir = path_1.default.join(ctx.globalRoot, cmdPathStr);
|
|
68
|
+
const effects = [
|
|
69
|
+
{ type: 'mkdir', path: path_1.default.dirname(destDir) }
|
|
70
|
+
];
|
|
71
|
+
if (options.move) {
|
|
72
|
+
effects.push({ type: 'move', src: srcDir, dest: destDir });
|
|
73
|
+
effects.push({ type: 'log', message: `Moved ${args.join(' ')} to global scope` });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
effects.push({ type: 'copy', src: srcDir, dest: destDir });
|
|
77
|
+
effects.push({ type: 'log', message: `Copied ${args.join(' ')} to global scope` });
|
|
78
|
+
}
|
|
79
|
+
return effects;
|
|
80
|
+
},
|
|
81
|
+
planPullLocal(args, ctx, options) {
|
|
82
|
+
if (!ctx.localRoot)
|
|
83
|
+
throw new Error('Not in a local context');
|
|
84
|
+
if (!options.existsInGlobal)
|
|
85
|
+
throw new Error(`Global command ${args.join(' ')} not found`);
|
|
86
|
+
if (options.existsInLocal)
|
|
87
|
+
throw new Error(`Local command ${args.join(' ')} already exists`);
|
|
88
|
+
const cmdPathStr = args.join(path_1.default.sep);
|
|
89
|
+
const srcDir = path_1.default.join(ctx.globalRoot, cmdPathStr);
|
|
90
|
+
const destDir = path_1.default.join(ctx.localRoot, '.agentctl', cmdPathStr);
|
|
91
|
+
const effects = [
|
|
92
|
+
{ type: 'mkdir', path: path_1.default.dirname(destDir) }
|
|
93
|
+
];
|
|
94
|
+
if (options.move) {
|
|
95
|
+
effects.push({ type: 'move', src: srcDir, dest: destDir });
|
|
96
|
+
effects.push({ type: 'log', message: `Moved ${args.join(' ')} to local scope` });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
effects.push({ type: 'copy', src: srcDir, dest: destDir });
|
|
100
|
+
effects.push({ type: 'log', message: `Copied ${args.join(' ')} to local scope` });
|
|
101
|
+
}
|
|
102
|
+
return effects;
|
|
103
|
+
},
|
|
104
|
+
planRemove(args, ctx, options) {
|
|
105
|
+
if (!options.resolvedPath) {
|
|
106
|
+
throw new Error(`Command ${args.join(' ')} not found${options.global ? ' in global scope' : ''}`);
|
|
107
|
+
}
|
|
108
|
+
const targetDir = path_1.default.dirname(options.resolvedPath);
|
|
109
|
+
return [
|
|
110
|
+
{ type: 'remove', path: targetDir },
|
|
111
|
+
{ type: 'log', message: `Removed ${options.scope} command: ${args.join(' ')}` }
|
|
112
|
+
];
|
|
113
|
+
},
|
|
114
|
+
planMove(srcArgs, destArgs, ctx, options) {
|
|
115
|
+
if (!options.resolvedSrc)
|
|
116
|
+
throw new Error(`Command ${srcArgs.join(' ')} not found`);
|
|
117
|
+
if (!options.rootDir || !options.agentctlDir)
|
|
118
|
+
throw new Error('Cannot determine root for move');
|
|
119
|
+
if (options.destExists)
|
|
120
|
+
throw new Error(`Destination ${destArgs.join(' ')} already exists`);
|
|
121
|
+
if (options.cappedAncestor)
|
|
122
|
+
throw new Error(`Cannot nest command under capped command: ${options.cappedAncestor.relPath}`);
|
|
123
|
+
const srcDir = path_1.default.dirname(options.resolvedSrc.manifestPath);
|
|
124
|
+
const destPathStr = destArgs.join(path_1.default.sep);
|
|
125
|
+
const destDir = path_1.default.join(options.agentctlDir, destPathStr);
|
|
126
|
+
const effects = [
|
|
127
|
+
{ type: 'move', src: srcDir, dest: destDir }
|
|
128
|
+
];
|
|
129
|
+
const updatedManifest = { ...options.resolvedSrc.manifest, name: destArgs[destArgs.length - 1] };
|
|
130
|
+
effects.push({ type: 'writeJson', path: path_1.default.join(destDir, 'manifest.json'), content: updatedManifest });
|
|
131
|
+
effects.push({ type: 'log', message: `Moved ${srcArgs.join(' ')} to ${destArgs.join(' ')}` });
|
|
132
|
+
return effects;
|
|
133
|
+
},
|
|
134
|
+
planInstallSkill(agent, ctx, options) {
|
|
135
|
+
if (!skills_1.SUPPORTED_AGENTS.includes(agent)) {
|
|
136
|
+
throw new Error(`Agent '${agent}' not supported. Supported agents: ${skills_1.SUPPORTED_AGENTS.join(', ')}`);
|
|
137
|
+
}
|
|
138
|
+
let targetDir;
|
|
139
|
+
if (agent === 'cursor') {
|
|
140
|
+
targetDir = path_1.default.join(ctx.cwd, '.cursor', 'skills');
|
|
141
|
+
}
|
|
142
|
+
else if (agent === 'antigravity') {
|
|
143
|
+
if (options.global) {
|
|
144
|
+
const globalRoot = options.antigravityGlobalDir || utils_1.UtilsLogic.getAntigravityGlobalRoot({
|
|
145
|
+
platform: ctx.platform,
|
|
146
|
+
env: {}, // UtilsLogic should probably take full ctx or we just use ctx.homedir
|
|
147
|
+
homedir: ctx.homedir
|
|
148
|
+
});
|
|
149
|
+
targetDir = path_1.default.join(globalRoot, 'skills', 'agentctl');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
targetDir = path_1.default.join(ctx.cwd, '.agent', 'skills', 'agentctl');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else if (agent === 'agentsmd') {
|
|
156
|
+
targetDir = path_1.default.join(ctx.cwd, '.agents', 'skills', 'agentctl');
|
|
157
|
+
}
|
|
158
|
+
else if (agent === 'gemini') {
|
|
159
|
+
if (options.global) {
|
|
160
|
+
const globalRoot = options.geminiGlobalDir || path_1.default.join(ctx.homedir, '.gemini');
|
|
161
|
+
targetDir = path_1.default.join(globalRoot, 'skills', 'agentctl');
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
targetDir = path_1.default.join(ctx.cwd, '.gemini', 'skills', 'agentctl');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw new Error(`Agent logic for '${agent}' not implemented.`);
|
|
169
|
+
}
|
|
170
|
+
return [{ type: 'installSkill', targetDir, agent }];
|
|
171
|
+
}
|
|
172
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AppLogic = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
exports.AppLogic = {
|
|
9
|
+
planApp(args, result) {
|
|
10
|
+
if (!result) {
|
|
11
|
+
return [{ type: 'log', message: `Command '${args.join(' ')}' not found. Run 'agentctl list' to see available commands.` }];
|
|
12
|
+
}
|
|
13
|
+
const { manifest, args: remainingArgs, scope, manifestPath, cmdPath } = result;
|
|
14
|
+
if (manifest.run) {
|
|
15
|
+
const cmdDir = path_1.default.dirname(manifestPath);
|
|
16
|
+
let runCmd = manifest.run;
|
|
17
|
+
if (runCmd.startsWith('./') || runCmd.startsWith('.\\')) {
|
|
18
|
+
runCmd = path_1.default.resolve(cmdDir, runCmd);
|
|
19
|
+
}
|
|
20
|
+
runCmd = runCmd.replace(/{{DIR}}/g, cmdDir);
|
|
21
|
+
const fullCommand = `${runCmd} ${remainingArgs.join(' ')}`;
|
|
22
|
+
return [
|
|
23
|
+
{ type: 'log', message: `[${scope}] Running: ${fullCommand}` },
|
|
24
|
+
{
|
|
25
|
+
type: 'spawn',
|
|
26
|
+
command: fullCommand,
|
|
27
|
+
options: {
|
|
28
|
+
cwd: process.cwd(),
|
|
29
|
+
shell: true,
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
env: { ...process.env, AGENTCTL_SCOPE: scope }
|
|
32
|
+
},
|
|
33
|
+
onExit: (code) => {
|
|
34
|
+
process.exit(code || 0);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return exports.AppLogic.planGroupList(manifest, cmdPath, []); // Shell will call this again with actual children
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
planGroupList(manifest, cmdPath, allCommands) {
|
|
44
|
+
const effects = [
|
|
45
|
+
{ type: 'log', message: manifest.name },
|
|
46
|
+
{ type: 'log', message: manifest.description || 'No description' },
|
|
47
|
+
{ type: 'log', message: '\nSubcommands:' }
|
|
48
|
+
];
|
|
49
|
+
const prefix = cmdPath + ' ';
|
|
50
|
+
const depth = cmdPath.split(' ').length;
|
|
51
|
+
const children = allCommands.filter(c => c.path.startsWith(prefix) && c.path !== cmdPath);
|
|
52
|
+
const direct = children.filter(c => c.path.split(' ').length === depth + 1);
|
|
53
|
+
if (direct.length === 0) {
|
|
54
|
+
effects.push({ type: 'log', message: ' (No subcommands found)' });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
for (const child of direct) {
|
|
58
|
+
const name = child.path.split(' ').pop();
|
|
59
|
+
effects.push({ type: 'log', message: ` ${name}\t${child.description}` });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return effects;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.planInstallClone = planInstallClone;
|
|
7
|
+
exports.planInstallCopy = planInstallCopy;
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function planInstallClone(ctx, deps) {
|
|
10
|
+
if (!ctx.global && !ctx.localRoot) {
|
|
11
|
+
throw new Error('Not in a local context. Run inside a project or use --global');
|
|
12
|
+
}
|
|
13
|
+
const tempDir = path_1.default.join(ctx.osTmpdir, deps.tempFolderName);
|
|
14
|
+
const effects = [];
|
|
15
|
+
// 1. Create a log message
|
|
16
|
+
effects.push({
|
|
17
|
+
type: 'log',
|
|
18
|
+
message: `Fetching ${ctx.repoUrl}...`
|
|
19
|
+
});
|
|
20
|
+
// 2. Clone the repository shallowly into a temp directory
|
|
21
|
+
effects.push({
|
|
22
|
+
type: 'gitClone',
|
|
23
|
+
url: ctx.repoUrl,
|
|
24
|
+
dest: tempDir
|
|
25
|
+
});
|
|
26
|
+
return { effects, tempDir };
|
|
27
|
+
}
|
|
28
|
+
function planInstallCopy(ctx, deps) {
|
|
29
|
+
if (!ctx.global && !ctx.localRoot) {
|
|
30
|
+
throw new Error('Not in a local context. Run inside a project or use --global');
|
|
31
|
+
}
|
|
32
|
+
const rootDir = ctx.global ? ctx.globalRoot : ctx.localRoot;
|
|
33
|
+
const agentctlDir = ctx.global ? rootDir : path_1.default.join(rootDir, '.agentctl');
|
|
34
|
+
const targetDir = path_1.default.join(agentctlDir, ...ctx.pathParts);
|
|
35
|
+
const effects = [];
|
|
36
|
+
if (!ctx.allowCollisions) {
|
|
37
|
+
// Simple collision detection based on paths relative to targetDir
|
|
38
|
+
const collisions = deps.existingItems.filter(item => deps.downloadedItems.includes(item));
|
|
39
|
+
if (collisions.length > 0) {
|
|
40
|
+
effects.push({ type: 'remove', path: path_1.default.dirname(deps.tempAgentctlDir) }); // cleanup
|
|
41
|
+
throw new Error(`Installation aborted due to collisions without --allow-collisions flag: \n${collisions.join('\n')}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Ensure target path exists
|
|
45
|
+
effects.push({ type: 'mkdir', path: targetDir });
|
|
46
|
+
// Copy contents
|
|
47
|
+
effects.push({
|
|
48
|
+
type: 'copy',
|
|
49
|
+
src: deps.tempAgentctlDir,
|
|
50
|
+
dest: targetDir,
|
|
51
|
+
options: { overwrite: ctx.allowCollisions }
|
|
52
|
+
});
|
|
53
|
+
// Clean up temp dir
|
|
54
|
+
effects.push({ type: 'remove', path: path_1.default.dirname(deps.tempAgentctlDir) });
|
|
55
|
+
effects.push({ type: 'log', message: `Successfully installed commands to ${targetDir}` });
|
|
56
|
+
return { effects };
|
|
57
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResolveLogic = void 0;
|
|
4
|
+
exports.ResolveLogic = {
|
|
5
|
+
decide(local, global, isCapped) {
|
|
6
|
+
if (!local?.manifest && !global.manifest) {
|
|
7
|
+
return { currentMatch: null, finalResult: null, shouldStop: true };
|
|
8
|
+
}
|
|
9
|
+
const localManifest = local?.manifest;
|
|
10
|
+
const globalManifest = global.manifest;
|
|
11
|
+
// 1. Local Capped -> Return Match immediately.
|
|
12
|
+
if (localManifest && isCapped(localManifest)) {
|
|
13
|
+
return {
|
|
14
|
+
currentMatch: null,
|
|
15
|
+
finalResult: {
|
|
16
|
+
scope: 'local',
|
|
17
|
+
manifest: localManifest,
|
|
18
|
+
manifestPath: local.path
|
|
19
|
+
},
|
|
20
|
+
shouldStop: true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// 2. Global Capped
|
|
24
|
+
if (globalManifest && isCapped(globalManifest)) {
|
|
25
|
+
if (localManifest) {
|
|
26
|
+
// Local Group shadows Global Capped
|
|
27
|
+
return {
|
|
28
|
+
currentMatch: {
|
|
29
|
+
scope: 'local',
|
|
30
|
+
manifest: localManifest,
|
|
31
|
+
manifestPath: local.path
|
|
32
|
+
},
|
|
33
|
+
finalResult: null,
|
|
34
|
+
shouldStop: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return {
|
|
39
|
+
currentMatch: null,
|
|
40
|
+
finalResult: {
|
|
41
|
+
scope: 'global',
|
|
42
|
+
manifest: globalManifest,
|
|
43
|
+
manifestPath: global.path
|
|
44
|
+
},
|
|
45
|
+
shouldStop: true
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// 3. Neither capped
|
|
50
|
+
if (localManifest) {
|
|
51
|
+
return {
|
|
52
|
+
currentMatch: {
|
|
53
|
+
scope: 'local',
|
|
54
|
+
manifest: localManifest,
|
|
55
|
+
manifestPath: local.path
|
|
56
|
+
},
|
|
57
|
+
finalResult: null,
|
|
58
|
+
shouldStop: false
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return {
|
|
63
|
+
currentMatch: {
|
|
64
|
+
scope: 'global',
|
|
65
|
+
manifest: globalManifest,
|
|
66
|
+
manifestPath: global.path
|
|
67
|
+
},
|
|
68
|
+
finalResult: null,
|
|
69
|
+
shouldStop: false
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SkillsLogic = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
exports.SkillsLogic = {
|
|
9
|
+
getSourcePaths(ctx) {
|
|
10
|
+
return [
|
|
11
|
+
path_1.default.resolve(ctx.dirname, '../../skills/agentctl/SKILL.md'),
|
|
12
|
+
path_1.default.resolve(ctx.dirname, '../skills/agentctl/SKILL.md'),
|
|
13
|
+
path_1.default.resolve(ctx.dirname, '../../../skills/agentctl/SKILL.md')
|
|
14
|
+
];
|
|
15
|
+
},
|
|
16
|
+
getTargetPath(targetDir, agent) {
|
|
17
|
+
const filename = agent === 'cursor' ? 'agentctl.md' : 'SKILL.md';
|
|
18
|
+
return path_1.default.join(targetDir, filename);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.UtilsLogic = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
exports.UtilsLogic = {
|
|
9
|
+
getGlobalRoot(ctx) {
|
|
10
|
+
if (ctx.platform === 'win32') {
|
|
11
|
+
return path_1.default.join(ctx.env.APPDATA || path_1.default.join(ctx.homedir, 'AppData', 'Roaming'), 'agentctl');
|
|
12
|
+
}
|
|
13
|
+
return path_1.default.join(ctx.homedir, '.config', 'agentctl');
|
|
14
|
+
},
|
|
15
|
+
getAntigravityGlobalRoot(ctx) {
|
|
16
|
+
return path_1.default.join(ctx.homedir, '.gemini', 'antigravity');
|
|
17
|
+
},
|
|
18
|
+
findLocalRoot(cwd, existsSync) {
|
|
19
|
+
let current = path_1.default.resolve(cwd);
|
|
20
|
+
const root = path_1.default.parse(current).root;
|
|
21
|
+
for (;;) {
|
|
22
|
+
if (existsSync(path_1.default.join(current, '.agentctl'))) {
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
if (current === root) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
current = path_1.default.dirname(current);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
package/dist/manifest.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
10
15
|
};
|
|
11
16
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -15,16 +20,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
20
|
exports.readManifest = readManifest;
|
|
16
21
|
exports.isCappedManifest = isCappedManifest;
|
|
17
22
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
23
|
+
const manifest_1 = require("./logic/manifest");
|
|
24
|
+
__exportStar(require("./logic/manifest"), exports);
|
|
25
|
+
async function readManifest(p) {
|
|
26
|
+
try {
|
|
27
|
+
return await fs_extra_1.default.readJson(p);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
function isCappedManifest(m) {
|
|
29
|
-
return
|
|
34
|
+
return manifest_1.ManifestLogic.isCappedManifest(m);
|
|
30
35
|
}
|
package/dist/resolve.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -17,107 +8,59 @@ const path_1 = __importDefault(require("path"));
|
|
|
17
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
9
|
const fs_utils_1 = require("./fs-utils");
|
|
19
10
|
const manifest_1 = require("./manifest");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
if (yield fs_extra_1.default.pathExists(mPath)) {
|
|
41
|
-
localManifest = yield (0, manifest_1.readManifest)(mPath);
|
|
42
|
-
}
|
|
43
|
-
if (!localManifest && (yield fs_extra_1.default.stat(localPath)).isDirectory()) {
|
|
44
|
-
// Implicit group
|
|
45
|
-
localManifest = { name: args[i], type: 'group' };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// check global
|
|
49
|
-
if (yield fs_extra_1.default.pathExists(globalPath)) {
|
|
50
|
-
const mPath = path_1.default.join(globalPath, 'manifest.json');
|
|
51
|
-
if (yield fs_extra_1.default.pathExists(mPath)) {
|
|
52
|
-
globalManifest = yield (0, manifest_1.readManifest)(mPath);
|
|
53
|
-
}
|
|
54
|
-
if (!globalManifest && (yield fs_extra_1.default.stat(globalPath)).isDirectory()) {
|
|
55
|
-
globalManifest = { name: args[i], type: 'group' };
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (!localManifest && !globalManifest) {
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
const remainingArgs = args.slice(i + 1);
|
|
62
|
-
// Priority logic
|
|
63
|
-
// 1. Local Capped -> Return Match immediately.
|
|
64
|
-
if (localManifest && (0, manifest_1.isCappedManifest)(localManifest)) {
|
|
65
|
-
return {
|
|
66
|
-
manifest: localManifest,
|
|
67
|
-
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
68
|
-
args: remainingArgs,
|
|
69
|
-
scope: 'local',
|
|
70
|
-
cmdPath
|
|
71
|
-
};
|
|
11
|
+
const resolve_1 = require("./logic/resolve");
|
|
12
|
+
async function resolveCommand(args, options = {}) {
|
|
13
|
+
const cwd = options.cwd || process.cwd();
|
|
14
|
+
const localRoot = !options.global ? (0, fs_utils_1.findLocalRoot)(cwd) : null;
|
|
15
|
+
const globalRoot = options.globalDir || (0, fs_utils_1.getGlobalRoot)();
|
|
16
|
+
const localAgentctl = localRoot ? path_1.default.join(localRoot, '.agentctl') : null;
|
|
17
|
+
let currentMatch = null;
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
const currentArgs = args.slice(0, i + 1);
|
|
20
|
+
const relPath = currentArgs.join(path_1.default.sep);
|
|
21
|
+
const cmdPath = currentArgs.join(' ');
|
|
22
|
+
const localPath = localAgentctl ? path_1.default.join(localAgentctl, relPath) : null;
|
|
23
|
+
const globalPath = path_1.default.join(globalRoot, relPath);
|
|
24
|
+
const getFacts = async (p) => {
|
|
25
|
+
if (!p || !(await fs_extra_1.default.pathExists(p)))
|
|
26
|
+
return null;
|
|
27
|
+
const mPath = path_1.default.join(p, 'manifest.json');
|
|
28
|
+
let manifest = null;
|
|
29
|
+
if (await fs_extra_1.default.pathExists(mPath)) {
|
|
30
|
+
manifest = await (0, manifest_1.readManifest)(mPath);
|
|
72
31
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Local exists (must be group since checked capped above).
|
|
78
|
-
// Shadowed. Treat as Local Group.
|
|
79
|
-
currentMatch = {
|
|
80
|
-
manifest: localManifest,
|
|
81
|
-
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
82
|
-
args: remainingArgs,
|
|
83
|
-
scope: 'local',
|
|
84
|
-
cmdPath
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// Not shadowed. Global Capped wins. Return immediately.
|
|
89
|
-
return {
|
|
90
|
-
manifest: globalManifest,
|
|
91
|
-
manifestPath: path_1.default.join(globalPath, 'manifest.json'),
|
|
92
|
-
args: remainingArgs,
|
|
93
|
-
scope: 'global',
|
|
94
|
-
cmdPath
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// Neither is capped. Both are groups (or one is).
|
|
100
|
-
// Local wins if exists.
|
|
101
|
-
if (localManifest) {
|
|
102
|
-
currentMatch = {
|
|
103
|
-
manifest: localManifest,
|
|
104
|
-
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
105
|
-
args: remainingArgs,
|
|
106
|
-
scope: 'local',
|
|
107
|
-
cmdPath
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
currentMatch = {
|
|
112
|
-
manifest: globalManifest,
|
|
113
|
-
manifestPath: path_1.default.join(globalPath, 'manifest.json'),
|
|
114
|
-
args: remainingArgs,
|
|
115
|
-
scope: 'global',
|
|
116
|
-
cmdPath
|
|
117
|
-
};
|
|
118
|
-
}
|
|
32
|
+
const stats = await fs_extra_1.default.stat(p);
|
|
33
|
+
const isDir = stats.isDirectory();
|
|
34
|
+
if (!manifest && isDir) {
|
|
35
|
+
manifest = { name: args[i], type: 'group' };
|
|
119
36
|
}
|
|
37
|
+
return { exists: true, manifest, isDir, path: mPath };
|
|
38
|
+
};
|
|
39
|
+
const localFacts = await getFacts(localPath);
|
|
40
|
+
const globalFacts = (await getFacts(globalPath)) || { exists: false, manifest: null, isDir: false, path: path_1.default.join(globalPath, 'manifest.json') };
|
|
41
|
+
const decision = resolve_1.ResolveLogic.decide(localFacts, globalFacts, manifest_1.isCappedManifest);
|
|
42
|
+
const remainingArgs = args.slice(i + 1);
|
|
43
|
+
if (decision.finalResult) {
|
|
44
|
+
return {
|
|
45
|
+
manifest: decision.finalResult.manifest,
|
|
46
|
+
manifestPath: decision.finalResult.manifestPath,
|
|
47
|
+
args: remainingArgs,
|
|
48
|
+
scope: decision.finalResult.scope,
|
|
49
|
+
cmdPath
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (decision.currentMatch) {
|
|
53
|
+
currentMatch = {
|
|
54
|
+
manifest: decision.currentMatch.manifest,
|
|
55
|
+
manifestPath: decision.currentMatch.manifestPath,
|
|
56
|
+
args: remainingArgs,
|
|
57
|
+
scope: decision.currentMatch.scope,
|
|
58
|
+
cmdPath
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (decision.shouldStop) {
|
|
62
|
+
break;
|
|
120
63
|
}
|
|
121
|
-
|
|
122
|
-
|
|
64
|
+
}
|
|
65
|
+
return currentMatch;
|
|
123
66
|
}
|