@michaelhartmayer/agentctl 1.1.0 → 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.
@@ -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,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ManifestLogic = void 0;
4
+ exports.ManifestLogic = {
5
+ isCappedManifest(m) {
6
+ return !!m.run || m.type === 'scaffold' || m.type === 'alias';
7
+ }
8
+ };
@@ -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,4 +1,18 @@
1
1
  "use strict";
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);
15
+ };
2
16
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
18
  };
@@ -6,6 +20,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
20
  exports.readManifest = readManifest;
7
21
  exports.isCappedManifest = isCappedManifest;
8
22
  const fs_extra_1 = __importDefault(require("fs-extra"));
23
+ const manifest_1 = require("./logic/manifest");
24
+ __exportStar(require("./logic/manifest"), exports);
9
25
  async function readManifest(p) {
10
26
  try {
11
27
  return await fs_extra_1.default.readJson(p);
@@ -15,5 +31,5 @@ async function readManifest(p) {
15
31
  }
16
32
  }
17
33
  function isCappedManifest(m) {
18
- return !!m.run || m.type === 'scaffold' || m.type === 'alias';
34
+ return manifest_1.ManifestLogic.isCappedManifest(m);
19
35
  }
package/dist/resolve.js CHANGED
@@ -8,104 +8,58 @@ const path_1 = __importDefault(require("path"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const fs_utils_1 = require("./fs-utils");
10
10
  const manifest_1 = require("./manifest");
11
+ const resolve_1 = require("./logic/resolve");
11
12
  async function resolveCommand(args, options = {}) {
12
13
  const cwd = options.cwd || process.cwd();
13
14
  const localRoot = !options.global ? (0, fs_utils_1.findLocalRoot)(cwd) : null;
14
15
  const globalRoot = options.globalDir || (0, fs_utils_1.getGlobalRoot)();
15
16
  const localAgentctl = localRoot ? path_1.default.join(localRoot, '.agentctl') : null;
16
17
  let currentMatch = null;
17
- // Iterate through args to find longest match
18
18
  for (let i = 0; i < args.length; i++) {
19
- // Path corresponding to args[0..i]
20
19
  const currentArgs = args.slice(0, i + 1);
21
20
  const relPath = currentArgs.join(path_1.default.sep);
22
21
  const cmdPath = currentArgs.join(' ');
23
22
  const localPath = localAgentctl ? path_1.default.join(localAgentctl, relPath) : null;
24
23
  const globalPath = path_1.default.join(globalRoot, relPath);
25
- let localManifest = null;
26
- let globalManifest = null;
27
- // check local
28
- if (localPath && await fs_extra_1.default.pathExists(localPath)) {
29
- const mPath = path_1.default.join(localPath, 'manifest.json');
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;
30
29
  if (await fs_extra_1.default.pathExists(mPath)) {
31
- localManifest = await (0, manifest_1.readManifest)(mPath);
30
+ manifest = await (0, manifest_1.readManifest)(mPath);
32
31
  }
33
- if (!localManifest && (await fs_extra_1.default.stat(localPath)).isDirectory()) {
34
- // Implicit group
35
- localManifest = { name: args[i], type: 'group' };
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' };
36
36
  }
37
- }
38
- // check global
39
- if (await fs_extra_1.default.pathExists(globalPath)) {
40
- const mPath = path_1.default.join(globalPath, 'manifest.json');
41
- if (await fs_extra_1.default.pathExists(mPath)) {
42
- globalManifest = await (0, manifest_1.readManifest)(mPath);
43
- }
44
- if (!globalManifest && (await fs_extra_1.default.stat(globalPath)).isDirectory()) {
45
- globalManifest = { name: args[i], type: 'group' };
46
- }
47
- }
48
- if (!localManifest && !globalManifest) {
49
- break;
50
- }
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);
51
42
  const remainingArgs = args.slice(i + 1);
52
- // Priority logic
53
- // 1. Local Capped -> Return Match immediately.
54
- if (localManifest && (0, manifest_1.isCappedManifest)(localManifest)) {
43
+ if (decision.finalResult) {
55
44
  return {
56
- manifest: localManifest,
57
- manifestPath: path_1.default.join(localPath, 'manifest.json'),
45
+ manifest: decision.finalResult.manifest,
46
+ manifestPath: decision.finalResult.manifestPath,
58
47
  args: remainingArgs,
59
- scope: 'local',
48
+ scope: decision.finalResult.scope,
60
49
  cmdPath
61
50
  };
62
51
  }
63
- // 2. Global Capped
64
- if (globalManifest && (0, manifest_1.isCappedManifest)(globalManifest)) {
65
- // Check if shadowed by Local Group
66
- if (localManifest) {
67
- // Local exists (must be group since checked capped above).
68
- // Shadowed. Treat as Local Group.
69
- currentMatch = {
70
- manifest: localManifest,
71
- manifestPath: path_1.default.join(localPath, 'manifest.json'),
72
- args: remainingArgs,
73
- scope: 'local',
74
- cmdPath
75
- };
76
- }
77
- else {
78
- // Not shadowed. Global Capped wins. Return immediately.
79
- return {
80
- manifest: globalManifest,
81
- manifestPath: path_1.default.join(globalPath, 'manifest.json'),
82
- args: remainingArgs,
83
- scope: 'global',
84
- cmdPath
85
- };
86
- }
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
+ };
87
60
  }
88
- else {
89
- // Neither is capped. Both are groups (or one is).
90
- // Local wins if exists.
91
- if (localManifest) {
92
- currentMatch = {
93
- manifest: localManifest,
94
- manifestPath: path_1.default.join(localPath, 'manifest.json'),
95
- args: remainingArgs,
96
- scope: 'local',
97
- cmdPath
98
- };
99
- }
100
- else {
101
- currentMatch = {
102
- manifest: globalManifest,
103
- manifestPath: path_1.default.join(globalPath, 'manifest.json'),
104
- args: remainingArgs,
105
- scope: 'global',
106
- cmdPath
107
- };
108
- }
61
+ if (decision.shouldStop) {
62
+ break;
109
63
  }
110
64
  }
111
65
  return currentMatch;
package/dist/skills.js CHANGED
@@ -5,35 +5,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SUPPORTED_AGENTS = void 0;
7
7
  exports.copySkill = copySkill;
8
- const path_1 = __importDefault(require("path"));
9
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const skills_1 = require("./logic/skills");
10
10
  exports.SUPPORTED_AGENTS = ['cursor', 'antigravity', 'agentsmd', 'gemini'];
11
11
  async function copySkill(targetDir, agent) {
12
- // We assume the skill file is located at ../skills/agentctl/SKILL.md relative to specific dist/src/ location
13
- // Or we find it in the project root if running from source.
14
- // In production (dist), structure might be:
15
- // dist/index.js
16
- // skills/agentctl/SKILL.md (if we copy it to dist)
17
- // Let's try to locate the source SKILL.md
18
- // If we are in /src, it is in ../skills/agentctl/SKILL.md
19
- // If we are in /dist/src (tsc default?), it depends on build.
20
- // Robust finding:
21
- let sourcePath = path_1.default.resolve(__dirname, '../../skills/agentctl/SKILL.md');
22
- if (!fs_extra_1.default.existsSync(sourcePath)) {
23
- // Try looking in src check (dev mode)
24
- sourcePath = path_1.default.resolve(__dirname, '../skills/agentctl/SKILL.md');
12
+ const sources = skills_1.SkillsLogic.getSourcePaths({ dirname: __dirname });
13
+ let sourcePath = null;
14
+ for (const p of sources) {
15
+ if (fs_extra_1.default.existsSync(p)) {
16
+ sourcePath = p;
17
+ break;
18
+ }
25
19
  }
26
- if (!fs_extra_1.default.existsSync(sourcePath)) {
27
- // Fallback for when running from dist/src
28
- sourcePath = path_1.default.resolve(__dirname, '../../../skills/agentctl/SKILL.md');
29
- }
30
- if (!fs_extra_1.default.existsSync(sourcePath)) {
31
- throw new Error(`Could not locate source SKILL.md. Checked: ${path_1.default.resolve(__dirname, '../../skills/agentctl/SKILL.md')}`);
20
+ if (!sourcePath) {
21
+ throw new Error(`Could not locate source SKILL.md. Checked: ${sources[0]}`);
32
22
  }
33
23
  await fs_extra_1.default.ensureDir(targetDir);
34
- // Determine filename
35
- const filename = agent === 'cursor' ? 'agentctl.md' : 'SKILL.md';
36
- const targetFile = path_1.default.join(targetDir, filename);
24
+ const targetFile = skills_1.SkillsLogic.getTargetPath(targetDir, agent);
37
25
  await fs_extra_1.default.copy(sourcePath, targetFile, { overwrite: true });
38
26
  return targetFile;
39
27
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "description": "Agent Controller - A unified interface for humans and AI agents",
7
- "version": "1.1.0",
7
+ "version": "1.1.3",
8
8
  "main": "dist/index.js",
9
9
  "bin": {
10
10
  "agentctl": "./dist/index.js"