@ekanos/cli 0.10.0 → 0.12.0

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.
Files changed (69) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/agents-edit.d.ts +49 -0
  3. package/dist/commands/agents-edit.js +385 -0
  4. package/dist/commands/agents-edit.js.map +1 -0
  5. package/dist/commands/agents.d.ts +24 -1
  6. package/dist/commands/agents.js +117 -8
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/apps.d.ts +6 -2
  9. package/dist/commands/apps.js +249 -4
  10. package/dist/commands/apps.js.map +1 -1
  11. package/dist/commands/reviewers.d.ts +21 -0
  12. package/dist/commands/reviewers.js +162 -0
  13. package/dist/commands/reviewers.js.map +1 -0
  14. package/dist/commands/routes.d.ts +23 -0
  15. package/dist/commands/routes.js +277 -0
  16. package/dist/commands/routes.js.map +1 -0
  17. package/dist/commands/runs.d.ts +42 -0
  18. package/dist/commands/runs.js +362 -0
  19. package/dist/commands/runs.js.map +1 -0
  20. package/dist/commands/skills.d.ts +60 -0
  21. package/dist/commands/skills.js +397 -0
  22. package/dist/commands/skills.js.map +1 -0
  23. package/dist/commands/team-agents-agents.d.ts +9 -0
  24. package/dist/commands/team-agents-agents.js +188 -0
  25. package/dist/commands/team-agents-agents.js.map +1 -0
  26. package/dist/commands/team-agents-mcp.d.ts +4 -0
  27. package/dist/commands/team-agents-mcp.js +218 -0
  28. package/dist/commands/team-agents-mcp.js.map +1 -0
  29. package/dist/commands/team-agents-triggers.d.ts +12 -0
  30. package/dist/commands/team-agents-triggers.js +251 -0
  31. package/dist/commands/team-agents-triggers.js.map +1 -0
  32. package/dist/commands/team-agents.d.ts +31 -0
  33. package/dist/commands/team-agents.js +71 -0
  34. package/dist/commands/team-agents.js.map +1 -0
  35. package/dist/commands/teams-onboard.d.ts +51 -0
  36. package/dist/commands/teams-onboard.js +261 -0
  37. package/dist/commands/teams-onboard.js.map +1 -0
  38. package/dist/commands/teams.d.ts +16 -1
  39. package/dist/commands/teams.js +22 -3
  40. package/dist/commands/teams.js.map +1 -1
  41. package/dist/connections-api.d.ts +125 -0
  42. package/dist/connections-api.js +156 -0
  43. package/dist/connections-api.js.map +1 -0
  44. package/dist/index.js +226 -6
  45. package/dist/index.js.map +1 -1
  46. package/dist/reviewers-api.d.ts +33 -0
  47. package/dist/reviewers-api.js +81 -0
  48. package/dist/reviewers-api.js.map +1 -0
  49. package/dist/routes-api.d.ts +105 -0
  50. package/dist/routes-api.js +147 -0
  51. package/dist/routes-api.js.map +1 -0
  52. package/dist/runs-api.d.ts +34 -0
  53. package/dist/runs-api.js +112 -0
  54. package/dist/runs-api.js.map +1 -0
  55. package/dist/skills-api.d.ts +28 -0
  56. package/dist/skills-api.js +104 -0
  57. package/dist/skills-api.js.map +1 -0
  58. package/dist/teams-api.d.ts +45 -3
  59. package/dist/teams-api.js +72 -3
  60. package/dist/teams-api.js.map +1 -1
  61. package/package.json +2 -2
  62. package/templates/package.json.tmpl +4 -4
  63. package/templates/skills/build-agent/SKILL.md +94 -10
  64. package/templates/skills/build-agent/references/agent-json.md +85 -4
  65. package/templates/skills/build-agent/references/rollout.md +9 -1
  66. package/templates/skills/build-agent/references/set-up-an-integration.md +59 -3
  67. package/templates/skills/build-agent/references/skills.md +81 -0
  68. package/templates/skills/build-agent/references/triggers.md +25 -5
  69. package/templates/skills/ekanos-platform/SKILL.md +116 -5
package/README.md CHANGED
@@ -29,6 +29,7 @@ npx @ekanos/cli dev
29
29
  | `sources` | List every Ekanos source you hold a developer or source-admin seat on (`GET /api/partner/sources`), with a marker on this project's current publish target. |
30
30
  | `catalog` | `ekanos catalog [<product-slug>]` — what a source can already use (`GET /api/partner/catalog`): every product enabled, switched off or available to enable, with its tools, agent triggers and generated setup steps. Check it before building something the source may already have. |
31
31
  | `agents` | `ekanos agents init | list | show | pull | apply | publish | rollback` — the source's agents, held by Ekanos (`/api/partner/agents`). `init` sets up a folder for designing agents with Claude (the `build-agent` and `ekanos-platform` skills, the docs MCP server, a CLAUDE.md) without scaffolding an integration. `apply` stores a new version as a draft, `publish` makes a version the one every team runs, `rollback` publishes an earlier one. Either seat applies; only the source admin publishes.
32
+ | `reviewers` | `ekanos reviewers list \| add <email> --reach source\|team [--team <slug>] \| remove <email> [--team <slug>]` — a Source admin names who decides agent reply drafts held under an `approve` policy (`/api/partner/reviewers`). A `source`-reach reviewer decides every team's drafts; a `team`-reach reviewer one team's. The reviewer must be a member of one of the Source's teams. |
32
33
  | `use` | `ekanos use <source-slug>` — set this project's publish target to a source you hold a seat on. Refuses (`forbidden`, with your actual seats in the hint) for a slug you don't hold a seat on, so a typo can't silently repoint a project. |
33
34
  | `publish` | Validate, pack and submit the integration to an Ekanos host — refusing (`forbidden`) a target you hold no seat on, and confirming an ambiguous multi-seat target before submitting. See [Publish target verification](#publish-target-verification). |
34
35
  | `upgrade` | Move this toolchain to a coherent target version, with verify-and-rollback. `--check` reports current vs. target and changes nothing; a bare `upgrade` installs, verifies (`validate` + the test script), and rolls back automatically on any failure. See [Keeping the toolchain current](#keeping-the-toolchain-current). |
@@ -0,0 +1,49 @@
1
+ import { type AgentTemplateBody, type AgentTemplateDetail, type AgentTemplateLink, type AgentTemplateResult, type AgentTemplateTrigger } from '@ekanos/integration-schema';
2
+ import type { CliContext } from '../context.js';
3
+ import type { ExitCode } from '../exit-codes.js';
4
+ import type { ResultRow } from '../ui.js';
5
+ /**
6
+ * Direct edits to one template (plan 239.10): `agents policy | triggers |
7
+ * links | overrides <key> …`. Each reads the latest version, changes the
8
+ * body, and stores it as the next version — a draft, exactly as `apply`
9
+ * would — then `--publish` rolls it out. Nothing local is written: a pulled
10
+ * `agent.json` is left alone, and the result says when it fell behind.
11
+ */
12
+ export type TemplateEditSubcommand = 'policy' | 'triggers' | 'links' | 'overrides';
13
+ export interface TemplateEditArgs {
14
+ subcommand: TemplateEditSubcommand;
15
+ key: string;
16
+ /** Positionals after the key: an action and its arguments. */
17
+ rest: string[];
18
+ event?: string;
19
+ filter?: string[];
20
+ prompt?: string;
21
+ disabled: boolean;
22
+ cron?: string;
23
+ timezone?: string;
24
+ kind?: string;
25
+ mode?: string;
26
+ reads?: string;
27
+ writes?: string;
28
+ publish: boolean;
29
+ dir: string;
30
+ }
31
+ export interface TemplateEditDeps {
32
+ host: string;
33
+ source: string;
34
+ show: () => Promise<AgentTemplateDetail>;
35
+ apply: (body: AgentTemplateBody, baseVersion: number) => Promise<AgentTemplateResult>;
36
+ /** Confirms (or refuses without --yes) and publishes `version`. */
37
+ publish: (version: number) => Promise<AgentTemplateResult>;
38
+ summarize: (result: AgentTemplateResult) => string;
39
+ }
40
+ export declare function runTemplateEdit(ctx: CliContext, args: TemplateEditArgs, deps: TemplateEditDeps): Promise<ExitCode>;
41
+ export declare function areaRows(subcommand: TemplateEditSubcommand, body: AgentTemplateBody): ResultRow[];
42
+ export declare function describeTrigger(trigger: AgentTemplateTrigger): string;
43
+ export declare function describeLink(link: AgentTemplateLink): string;
44
+ /**
45
+ * `--filter field=value`, repeatable. A value with a comma is a list
46
+ * (`tags=vip,urgent`), `true`/`false` a switch, anything else text.
47
+ */
48
+ export declare function parseFilters(raw: readonly string[]): Record<string, string | boolean | string[]>;
49
+ export declare function isTemplateEditSubcommand(value: string): value is TemplateEditSubcommand;
@@ -0,0 +1,385 @@
1
+ import { AgentTemplateBodySchema, AgentTemplateKeySchema, resolveAgentTemplateTeamOverrides, } from '@ekanos/integration-schema';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { invalidStateError, usageError, validationError } from '../errors.js';
5
+ import { classifyPath } from '../safe-fs.js';
6
+ const AREAS = ['policy', 'triggers', 'links'];
7
+ export async function runTemplateEdit(ctx, args, deps) {
8
+ const template = await deps.show();
9
+ const [action, ...params] = args.rest;
10
+ const showing = action === undefined || action === 'list' || action === 'show';
11
+ if (showing &&
12
+ !(args.subcommand === 'policy' && (args.reads || args.writes))) {
13
+ if (params.length > 0) {
14
+ throw usageError(`"agents ${args.subcommand} ${args.key} ${action}" takes no more arguments.`, HELP[args.subcommand]);
15
+ }
16
+ return showArea(ctx, args.subcommand, template, deps.host);
17
+ }
18
+ const edit = editBody(args, template, action, params);
19
+ const parsed = AgentTemplateBodySchema.safeParse(edit.body);
20
+ if (!parsed.success) {
21
+ throw validationError([
22
+ `That change would make ${args.key} invalid:`,
23
+ ...parsed.error.issues.map((issue) => ` ${issue.path.join('.') || '(root)'}: ${issue.message}`),
24
+ ].join('\n'), HELP[args.subcommand]);
25
+ }
26
+ const applied = await deps.apply(parsed.data, template.latestVersion);
27
+ const published = args.publish && applied.outcome !== 'unchanged'
28
+ ? await deps.publish(applied.version)
29
+ : null;
30
+ const stale = staleLocalFile(args.dir, args.key, applied.version);
31
+ const notes = [
32
+ published
33
+ ? null
34
+ : applied.outcome === 'unchanged'
35
+ ? 'Nothing changed.'
36
+ : `Teams still run the published version. Run "ekanos agents publish ${args.key}" to roll it out to teams.`,
37
+ stale
38
+ ? `${stale} is now behind the host (v${applied.version}). "ekanos agents pull ${args.key}" refreshes it.`
39
+ : null,
40
+ ].filter((note) => note !== null);
41
+ const human = [
42
+ ctx.ui.result({
43
+ title: `${edit.change} (${args.key} v${applied.version})`,
44
+ tone: applied.outcome === 'unchanged' ? 'warn' : 'ok',
45
+ rows: areaRows(args.subcommand, parsed.data),
46
+ notes,
47
+ }),
48
+ published ? deps.summarize(published) : null,
49
+ ]
50
+ .filter(Boolean)
51
+ .join('\n\n');
52
+ return ctx.succeed(Object.assign(Object.assign({ host: deps.host, change: edit.change }, applied), { published: published !== null && published !== void 0 ? published : null }), human);
53
+ }
54
+ function showArea(ctx, subcommand, template, host) {
55
+ var _a, _b, _c;
56
+ const { body } = template;
57
+ const switches = resolveAgentTemplateTeamOverrides(body);
58
+ const data = Object.assign(Object.assign(Object.assign({ host, key: template.key, version: template.latestVersion, publishedVersion: template.publishedVersion, teamOverrides: switches }, (subcommand === 'policy' ? { policy: body.policy } : {})), (subcommand === 'triggers' ? { triggers: (_a = body.triggers) !== null && _a !== void 0 ? _a : [] } : {})), (subcommand === 'links' ? { links: (_b = body.links) !== null && _b !== void 0 ? _b : [] } : {}));
59
+ return ctx.succeed(data, ctx.ui.result({
60
+ title: `${TITLE[subcommand]} of ${template.key} (v${template.latestVersion} latest)`,
61
+ rows: areaRows(subcommand, body),
62
+ notes: template.publishedVersion === template.latestVersion
63
+ ? []
64
+ : [
65
+ `Teams run v${(_c = template.publishedVersion) !== null && _c !== void 0 ? _c : 'none'}; this is the latest draft.`,
66
+ ],
67
+ }));
68
+ }
69
+ const TITLE = {
70
+ policy: 'Approvals',
71
+ triggers: 'Triggers',
72
+ links: 'Links',
73
+ overrides: 'Team overrides',
74
+ };
75
+ export function areaRows(subcommand, body) {
76
+ const switches = resolveAgentTemplateTeamOverrides(body);
77
+ const teams = (area) => switches[area]
78
+ ? 'may keep their own differences'
79
+ : 'follow the template exactly';
80
+ switch (subcommand) {
81
+ case 'policy':
82
+ return [
83
+ ['Reads', body.policy.reads],
84
+ ['Writes', body.policy.writes],
85
+ [
86
+ 'Per tool',
87
+ Object.keys(body.policy.overrides).length > 0
88
+ ? Object.entries(body.policy.overrides).map(([tool, mode]) => `${tool}: ${mode}`)
89
+ : 'none',
90
+ ],
91
+ ['Teams', teams('policy')],
92
+ ];
93
+ case 'triggers':
94
+ return [
95
+ [
96
+ 'Triggers',
97
+ body.triggers === undefined
98
+ ? 'none declared (each team sets its own)'
99
+ : body.triggers.length > 0
100
+ ? body.triggers.map(describeTrigger)
101
+ : 'none',
102
+ ],
103
+ ['Teams', teams('triggers')],
104
+ ];
105
+ case 'links':
106
+ return [
107
+ [
108
+ 'Links',
109
+ body.links === undefined
110
+ ? 'none declared (each team sets its own)'
111
+ : body.links.length > 0
112
+ ? body.links.map(describeLink)
113
+ : 'none',
114
+ ],
115
+ ['Teams', teams('links')],
116
+ ];
117
+ case 'overrides':
118
+ return AREAS.map((area) => { var _a; return [
119
+ area,
120
+ `${switches[area] ? 'on' : 'off'} — teams ${teams(area)}` +
121
+ (((_a = body.teamOverrides) === null || _a === void 0 ? void 0 : _a[area]) === undefined ? ' (default)' : ''),
122
+ ]; });
123
+ }
124
+ }
125
+ export function describeTrigger(trigger) {
126
+ const head = trigger.type === 'event'
127
+ ? `${trigger.key}: on ${trigger.event}` +
128
+ (Object.keys(trigger.filter).length > 0
129
+ ? ` where ${Object.entries(trigger.filter)
130
+ .map(([field, value]) => `${field}=${Array.isArray(value) ? value.join(',') : String(value)}`)
131
+ .join(' ')}`
132
+ : '')
133
+ : trigger.type === 'schedule'
134
+ ? `${trigger.key}: every "${trigger.cron}" (${trigger.timezone})`
135
+ : `${trigger.key}: webhook (each team gets its own URL and secret)`;
136
+ return trigger.enabled ? head : `${head} — off`;
137
+ }
138
+ export function describeLink(link) {
139
+ var _a;
140
+ return link.kind === 'talk'
141
+ ? `may ask ${link.to}`
142
+ : `hands work to ${link.to} (${(_a = link.mode) !== null && _a !== void 0 ? _a : 'await'})`;
143
+ }
144
+ function editBody(args, template, action, params) {
145
+ const body = structuredClone(template.body);
146
+ switch (args.subcommand) {
147
+ case 'policy':
148
+ return editPolicy(args, body, action, params);
149
+ case 'triggers':
150
+ return editTriggers(args, body, action, params);
151
+ case 'links':
152
+ return editLinks(args, body, action, params);
153
+ case 'overrides':
154
+ return editOverrides(args, body, action, params);
155
+ }
156
+ }
157
+ function approval(value, what) {
158
+ if (value === 'auto' || value === 'approve')
159
+ return value;
160
+ throw usageError(`${what} must be "auto" or "approve" (got "${value !== null && value !== void 0 ? value : ''}").`, HELP.policy);
161
+ }
162
+ function editPolicy(args, body, action, params) {
163
+ const changes = [];
164
+ if (args.reads !== undefined) {
165
+ body.policy.reads = approval(args.reads, '--reads');
166
+ changes.push(`reads ${body.policy.reads}`);
167
+ }
168
+ if (args.writes !== undefined) {
169
+ body.policy.writes = approval(args.writes, '--writes');
170
+ changes.push(`writes ${body.policy.writes}`);
171
+ }
172
+ if (action === 'set') {
173
+ const [tool, mode, ...extra] = params;
174
+ if (!tool || extra.length > 0) {
175
+ throw usageError('policy set needs a tool and a mode.', HELP.policy);
176
+ }
177
+ body.policy.overrides[tool] = approval(mode, tool);
178
+ changes.push(`${tool} ${body.policy.overrides[tool]}`);
179
+ }
180
+ else if (action === 'unset') {
181
+ const [tool, ...extra] = params;
182
+ if (!tool || extra.length > 0) {
183
+ throw usageError('policy unset needs a tool.', HELP.policy);
184
+ }
185
+ if (!(tool in body.policy.overrides)) {
186
+ throw invalidStateError(`${tool} has no setting of its own on ${args.key}.`, `"ekanos agents policy ${args.key}" lists them.`);
187
+ }
188
+ delete body.policy.overrides[tool];
189
+ changes.push(`${tool} back to the default`);
190
+ }
191
+ else if (action !== undefined && action !== 'show' && action !== 'list') {
192
+ throw usageError(`Unknown policy action "${action}".`, HELP.policy);
193
+ }
194
+ if (changes.length === 0) {
195
+ throw usageError('Nothing to change.', HELP.policy);
196
+ }
197
+ return { body, change: `Approvals: ${changes.join(', ')}` };
198
+ }
199
+ function editTriggers(args, body, action, params) {
200
+ var _a, _b, _c, _d;
201
+ const triggers = [...((_a = body.triggers) !== null && _a !== void 0 ? _a : [])];
202
+ if (action === 'remove') {
203
+ const [triggerKey, ...extra] = params;
204
+ if (!triggerKey || extra.length > 0) {
205
+ throw usageError('triggers remove needs a trigger key.', HELP.triggers);
206
+ }
207
+ const kept = triggers.filter((trigger) => trigger.key !== triggerKey);
208
+ if (kept.length === triggers.length) {
209
+ throw invalidStateError(`${args.key} has no trigger "${triggerKey}".`, `"ekanos agents triggers ${args.key}" lists them.`);
210
+ }
211
+ return {
212
+ body: Object.assign(Object.assign({}, body), { triggers: kept }),
213
+ change: `Removed trigger ${triggerKey}`,
214
+ };
215
+ }
216
+ if (action !== 'add') {
217
+ throw usageError(`Unknown triggers action "${action}".`, HELP.triggers);
218
+ }
219
+ const [type, triggerKey, ...extra] = params;
220
+ if (!type || !triggerKey || extra.length > 0) {
221
+ throw usageError('triggers add needs a type (event, schedule or webhook) and a key.', HELP.triggers);
222
+ }
223
+ if (triggers.some((trigger) => trigger.key === triggerKey)) {
224
+ throw invalidStateError(`${args.key} already has a trigger "${triggerKey}".`, `Remove it first ("ekanos agents triggers ${args.key} remove ${triggerKey}") or pick another key.`);
225
+ }
226
+ const common = {
227
+ key: triggerKey,
228
+ prompt: (_b = args.prompt) !== null && _b !== void 0 ? _b : '',
229
+ enabled: !args.disabled,
230
+ };
231
+ let trigger;
232
+ if (type === 'event') {
233
+ if (!args.event) {
234
+ throw usageError('An event trigger needs --event <slug.event>.', HELP.triggers);
235
+ }
236
+ trigger = Object.assign(Object.assign({}, common), { type: 'event', event: args.event, filter: parseFilters((_c = args.filter) !== null && _c !== void 0 ? _c : []) });
237
+ }
238
+ else if (type === 'schedule') {
239
+ if (!args.cron || !args.prompt) {
240
+ throw usageError('A schedule trigger needs --cron "<m h dom mon dow>" and --prompt.', HELP.triggers);
241
+ }
242
+ trigger = Object.assign(Object.assign({}, common), { type: 'schedule', cron: args.cron, timezone: (_d = args.timezone) !== null && _d !== void 0 ? _d : 'UTC' });
243
+ }
244
+ else if (type === 'webhook') {
245
+ if (!args.prompt) {
246
+ throw usageError('A webhook trigger needs --prompt (what to do with each request).', HELP.triggers);
247
+ }
248
+ trigger = Object.assign(Object.assign({}, common), { type: 'webhook' });
249
+ }
250
+ else {
251
+ throw usageError(`Unknown trigger type "${type}": use event, schedule or webhook.`, HELP.triggers);
252
+ }
253
+ return {
254
+ body: Object.assign(Object.assign({}, body), { triggers: [...triggers, trigger] }),
255
+ change: `Added trigger ${triggerKey}`,
256
+ };
257
+ }
258
+ /**
259
+ * `--filter field=value`, repeatable. A value with a comma is a list
260
+ * (`tags=vip,urgent`), `true`/`false` a switch, anything else text.
261
+ */
262
+ export function parseFilters(raw) {
263
+ const filter = {};
264
+ for (const entry of raw) {
265
+ const at = entry.indexOf('=');
266
+ if (at <= 0) {
267
+ throw usageError(`--filter "${entry}" must be field=value.`, 'e.g. --filter tags=vip,urgent --filter reply_as=internal_note');
268
+ }
269
+ const field = entry.slice(0, at).trim();
270
+ const value = entry.slice(at + 1).trim();
271
+ filter[field] = value.includes(',')
272
+ ? value
273
+ .split(',')
274
+ .map((item) => item.trim())
275
+ .filter(Boolean)
276
+ : value === 'true'
277
+ ? true
278
+ : value === 'false'
279
+ ? false
280
+ : value;
281
+ }
282
+ return filter;
283
+ }
284
+ function editLinks(args, body, action, params) {
285
+ var _a;
286
+ const links = [...((_a = body.links) !== null && _a !== void 0 ? _a : [])];
287
+ const [to, ...extra] = params;
288
+ if ((action === 'add' || action === 'remove') && (!to || extra.length > 0)) {
289
+ throw usageError(`links ${action} needs the other agent's key.`, HELP.links);
290
+ }
291
+ const kind = args.kind === undefined
292
+ ? undefined
293
+ : args.kind === 'talk' || args.kind === 'delegate'
294
+ ? args.kind
295
+ : (() => {
296
+ throw usageError(`--kind must be "talk" or "delegate" (got "${args.kind}").`, HELP.links);
297
+ })();
298
+ if (action === 'add') {
299
+ const target = AgentTemplateKeySchema.safeParse(to);
300
+ if (!target.success || to === args.key) {
301
+ throw usageError(`"${to}" is not another agent's key.`, '"ekanos agents list" shows them.');
302
+ }
303
+ if (!kind) {
304
+ throw usageError('links add needs --kind talk|delegate.', HELP.links);
305
+ }
306
+ if (args.mode !== undefined && kind === 'talk') {
307
+ throw usageError('Only a delegate link takes --mode.', HELP.links);
308
+ }
309
+ const mode = args.mode === undefined
310
+ ? undefined
311
+ : args.mode === 'await' || args.mode === 'handoff'
312
+ ? args.mode
313
+ : (() => {
314
+ throw usageError(`--mode must be "await" or "handoff" (got "${args.mode}").`, HELP.links);
315
+ })();
316
+ const link = Object.assign({ to: target.data, kind }, (mode ? { mode } : {}));
317
+ const others = links.filter((existing) => !(existing.to === to && existing.kind === kind));
318
+ return {
319
+ body: Object.assign(Object.assign({}, body), { links: [...others, link] }),
320
+ change: `Linked ${args.key} → ${to} (${describeLink(link)})`,
321
+ };
322
+ }
323
+ if (action === 'remove') {
324
+ const kept = links.filter((link) => !(link.to === to && (kind === undefined || link.kind === kind)));
325
+ if (kept.length === links.length) {
326
+ throw invalidStateError(`${args.key} has no link to "${to}"${kind ? ` of kind ${kind}` : ''}.`, `"ekanos agents links ${args.key}" lists them.`);
327
+ }
328
+ return {
329
+ body: Object.assign(Object.assign({}, body), { links: kept }),
330
+ change: `Removed link ${args.key} → ${to}`,
331
+ };
332
+ }
333
+ throw usageError(`Unknown links action "${action}".`, HELP.links);
334
+ }
335
+ function editOverrides(args, body, action, params) {
336
+ if (action !== 'enable' && action !== 'disable') {
337
+ throw usageError(`Unknown overrides action "${action}".`, HELP.overrides);
338
+ }
339
+ const [area, ...extra] = params;
340
+ if (!AREAS.some((name) => name === area) || extra.length > 0) {
341
+ throw usageError(`overrides ${action} needs one area: policy, triggers or links.`, HELP.overrides);
342
+ }
343
+ const name = area;
344
+ return {
345
+ body: Object.assign(Object.assign({}, body), { teamOverrides: Object.assign(Object.assign({}, body.teamOverrides), { [name]: action === 'enable' }) }),
346
+ change: action === 'enable'
347
+ ? `Teams may now keep their own ${name}`
348
+ : `Teams now follow the template's ${name} exactly (their own are kept, inactive)`,
349
+ };
350
+ }
351
+ /** A pulled `agents/<key>/agent.json` that is now behind the stored version. */
352
+ function staleLocalFile(dir, key, version) {
353
+ const file = path.join(dir, 'agents', key, 'agent.json');
354
+ if (classifyPath(file) !== 'file')
355
+ return null;
356
+ try {
357
+ const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
358
+ return typeof raw.baseVersion === 'number' && raw.baseVersion < version
359
+ ? path.relative(dir, file)
360
+ : null;
361
+ }
362
+ catch (_a) {
363
+ return null;
364
+ }
365
+ }
366
+ const HELP = {
367
+ policy: 'e.g. "ekanos agents policy <key> set zendesk_add_comment approve", ' +
368
+ '"ekanos agents policy <key> unset <tool>", ' +
369
+ '"ekanos agents policy <key> --reads auto --writes approve".',
370
+ triggers: 'e.g. "ekanos agents triggers <key> add event new-tickets --event zendesk.ticket_created ' +
371
+ '--filter tags=vip,urgent --filter reply_as=internal_note", ' +
372
+ '"… add schedule daily --cron \\"0 9 * * 1-5\\" --timezone America/Los_Angeles --prompt \\"…\\"", ' +
373
+ '"… add webhook inbound --prompt \\"…\\"", "… remove <trigger-key>".',
374
+ links: 'e.g. "ekanos agents links <key> add billing-agent --kind delegate --mode handoff", ' +
375
+ '"ekanos agents links <key> remove billing-agent [--kind talk]".',
376
+ overrides: 'e.g. "ekanos agents overrides <key> enable triggers", ' +
377
+ '"ekanos agents overrides <key> disable policy".',
378
+ };
379
+ export function isTemplateEditSubcommand(value) {
380
+ return (value === 'policy' ||
381
+ value === 'triggers' ||
382
+ value === 'links' ||
383
+ value === 'overrides');
384
+ }
385
+ //# sourceMappingURL=agents-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-edit.js","sourceRoot":"","sources":["../../src/commands/agents-edit.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AgentTemplateBody,\n AgentTemplateBodySchema,\n type AgentTemplateDetail,\n AgentTemplateKeySchema,\n type AgentTemplateLink,\n type AgentTemplateResult,\n type AgentTemplateTrigger,\n resolveAgentTemplateTeamOverrides,\n} from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { CliContext } from '../context';\nimport { invalidStateError, usageError, validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { classifyPath } from '../safe-fs';\nimport type { ResultRow } from '../ui';\n\n/**\n * Direct edits to one template (plan 239.10): `agents policy | triggers |\n * links | overrides <key> …`. Each reads the latest version, changes the\n * body, and stores it as the next version — a draft, exactly as `apply`\n * would — then `--publish` rolls it out. Nothing local is written: a pulled\n * `agent.json` is left alone, and the result says when it fell behind.\n */\n\nexport type TemplateEditSubcommand =\n | 'policy'\n | 'triggers'\n | 'links'\n | 'overrides';\n\nexport interface TemplateEditArgs {\n subcommand: TemplateEditSubcommand;\n key: string;\n /** Positionals after the key: an action and its arguments. */\n rest: string[];\n event?: string;\n filter?: string[];\n prompt?: string;\n disabled: boolean;\n cron?: string;\n timezone?: string;\n kind?: string;\n mode?: string;\n reads?: string;\n writes?: string;\n publish: boolean;\n dir: string;\n}\n\nexport interface TemplateEditDeps {\n host: string;\n source: string;\n show: () => Promise<AgentTemplateDetail>;\n apply: (\n body: AgentTemplateBody,\n baseVersion: number,\n ) => Promise<AgentTemplateResult>;\n /** Confirms (or refuses without --yes) and publishes `version`. */\n publish: (version: number) => Promise<AgentTemplateResult>;\n summarize: (result: AgentTemplateResult) => string;\n}\n\ntype Edit = { body: AgentTemplateBody; change: string };\n\nconst AREAS = ['policy', 'triggers', 'links'] as const;\n\nexport async function runTemplateEdit(\n ctx: CliContext,\n args: TemplateEditArgs,\n deps: TemplateEditDeps,\n): Promise<ExitCode> {\n const template = await deps.show();\n const [action, ...params] = args.rest;\n const showing =\n action === undefined || action === 'list' || action === 'show';\n\n if (\n showing &&\n !(args.subcommand === 'policy' && (args.reads || args.writes))\n ) {\n if (params.length > 0) {\n throw usageError(\n `\"agents ${args.subcommand} ${args.key} ${action}\" takes no more arguments.`,\n HELP[args.subcommand],\n );\n }\n return showArea(ctx, args.subcommand, template, deps.host);\n }\n\n const edit = editBody(args, template, action, params);\n const parsed = AgentTemplateBodySchema.safeParse(edit.body);\n if (!parsed.success) {\n throw validationError(\n [\n `That change would make ${args.key} invalid:`,\n ...parsed.error.issues.map(\n (issue) => ` ${issue.path.join('.') || '(root)'}: ${issue.message}`,\n ),\n ].join('\\n'),\n HELP[args.subcommand],\n );\n }\n\n const applied = await deps.apply(parsed.data, template.latestVersion);\n const published =\n args.publish && applied.outcome !== 'unchanged'\n ? await deps.publish(applied.version)\n : null;\n const stale = staleLocalFile(args.dir, args.key, applied.version);\n\n const notes = [\n published\n ? null\n : applied.outcome === 'unchanged'\n ? 'Nothing changed.'\n : `Teams still run the published version. Run \"ekanos agents publish ${args.key}\" to roll it out to teams.`,\n stale\n ? `${stale} is now behind the host (v${applied.version}). \"ekanos agents pull ${args.key}\" refreshes it.`\n : null,\n ].filter((note): note is string => note !== null);\n\n const human = [\n ctx.ui.result({\n title: `${edit.change} (${args.key} v${applied.version})`,\n tone: applied.outcome === 'unchanged' ? 'warn' : 'ok',\n rows: areaRows(args.subcommand, parsed.data),\n notes,\n }),\n published ? deps.summarize(published) : null,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n\n return ctx.succeed(\n {\n host: deps.host,\n change: edit.change,\n ...applied,\n published: published ?? null,\n },\n human,\n );\n}\n\nfunction showArea(\n ctx: CliContext,\n subcommand: TemplateEditSubcommand,\n template: AgentTemplateDetail,\n host: string,\n): ExitCode {\n const { body } = template;\n const switches = resolveAgentTemplateTeamOverrides(body);\n const data = {\n host,\n key: template.key,\n version: template.latestVersion,\n publishedVersion: template.publishedVersion,\n teamOverrides: switches,\n ...(subcommand === 'policy' ? { policy: body.policy } : {}),\n ...(subcommand === 'triggers' ? { triggers: body.triggers ?? [] } : {}),\n ...(subcommand === 'links' ? { links: body.links ?? [] } : {}),\n };\n return ctx.succeed(\n data,\n ctx.ui.result({\n title: `${TITLE[subcommand]} of ${template.key} (v${template.latestVersion} latest)`,\n rows: areaRows(subcommand, body),\n notes:\n template.publishedVersion === template.latestVersion\n ? []\n : [\n `Teams run v${template.publishedVersion ?? 'none'}; this is the latest draft.`,\n ],\n }),\n );\n}\n\nconst TITLE: Record<TemplateEditSubcommand, string> = {\n policy: 'Approvals',\n triggers: 'Triggers',\n links: 'Links',\n overrides: 'Team overrides',\n};\n\nexport function areaRows(\n subcommand: TemplateEditSubcommand,\n body: AgentTemplateBody,\n): ResultRow[] {\n const switches = resolveAgentTemplateTeamOverrides(body);\n const teams = (area: (typeof AREAS)[number]) =>\n switches[area]\n ? 'may keep their own differences'\n : 'follow the template exactly';\n switch (subcommand) {\n case 'policy':\n return [\n ['Reads', body.policy.reads],\n ['Writes', body.policy.writes],\n [\n 'Per tool',\n Object.keys(body.policy.overrides).length > 0\n ? Object.entries(body.policy.overrides).map(\n ([tool, mode]) => `${tool}: ${mode}`,\n )\n : 'none',\n ],\n ['Teams', teams('policy')],\n ];\n case 'triggers':\n return [\n [\n 'Triggers',\n body.triggers === undefined\n ? 'none declared (each team sets its own)'\n : body.triggers.length > 0\n ? body.triggers.map(describeTrigger)\n : 'none',\n ],\n ['Teams', teams('triggers')],\n ];\n case 'links':\n return [\n [\n 'Links',\n body.links === undefined\n ? 'none declared (each team sets its own)'\n : body.links.length > 0\n ? body.links.map(describeLink)\n : 'none',\n ],\n ['Teams', teams('links')],\n ];\n case 'overrides':\n return AREAS.map((area): ResultRow => [\n area,\n `${switches[area] ? 'on' : 'off'} — teams ${teams(area)}` +\n (body.teamOverrides?.[area] === undefined ? ' (default)' : ''),\n ]);\n }\n}\n\nexport function describeTrigger(trigger: AgentTemplateTrigger): string {\n const head =\n trigger.type === 'event'\n ? `${trigger.key}: on ${trigger.event}` +\n (Object.keys(trigger.filter).length > 0\n ? ` where ${Object.entries(trigger.filter)\n .map(\n ([field, value]) =>\n `${field}=${Array.isArray(value) ? value.join(',') : String(value)}`,\n )\n .join(' ')}`\n : '')\n : trigger.type === 'schedule'\n ? `${trigger.key}: every \"${trigger.cron}\" (${trigger.timezone})`\n : `${trigger.key}: webhook (each team gets its own URL and secret)`;\n return trigger.enabled ? head : `${head} — off`;\n}\n\nexport function describeLink(link: AgentTemplateLink): string {\n return link.kind === 'talk'\n ? `may ask ${link.to}`\n : `hands work to ${link.to} (${link.mode ?? 'await'})`;\n}\n\nfunction editBody(\n args: TemplateEditArgs,\n template: AgentTemplateDetail,\n action: string | undefined,\n params: string[],\n): Edit {\n const body = structuredClone(template.body);\n switch (args.subcommand) {\n case 'policy':\n return editPolicy(args, body, action, params);\n case 'triggers':\n return editTriggers(args, body, action, params);\n case 'links':\n return editLinks(args, body, action, params);\n case 'overrides':\n return editOverrides(args, body, action, params);\n }\n}\n\nfunction approval(value: string | undefined, what: string) {\n if (value === 'auto' || value === 'approve') return value;\n throw usageError(\n `${what} must be \"auto\" or \"approve\" (got \"${value ?? ''}\").`,\n HELP.policy,\n );\n}\n\nfunction editPolicy(\n args: TemplateEditArgs,\n body: AgentTemplateBody,\n action: string | undefined,\n params: string[],\n): Edit {\n const changes: string[] = [];\n if (args.reads !== undefined) {\n body.policy.reads = approval(args.reads, '--reads');\n changes.push(`reads ${body.policy.reads}`);\n }\n if (args.writes !== undefined) {\n body.policy.writes = approval(args.writes, '--writes');\n changes.push(`writes ${body.policy.writes}`);\n }\n if (action === 'set') {\n const [tool, mode, ...extra] = params;\n if (!tool || extra.length > 0) {\n throw usageError('policy set needs a tool and a mode.', HELP.policy);\n }\n body.policy.overrides[tool] = approval(mode, tool);\n changes.push(`${tool} ${body.policy.overrides[tool]}`);\n } else if (action === 'unset') {\n const [tool, ...extra] = params;\n if (!tool || extra.length > 0) {\n throw usageError('policy unset needs a tool.', HELP.policy);\n }\n if (!(tool in body.policy.overrides)) {\n throw invalidStateError(\n `${tool} has no setting of its own on ${args.key}.`,\n `\"ekanos agents policy ${args.key}\" lists them.`,\n );\n }\n delete body.policy.overrides[tool];\n changes.push(`${tool} back to the default`);\n } else if (action !== undefined && action !== 'show' && action !== 'list') {\n throw usageError(`Unknown policy action \"${action}\".`, HELP.policy);\n }\n if (changes.length === 0) {\n throw usageError('Nothing to change.', HELP.policy);\n }\n return { body, change: `Approvals: ${changes.join(', ')}` };\n}\n\nfunction editTriggers(\n args: TemplateEditArgs,\n body: AgentTemplateBody,\n action: string | undefined,\n params: string[],\n): Edit {\n const triggers = [...(body.triggers ?? [])];\n if (action === 'remove') {\n const [triggerKey, ...extra] = params;\n if (!triggerKey || extra.length > 0) {\n throw usageError('triggers remove needs a trigger key.', HELP.triggers);\n }\n const kept = triggers.filter((trigger) => trigger.key !== triggerKey);\n if (kept.length === triggers.length) {\n throw invalidStateError(\n `${args.key} has no trigger \"${triggerKey}\".`,\n `\"ekanos agents triggers ${args.key}\" lists them.`,\n );\n }\n return {\n body: { ...body, triggers: kept },\n change: `Removed trigger ${triggerKey}`,\n };\n }\n if (action !== 'add') {\n throw usageError(`Unknown triggers action \"${action}\".`, HELP.triggers);\n }\n\n const [type, triggerKey, ...extra] = params;\n if (!type || !triggerKey || extra.length > 0) {\n throw usageError(\n 'triggers add needs a type (event, schedule or webhook) and a key.',\n HELP.triggers,\n );\n }\n if (triggers.some((trigger) => trigger.key === triggerKey)) {\n throw invalidStateError(\n `${args.key} already has a trigger \"${triggerKey}\".`,\n `Remove it first (\"ekanos agents triggers ${args.key} remove ${triggerKey}\") or pick another key.`,\n );\n }\n const common = {\n key: triggerKey,\n prompt: args.prompt ?? '',\n enabled: !args.disabled,\n };\n let trigger: AgentTemplateTrigger;\n if (type === 'event') {\n if (!args.event) {\n throw usageError(\n 'An event trigger needs --event <slug.event>.',\n HELP.triggers,\n );\n }\n trigger = {\n ...common,\n type: 'event',\n event: args.event,\n filter: parseFilters(args.filter ?? []),\n };\n } else if (type === 'schedule') {\n if (!args.cron || !args.prompt) {\n throw usageError(\n 'A schedule trigger needs --cron \"<m h dom mon dow>\" and --prompt.',\n HELP.triggers,\n );\n }\n trigger = {\n ...common,\n type: 'schedule',\n cron: args.cron,\n timezone: args.timezone ?? 'UTC',\n };\n } else if (type === 'webhook') {\n if (!args.prompt) {\n throw usageError(\n 'A webhook trigger needs --prompt (what to do with each request).',\n HELP.triggers,\n );\n }\n trigger = { ...common, type: 'webhook' };\n } else {\n throw usageError(\n `Unknown trigger type \"${type}\": use event, schedule or webhook.`,\n HELP.triggers,\n );\n }\n return {\n body: { ...body, triggers: [...triggers, trigger] },\n change: `Added trigger ${triggerKey}`,\n };\n}\n\n/**\n * `--filter field=value`, repeatable. A value with a comma is a list\n * (`tags=vip,urgent`), `true`/`false` a switch, anything else text.\n */\nexport function parseFilters(\n raw: readonly string[],\n): Record<string, string | boolean | string[]> {\n const filter: Record<string, string | boolean | string[]> = {};\n for (const entry of raw) {\n const at = entry.indexOf('=');\n if (at <= 0) {\n throw usageError(\n `--filter \"${entry}\" must be field=value.`,\n 'e.g. --filter tags=vip,urgent --filter reply_as=internal_note',\n );\n }\n const field = entry.slice(0, at).trim();\n const value = entry.slice(at + 1).trim();\n filter[field] = value.includes(',')\n ? value\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean)\n : value === 'true'\n ? true\n : value === 'false'\n ? false\n : value;\n }\n return filter;\n}\n\nfunction editLinks(\n args: TemplateEditArgs,\n body: AgentTemplateBody,\n action: string | undefined,\n params: string[],\n): Edit {\n const links = [...(body.links ?? [])];\n const [to, ...extra] = params;\n if ((action === 'add' || action === 'remove') && (!to || extra.length > 0)) {\n throw usageError(\n `links ${action} needs the other agent's key.`,\n HELP.links,\n );\n }\n const kind =\n args.kind === undefined\n ? undefined\n : args.kind === 'talk' || args.kind === 'delegate'\n ? args.kind\n : (() => {\n throw usageError(\n `--kind must be \"talk\" or \"delegate\" (got \"${args.kind}\").`,\n HELP.links,\n );\n })();\n\n if (action === 'add') {\n const target = AgentTemplateKeySchema.safeParse(to);\n if (!target.success || to === args.key) {\n throw usageError(\n `\"${to}\" is not another agent's key.`,\n '\"ekanos agents list\" shows them.',\n );\n }\n if (!kind) {\n throw usageError('links add needs --kind talk|delegate.', HELP.links);\n }\n if (args.mode !== undefined && kind === 'talk') {\n throw usageError('Only a delegate link takes --mode.', HELP.links);\n }\n const mode =\n args.mode === undefined\n ? undefined\n : args.mode === 'await' || args.mode === 'handoff'\n ? args.mode\n : (() => {\n throw usageError(\n `--mode must be \"await\" or \"handoff\" (got \"${args.mode}\").`,\n HELP.links,\n );\n })();\n const link: AgentTemplateLink = {\n to: target.data,\n kind,\n ...(mode ? { mode } : {}),\n };\n const others = links.filter(\n (existing) => !(existing.to === to && existing.kind === kind),\n );\n return {\n body: { ...body, links: [...others, link] },\n change: `Linked ${args.key} → ${to} (${describeLink(link)})`,\n };\n }\n if (action === 'remove') {\n const kept = links.filter(\n (link) => !(link.to === to && (kind === undefined || link.kind === kind)),\n );\n if (kept.length === links.length) {\n throw invalidStateError(\n `${args.key} has no link to \"${to}\"${kind ? ` of kind ${kind}` : ''}.`,\n `\"ekanos agents links ${args.key}\" lists them.`,\n );\n }\n return {\n body: { ...body, links: kept },\n change: `Removed link ${args.key} → ${to}`,\n };\n }\n throw usageError(`Unknown links action \"${action}\".`, HELP.links);\n}\n\nfunction editOverrides(\n args: TemplateEditArgs,\n body: AgentTemplateBody,\n action: string | undefined,\n params: string[],\n): Edit {\n if (action !== 'enable' && action !== 'disable') {\n throw usageError(`Unknown overrides action \"${action}\".`, HELP.overrides);\n }\n const [area, ...extra] = params;\n if (!AREAS.some((name) => name === area) || extra.length > 0) {\n throw usageError(\n `overrides ${action} needs one area: policy, triggers or links.`,\n HELP.overrides,\n );\n }\n const name = area as (typeof AREAS)[number];\n return {\n body: {\n ...body,\n teamOverrides: { ...body.teamOverrides, [name]: action === 'enable' },\n },\n change:\n action === 'enable'\n ? `Teams may now keep their own ${name}`\n : `Teams now follow the template's ${name} exactly (their own are kept, inactive)`,\n };\n}\n\n/** A pulled `agents/<key>/agent.json` that is now behind the stored version. */\nfunction staleLocalFile(\n dir: string,\n key: string,\n version: number,\n): string | null {\n const file = path.join(dir, 'agents', key, 'agent.json');\n if (classifyPath(file) !== 'file') return null;\n try {\n const raw = JSON.parse(fs.readFileSync(file, 'utf8')) as {\n baseVersion?: unknown;\n };\n return typeof raw.baseVersion === 'number' && raw.baseVersion < version\n ? path.relative(dir, file)\n : null;\n } catch {\n return null;\n }\n}\n\nconst HELP: Record<TemplateEditSubcommand, string> = {\n policy:\n 'e.g. \"ekanos agents policy <key> set zendesk_add_comment approve\", ' +\n '\"ekanos agents policy <key> unset <tool>\", ' +\n '\"ekanos agents policy <key> --reads auto --writes approve\".',\n triggers:\n 'e.g. \"ekanos agents triggers <key> add event new-tickets --event zendesk.ticket_created ' +\n '--filter tags=vip,urgent --filter reply_as=internal_note\", ' +\n '\"… add schedule daily --cron \\\\\"0 9 * * 1-5\\\\\" --timezone America/Los_Angeles --prompt \\\\\"…\\\\\"\", ' +\n '\"… add webhook inbound --prompt \\\\\"…\\\\\"\", \"… remove <trigger-key>\".',\n links:\n 'e.g. \"ekanos agents links <key> add billing-agent --kind delegate --mode handoff\", ' +\n '\"ekanos agents links <key> remove billing-agent [--kind talk]\".',\n overrides:\n 'e.g. \"ekanos agents overrides <key> enable triggers\", ' +\n '\"ekanos agents overrides <key> disable policy\".',\n};\n\nexport function isTemplateEditSubcommand(\n value: string,\n): value is TemplateEditSubcommand {\n return (\n value === 'policy' ||\n value === 'triggers' ||\n value === 'links' ||\n value === 'overrides'\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { type AgentTemplateBody } from '@ekanos/integration-schema';
2
2
  import type { CliContext } from '../context.js';
3
3
  import type { ExitCode } from '../exit-codes.js';
4
- export declare const AGENTS_SUBCOMMANDS: readonly ['init', 'list', 'show', 'pull', 'apply', 'publish', 'rollback'];
4
+ export declare const AGENTS_SUBCOMMANDS: readonly ['init', 'list', 'show', 'pull', 'apply', 'publish', 'rollback', 'test', 'policy', 'triggers', 'links', 'overrides'];
5
5
  /** The file `pull` writes and `apply` reads, inside an agent's directory. */
6
6
  export declare const AGENT_FILE = "agent.json";
7
7
  /** Instructions as Markdown, beside it. Used when `agent.json` has none. */
@@ -20,12 +20,35 @@ export interface AgentsArgs {
20
20
  yes: boolean;
21
21
  /** `init`: replace the skills and CLAUDE.md even when they exist. */
22
22
  force?: boolean;
23
+ /**
24
+ * `apply`: push the skill folders beside the agent (`<dir>/skills/<slug>/`)
25
+ * first, and name them in its skills. On unless `--no-skills`.
26
+ */
27
+ skills?: boolean;
23
28
  /** `init`: `--claude` adds the docs to Claude Code without asking; `--no-claude` skips it. */
24
29
  claude?: boolean;
30
+ /** `policy | triggers | links | overrides`: positionals after the key. */
31
+ rest?: string[];
32
+ event?: string;
33
+ filter?: string[];
34
+ disabled?: boolean;
35
+ cron?: string;
36
+ timezone?: string;
37
+ kind?: string;
38
+ mode?: string;
39
+ reads?: string;
40
+ writes?: string;
41
+ /** `policy | triggers | links | overrides`: publish the stored version right away. */
42
+ publish?: boolean;
25
43
  dir: string;
26
44
  env: Record<string, string | undefined>;
27
45
  stdinIsTTY?: boolean;
28
46
  confirm?: (question: string) => Promise<boolean>;
47
+ /** `test`: the team to run the draft on, and what to give it. Also a trigger's prompt. */
48
+ team?: string;
49
+ prompt?: string;
50
+ input?: string;
51
+ follow?: boolean;
29
52
  }
30
53
  /**
31
54
  * `agents` — the Source's agent templates (plan 239.0 / 239.2). Ekanos holds