@eve-horizon/cli 0.2.10 → 0.2.11

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 CHANGED
@@ -175,6 +175,32 @@ eve job approve MyProj-abc123 --comment "LGTM"
175
175
  eve job reject MyProj-abc123 --reason "Missing tests"
176
176
  ```
177
177
 
178
+ ### Agents
179
+
180
+ ```bash
181
+ # Inspect agent policy + harness readiness
182
+ eve agents config --json
183
+
184
+ # Sync agents/teams/chat config from repo (deterministic)
185
+ eve agents sync --project proj_xxx --ref main
186
+
187
+ # Local dev sync (requires local API + allow dirty)
188
+ eve agents sync --project proj_xxx --local --allow-dirty
189
+ ```
190
+
191
+ ### Integrations + Chat
192
+
193
+ ```bash
194
+ # Connect Slack workspace (stub OAuth)
195
+ eve integrations slack connect --org org_xxx --team-id T123 --token xoxb-...
196
+
197
+ # List integrations for org
198
+ eve integrations list --org org_xxx
199
+
200
+ # Simulate inbound Slack message
201
+ eve chat simulate --project proj_xxx --team-id T123 --channel-id C123 --user-id U123 --text "hello"
202
+ ```
203
+
178
204
  #### Job Results
179
205
 
180
206
  Fetch and display completed job results:
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleAgents = handleAgents;
4
+ const node_child_process_1 = require("node:child_process");
4
5
  const node_fs_1 = require("node:fs");
5
6
  const node_path_1 = require("node:path");
6
7
  const yaml_1 = require("yaml");
@@ -8,6 +9,7 @@ const harness_capabilities_js_1 = require("../lib/harness-capabilities.js");
8
9
  const args_1 = require("../lib/args");
9
10
  const client_1 = require("../lib/client");
10
11
  const output_1 = require("../lib/output");
12
+ const git_js_1 = require("../lib/git.js");
11
13
  function readYamlFile(filePath) {
12
14
  const raw = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
13
15
  const parsed = (0, yaml_1.parse)(raw);
@@ -16,6 +18,41 @@ function readYamlFile(filePath) {
16
18
  }
17
19
  return parsed;
18
20
  }
21
+ function resolveAgentsConfigPaths(repoRoot, manifest) {
22
+ const xEve = manifest['x-eve'] ||
23
+ manifest['x_eve'] ||
24
+ {};
25
+ const agentsBlock = xEve['agents'] || {};
26
+ const chatBlock = xEve['chat'] || {};
27
+ const manifestChat = manifest['chat'] || {};
28
+ const agentsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.config_path) ?? 'agents/agents.yaml');
29
+ const teamsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.teams_path) ?? 'agents/teams.yaml');
30
+ const chatPath = (0, node_path_1.resolve)(repoRoot, pickString(chatBlock.config_path) ?? pickString(manifestChat.config_path) ?? 'agents/chat.yaml');
31
+ return { agentsPath, teamsPath, chatPath };
32
+ }
33
+ function pickString(value) {
34
+ return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
35
+ }
36
+ function ensureFileExists(path, label) {
37
+ if (!(0, node_fs_1.existsSync)(path)) {
38
+ throw new Error(`Missing ${label} at ${path}. Update manifest config_path or add the file.`);
39
+ }
40
+ return path;
41
+ }
42
+ function isLocalApiUrl(apiUrl) {
43
+ try {
44
+ const url = new URL(apiUrl);
45
+ const host = url.hostname.toLowerCase();
46
+ return (host === 'localhost' ||
47
+ host === '127.0.0.1' ||
48
+ host === '0.0.0.0' ||
49
+ host.endsWith('.lvh.me') ||
50
+ host.endsWith('.localhost'));
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
19
56
  function loadAgentsConfig(repoRoot) {
20
57
  const eveDir = (0, node_path_1.join)(repoRoot, '.eve');
21
58
  const manifestPath = (0, node_path_1.join)(eveDir, 'manifest.yaml');
@@ -34,45 +71,126 @@ async function handleAgents(subcommand, positionals, flags, context) {
34
71
  const command = subcommand ?? 'config';
35
72
  const json = Boolean(flags.json);
36
73
  const includeHarnesses = !((0, args_1.getBooleanFlag)(flags, ['no-harnesses']) ?? false);
37
- const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['path']) ?? process.cwd());
38
- if (command !== 'config') {
39
- throw new Error('Usage: eve agents config [--path <dir>] [--no-harnesses]');
40
- }
41
- const result = loadAgentsConfig(repoRoot);
42
- const response = {
43
- repo_root: repoRoot,
44
- source: result.source,
45
- policy: result.policy,
46
- };
47
- if (result.manifest_defaults) {
48
- response.manifest_defaults = result.manifest_defaults;
49
- }
50
- if (includeHarnesses) {
51
- const harnesses = await (0, client_1.requestJson)(context, '/harnesses');
52
- response.harnesses = harnesses.data;
53
- response.capabilities = harness_capabilities_js_1.HARNESS_CAPABILITIES;
54
- }
55
- if (json) {
56
- (0, output_1.outputJson)(response, json);
57
- return;
58
- }
59
- console.log(`Agents config source: ${result.source.type}`);
60
- if ('path' in result.source) {
61
- console.log(`Path: ${result.source.path}`);
62
- }
63
- if (!result.policy) {
64
- console.log('No policy found. Add x-eve.agents to .eve/manifest.yaml.');
65
- }
66
- else {
67
- const profiles = result.policy.profiles || {};
68
- const profileNames = Object.keys(profiles);
69
- console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
70
- }
71
- if (includeHarnesses) {
72
- const harnesses = response.harnesses;
73
- if (harnesses?.length) {
74
- const ready = harnesses.filter((h) => h.auth.available).length;
75
- console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
74
+ const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir', 'path']) ?? process.cwd());
75
+ switch (command) {
76
+ case 'config': {
77
+ const result = loadAgentsConfig(repoRoot);
78
+ const response = {
79
+ repo_root: repoRoot,
80
+ source: result.source,
81
+ policy: result.policy,
82
+ };
83
+ if (result.manifest_defaults) {
84
+ response.manifest_defaults = result.manifest_defaults;
85
+ }
86
+ if (includeHarnesses) {
87
+ const harnesses = await (0, client_1.requestJson)(context, '/harnesses');
88
+ response.harnesses = harnesses.data;
89
+ response.capabilities = harness_capabilities_js_1.HARNESS_CAPABILITIES;
90
+ }
91
+ if (json) {
92
+ (0, output_1.outputJson)(response, json);
93
+ return;
94
+ }
95
+ console.log(`Agents config source: ${result.source.type}`);
96
+ if ('path' in result.source) {
97
+ console.log(`Path: ${result.source.path}`);
98
+ }
99
+ if (!result.policy) {
100
+ console.log('No policy found. Add x-eve.agents to .eve/manifest.yaml.');
101
+ }
102
+ else {
103
+ const profiles = result.policy.profiles || {};
104
+ const profileNames = Object.keys(profiles);
105
+ console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
106
+ }
107
+ if (includeHarnesses) {
108
+ const harnesses = response.harnesses;
109
+ if (harnesses?.length) {
110
+ const ready = harnesses.filter((h) => h.auth.available).length;
111
+ console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
112
+ }
113
+ }
114
+ return;
115
+ }
116
+ case 'sync': {
117
+ const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
118
+ if (!projectId) {
119
+ throw new Error('Missing project id. Provide --project or set a profile default.');
120
+ }
121
+ const ref = (0, args_1.getStringFlag)(flags, ['ref']);
122
+ const local = (0, args_1.getBooleanFlag)(flags, ['local']) ?? false;
123
+ const allowDirty = (0, args_1.getBooleanFlag)(flags, ['allow-dirty', 'allow_dirty']) ?? false;
124
+ const forceNonlocal = (0, args_1.getBooleanFlag)(flags, ['force-nonlocal', 'force_nonlocal']) ?? false;
125
+ if (local && ref) {
126
+ throw new Error('Use either --local or --ref, not both.');
127
+ }
128
+ if (!local && !ref) {
129
+ throw new Error('Missing --ref. Use --ref <sha|branch> or --local for dev sync.');
130
+ }
131
+ if (local && !forceNonlocal && !isLocalApiUrl(context.apiUrl)) {
132
+ throw new Error(`--local sync is only allowed for local API URLs (localhost or *.lvh.me). ` +
133
+ `Current API: ${context.apiUrl}. Use --force-nonlocal to override.`);
134
+ }
135
+ const gitRoot = (0, git_js_1.getGitRoot)(repoRoot);
136
+ if (!gitRoot) {
137
+ throw new Error('Not a git repository. Run from the repo root or pass --repo-dir <path>.');
138
+ }
139
+ const dirty = (0, git_js_1.isGitDirty)(gitRoot);
140
+ if (dirty && !allowDirty) {
141
+ throw new Error('Working tree is dirty. Commit changes or pass --allow-dirty to sync anyway.');
142
+ }
143
+ const manifestPath = (0, node_path_1.join)(repoRoot, '.eve', 'manifest.yaml');
144
+ if (!(0, node_fs_1.existsSync)(manifestPath)) {
145
+ throw new Error(`Missing manifest at ${manifestPath}. Expected .eve/manifest.yaml.`);
146
+ }
147
+ const manifest = readYamlFile(manifestPath);
148
+ const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
149
+ const agentsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.agentsPath, 'agents config'), 'utf-8');
150
+ const teamsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.teamsPath, 'teams config'), 'utf-8');
151
+ const chatYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.chatPath, 'chat config'), 'utf-8');
152
+ let gitSha;
153
+ let branch;
154
+ let gitRef = ref ?? 'local';
155
+ if (ref) {
156
+ gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoRoot);
157
+ branch = (0, git_js_1.resolveGitBranch)(gitRoot, ref) ?? undefined;
158
+ }
159
+ else {
160
+ gitSha = (0, node_child_process_1.execSync)('git rev-parse HEAD', { cwd: gitRoot, encoding: 'utf-8' }).trim();
161
+ branch = (0, git_js_1.getGitBranch)(gitRoot) ?? undefined;
162
+ }
163
+ if (dirty) {
164
+ gitRef = `dirty:${gitRef}`;
165
+ }
166
+ const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/agents/sync`, {
167
+ method: 'POST',
168
+ body: {
169
+ agents_yaml: agentsYaml,
170
+ teams_yaml: teamsYaml,
171
+ chat_yaml: chatYaml,
172
+ git_sha: gitSha,
173
+ branch,
174
+ git_ref: gitRef,
175
+ },
176
+ });
177
+ if (json) {
178
+ (0, output_1.outputJson)(response, json);
179
+ return;
180
+ }
181
+ console.log(`✓ Agents config synced to ${projectId}`);
182
+ console.log(` Agents: ${configPaths.agentsPath}`);
183
+ console.log(` Teams: ${configPaths.teamsPath}`);
184
+ console.log(` Chat: ${configPaths.chatPath}`);
185
+ if (gitSha)
186
+ console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
187
+ if (branch)
188
+ console.log(` Branch: ${branch}`);
189
+ if (dirty)
190
+ console.log(' Warning: working tree dirty — marked non-deployable');
191
+ return;
76
192
  }
193
+ default:
194
+ throw new Error('Usage: eve agents <config|sync>');
77
195
  }
78
196
  }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleChat = handleChat;
4
+ const args_1 = require("../lib/args");
5
+ const client_1 = require("../lib/client");
6
+ const output_1 = require("../lib/output");
7
+ async function handleChat(subcommand, _positionals, flags, context) {
8
+ const json = Boolean(flags.json);
9
+ switch (subcommand) {
10
+ case 'simulate': {
11
+ const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
12
+ if (!projectId) {
13
+ throw new Error('Missing project id. Provide --project or set a profile default.');
14
+ }
15
+ const provider = (0, args_1.getStringFlag)(flags, ['provider']) ?? 'slack';
16
+ const teamId = (0, args_1.getStringFlag)(flags, ['team-id']);
17
+ const text = (0, args_1.getStringFlag)(flags, ['text']);
18
+ if (!teamId || !text) {
19
+ throw new Error('Usage: eve chat simulate --project <id> --team-id <team> --text <message>');
20
+ }
21
+ const channelId = (0, args_1.getStringFlag)(flags, ['channel-id']);
22
+ const userId = (0, args_1.getStringFlag)(flags, ['user-id']);
23
+ const threadKey = (0, args_1.getStringFlag)(flags, ['thread-key']);
24
+ const metadataFlag = (0, args_1.getStringFlag)(flags, ['metadata']);
25
+ let metadata;
26
+ if (metadataFlag) {
27
+ try {
28
+ const parsed = JSON.parse(metadataFlag);
29
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
30
+ metadata = parsed;
31
+ }
32
+ }
33
+ catch {
34
+ throw new Error('Invalid --metadata (must be JSON object)');
35
+ }
36
+ }
37
+ const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/chat/simulate`, {
38
+ method: 'POST',
39
+ body: {
40
+ provider,
41
+ team_id: teamId,
42
+ channel_id: channelId,
43
+ user_id: userId,
44
+ text,
45
+ thread_key: threadKey,
46
+ metadata,
47
+ },
48
+ });
49
+ (0, output_1.outputJson)(response, json, `✓ Chat simulated (thread: ${response.thread_id})`);
50
+ return;
51
+ }
52
+ default:
53
+ throw new Error('Usage: eve chat <simulate>');
54
+ }
55
+ }
@@ -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/index.js CHANGED
@@ -26,6 +26,8 @@ const init_1 = require("./commands/init");
26
26
  const release_1 = require("./commands/release");
27
27
  const manifest_1 = require("./commands/manifest");
28
28
  const build_1 = require("./commands/build");
29
+ const integrations_1 = require("./commands/integrations");
30
+ const chat_1 = require("./commands/chat");
29
31
  async function main() {
30
32
  const { flags, positionals } = (0, args_1.parseArgs)(process.argv.slice(2));
31
33
  const command = positionals[0];
@@ -114,6 +116,12 @@ async function main() {
114
116
  case 'build':
115
117
  await (0, build_1.handleBuild)(subcommand, rest, flags, context);
116
118
  return;
119
+ case 'integrations':
120
+ await (0, integrations_1.handleIntegrations)(subcommand, rest, flags, context);
121
+ return;
122
+ case 'chat':
123
+ await (0, chat_1.handleChat)(subcommand, rest, flags, context);
124
+ return;
117
125
  default:
118
126
  (0, help_1.showMainHelp)();
119
127
  }
package/dist/lib/git.js CHANGED
@@ -5,6 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.isGitSha = isGitSha;
7
7
  exports.resolveGitRef = resolveGitRef;
8
+ exports.getGitRoot = getGitRoot;
9
+ exports.isGitDirty = isGitDirty;
10
+ exports.getGitBranch = getGitBranch;
11
+ exports.resolveGitBranch = resolveGitBranch;
8
12
  const node_child_process_1 = require("node:child_process");
9
13
  const node_path_1 = __importDefault(require("node:path"));
10
14
  const client_js_1 = require("./client.js");
@@ -50,12 +54,55 @@ async function resolveGitRef(context, projectId, ref, repoDir) {
50
54
  'Make sure the ref exists in the repository, or use a 40-character SHA.');
51
55
  }
52
56
  }
53
- function getGitRoot() {
57
+ function getGitRoot(repoDir) {
54
58
  try {
55
59
  return (0, node_child_process_1.execSync)('git rev-parse --show-toplevel', {
60
+ cwd: repoDir,
61
+ encoding: 'utf-8',
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ }).trim();
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function isGitDirty(repoDir) {
70
+ try {
71
+ const status = (0, node_child_process_1.execSync)('git status --porcelain', {
72
+ cwd: repoDir,
73
+ encoding: 'utf-8',
74
+ stdio: ['pipe', 'pipe', 'pipe'],
75
+ }).trim();
76
+ return status.length > 0;
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ function getGitBranch(repoDir) {
83
+ try {
84
+ const branch = (0, node_child_process_1.execSync)('git branch --show-current', {
85
+ cwd: repoDir,
56
86
  encoding: 'utf-8',
57
87
  stdio: ['pipe', 'pipe', 'pipe'],
58
88
  }).trim();
89
+ return branch || null;
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ }
95
+ function resolveGitBranch(repoDir, ref) {
96
+ if (!ref || isGitSha(ref)) {
97
+ return null;
98
+ }
99
+ try {
100
+ (0, node_child_process_1.execSync)(`git show-ref --verify --quiet refs/heads/${ref}`, {
101
+ cwd: repoDir,
102
+ encoding: 'utf-8',
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ });
105
+ return ref;
59
106
  }
60
107
  catch {
61
108
  return null;
package/dist/lib/help.js CHANGED
@@ -510,13 +510,14 @@ Jobs default to 'ready' phase, making them immediately schedulable.`,
510
510
  },
511
511
  agents: {
512
512
  description: 'Inspect agent policy config and harness capabilities for orchestration. Default profile: primary-orchestrator.',
513
- usage: 'eve agents config [--path <dir>] [--no-harnesses]',
513
+ usage: 'eve agents <config|sync> [options]',
514
514
  subcommands: {
515
515
  config: {
516
516
  description: 'Show agent policy (profiles/councils) and harness availability',
517
517
  usage: 'eve agents config [--path <dir>] [--no-harnesses]',
518
518
  options: [
519
519
  '--path <dir> Repository root to inspect (default: cwd)',
520
+ '--repo-dir <dir> Alias for --path',
520
521
  '--no-harnesses Skip harness availability lookup',
521
522
  ],
522
523
  examples: [
@@ -525,8 +526,25 @@ Jobs default to 'ready' phase, making them immediately schedulable.`,
525
526
  'eve agents config --path ../my-repo',
526
527
  ],
527
528
  },
529
+ sync: {
530
+ description: 'Sync agents.yaml/teams.yaml/chat.yaml to the API',
531
+ usage: 'eve agents sync --project <id> --ref <sha|branch> [--repo-dir <path>]',
532
+ options: [
533
+ '--project <id> Project ID (uses profile default)',
534
+ '--ref <ref> Git ref to sync (required unless --local)',
535
+ '--local Allow local sync (only for localhost/lvh.me API)',
536
+ '--allow-dirty Allow syncing a dirty working tree',
537
+ '--repo-dir <path> Repository root (default: cwd)',
538
+ '--force-nonlocal Allow --local against non-local API URL',
539
+ ],
540
+ examples: [
541
+ 'eve agents sync --project proj_xxx --ref main',
542
+ 'eve agents sync --project proj_xxx --ref HEAD',
543
+ 'eve agents sync --project proj_xxx --local --allow-dirty',
544
+ ],
545
+ },
528
546
  },
529
- examples: ['eve agents config --json'],
547
+ examples: ['eve agents config --json', 'eve agents sync --project proj_xxx --ref main'],
530
548
  },
531
549
  profile: {
532
550
  description: `Manage repo-local CLI profiles. Profiles store defaults (API URL, org, project) so you don't
@@ -1382,6 +1400,58 @@ eve-new-project-setup skill to complete configuration.`,
1382
1400
  'eve system orchestrator status',
1383
1401
  ],
1384
1402
  },
1403
+ integrations: {
1404
+ description: 'Manage chat integrations (Slack) for an organization.',
1405
+ usage: 'eve integrations <subcommand> [options]',
1406
+ subcommands: {
1407
+ list: {
1408
+ description: 'List integrations for an org',
1409
+ usage: 'eve integrations list --org <org_id>',
1410
+ options: ['--org <id> Organization ID'],
1411
+ },
1412
+ slack: {
1413
+ description: 'Connect a Slack workspace (stub OAuth)',
1414
+ usage: 'eve integrations slack connect --org <org_id> --team-id <team_id> [--token <token>]',
1415
+ options: [
1416
+ '--org <id> Organization ID',
1417
+ '--team-id <id> Slack team ID',
1418
+ '--token <token> Slack access token (stored in tokens_json)',
1419
+ '--tokens-json <json> Raw tokens_json payload',
1420
+ '--status <status> Integration status (default: active)',
1421
+ ],
1422
+ },
1423
+ test: {
1424
+ description: 'Test an integration',
1425
+ usage: 'eve integrations test <integration_id>',
1426
+ },
1427
+ },
1428
+ examples: [
1429
+ 'eve integrations list --org org_xxx',
1430
+ 'eve integrations slack connect --org org_xxx --team-id T123 --token xoxb-...',
1431
+ ],
1432
+ },
1433
+ chat: {
1434
+ description: 'Chat tooling for gateway testing.',
1435
+ usage: 'eve chat <subcommand> [options]',
1436
+ subcommands: {
1437
+ simulate: {
1438
+ description: 'Simulate an inbound chat message',
1439
+ usage: 'eve chat simulate --project <id> --team-id <team> --text <message>',
1440
+ options: [
1441
+ '--project <id> Project ID (uses profile default)',
1442
+ '--provider <name> Provider name (default: slack)',
1443
+ '--team-id <id> Slack team ID',
1444
+ '--channel-id <id> Channel ID',
1445
+ '--user-id <id> User ID',
1446
+ '--thread-key <key> Thread key override',
1447
+ '--metadata <json> Extra metadata JSON',
1448
+ ],
1449
+ },
1450
+ },
1451
+ examples: [
1452
+ 'eve chat simulate --project proj_xxx --team-id T123 --text "hello"',
1453
+ ],
1454
+ },
1385
1455
  };
1386
1456
  function showMainHelp() {
1387
1457
  console.log('Eve Horizon CLI');
@@ -1407,6 +1477,8 @@ function showMainHelp() {
1407
1477
  console.log(' secrets Manage secrets (project/org/user scope)');
1408
1478
  console.log(' harness Inspect harnesses and auth status');
1409
1479
  console.log(' agents Inspect agent policy and harness capabilities');
1480
+ console.log(' integrations Manage chat integrations (Slack)');
1481
+ console.log(' chat Simulate chat messages (gateway testing)');
1410
1482
  console.log(' profile Manage CLI profiles (API URL, defaults)');
1411
1483
  console.log(' auth Authentication (login, logout, status)');
1412
1484
  console.log(' admin User and identity management (invite)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eve-horizon/cli",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {