@michaelhartmayer/agentctl 1.0.0 → 1.0.2
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/package.json +13 -2
- package/.eslintignore +0 -5
- package/.eslintrc.json +0 -22
- package/.husky/pre-commit +0 -2
- package/src/ctl.ts +0 -356
- package/src/fs-utils.ts +0 -30
- package/src/index.ts +0 -331
- package/src/manifest.ts +0 -21
- package/src/resolve.ts +0 -124
- package/src/skills.ts +0 -42
- package/tests/alias.test.ts +0 -48
- package/tests/edge_cases.test.ts +0 -699
- package/tests/group.test.ts +0 -48
- package/tests/helpers.ts +0 -16
- package/tests/introspection.test.ts +0 -71
- package/tests/lifecycle-guards.test.ts +0 -44
- package/tests/lifecycle.test.ts +0 -59
- package/tests/manifest.test.ts +0 -29
- package/tests/resolve-priority.test.ts +0 -72
- package/tests/resolve.test.ts +0 -78
- package/tests/scaffold.test.ts +0 -61
- package/tests/scoping-guards.test.ts +0 -74
- package/tests/scoping.test.ts +0 -66
- package/tests/skills.test.ts +0 -62
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -9
package/package.json
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"description": "Agent Controller - A unified interface for humans and AI agents",
|
|
7
|
-
"version": "1.0.
|
|
7
|
+
"version": "1.0.2",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
10
10
|
"agentctl": "./dist/index.js"
|
|
11
11
|
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"skills",
|
|
15
|
+
"scripts",
|
|
16
|
+
"agentctl.cmd",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
12
19
|
"scripts": {
|
|
13
20
|
"build": "tsc",
|
|
14
21
|
"test": "vitest run",
|
|
@@ -16,6 +23,7 @@
|
|
|
16
23
|
"lint": "eslint src tests",
|
|
17
24
|
"register:path": "node scripts/register-path.js",
|
|
18
25
|
"unregister:path": "node scripts/unregister-path.js",
|
|
26
|
+
"release": "standard-version",
|
|
19
27
|
"prepublishOnly": "npm run build && npm run test",
|
|
20
28
|
"prepare": "husky"
|
|
21
29
|
},
|
|
@@ -34,6 +42,8 @@
|
|
|
34
42
|
"fs-extra": "^11.3.3"
|
|
35
43
|
},
|
|
36
44
|
"devDependencies": {
|
|
45
|
+
"@commitlint/cli": "^20.4.1",
|
|
46
|
+
"@commitlint/config-conventional": "^20.4.1",
|
|
37
47
|
"@types/fs-extra": "^11.0.4",
|
|
38
48
|
"@types/node": "^25.2.3",
|
|
39
49
|
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
@@ -42,8 +52,9 @@
|
|
|
42
52
|
"eslint": "^8.57.1",
|
|
43
53
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
44
54
|
"husky": "^9.1.7",
|
|
55
|
+
"standard-version": "^9.5.0",
|
|
45
56
|
"ts-node": "^10.9.2",
|
|
46
57
|
"typescript": "^5.9.3",
|
|
47
58
|
"vitest": "^4.0.18"
|
|
48
59
|
}
|
|
49
|
-
}
|
|
60
|
+
}
|
package/.eslintignore
DELETED
package/.eslintrc.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"plugins": [
|
|
4
|
-
"@typescript-eslint",
|
|
5
|
-
"eslint-comments"
|
|
6
|
-
],
|
|
7
|
-
"extends": [
|
|
8
|
-
"eslint:recommended",
|
|
9
|
-
"plugin:@typescript-eslint/recommended",
|
|
10
|
-
"plugin:eslint-comments/recommended"
|
|
11
|
-
],
|
|
12
|
-
"rules": {
|
|
13
|
-
"eslint-comments/no-use": "error",
|
|
14
|
-
"@typescript-eslint/no-explicit-any": "warn",
|
|
15
|
-
"@typescript-eslint/no-unused-vars": [
|
|
16
|
-
"error",
|
|
17
|
-
{
|
|
18
|
-
"argsIgnorePattern": "^_"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
}
|
package/.husky/pre-commit
DELETED
package/src/ctl.ts
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { resolveCommand } from './resolve';
|
|
4
|
-
import { findLocalRoot, getGlobalRoot, getAntigravityGlobalRoot } from './fs-utils';
|
|
5
|
-
import { readManifest, isCappedManifest, Manifest } from './manifest';
|
|
6
|
-
|
|
7
|
-
export interface ListItem {
|
|
8
|
-
path: string;
|
|
9
|
-
type: string;
|
|
10
|
-
scope: 'local' | 'global';
|
|
11
|
-
description: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface InspectResult {
|
|
15
|
-
manifest: Manifest | null;
|
|
16
|
-
resolvedPath: string;
|
|
17
|
-
scope: 'local' | 'global';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function scaffold(args: string[], options: { cwd?: string } = {}) {
|
|
21
|
-
const { targetDir, name, isWin } = await prepareCommand(args, options);
|
|
22
|
-
|
|
23
|
-
const scriptName = isWin ? 'command.cmd' : 'command.sh';
|
|
24
|
-
const scriptPath = path.join(targetDir, scriptName);
|
|
25
|
-
const scriptContent = isWin
|
|
26
|
-
? '@echo off\r\nREM Add your command logic here\r\necho Not implemented'
|
|
27
|
-
: '#!/usr/bin/env bash\n# Add your command logic here\necho "Not implemented"';
|
|
28
|
-
|
|
29
|
-
await fs.writeFile(scriptPath, scriptContent);
|
|
30
|
-
if (!isWin) await fs.chmod(scriptPath, 0o755);
|
|
31
|
-
|
|
32
|
-
const manifest = {
|
|
33
|
-
name,
|
|
34
|
-
description: '',
|
|
35
|
-
type: 'scaffold' as const,
|
|
36
|
-
run: `./${scriptName}`,
|
|
37
|
-
};
|
|
38
|
-
await fs.writeJson(path.join(targetDir, 'manifest.json'), manifest, { spaces: 2 });
|
|
39
|
-
console.log(`Scaffolded command: ${args.join(' ')}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function alias(args: string[], target: string, options: { cwd?: string } = {}) {
|
|
43
|
-
const { targetDir, name } = await prepareCommand(args, options);
|
|
44
|
-
|
|
45
|
-
const manifest = {
|
|
46
|
-
name,
|
|
47
|
-
description: '',
|
|
48
|
-
type: 'alias' as const,
|
|
49
|
-
run: target,
|
|
50
|
-
};
|
|
51
|
-
await fs.writeJson(path.join(targetDir, 'manifest.json'), manifest, { spaces: 2 });
|
|
52
|
-
console.log(`Aliased command: ${args.join(' ')} -> ${target}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function group(args: string[], options: { cwd?: string } = {}) {
|
|
56
|
-
const { targetDir, name } = await prepareCommand(args, options);
|
|
57
|
-
|
|
58
|
-
const manifest = {
|
|
59
|
-
name,
|
|
60
|
-
description: '',
|
|
61
|
-
type: 'group' as const,
|
|
62
|
-
};
|
|
63
|
-
await fs.writeJson(path.join(targetDir, 'manifest.json'), manifest, { spaces: 2 });
|
|
64
|
-
console.log(`Created group: ${args.join(' ')}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export async function pushGlobal(args: string[], options: { cwd?: string, globalDir?: string, move?: boolean, copy?: boolean } = {}) {
|
|
69
|
-
const cwd = options.cwd || process.cwd();
|
|
70
|
-
const localRoot = findLocalRoot(cwd);
|
|
71
|
-
if (!localRoot) throw new Error('Not in a local context');
|
|
72
|
-
const globalRoot = options.globalDir || getGlobalRoot();
|
|
73
|
-
|
|
74
|
-
const localAgentctl = path.join(localRoot, '.agentctl');
|
|
75
|
-
const cmdPathStr = args.join(path.sep);
|
|
76
|
-
const srcDir = path.join(localAgentctl, cmdPathStr);
|
|
77
|
-
|
|
78
|
-
if (!await fs.pathExists(srcDir)) {
|
|
79
|
-
throw new Error(`Local command ${args.join(' ')} not found`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const destDir = path.join(globalRoot, cmdPathStr);
|
|
83
|
-
|
|
84
|
-
if (await fs.pathExists(destDir)) {
|
|
85
|
-
throw new Error(`Global command ${args.join(' ')} already exists`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
await fs.ensureDir(path.dirname(destDir));
|
|
89
|
-
if (options.move) {
|
|
90
|
-
await fs.move(srcDir, destDir);
|
|
91
|
-
console.log(`Moved ${args.join(' ')} to global scope`);
|
|
92
|
-
} else {
|
|
93
|
-
await fs.copy(srcDir, destDir);
|
|
94
|
-
console.log(`Copied ${args.join(' ')} to global scope`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function pullLocal(args: string[], options: { cwd?: string, globalDir?: string, move?: boolean, copy?: boolean } = {}) {
|
|
99
|
-
const cwd = options.cwd || process.cwd();
|
|
100
|
-
const localRoot = findLocalRoot(cwd);
|
|
101
|
-
if (!localRoot) throw new Error('Not in a local context');
|
|
102
|
-
const globalRoot = options.globalDir || getGlobalRoot();
|
|
103
|
-
|
|
104
|
-
const cmdPathStr = args.join(path.sep);
|
|
105
|
-
const srcDir = path.join(globalRoot, cmdPathStr);
|
|
106
|
-
|
|
107
|
-
if (!await fs.pathExists(srcDir)) {
|
|
108
|
-
throw new Error(`Global command ${args.join(' ')} not found`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const localAgentctl = path.join(localRoot, '.agentctl');
|
|
112
|
-
const destDir = path.join(localAgentctl, cmdPathStr);
|
|
113
|
-
|
|
114
|
-
if (await fs.pathExists(destDir)) {
|
|
115
|
-
throw new Error(`Local command ${args.join(' ')} already exists`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
await fs.ensureDir(path.dirname(destDir));
|
|
119
|
-
if (options.move) {
|
|
120
|
-
await fs.move(srcDir, destDir);
|
|
121
|
-
console.log(`Moved ${args.join(' ')} to local scope`);
|
|
122
|
-
} else {
|
|
123
|
-
await fs.copy(srcDir, destDir);
|
|
124
|
-
console.log(`Copied ${args.join(' ')} to local scope`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
import { copySkill, SUPPORTED_AGENTS } from './skills';
|
|
130
|
-
|
|
131
|
-
export async function installSkill(agent: string, options: { cwd?: string, global?: boolean, antigravityGlobalDir?: string, geminiGlobalDir?: string } = {}) {
|
|
132
|
-
const cwd = options.cwd || process.cwd();
|
|
133
|
-
|
|
134
|
-
if (!SUPPORTED_AGENTS.includes(agent)) {
|
|
135
|
-
throw new Error(`Agent '${agent}' not supported. Supported agents: ${SUPPORTED_AGENTS.join(', ')}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
let targetDir: string;
|
|
139
|
-
|
|
140
|
-
if (agent === 'cursor') {
|
|
141
|
-
targetDir = path.join(cwd, '.cursor', 'skills');
|
|
142
|
-
} else if (agent === 'antigravity') {
|
|
143
|
-
if (options.global) {
|
|
144
|
-
const globalRoot = options.antigravityGlobalDir || getAntigravityGlobalRoot();
|
|
145
|
-
targetDir = path.join(globalRoot, 'skills', 'agentctl');
|
|
146
|
-
} else {
|
|
147
|
-
targetDir = path.join(cwd, '.agent', 'skills', 'agentctl');
|
|
148
|
-
}
|
|
149
|
-
} else if (agent === 'agentsmd') {
|
|
150
|
-
targetDir = path.join(cwd, '.agents', 'skills', 'agentctl');
|
|
151
|
-
} else if (agent === 'gemini') {
|
|
152
|
-
if (options.global) {
|
|
153
|
-
const globalRoot = options.geminiGlobalDir || path.join(process.env.HOME || process.env.USERPROFILE!, '.gemini');
|
|
154
|
-
targetDir = path.join(globalRoot, 'skills', 'agentctl');
|
|
155
|
-
} else {
|
|
156
|
-
targetDir = path.join(cwd, '.gemini', 'skills', 'agentctl');
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
throw new Error(`Agent logic for '${agent}' not implemented.`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const p = await copySkill(targetDir, agent);
|
|
163
|
-
console.log(`Installed skill for ${agent} at ${p}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export async function rm(args: string[], options: { cwd?: string, globalDir?: string, global?: boolean } = {}) {
|
|
167
|
-
const resolved = await resolveCommand(args, options);
|
|
168
|
-
if (!resolved) {
|
|
169
|
-
throw new Error(`Command ${args.join(' ')} not found${options.global ? ' in global scope' : ''}`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const targetDir = path.dirname(resolved.manifestPath);
|
|
173
|
-
await fs.remove(targetDir);
|
|
174
|
-
console.log(`Removed ${resolved.scope} command: ${args.join(' ')}`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export async function mv(srcArgs: string[], destArgs: string[], options: { cwd?: string, globalDir?: string, global?: boolean } = {}) {
|
|
178
|
-
const resolved = await resolveCommand(srcArgs, options);
|
|
179
|
-
if (!resolved) {
|
|
180
|
-
throw new Error(`Command ${srcArgs.join(' ')} not found`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const srcDir = path.dirname(resolved.manifestPath);
|
|
184
|
-
|
|
185
|
-
const rootDir = resolved.scope === 'local'
|
|
186
|
-
? findLocalRoot(options.cwd || process.cwd())
|
|
187
|
-
: (options.globalDir || getGlobalRoot());
|
|
188
|
-
|
|
189
|
-
if (!rootDir) throw new Error('Cannot determine root for move');
|
|
190
|
-
|
|
191
|
-
const agentctlDir = resolved.scope === 'local' ? path.join(rootDir, '.agentctl') : rootDir;
|
|
192
|
-
// For global, rootDir IS the agentctl dir (config dir). Local has .agentctl subdir.
|
|
193
|
-
|
|
194
|
-
const destPathStr = destArgs.join(path.sep);
|
|
195
|
-
const destDir = path.join(agentctlDir, destPathStr);
|
|
196
|
-
|
|
197
|
-
if (await fs.pathExists(destDir)) {
|
|
198
|
-
throw new Error(`Destination ${destArgs.join(' ')} already exists`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Check parent validity (nesting under capped)
|
|
202
|
-
let current = path.dirname(destDir);
|
|
203
|
-
while (current.length >= agentctlDir.length && !isSamePath(current, path.dirname(agentctlDir))) {
|
|
204
|
-
if (await isCapped(current)) {
|
|
205
|
-
const relPath = path.relative(agentctlDir, current); // relative to base
|
|
206
|
-
throw new Error(`Cannot nest command under capped command: ${relPath}`);
|
|
207
|
-
}
|
|
208
|
-
current = path.dirname(current);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
await fs.move(srcDir, destDir);
|
|
212
|
-
|
|
213
|
-
// Update manifest name
|
|
214
|
-
const manifestPath = path.join(destDir, 'manifest.json');
|
|
215
|
-
if (await fs.pathExists(manifestPath)) {
|
|
216
|
-
const manifest = await fs.readJson(manifestPath);
|
|
217
|
-
manifest.name = destArgs[destArgs.length - 1];
|
|
218
|
-
await fs.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
console.log(`Moved ${srcArgs.join(' ')} to ${destArgs.join(' ')}`);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export async function inspect(args: string[], options: { cwd?: string, globalDir?: string } = {}): Promise<InspectResult | null> {
|
|
225
|
-
const resolved = await resolveCommand(args, options);
|
|
226
|
-
if (!resolved) {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
manifest: resolved.manifest,
|
|
231
|
-
resolvedPath: resolved.manifestPath,
|
|
232
|
-
scope: resolved.scope
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export async function list(options: { cwd?: string, globalDir?: string } = {}): Promise<ListItem[]> {
|
|
237
|
-
const cwd = options.cwd || process.cwd();
|
|
238
|
-
const localRoot = findLocalRoot(cwd);
|
|
239
|
-
const globalRoot = options.globalDir || getGlobalRoot();
|
|
240
|
-
|
|
241
|
-
const commands = new Map<string, ListItem>();
|
|
242
|
-
|
|
243
|
-
async function walk(dir: string, prefix: string[], scope: 'local' | 'global') {
|
|
244
|
-
if (!await fs.pathExists(dir)) return;
|
|
245
|
-
|
|
246
|
-
const files = await fs.readdir(dir);
|
|
247
|
-
for (const file of files) {
|
|
248
|
-
const filePath = path.join(dir, file);
|
|
249
|
-
let stats;
|
|
250
|
-
try {
|
|
251
|
-
stats = await fs.stat(filePath);
|
|
252
|
-
} catch { continue; }
|
|
253
|
-
|
|
254
|
-
if (!stats.isDirectory()) continue;
|
|
255
|
-
|
|
256
|
-
const cmdPathParts = [...prefix, file];
|
|
257
|
-
const cmdPath = cmdPathParts.join(' ');
|
|
258
|
-
|
|
259
|
-
let manifest: Manifest | null = null;
|
|
260
|
-
const mPath = path.join(filePath, 'manifest.json');
|
|
261
|
-
if (await fs.pathExists(mPath)) {
|
|
262
|
-
manifest = await readManifest(mPath);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
let type = 'group';
|
|
266
|
-
if (manifest) {
|
|
267
|
-
if (isCappedManifest(manifest)) {
|
|
268
|
-
type = manifest.type || 'scaffold';
|
|
269
|
-
} else if (manifest.type) {
|
|
270
|
-
type = manifest.type;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const item = {
|
|
275
|
-
path: cmdPath,
|
|
276
|
-
type,
|
|
277
|
-
scope,
|
|
278
|
-
description: manifest?.description || ''
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
if (!commands.has(cmdPath)) {
|
|
282
|
-
commands.set(cmdPath, item);
|
|
283
|
-
const effectiveManifest = manifest || { name: file, type: 'group' } as Manifest;
|
|
284
|
-
if (!isCappedManifest(effectiveManifest)) {
|
|
285
|
-
await walk(filePath, cmdPathParts, scope);
|
|
286
|
-
}
|
|
287
|
-
} else {
|
|
288
|
-
const existing = commands.get(cmdPath);
|
|
289
|
-
if (existing && existing.scope === 'local') {
|
|
290
|
-
if (existing.type === 'group' && type === 'group') {
|
|
291
|
-
await walk(filePath, cmdPathParts, scope);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (localRoot) {
|
|
299
|
-
await walk(path.join(localRoot, '.agentctl'), [], 'local');
|
|
300
|
-
}
|
|
301
|
-
await walk(globalRoot, [], 'global');
|
|
302
|
-
|
|
303
|
-
return Array.from(commands.values());
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Helpers
|
|
307
|
-
async function prepareCommand(args: string[], options: { cwd?: string } = {}) {
|
|
308
|
-
const cwd = options.cwd || process.cwd();
|
|
309
|
-
const rootDir = cwd;
|
|
310
|
-
const agentctlDir = path.join(rootDir, '.agentctl');
|
|
311
|
-
|
|
312
|
-
if (args.length === 0) throw new Error('No command path provided');
|
|
313
|
-
|
|
314
|
-
const cmdPath = args.join(path.sep);
|
|
315
|
-
const targetDir = path.join(agentctlDir, cmdPath);
|
|
316
|
-
|
|
317
|
-
if (await fs.pathExists(targetDir)) {
|
|
318
|
-
throw new Error(`Command ${args.join(' ')} already exists`);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
let current = path.dirname(targetDir);
|
|
322
|
-
while (current.length >= agentctlDir.length && !isSamePath(current, path.dirname(agentctlDir))) {
|
|
323
|
-
if (await isCapped(current)) {
|
|
324
|
-
const relPath = path.relative(agentctlDir, current); // This uses cwd for resolution if agentctlDir is cwd resolved
|
|
325
|
-
// Need to verify relative works predictably
|
|
326
|
-
// agentctlDir comes from path.join(cwd, '.agentctl').
|
|
327
|
-
throw new Error(`Cannot nest command under capped command: ${relPath}`);
|
|
328
|
-
}
|
|
329
|
-
current = path.dirname(current);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
await fs.ensureDir(targetDir);
|
|
333
|
-
|
|
334
|
-
return {
|
|
335
|
-
targetDir,
|
|
336
|
-
name: args[args.length - 1],
|
|
337
|
-
isWin: process.platform === 'win32'
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
async function isCapped(dir: string): Promise<boolean> {
|
|
342
|
-
const manifestPath = path.join(dir, 'manifest.json');
|
|
343
|
-
if (await fs.pathExists(manifestPath)) {
|
|
344
|
-
try {
|
|
345
|
-
const m = await fs.readJson(manifestPath);
|
|
346
|
-
return isCappedManifest(m);
|
|
347
|
-
} catch {
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function isSamePath(p1: string, p2: string) {
|
|
355
|
-
return path.relative(p1, p2) === '';
|
|
356
|
-
}
|
package/src/fs-utils.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
|
|
5
|
-
export function getGlobalRoot() {
|
|
6
|
-
if (process.platform === 'win32') {
|
|
7
|
-
return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'agentctl');
|
|
8
|
-
}
|
|
9
|
-
return path.join(os.homedir(), '.config', 'agentctl');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getAntigravityGlobalRoot() {
|
|
13
|
-
return path.join(os.homedir(), '.gemini', 'antigravity');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function findLocalRoot(cwd: string = process.cwd()): string | null {
|
|
17
|
-
let current = path.resolve(cwd);
|
|
18
|
-
const root = path.parse(current).root;
|
|
19
|
-
// Safety break and root check
|
|
20
|
-
// Using for(;;) to avoid no-constant-condition
|
|
21
|
-
for (; ;) {
|
|
22
|
-
if (fs.existsSync(path.join(current, '.agentctl'))) {
|
|
23
|
-
return current;
|
|
24
|
-
}
|
|
25
|
-
if (current === root) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
current = path.dirname(current);
|
|
29
|
-
}
|
|
30
|
-
}
|