@eve-horizon/cli 0.2.10 → 0.2.12
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 +26 -0
- package/dist/commands/agents.js +401 -34
- package/dist/commands/auth.js +30 -1
- package/dist/commands/chat.js +55 -0
- package/dist/commands/env.js +142 -3
- package/dist/commands/init.js +16 -26
- package/dist/commands/integrations.js +73 -0
- package/dist/commands/job.js +20 -0
- package/dist/commands/migrate.js +136 -0
- package/dist/commands/org.js +45 -3
- package/dist/commands/packs.js +197 -0
- package/dist/commands/project.js +38 -1
- package/dist/commands/skills.js +15 -98
- package/dist/commands/supervise.js +60 -0
- package/dist/commands/system.js +1 -1
- package/dist/commands/thread.js +153 -0
- package/dist/index.js +24 -0
- package/dist/lib/git.js +48 -1
- package/dist/lib/help.js +339 -18
- package/package.json +3 -2
package/dist/commands/env.js
CHANGED
|
@@ -57,16 +57,19 @@ async function handleEnv(subcommand, positionals, flags, context) {
|
|
|
57
57
|
return handleLogs(positionals, flags, context, json);
|
|
58
58
|
case 'diagnose':
|
|
59
59
|
return handleDiagnose(positionals, flags, context, json);
|
|
60
|
+
case 'services':
|
|
61
|
+
return handleServices(positionals, flags, context, json);
|
|
60
62
|
case 'delete':
|
|
61
63
|
return handleDelete(positionals, flags, context, json);
|
|
62
64
|
default:
|
|
63
|
-
throw new Error('Usage: eve env <list|show|create|deploy|logs|delete>\n' +
|
|
65
|
+
throw new Error('Usage: eve env <list|show|create|deploy|logs|diagnose|services|delete>\n' +
|
|
64
66
|
' list [project] - list environments for a project\n' +
|
|
65
67
|
' show <project> <name> - show details of an environment\n' +
|
|
66
68
|
' create <name> --type=<type> [options] - create an environment\n' +
|
|
67
69
|
' deploy <env> --ref <sha> [--direct] [--inputs <json>] [--repo-dir <path>] - deploy to an environment\n' +
|
|
68
70
|
' logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] - get service logs\n' +
|
|
69
71
|
' diagnose <project> <env> - diagnose deployment health and events\n' +
|
|
72
|
+
' services <project> <env> - show per-service pod status summary\n' +
|
|
70
73
|
' delete <name> [--project=<id>] [--force] - delete an environment');
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -318,12 +321,16 @@ async function handleLogs(positionals, flags, context, json) {
|
|
|
318
321
|
const envName = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['env', 'name']);
|
|
319
322
|
const service = positionals[2] ?? (0, args_1.getStringFlag)(flags, ['service']);
|
|
320
323
|
if (!projectId || !envName || !service) {
|
|
321
|
-
throw new Error('Usage: eve env logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>]');
|
|
324
|
+
throw new Error('Usage: eve env logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] [--pod <name>] [--container <name>] [--previous] [--all-pods]');
|
|
322
325
|
}
|
|
323
326
|
const query = buildQuery({
|
|
324
327
|
since: (0, args_1.getStringFlag)(flags, ['since']),
|
|
325
328
|
tail: (0, args_1.getStringFlag)(flags, ['tail']),
|
|
326
329
|
grep: (0, args_1.getStringFlag)(flags, ['grep']),
|
|
330
|
+
pod: (0, args_1.getStringFlag)(flags, ['pod']),
|
|
331
|
+
container: (0, args_1.getStringFlag)(flags, ['container']),
|
|
332
|
+
previous: (0, args_1.toBoolean)(flags.previous) ?? undefined,
|
|
333
|
+
all_pods: (0, args_1.toBoolean)(flags['all-pods']) ?? (0, args_1.toBoolean)(flags.all_pods) ?? undefined,
|
|
327
334
|
});
|
|
328
335
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/services/${service}/logs${query}`);
|
|
329
336
|
if (json) {
|
|
@@ -335,7 +342,13 @@ async function handleLogs(positionals, flags, context, json) {
|
|
|
335
342
|
return;
|
|
336
343
|
}
|
|
337
344
|
for (const entry of response.logs) {
|
|
338
|
-
|
|
345
|
+
const prefixParts = [];
|
|
346
|
+
if (entry.pod)
|
|
347
|
+
prefixParts.push(entry.pod);
|
|
348
|
+
if (entry.container)
|
|
349
|
+
prefixParts.push(entry.container);
|
|
350
|
+
const prefix = prefixParts.length > 0 ? ` ${prefixParts.join('/')}` : '';
|
|
351
|
+
console.log(`[${entry.timestamp}]${prefix} ${entry.line}`);
|
|
339
352
|
}
|
|
340
353
|
}
|
|
341
354
|
/**
|
|
@@ -358,6 +371,34 @@ async function handleDiagnose(positionals, flags, context, json) {
|
|
|
358
371
|
}
|
|
359
372
|
formatEnvDiagnose(response);
|
|
360
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* eve env services <project> <env>
|
|
376
|
+
* Show a per-service summary using env diagnose data.
|
|
377
|
+
*/
|
|
378
|
+
async function handleServices(positionals, flags, context, json) {
|
|
379
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
380
|
+
const envName = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['env']);
|
|
381
|
+
if (!projectId || !envName) {
|
|
382
|
+
throw new Error('Usage: eve env services <project> <env>');
|
|
383
|
+
}
|
|
384
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/diagnose`);
|
|
385
|
+
const services = buildServiceSummaries(response);
|
|
386
|
+
if (json) {
|
|
387
|
+
(0, output_1.outputJson)({
|
|
388
|
+
project_id: response.project_id,
|
|
389
|
+
env_name: response.env_name,
|
|
390
|
+
namespace: response.namespace,
|
|
391
|
+
status: response.status,
|
|
392
|
+
ready: response.ready,
|
|
393
|
+
checked_at: response.checked_at,
|
|
394
|
+
services,
|
|
395
|
+
deployments: response.deployments,
|
|
396
|
+
warnings: response.warnings,
|
|
397
|
+
}, json);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
formatEnvServices(response, services);
|
|
401
|
+
}
|
|
361
402
|
/**
|
|
362
403
|
* eve env delete <name> [--project=<id>] [--force]
|
|
363
404
|
* Delete an environment
|
|
@@ -498,6 +539,104 @@ function formatEnvironmentDetails(env, health) {
|
|
|
498
539
|
console.log(` Created: ${formatDate(env.created_at)}`);
|
|
499
540
|
console.log(` Updated: ${formatDate(env.updated_at)}`);
|
|
500
541
|
}
|
|
542
|
+
function buildServiceSummaries(report) {
|
|
543
|
+
const summaries = new Map();
|
|
544
|
+
for (const pod of report.pods) {
|
|
545
|
+
const component = getPodComponent(pod.labels);
|
|
546
|
+
const existing = summaries.get(component) ?? {
|
|
547
|
+
name: component,
|
|
548
|
+
pods_total: 0,
|
|
549
|
+
pods_ready: 0,
|
|
550
|
+
restarts: 0,
|
|
551
|
+
phases: [],
|
|
552
|
+
};
|
|
553
|
+
existing.pods_total += 1;
|
|
554
|
+
if (pod.ready) {
|
|
555
|
+
existing.pods_ready += 1;
|
|
556
|
+
}
|
|
557
|
+
existing.restarts += pod.restarts;
|
|
558
|
+
if (!existing.phases.includes(pod.phase)) {
|
|
559
|
+
existing.phases.push(pod.phase);
|
|
560
|
+
}
|
|
561
|
+
summaries.set(component, existing);
|
|
562
|
+
}
|
|
563
|
+
return Array.from(summaries.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
564
|
+
}
|
|
565
|
+
function formatEnvServices(report, services) {
|
|
566
|
+
console.log(`Environment Services: ${report.env_name}`);
|
|
567
|
+
console.log('');
|
|
568
|
+
console.log(` Namespace: ${report.namespace || '(none)'}`);
|
|
569
|
+
console.log(` Status: ${report.status}`);
|
|
570
|
+
console.log(` Ready: ${report.ready ? 'yes' : 'no'}`);
|
|
571
|
+
console.log(` K8s: ${report.k8s_available ? 'available' : 'unavailable'}`);
|
|
572
|
+
if (report.warnings && report.warnings.length > 0) {
|
|
573
|
+
console.log('');
|
|
574
|
+
console.log('Warnings:');
|
|
575
|
+
for (const warning of report.warnings) {
|
|
576
|
+
console.log(` - ${warning}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (services.length === 0) {
|
|
580
|
+
console.log('');
|
|
581
|
+
console.log('No service pods found.');
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
console.log('');
|
|
585
|
+
console.log('Services (from pods):');
|
|
586
|
+
const nameWidth = Math.max(7, ...services.map((s) => s.name.length));
|
|
587
|
+
const totalWidth = Math.max(4, ...services.map((s) => String(s.pods_total).length));
|
|
588
|
+
const readyWidth = Math.max(5, ...services.map((s) => String(s.pods_ready).length));
|
|
589
|
+
const restartsWidth = Math.max(8, ...services.map((s) => String(s.restarts).length));
|
|
590
|
+
const phasesWidth = Math.max(6, ...services.map((s) => s.phases.join(', ').length));
|
|
591
|
+
const header = [
|
|
592
|
+
padRight('Service', nameWidth),
|
|
593
|
+
padRight('Pods', totalWidth),
|
|
594
|
+
padRight('Ready', readyWidth),
|
|
595
|
+
padRight('Restarts', restartsWidth),
|
|
596
|
+
padRight('Phases', phasesWidth),
|
|
597
|
+
].join(' ');
|
|
598
|
+
console.log(header);
|
|
599
|
+
console.log('-'.repeat(header.length));
|
|
600
|
+
for (const service of services) {
|
|
601
|
+
console.log([
|
|
602
|
+
padRight(service.name, nameWidth),
|
|
603
|
+
padRight(String(service.pods_total), totalWidth),
|
|
604
|
+
padRight(String(service.pods_ready), readyWidth),
|
|
605
|
+
padRight(String(service.restarts), restartsWidth),
|
|
606
|
+
padRight(service.phases.join(', '), phasesWidth),
|
|
607
|
+
].join(' '));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (report.deployments.length > 0) {
|
|
611
|
+
console.log('');
|
|
612
|
+
console.log('Deployments:');
|
|
613
|
+
const nameWidth = Math.max(4, ...report.deployments.map((d) => d.name.length));
|
|
614
|
+
const readyWidth = Math.max(5, ...report.deployments.map((d) => `${d.available_replicas}/${d.desired_replicas}`.length));
|
|
615
|
+
const header = [
|
|
616
|
+
padRight('Name', nameWidth),
|
|
617
|
+
padRight('Ready', readyWidth),
|
|
618
|
+
'Status',
|
|
619
|
+
].join(' ');
|
|
620
|
+
console.log(header);
|
|
621
|
+
console.log('-'.repeat(header.length));
|
|
622
|
+
for (const deployment of report.deployments) {
|
|
623
|
+
const readiness = `${deployment.available_replicas}/${deployment.desired_replicas}`;
|
|
624
|
+
const status = deployment.ready ? 'ready' : 'not-ready';
|
|
625
|
+
console.log([
|
|
626
|
+
padRight(deployment.name, nameWidth),
|
|
627
|
+
padRight(readiness, readyWidth),
|
|
628
|
+
status,
|
|
629
|
+
].join(' '));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function getPodComponent(labels) {
|
|
634
|
+
return (labels['eve.component'] ||
|
|
635
|
+
labels['app.kubernetes.io/name'] ||
|
|
636
|
+
labels['app'] ||
|
|
637
|
+
labels['component'] ||
|
|
638
|
+
'unknown');
|
|
639
|
+
}
|
|
501
640
|
function formatEnvDiagnose(report) {
|
|
502
641
|
console.log(`Environment Diagnose: ${report.env_name}`);
|
|
503
642
|
console.log('');
|
package/dist/commands/init.js
CHANGED
|
@@ -192,16 +192,15 @@ async function installSkills(projectRoot) {
|
|
|
192
192
|
console.log('No skills.txt found, skipping skill installation');
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
|
-
// Check if
|
|
196
|
-
const result = (0, node_child_process_1.spawnSync)('which', ['
|
|
195
|
+
// Check if skills CLI is available
|
|
196
|
+
const result = (0, node_child_process_1.spawnSync)('which', ['skills'], { encoding: 'utf8' });
|
|
197
197
|
if (result.status !== 0) {
|
|
198
|
-
console.log('
|
|
199
|
-
console.log(' npm install -g
|
|
198
|
+
console.log('skills CLI not found. Skills will be installed when you run:');
|
|
199
|
+
console.log(' npm install -g skills');
|
|
200
200
|
console.log(' eve skills install');
|
|
201
201
|
return;
|
|
202
202
|
}
|
|
203
|
-
|
|
204
|
-
// We directly call the openskills commands here for simplicity
|
|
203
|
+
const agents = ['claude-code', 'codex', 'gemini-cli'];
|
|
205
204
|
try {
|
|
206
205
|
const content = fs.readFileSync(skillsTxt, 'utf-8');
|
|
207
206
|
const lines = content.split('\n')
|
|
@@ -209,30 +208,21 @@ async function installSkills(projectRoot) {
|
|
|
209
208
|
.filter(line => line.length > 0);
|
|
210
209
|
for (const source of lines) {
|
|
211
210
|
console.log(` Installing: ${source}`);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
for (const agent of agents) {
|
|
212
|
+
try {
|
|
213
|
+
(0, node_child_process_1.execSync)(`skills add ${JSON.stringify(source)} -a ${agent} -y --all`, {
|
|
214
|
+
cwd: projectRoot,
|
|
215
|
+
stdio: 'inherit',
|
|
216
|
+
timeout: 120000,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
console.log(` Warning: Failed to install ${source} for ${agent}`);
|
|
221
|
+
}
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
// Ensure symlink
|
|
224
225
|
ensureSkillsSymlink(projectRoot);
|
|
225
|
-
// Sync AGENTS.md
|
|
226
|
-
try {
|
|
227
|
-
(0, node_child_process_1.execSync)('openskills sync --yes', {
|
|
228
|
-
cwd: projectRoot,
|
|
229
|
-
stdio: 'inherit',
|
|
230
|
-
timeout: 30000,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
console.log('Warning: Failed to sync AGENTS.md');
|
|
235
|
-
}
|
|
236
226
|
// Commit skill changes
|
|
237
227
|
try {
|
|
238
228
|
(0, node_child_process_1.execSync)('git add -A', { cwd: projectRoot, stdio: 'pipe' });
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleIntegrations = handleIntegrations;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handleIntegrations(subcommand, positionals, flags, context) {
|
|
8
|
+
const json = Boolean(flags.json);
|
|
9
|
+
const orgId = (0, args_1.getStringFlag)(flags, ['org']) ?? context.orgId;
|
|
10
|
+
switch (subcommand) {
|
|
11
|
+
case 'list': {
|
|
12
|
+
if (!orgId) {
|
|
13
|
+
throw new Error('Usage: eve integrations list --org <org_id>');
|
|
14
|
+
}
|
|
15
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations`);
|
|
16
|
+
(0, output_1.outputJson)(response, json);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
case 'slack': {
|
|
20
|
+
const action = positionals[0];
|
|
21
|
+
if (action !== 'connect') {
|
|
22
|
+
throw new Error('Usage: eve integrations slack connect --org <org_id> --team-id <team_id> [--token <token>]');
|
|
23
|
+
}
|
|
24
|
+
if (!orgId) {
|
|
25
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
26
|
+
}
|
|
27
|
+
const teamId = (0, args_1.getStringFlag)(flags, ['team-id']);
|
|
28
|
+
if (!teamId) {
|
|
29
|
+
throw new Error('Missing --team-id');
|
|
30
|
+
}
|
|
31
|
+
const token = (0, args_1.getStringFlag)(flags, ['token']);
|
|
32
|
+
const tokensJsonFlag = (0, args_1.getStringFlag)(flags, ['tokens-json']);
|
|
33
|
+
const status = (0, args_1.getStringFlag)(flags, ['status']);
|
|
34
|
+
let tokensJson;
|
|
35
|
+
if (tokensJsonFlag) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(tokensJsonFlag);
|
|
38
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
39
|
+
tokensJson = parsed;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error('Invalid --tokens-json (must be JSON object)');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!tokensJson && token) {
|
|
47
|
+
tokensJson = { access_token: token };
|
|
48
|
+
}
|
|
49
|
+
const body = { team_id: teamId };
|
|
50
|
+
if (tokensJson)
|
|
51
|
+
body.tokens_json = tokensJson;
|
|
52
|
+
if (status)
|
|
53
|
+
body.status = status;
|
|
54
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations/slack/connect`, { method: 'POST', body });
|
|
55
|
+
(0, output_1.outputJson)(response, json, `✓ Slack integration connected: ${response.id}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
case 'test': {
|
|
59
|
+
const integrationId = positionals[0];
|
|
60
|
+
if (!integrationId) {
|
|
61
|
+
throw new Error('Usage: eve integrations test <integration_id>');
|
|
62
|
+
}
|
|
63
|
+
if (!orgId) {
|
|
64
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
65
|
+
}
|
|
66
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations/${integrationId}/test`, { method: 'POST' });
|
|
67
|
+
(0, output_1.outputJson)(response, json, response.ok ? '✓ Integration test ok' : 'Integration test failed');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
throw new Error('Usage: eve integrations <list|slack|test>');
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/commands/job.js
CHANGED
|
@@ -1192,6 +1192,17 @@ function formatDiagnose(job, attempts, latestResult, logs) {
|
|
|
1192
1192
|
console.log(' ✓ Job completed successfully');
|
|
1193
1193
|
}
|
|
1194
1194
|
else if (job.phase === 'cancelled') {
|
|
1195
|
+
const agentRuntimeError = findAgentRuntimeError(job, latestResult);
|
|
1196
|
+
if (agentRuntimeError) {
|
|
1197
|
+
console.log(' ✗ Agent runtime error');
|
|
1198
|
+
console.log(` ${agentRuntimeError}`);
|
|
1199
|
+
console.log(' Hint: Check agent runtime status and logs:');
|
|
1200
|
+
console.log(' eve system status');
|
|
1201
|
+
console.log(' eve system logs agent-runtime --tail 200');
|
|
1202
|
+
console.log(' eve system pods');
|
|
1203
|
+
console.log('');
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1195
1206
|
// Cancelled jobs may have a failure reason in close_reason or attempt error
|
|
1196
1207
|
if (latestResult?.errorMessage?.includes('git clone')) {
|
|
1197
1208
|
console.log(' ✗ Git clone failed - check repo URL and credentials');
|
|
@@ -1232,6 +1243,15 @@ function formatDiagnose(job, attempts, latestResult, logs) {
|
|
|
1232
1243
|
}
|
|
1233
1244
|
}
|
|
1234
1245
|
}
|
|
1246
|
+
function findAgentRuntimeError(job, latestResult) {
|
|
1247
|
+
const candidates = [latestResult?.errorMessage, job.close_reason].filter(Boolean);
|
|
1248
|
+
for (const message of candidates) {
|
|
1249
|
+
if (message.includes('Agent runtime')) {
|
|
1250
|
+
return message;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1235
1255
|
/**
|
|
1236
1256
|
* Format job hierarchy as a tree
|
|
1237
1257
|
*/
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleMigrate = handleMigrate;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const yaml_1 = require("yaml");
|
|
7
|
+
const git_js_1 = require("../lib/git.js");
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Main Handler
|
|
10
|
+
// ============================================================================
|
|
11
|
+
async function handleMigrate(subcommand, _rest, _flags) {
|
|
12
|
+
switch (subcommand) {
|
|
13
|
+
case 'skills-to-packs':
|
|
14
|
+
await migrateSkillsToPacks();
|
|
15
|
+
return;
|
|
16
|
+
default:
|
|
17
|
+
console.log('Usage: eve migrate <subcommand>');
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log('Subcommands:');
|
|
20
|
+
console.log(' skills-to-packs Generate AgentPack config from skills.txt');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Subcommand Handlers
|
|
26
|
+
// ============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Read skills.txt and generate a suggested x-eve.packs YAML fragment
|
|
29
|
+
* for migration to AgentPacks.
|
|
30
|
+
*/
|
|
31
|
+
async function migrateSkillsToPacks() {
|
|
32
|
+
const repoRoot = (0, git_js_1.getGitRoot)();
|
|
33
|
+
if (!repoRoot) {
|
|
34
|
+
throw new Error('Not in a git repository. Run this from your project root.');
|
|
35
|
+
}
|
|
36
|
+
const skillsTxtPath = (0, node_path_1.join)(repoRoot, 'skills.txt');
|
|
37
|
+
if (!(0, node_fs_1.existsSync)(skillsTxtPath)) {
|
|
38
|
+
console.log('No skills.txt found at repository root. Nothing to migrate.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const content = (0, node_fs_1.readFileSync)(skillsTxtPath, 'utf-8');
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
const localSources = [];
|
|
44
|
+
const remoteSources = [];
|
|
45
|
+
for (const rawLine of lines) {
|
|
46
|
+
const line = rawLine.split('#')[0].trim();
|
|
47
|
+
if (!line)
|
|
48
|
+
continue;
|
|
49
|
+
// Skip glob patterns -- they need manual review
|
|
50
|
+
if (line.includes('*')) {
|
|
51
|
+
console.log(` [skip] Glob pattern needs manual review: ${line}`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const sourceType = classifySource(line);
|
|
55
|
+
if (sourceType === 'local') {
|
|
56
|
+
localSources.push(line);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
remoteSources.push(line);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (localSources.length === 0 && remoteSources.length === 0) {
|
|
63
|
+
console.log('skills.txt is empty or contains only comments/globs. Nothing to migrate.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Build the packs array
|
|
67
|
+
const packs = [];
|
|
68
|
+
for (const source of remoteSources) {
|
|
69
|
+
packs.push({ source: normalizeRemoteSource(source) });
|
|
70
|
+
}
|
|
71
|
+
for (const source of localSources) {
|
|
72
|
+
packs.push({ source });
|
|
73
|
+
}
|
|
74
|
+
// Build the YAML fragment
|
|
75
|
+
const fragment = {
|
|
76
|
+
'x-eve': {
|
|
77
|
+
packs,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
console.log('# Suggested AgentPack configuration for .eve/manifest.yaml');
|
|
81
|
+
console.log('# Review and merge into your existing manifest under the x-eve key.');
|
|
82
|
+
console.log('#');
|
|
83
|
+
if (localSources.length > 0) {
|
|
84
|
+
console.log('# Local paths are kept as-is. Consider publishing them as packs.');
|
|
85
|
+
}
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log((0, yaml_1.stringify)(fragment, { indent: 2 }).trimEnd());
|
|
88
|
+
console.log('');
|
|
89
|
+
console.log('# After adding packs to your manifest:');
|
|
90
|
+
console.log('# 1. Run: eve agents sync');
|
|
91
|
+
console.log('# 2. Delete skills.txt from your repo');
|
|
92
|
+
}
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Helper Functions
|
|
95
|
+
// ============================================================================
|
|
96
|
+
/**
|
|
97
|
+
* Classify a skills.txt line as local or remote.
|
|
98
|
+
*/
|
|
99
|
+
function classifySource(line) {
|
|
100
|
+
// URLs
|
|
101
|
+
if (line.startsWith('https://') || line.startsWith('http://')) {
|
|
102
|
+
return 'remote';
|
|
103
|
+
}
|
|
104
|
+
// Explicit GitHub prefix
|
|
105
|
+
if (line.startsWith('github:')) {
|
|
106
|
+
return 'remote';
|
|
107
|
+
}
|
|
108
|
+
// Local paths: absolute, home-relative, or dot-relative
|
|
109
|
+
if (line.startsWith('/') || line.startsWith('~') || line.startsWith('./') || line.startsWith('../')) {
|
|
110
|
+
return 'local';
|
|
111
|
+
}
|
|
112
|
+
// GitHub shorthand: owner/repo (single slash, no spaces)
|
|
113
|
+
if (line.includes('/') && !line.includes(' ') && !line.startsWith('.')) {
|
|
114
|
+
return 'remote';
|
|
115
|
+
}
|
|
116
|
+
// Default: treat as local
|
|
117
|
+
return 'local';
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Normalize remote source references for pack config.
|
|
121
|
+
* - `github:owner/repo` -> `owner/repo`
|
|
122
|
+
* - GitHub URLs -> `owner/repo`
|
|
123
|
+
* - owner/repo -> owner/repo (pass through)
|
|
124
|
+
*/
|
|
125
|
+
function normalizeRemoteSource(source) {
|
|
126
|
+
// Strip github: prefix
|
|
127
|
+
if (source.startsWith('github:')) {
|
|
128
|
+
return source.slice(7);
|
|
129
|
+
}
|
|
130
|
+
// Extract owner/repo from GitHub URL
|
|
131
|
+
const ghMatch = source.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
132
|
+
if (ghMatch) {
|
|
133
|
+
return ghMatch[1].replace(/\.git$/, '');
|
|
134
|
+
}
|
|
135
|
+
return source;
|
|
136
|
+
}
|
package/dist/commands/org.js
CHANGED
|
@@ -60,14 +60,19 @@ async function handleOrg(subcommand, positionals, flags, context) {
|
|
|
60
60
|
}
|
|
61
61
|
case 'update': {
|
|
62
62
|
const orgId = positionals[0];
|
|
63
|
-
if (!orgId)
|
|
64
|
-
throw new Error('Usage: eve org update <org_id> [--name <name>] [--deleted <bool>]');
|
|
63
|
+
if (!orgId) {
|
|
64
|
+
throw new Error('Usage: eve org update <org_id> [--name <name>] [--deleted <bool>] [--default-agent <slug>]');
|
|
65
|
+
}
|
|
65
66
|
const body = {};
|
|
66
67
|
if (typeof flags.name === 'string')
|
|
67
68
|
body.name = flags.name;
|
|
68
69
|
const deleted = (0, args_1.toBoolean)(flags.deleted);
|
|
69
70
|
if (deleted !== undefined)
|
|
70
71
|
body.deleted = deleted;
|
|
72
|
+
if (typeof flags['default-agent'] === 'string') {
|
|
73
|
+
const value = flags['default-agent'].trim();
|
|
74
|
+
body.default_agent_slug = (value === '' || value === 'none' || value === 'null') ? null : value;
|
|
75
|
+
}
|
|
71
76
|
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}`, { method: 'PATCH', body });
|
|
72
77
|
(0, output_1.outputJson)(response, json);
|
|
73
78
|
return;
|
|
@@ -80,8 +85,45 @@ async function handleOrg(subcommand, positionals, flags, context) {
|
|
|
80
85
|
(0, output_1.outputJson)(response, json, `✓ Organization deleted: ${orgId}`);
|
|
81
86
|
return;
|
|
82
87
|
}
|
|
88
|
+
case 'members': {
|
|
89
|
+
const action = positionals[0]; // list | add | remove
|
|
90
|
+
const orgId = typeof flags.org === 'string' ? flags.org : context.orgId;
|
|
91
|
+
if (!orgId) {
|
|
92
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
93
|
+
}
|
|
94
|
+
switch (action) {
|
|
95
|
+
case 'add': {
|
|
96
|
+
const email = (0, args_1.getStringFlag)(flags, ['email']) ?? positionals[1];
|
|
97
|
+
const role = (0, args_1.getStringFlag)(flags, ['role']) ?? 'member';
|
|
98
|
+
if (!email) {
|
|
99
|
+
throw new Error('Usage: eve org members add <email> [--role member|admin|owner]');
|
|
100
|
+
}
|
|
101
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/members`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
body: { email, role },
|
|
104
|
+
});
|
|
105
|
+
(0, output_1.outputJson)(response, json, `✓ Member added to org ${orgId}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
case 'remove': {
|
|
109
|
+
const userId = positionals[1];
|
|
110
|
+
if (!userId) {
|
|
111
|
+
throw new Error('Usage: eve org members remove <user_id>');
|
|
112
|
+
}
|
|
113
|
+
await (0, client_1.requestRaw)(context, `/orgs/${orgId}/members/${userId}`, { method: 'DELETE' });
|
|
114
|
+
(0, output_1.outputJson)({ org_id: orgId, user_id: userId, removed: true }, json, `✓ Member ${userId} removed from org ${orgId}`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
case 'list':
|
|
118
|
+
default: {
|
|
119
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/members`);
|
|
120
|
+
(0, output_1.outputJson)(response, json);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
83
125
|
default:
|
|
84
|
-
throw new Error('Usage: eve org <ensure|list|get|update|delete>');
|
|
126
|
+
throw new Error('Usage: eve org <ensure|list|get|update|delete|members>');
|
|
85
127
|
}
|
|
86
128
|
}
|
|
87
129
|
function normalizeOrgId(raw) {
|