@ekanos/cli 0.12.0 → 0.14.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 (175) hide show
  1. package/README.md +25 -8
  2. package/dist/agents-api.d.ts +11 -2
  3. package/dist/agents-api.js +39 -3
  4. package/dist/agents-api.js.map +1 -1
  5. package/dist/apps-api.d.ts +3 -2
  6. package/dist/apps-api.js +3 -7
  7. package/dist/apps-api.js.map +1 -1
  8. package/dist/auth/ekanos-api.d.ts +16 -2
  9. package/dist/auth/ekanos-api.js +101 -15
  10. package/dist/auth/ekanos-api.js.map +1 -1
  11. package/dist/auth/host-trust.d.ts +46 -0
  12. package/dist/auth/host-trust.js +79 -0
  13. package/dist/auth/host-trust.js.map +1 -0
  14. package/dist/auth/session.d.ts +10 -1
  15. package/dist/auth/session.js +28 -28
  16. package/dist/auth/session.js.map +1 -1
  17. package/dist/commands/agents-edit.d.ts +6 -2
  18. package/dist/commands/agents-edit.js +57 -26
  19. package/dist/commands/agents-edit.js.map +1 -1
  20. package/dist/commands/agents.d.ts +13 -2
  21. package/dist/commands/agents.js +148 -36
  22. package/dist/commands/agents.js.map +1 -1
  23. package/dist/commands/apps.d.ts +15 -4
  24. package/dist/commands/apps.js +133 -51
  25. package/dist/commands/apps.js.map +1 -1
  26. package/dist/commands/catalog.js +7 -6
  27. package/dist/commands/catalog.js.map +1 -1
  28. package/dist/commands/knowledge.d.ts +56 -0
  29. package/dist/commands/knowledge.js +456 -0
  30. package/dist/commands/knowledge.js.map +1 -0
  31. package/dist/commands/login.d.ts +4 -0
  32. package/dist/commands/login.js +30 -2
  33. package/dist/commands/login.js.map +1 -1
  34. package/dist/commands/logout.d.ts +9 -1
  35. package/dist/commands/logout.js +56 -7
  36. package/dist/commands/logout.js.map +1 -1
  37. package/dist/commands/publish.js +3 -3
  38. package/dist/commands/publish.js.map +1 -1
  39. package/dist/commands/reviewers.d.ts +2 -0
  40. package/dist/commands/reviewers.js +7 -3
  41. package/dist/commands/reviewers.js.map +1 -1
  42. package/dist/commands/routes.d.ts +2 -0
  43. package/dist/commands/routes.js +42 -9
  44. package/dist/commands/routes.js.map +1 -1
  45. package/dist/commands/runs.d.ts +13 -2
  46. package/dist/commands/runs.js +96 -15
  47. package/dist/commands/runs.js.map +1 -1
  48. package/dist/commands/scope-notice.d.ts +21 -0
  49. package/dist/commands/scope-notice.js +35 -0
  50. package/dist/commands/scope-notice.js.map +1 -0
  51. package/dist/commands/skills.d.ts +7 -1
  52. package/dist/commands/skills.js +80 -24
  53. package/dist/commands/skills.js.map +1 -1
  54. package/dist/commands/sources.d.ts +4 -0
  55. package/dist/commands/sources.js +233 -5
  56. package/dist/commands/sources.js.map +1 -1
  57. package/dist/commands/status.js +5 -4
  58. package/dist/commands/status.js.map +1 -1
  59. package/dist/commands/team-agent-readiness.d.ts +59 -0
  60. package/dist/commands/team-agent-readiness.js +341 -0
  61. package/dist/commands/team-agent-readiness.js.map +1 -0
  62. package/dist/commands/team-agents-agents.d.ts +24 -3
  63. package/dist/commands/team-agents-agents.js +336 -35
  64. package/dist/commands/team-agents-agents.js.map +1 -1
  65. package/dist/commands/team-agents-mcp.js +32 -3
  66. package/dist/commands/team-agents-mcp.js.map +1 -1
  67. package/dist/commands/team-agents-triggers.js +230 -54
  68. package/dist/commands/team-agents-triggers.js.map +1 -1
  69. package/dist/commands/team-agents.d.ts +3 -1
  70. package/dist/commands/team-agents.js +27 -7
  71. package/dist/commands/team-agents.js.map +1 -1
  72. package/dist/commands/teams-channel-install.d.ts +24 -0
  73. package/dist/commands/teams-channel-install.js +94 -0
  74. package/dist/commands/teams-channel-install.js.map +1 -0
  75. package/dist/commands/teams-connect.d.ts +66 -0
  76. package/dist/commands/teams-connect.js +397 -0
  77. package/dist/commands/teams-connect.js.map +1 -0
  78. package/dist/commands/teams-events.d.ts +12 -0
  79. package/dist/commands/teams-events.js +138 -0
  80. package/dist/commands/teams-events.js.map +1 -0
  81. package/dist/commands/teams-onboard.d.ts +16 -5
  82. package/dist/commands/teams-onboard.js +61 -36
  83. package/dist/commands/teams-onboard.js.map +1 -1
  84. package/dist/commands/teams-overview.d.ts +11 -0
  85. package/dist/commands/teams-overview.js +160 -0
  86. package/dist/commands/teams-overview.js.map +1 -0
  87. package/dist/commands/teams-scope.d.ts +10 -0
  88. package/dist/commands/teams-scope.js +109 -0
  89. package/dist/commands/teams-scope.js.map +1 -0
  90. package/dist/commands/teams.d.ts +64 -7
  91. package/dist/commands/teams.js +254 -60
  92. package/dist/commands/teams.js.map +1 -1
  93. package/dist/commands/upgrade.js +12 -2
  94. package/dist/commands/upgrade.js.map +1 -1
  95. package/dist/commands/use.js +2 -2
  96. package/dist/commands/use.js.map +1 -1
  97. package/dist/commands/whoami.js +1 -1
  98. package/dist/commands/whoami.js.map +1 -1
  99. package/dist/connections-api.js +1 -1
  100. package/dist/connections-api.js.map +1 -1
  101. package/dist/delegate.js +11 -4
  102. package/dist/delegate.js.map +1 -1
  103. package/dist/harness-scaffold.js +12 -0
  104. package/dist/harness-scaffold.js.map +1 -1
  105. package/dist/index.d.ts +6 -0
  106. package/dist/index.js +724 -152
  107. package/dist/index.js.map +1 -1
  108. package/dist/knowledge-api.d.ts +175 -0
  109. package/dist/knowledge-api.js +169 -0
  110. package/dist/knowledge-api.js.map +1 -0
  111. package/dist/open-url.d.ts +16 -0
  112. package/dist/open-url.js +73 -0
  113. package/dist/open-url.js.map +1 -0
  114. package/dist/parse-argv.d.ts +2 -0
  115. package/dist/parse-argv.js.map +1 -1
  116. package/dist/publish-api.d.ts +2 -1
  117. package/dist/publish-api.js +4 -3
  118. package/dist/publish-api.js.map +1 -1
  119. package/dist/pull-write.d.ts +27 -0
  120. package/dist/pull-write.js +60 -0
  121. package/dist/pull-write.js.map +1 -0
  122. package/dist/routes-api.d.ts +3 -0
  123. package/dist/routes-api.js +9 -4
  124. package/dist/routes-api.js.map +1 -1
  125. package/dist/runs-api.d.ts +7 -1
  126. package/dist/runs-api.js +21 -6
  127. package/dist/runs-api.js.map +1 -1
  128. package/dist/safe-fs.d.ts +2 -0
  129. package/dist/safe-fs.js +12 -6
  130. package/dist/safe-fs.js.map +1 -1
  131. package/dist/secret-file.d.ts +17 -0
  132. package/dist/secret-file.js +69 -0
  133. package/dist/secret-file.js.map +1 -0
  134. package/dist/skills-api.d.ts +10 -2
  135. package/dist/skills-api.js +37 -2
  136. package/dist/skills-api.js.map +1 -1
  137. package/dist/source-products-api.d.ts +63 -0
  138. package/dist/source-products-api.js +123 -0
  139. package/dist/source-products-api.js.map +1 -0
  140. package/dist/source-target.js +2 -1
  141. package/dist/source-target.js.map +1 -1
  142. package/dist/sources-api.d.ts +2 -1
  143. package/dist/sources-api.js +3 -2
  144. package/dist/sources-api.js.map +1 -1
  145. package/dist/team-connect-api.d.ts +286 -0
  146. package/dist/team-connect-api.js +107 -0
  147. package/dist/team-connect-api.js.map +1 -0
  148. package/dist/teams-api.d.ts +42 -1
  149. package/dist/teams-api.js +98 -20
  150. package/dist/teams-api.js.map +1 -1
  151. package/dist/toolchain-api.d.ts +9 -0
  152. package/dist/toolchain-api.js +17 -2
  153. package/dist/toolchain-api.js.map +1 -1
  154. package/dist/toolchain-resolve.d.ts +3 -0
  155. package/dist/toolchain-resolve.js +15 -1
  156. package/dist/toolchain-resolve.js.map +1 -1
  157. package/dist/toolchain.d.ts +6 -0
  158. package/dist/toolchain.js +19 -0
  159. package/dist/toolchain.js.map +1 -1
  160. package/dist/update-notice.js +2 -1
  161. package/dist/update-notice.js.map +1 -1
  162. package/package.json +2 -2
  163. package/templates/AGENTS.md.tmpl +6 -1
  164. package/templates/claude-skill.md.tmpl +5 -0
  165. package/templates/package.json.tmpl +4 -4
  166. package/templates/skills/build-agent/SKILL.md +115 -16
  167. package/templates/skills/build-agent/references/agent-json.md +26 -8
  168. package/templates/skills/build-agent/references/app-setup.md +2 -1
  169. package/templates/skills/build-agent/references/rollout.md +27 -4
  170. package/templates/skills/build-agent/references/set-up-an-integration.md +74 -15
  171. package/templates/skills/build-agent/references/skills.md +20 -1
  172. package/templates/skills/build-agent/references/triggers.md +84 -20
  173. package/templates/skills/build-integration/references/widget-design.md +3 -3
  174. package/templates/skills/ekanos-platform/SKILL.md +70 -17
  175. package/templates/skills/polish-widgets/references/patterns.md +2 -2
package/dist/index.js CHANGED
@@ -1,9 +1,22 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import * as fs from 'node:fs';
2
13
  import { runAgents } from './commands/agents.js';
14
+ import { TEMPLATE_EDIT_HELP } from './commands/agents-edit.js';
3
15
  import { runApps } from './commands/apps.js';
4
16
  import { runCatalog } from './commands/catalog.js';
5
17
  import { runDev } from './commands/dev.js';
6
18
  import { runInit } from './commands/init.js';
19
+ import { runKnowledge } from './commands/knowledge.js';
7
20
  import { runLogin } from './commands/login.js';
8
21
  import { runLogout } from './commands/logout.js';
9
22
  import { runPublish } from './commands/publish.js';
@@ -13,6 +26,7 @@ import { runApprovals, runRuns } from './commands/runs.js';
13
26
  import { runSkills } from './commands/skills.js';
14
27
  import { runSources } from './commands/sources.js';
15
28
  import { runStatus } from './commands/status.js';
29
+ import { TEAM_AGENT_AREAS, usageFor } from './commands/team-agents.js';
16
30
  import { runTeams } from './commands/teams.js';
17
31
  import { runTest } from './commands/test.js';
18
32
  import { runUpgrade } from './commands/upgrade.js';
@@ -27,189 +41,544 @@ import { parseArgv } from './parse-argv.js';
27
41
  export { CliContext } from './context.js';
28
42
  export { EXIT_CODES, ERROR_CODES } from './exit-codes.js';
29
43
  const GLOBAL_SPECS = {
30
- json: { type: 'boolean' },
31
- help: { type: 'boolean' },
32
- version: { type: 'boolean' },
44
+ json: {
45
+ type: 'boolean',
46
+ description: 'One JSON envelope on stdout (on by default when piped or under Claude Code; --no-json forces text)',
47
+ },
48
+ help: { type: 'boolean', description: 'Show help for the command' },
49
+ version: { type: 'boolean', description: 'Print the CLI version' },
50
+ };
51
+ /** The flags most commands share, described once. */
52
+ const HOST = {
53
+ type: 'string',
54
+ description: 'Ekanos host URL (else EKANOS_HOST, the "host" in ekanos.json, or the only stored login)',
55
+ };
56
+ const SOURCE = {
57
+ type: 'string',
58
+ description: 'The Source to act on (else the "source" in ekanos.json, or your only seat)',
59
+ };
60
+ const DIR = {
61
+ type: 'string',
62
+ description: 'Project directory, where ekanos.json and relative paths are read (default: the current one)',
33
63
  };
34
64
  const COMMAND_SPECS = {
35
65
  init: {
36
- slug: { type: 'string' },
37
- dir: { type: 'string' },
38
- force: { type: 'boolean' },
39
- yes: { type: 'boolean' },
66
+ slug: {
67
+ type: 'string',
68
+ description: 'Kebab-case id of the integration, e.g. acme-crm',
69
+ },
70
+ dir: {
71
+ type: 'string',
72
+ description: 'Target directory (default ./<slug>; --dir . scaffolds in place)',
73
+ },
74
+ force: {
75
+ type: 'boolean',
76
+ description: 'Overwrite files that already exist',
77
+ },
78
+ yes: {
79
+ type: 'boolean',
80
+ description: 'Accepted for scripts; init never prompts',
81
+ },
40
82
  // Repeatable: each occurrence appends one docs entry (URL or relative
41
83
  // path) to the scaffolded ekanos.json.
42
- docs: { type: 'string', repeatable: true },
84
+ docs: {
85
+ type: 'string',
86
+ repeatable: true,
87
+ description: "An http(s) URL or project-relative path to the API's docs, written to ekanos.json",
88
+ },
43
89
  },
44
90
  validate: {
45
- dir: { type: 'string' },
91
+ dir: DIR,
46
92
  },
47
93
  dev: {
48
- dir: { type: 'string' },
49
- port: { type: 'string' },
50
- host: { type: 'string' },
51
- force: { type: 'boolean' },
52
- start: { type: 'boolean' },
94
+ dir: DIR,
95
+ port: { type: 'string', description: 'Dev server port (default 3100)' },
96
+ host: {
97
+ type: 'string',
98
+ description: 'Interface to bind (default 127.0.0.1; anything else is exposed, with a warning)',
99
+ },
100
+ force: {
101
+ type: 'boolean',
102
+ description: 'Rewrite every generated file even when unchanged',
103
+ },
104
+ start: {
105
+ type: 'boolean',
106
+ description: 'Start the server even in JSON mode; --no-start only scaffolds',
107
+ },
53
108
  },
54
109
  test: {
55
- dir: { type: 'string' },
110
+ dir: DIR,
56
111
  // Skips the vitest JSON-reporter injection and the curated failures
57
112
  // appendix, reproducing the pre-appendix raw-passthrough behavior —
58
113
  // also the escape hatch if the vitest-detection heuristic ever guesses
59
114
  // wrong for a project. See `test-report.ts`.
60
- raw: { type: 'boolean' },
115
+ raw: {
116
+ type: 'boolean',
117
+ description: 'Run the test script as is, without the failures appendix and its reporter',
118
+ },
61
119
  },
62
120
  login: {
63
- host: { type: 'string' },
64
- force: { type: 'boolean' },
65
- claude: { type: 'boolean' },
121
+ host: {
122
+ type: 'string',
123
+ description: 'Ekanos host URL (else EKANOS_HOST, or the only stored login; never ekanos.json)',
124
+ },
125
+ force: {
126
+ type: 'boolean',
127
+ description: 'Sign in again even when the stored session is valid',
128
+ },
129
+ 'trust-host': {
130
+ type: 'boolean',
131
+ description: 'Sign in to a host you have never logged in to without being asked',
132
+ },
133
+ claude: {
134
+ type: 'boolean',
135
+ description: 'Add the Ekanos docs to Claude Code without asking; --no-claude skips it',
136
+ },
66
137
  },
67
138
  logout: {
68
- host: { type: 'string' },
69
- all: { type: 'boolean' },
139
+ host: HOST,
140
+ all: { type: 'boolean', description: 'Log out of every stored host' },
70
141
  },
71
142
  whoami: {
72
- host: { type: 'string' },
143
+ host: HOST,
73
144
  },
74
145
  status: {
75
- host: { type: 'string' },
76
- dir: { type: 'string' },
77
- findings: { type: 'boolean' },
78
- all: { type: 'boolean' },
79
- watch: { type: 'boolean' },
146
+ host: HOST,
147
+ dir: DIR,
148
+ findings: {
149
+ type: 'boolean',
150
+ description: 'Every finding of each version, not just the counts',
151
+ },
152
+ all: {
153
+ type: 'boolean',
154
+ description: "Expand the Source's other integrations into full version tables",
155
+ },
156
+ watch: {
157
+ type: 'boolean',
158
+ description: "Wait until this project's current version reaches a gate verdict",
159
+ },
80
160
  },
81
161
  sources: {
82
- host: { type: 'string' },
83
- dir: { type: 'string' },
162
+ host: HOST,
163
+ dir: DIR,
164
+ // `sources products [enable|disable <product>]`.
165
+ source: SOURCE,
166
+ yes: {
167
+ type: 'boolean',
168
+ description: 'Confirm products disable, scopes remove',
169
+ },
84
170
  },
85
171
  catalog: {
86
- host: { type: 'string' },
87
- source: { type: 'string' },
88
- dir: { type: 'string' },
172
+ host: HOST,
173
+ source: SOURCE,
174
+ dir: DIR,
89
175
  },
90
176
  apps: {
91
- host: { type: 'string' },
92
- source: { type: 'string' },
93
- dir: { type: 'string' },
94
- 'no-wait': { type: 'boolean' },
177
+ host: HOST,
178
+ source: SOURCE,
179
+ dir: DIR,
180
+ 'no-wait': {
181
+ type: 'boolean',
182
+ description: 'connect: print the links and return without waiting',
183
+ },
184
+ yes: {
185
+ type: 'boolean',
186
+ description: 'disconnect: confirm without the question (needed in a script or agent run)',
187
+ },
95
188
  },
96
189
  teams: {
97
- host: { type: 'string' },
98
- source: { type: 'string' },
99
- dir: { type: 'string' },
100
- connection: { type: 'string' },
101
- slug: { type: 'string' },
102
- owner: { type: 'string' },
103
- role: { type: 'string' },
104
- yes: { type: 'boolean' },
190
+ host: HOST,
191
+ source: SOURCE,
192
+ dir: DIR,
193
+ connection: {
194
+ type: 'string',
195
+ description: 'connect, app set: which of the integration\'s connections (e.g. "agent" for Linear\'s agent)',
196
+ },
197
+ // `teams app set`: which fields to enter, non-secret values, removals.
198
+ field: {
199
+ type: 'string',
200
+ repeatable: true,
201
+ description: 'app set: enter just this field at your terminal',
202
+ },
203
+ set: {
204
+ type: 'string',
205
+ repeatable: true,
206
+ description: 'app set: <name>=<value> for a non-secret field',
207
+ },
208
+ clear: {
209
+ type: 'string',
210
+ repeatable: true,
211
+ description: "app set: remove this field's saved value",
212
+ },
213
+ slug: {
214
+ type: 'string',
215
+ description: "create: the team's slug (default: from its name)",
216
+ },
217
+ owner: {
218
+ type: 'string',
219
+ description: 'create: invite this email as the owner instead of you',
220
+ },
221
+ role: {
222
+ type: 'string',
223
+ description: 'members invite: the role to invite as (default member)',
224
+ },
225
+ yes: {
226
+ type: 'boolean',
227
+ description: 'Confirm a change that removes, pauses, revokes or opens something up (members remove, webhook, disconnect, agents disable, triggers add slack, triggers remove|rotate, links remove, mcp remove, scope set that drops a scope)',
228
+ },
105
229
  // `teams agents|triggers|links|mcp` (team-agents.ts).
106
- as: { type: 'string' },
107
- reads: { type: 'string' },
108
- writes: { type: 'string' },
109
- effect: { type: 'string' },
110
- kind: { type: 'string' },
111
- mode: { type: 'string' },
112
- url: { type: 'string' },
113
- auth: { type: 'string' },
114
- name: { type: 'string' },
115
- cron: { type: 'string' },
116
- timezone: { type: 'string' },
117
- prompt: { type: 'string' },
118
- filter: { type: 'string' },
230
+ as: {
231
+ type: 'string',
232
+ description: 'connect: the member who signs in; agents enable: the member the agent runs as',
233
+ },
234
+ // `teams agents enable|pin … --draft`: test the unpublished draft there.
235
+ draft: {
236
+ type: 'boolean',
237
+ description: "agents enable|pin: put this team alone on the template's unpublished draft",
238
+ },
239
+ // `teams agents enable|policy`: also confirm, as yourself, that the
240
+ // agent may use your connections.
241
+ 'confirm-access': {
242
+ type: 'boolean',
243
+ description: 'agents enable|policy: also confirm, as yourself, that it may use your connections',
244
+ },
245
+ reads: {
246
+ type: 'string',
247
+ description: 'agents policy: auto|approve for every read tool',
248
+ },
249
+ writes: {
250
+ type: 'string',
251
+ description: 'agents policy: auto|approve for every write tool',
252
+ },
253
+ effect: {
254
+ type: 'string',
255
+ description: 'Not read: "teams mcp effect" takes the effect as an argument',
256
+ },
257
+ kind: { type: 'string', description: 'links add: talk|delegate' },
258
+ mode: {
259
+ type: 'string',
260
+ description: 'links add: await|handoff, for a delegate link',
261
+ },
262
+ url: { type: 'string', description: "mcp add: the server's URL" },
263
+ auth: {
264
+ type: 'string',
265
+ description: 'mcp add: none|token|oauth (default oauth)',
266
+ },
267
+ name: { type: 'string', description: "mcp add: the server's display name" },
268
+ cron: {
269
+ type: 'string',
270
+ description: 'triggers add schedule: a cron expression',
271
+ },
272
+ timezone: {
273
+ type: 'string',
274
+ description: 'triggers add schedule: an IANA time zone (default UTC)',
275
+ },
276
+ prompt: {
277
+ type: 'string',
278
+ description: 'triggers add: what the agent is told when it fires',
279
+ },
280
+ // Repeatable `field=value`, or one JSON object — as at the template level.
281
+ filter: {
282
+ type: 'string',
283
+ repeatable: true,
284
+ description: 'triggers add event: field=a,b (repeatable) or one JSON object',
285
+ },
286
+ // `teams <area> … --team <slug>`: the team, in place of the positional.
287
+ team: {
288
+ type: 'string',
289
+ description: 'The team, in place of the <team> argument (teams <area> … --team <slug>)',
290
+ },
291
+ // `teams triggers add … slack|linear`: where it answers. `--no-mention`
292
+ // is the negation of `mention`.
293
+ channel: {
294
+ type: 'string',
295
+ repeatable: true,
296
+ description: 'triggers add slack: a channel it answers in (#name or id)',
297
+ },
298
+ dms: {
299
+ type: 'boolean',
300
+ description: 'triggers add slack: also answer direct messages',
301
+ },
302
+ label: {
303
+ type: 'string',
304
+ repeatable: true,
305
+ description: 'triggers add linear: a label that hands an issue to it',
306
+ },
307
+ mention: {
308
+ type: 'boolean',
309
+ description: 'triggers add linear: --no-mention ignores @mentions',
310
+ },
311
+ // `teams connect`: `--no-wait`, `--no-open` (the negation of `open`),
312
+ // `--link-only`.
313
+ 'no-wait': {
314
+ type: 'boolean',
315
+ description: 'connect: print the sign-in link and return at once',
316
+ },
317
+ open: {
318
+ type: 'boolean',
319
+ description: 'connect: --no-open prints the link without opening a browser',
320
+ },
321
+ 'link-only': {
322
+ type: 'boolean',
323
+ description: "connect: print the team's page instead of connecting",
324
+ },
325
+ // `teams events <team> [--product] [--since] [--limit]`.
326
+ product: {
327
+ type: 'string',
328
+ description: 'events: only this product (or name it after the team)',
329
+ },
330
+ since: {
331
+ type: 'string',
332
+ description: 'events: since 30m, 24h, 7d or a date',
333
+ },
334
+ limit: { type: 'string', description: 'events: how many, 1 to 100' },
335
+ // `teams connect --reconnect`.
336
+ reconnect: {
337
+ type: 'boolean',
338
+ description: 'connect: connect again though it reads as connected (revoked or expired)',
339
+ },
119
340
  },
120
341
  reviewers: {
121
- host: { type: 'string' },
122
- source: { type: 'string' },
123
- dir: { type: 'string' },
124
- reach: { type: 'string' },
125
- team: { type: 'string' },
342
+ host: HOST,
343
+ source: SOURCE,
344
+ dir: DIR,
345
+ reach: {
346
+ type: 'string',
347
+ description: "add: source (every team's drafts) or team (one team's)",
348
+ },
349
+ team: {
350
+ type: 'string',
351
+ description: "add --reach team, remove: the team's slug",
352
+ },
353
+ yes: { type: 'boolean', description: 'remove: confirm' },
126
354
  },
127
355
  routes: {
128
- host: { type: 'string' },
129
- source: { type: 'string' },
130
- dir: { type: 'string' },
131
- label: { type: 'string' },
132
- webhook: { type: 'string' },
356
+ host: HOST,
357
+ source: SOURCE,
358
+ dir: DIR,
359
+ label: { type: 'string', description: 'set: a name for the value' },
360
+ webhook: {
361
+ type: 'string',
362
+ description: 'webhook: which webhook, when the integration has several',
363
+ },
364
+ yes: {
365
+ type: 'boolean',
366
+ description: 'remove: confirm; webhook: replace the live URL, revoking it',
367
+ },
133
368
  },
134
369
  agents: {
135
- host: { type: 'string' },
136
- source: { type: 'string' },
137
- dir: { type: 'string' },
370
+ host: HOST,
371
+ source: SOURCE,
372
+ dir: DIR,
138
373
  // Not `--version`: that is global, and prints the CLI's own version.
139
- to: { type: 'string' },
140
- 'base-version': { type: 'string' },
141
- overwrite: { type: 'boolean' },
142
- out: { type: 'string' },
143
- yes: { type: 'boolean' },
144
- force: { type: 'boolean' },
145
- claude: { type: 'boolean' },
374
+ to: {
375
+ type: 'string',
376
+ description: 'publish, rollback: the version teams run',
377
+ },
378
+ 'base-version': {
379
+ type: 'string',
380
+ description: 'apply: the version your edit starts from (refused when the host has moved on)',
381
+ },
382
+ overwrite: {
383
+ type: 'boolean',
384
+ description: 'apply: replace a newer version on the host anyway',
385
+ },
386
+ out: {
387
+ type: 'string',
388
+ description: 'pull: where to write the template (default agents/<key>)',
389
+ },
390
+ yes: {
391
+ type: 'boolean',
392
+ description: 'publish, rollback, archive, --publish: confirm (needed in a script or agent run)',
393
+ },
394
+ all: {
395
+ type: 'boolean',
396
+ description: 'list: include archived agents',
397
+ },
398
+ force: {
399
+ type: 'boolean',
400
+ description: 'init: replace the skills and CLAUDE.md that exist; pull: overwrite local files that differ',
401
+ },
402
+ claude: {
403
+ type: 'boolean',
404
+ description: 'init: add the docs to Claude Code without asking; --no-claude skips it',
405
+ },
146
406
  // `agents test <key> --team <slug> --prompt|--input [--follow]`.
147
- team: { type: 'string' },
148
- prompt: { type: 'string' },
149
- input: { type: 'string' },
150
- follow: { type: 'boolean' },
407
+ team: { type: 'string', description: 'test: the team to run the draft on' },
408
+ prompt: {
409
+ type: 'string',
410
+ description: 'test: the message; triggers add: what the agent is told',
411
+ },
412
+ input: { type: 'string', description: 'test: a JSON file to run with' },
413
+ follow: {
414
+ type: 'boolean',
415
+ description: 'test: wait for the run and print its result',
416
+ },
151
417
  // `apply`: `--no-skills` skips pushing the skill folders beside the agent.
152
- skills: { type: 'boolean' },
418
+ skills: {
419
+ type: 'boolean',
420
+ description: 'apply: --no-skills skips pushing <dir>/skills/<slug>/',
421
+ },
153
422
  // policy | triggers | links | overrides
154
- event: { type: 'string' },
155
- filter: { type: 'string', repeatable: true },
156
- disabled: { type: 'boolean' },
157
- cron: { type: 'string' },
158
- timezone: { type: 'string' },
159
- kind: { type: 'string' },
160
- mode: { type: 'string' },
161
- reads: { type: 'string' },
162
- writes: { type: 'string' },
163
- publish: { type: 'boolean' },
423
+ event: {
424
+ type: 'string',
425
+ description: 'triggers add event: <product>.<event>',
426
+ },
427
+ filter: {
428
+ type: 'string',
429
+ repeatable: true,
430
+ description: 'triggers add event: field=a,b (repeat for more fields)',
431
+ },
432
+ disabled: {
433
+ type: 'boolean',
434
+ description: 'triggers add: store the trigger turned off',
435
+ },
436
+ cron: {
437
+ type: 'string',
438
+ description: 'triggers add schedule: a cron expression',
439
+ },
440
+ timezone: {
441
+ type: 'string',
442
+ description: 'triggers add schedule: an IANA time zone',
443
+ },
444
+ kind: { type: 'string', description: 'links: talk|delegate' },
445
+ mode: {
446
+ type: 'string',
447
+ description: 'links add: await|handoff, for a delegate link',
448
+ },
449
+ reads: {
450
+ type: 'string',
451
+ description: 'policy: auto|approve for every read tool',
452
+ },
453
+ writes: {
454
+ type: 'string',
455
+ description: 'policy: auto|approve for every write tool',
456
+ },
457
+ publish: {
458
+ type: 'boolean',
459
+ description: 'policy, triggers, links, overrides: publish the new version too (with --yes)',
460
+ },
461
+ // `publish | rollback` (and `--publish`): also confirm, as yourself, on
462
+ // every team where the agent runs as you.
463
+ 'confirm-access': {
464
+ type: 'boolean',
465
+ description: 'publish, rollback, --publish: also confirm, as yourself, on every team where it runs as you',
466
+ },
164
467
  },
165
468
  skills: {
166
- host: { type: 'string' },
167
- source: { type: 'string' },
168
- dir: { type: 'string' },
169
- team: { type: 'string' },
170
- out: { type: 'string' },
469
+ host: HOST,
470
+ source: SOURCE,
471
+ dir: DIR,
472
+ team: {
473
+ type: 'string',
474
+ description: "That team's own skills instead of the Source's",
475
+ },
476
+ out: {
477
+ type: 'string',
478
+ description: 'pull: where to write the skill folder',
479
+ },
480
+ force: {
481
+ type: 'boolean',
482
+ description: 'pull: overwrite local files that differ from the host',
483
+ },
484
+ yes: {
485
+ type: 'boolean',
486
+ description: 'archive: confirm (needed in a script or agent run)',
487
+ },
488
+ },
489
+ knowledge: {
490
+ host: HOST,
491
+ source: SOURCE,
492
+ dir: DIR,
493
+ team: {
494
+ type: 'string',
495
+ description: "That team's collection instead of General",
496
+ },
497
+ out: { type: 'string', description: 'pull: where to write the articles' },
498
+ force: {
499
+ type: 'boolean',
500
+ description: 'pull: overwrite local files that differ from the host',
501
+ },
502
+ prune: {
503
+ type: 'boolean',
504
+ description: 'push: also delete the articles an earlier push of this folder made whose files are gone (never ones written or edited in the app)',
505
+ },
506
+ yes: {
507
+ type: 'boolean',
508
+ description: 'push --prune: delete without asking (needed in a script or agent run)',
509
+ },
171
510
  },
172
511
  runs: {
173
- host: { type: 'string' },
174
- source: { type: 'string' },
175
- dir: { type: 'string' },
176
- team: { type: 'string' },
177
- agent: { type: 'string' },
178
- prompt: { type: 'string' },
179
- input: { type: 'string' },
180
- follow: { type: 'boolean' },
512
+ host: HOST,
513
+ source: SOURCE,
514
+ dir: DIR,
515
+ team: { type: 'string', description: 'The team whose runs these are' },
516
+ agent: { type: 'string', description: "list: only this agent's runs" },
517
+ status: {
518
+ type: 'string',
519
+ description: 'list: only runs in this status (running, awaiting_approval, completed, failed, cancelled)',
520
+ },
521
+ since: {
522
+ type: 'string',
523
+ description: 'list: only runs started since 30m, 24h, 7d or a date',
524
+ },
525
+ prompt: { type: 'string', description: 'start: the message' },
526
+ input: { type: 'string', description: 'start: a JSON file to run with' },
527
+ follow: {
528
+ type: 'boolean',
529
+ description: 'start: wait for the run and print its result',
530
+ },
531
+ // `runs cancel <run-id> --yes`.
532
+ yes: { type: 'boolean', description: 'cancel: confirm' },
181
533
  },
182
534
  approvals: {
183
- host: { type: 'string' },
184
- source: { type: 'string' },
185
- dir: { type: 'string' },
186
- team: { type: 'string' },
187
- reason: { type: 'string' },
188
- body: { type: 'string' },
535
+ host: HOST,
536
+ source: SOURCE,
537
+ dir: DIR,
538
+ team: { type: 'string', description: 'The team whose runs wait on these' },
539
+ reason: { type: 'string', description: 'approve, reject: why' },
540
+ body: {
541
+ type: 'string',
542
+ description: 'edit: a file with the reply to send instead',
543
+ },
189
544
  },
190
545
  use: {
191
- host: { type: 'string' },
192
- dir: { type: 'string' },
546
+ host: HOST,
547
+ dir: DIR,
193
548
  },
194
549
  publish: {
195
- host: { type: 'string' },
196
- source: { type: 'string' },
197
- dir: { type: 'string' },
198
- yes: { type: 'boolean' },
550
+ host: HOST,
551
+ source: SOURCE,
552
+ dir: DIR,
553
+ yes: {
554
+ type: 'boolean',
555
+ description: 'Publish to the ekanos.json source without asking (needed in a script with several seats)',
556
+ },
199
557
  },
200
558
  upgrade: {
201
- dir: { type: 'string' },
202
- host: { type: 'string' },
203
- check: { type: 'boolean' },
204
- yes: { type: 'boolean' },
559
+ dir: DIR,
560
+ host: HOST,
561
+ check: {
562
+ type: 'boolean',
563
+ description: 'Report current and target versions; change nothing',
564
+ },
565
+ yes: {
566
+ type: 'boolean',
567
+ description: 'Upgrade without asking (needed outside a terminal)',
568
+ },
205
569
  // `--no-verify` is the negation of this — parse-argv's `--no-<bool>`
206
570
  // convention (same as `--no-json`/`--no-start`), not a flag of its own.
207
- verify: { type: 'boolean' },
571
+ verify: {
572
+ type: 'boolean',
573
+ description: '--no-verify skips the validate and test run after installing',
574
+ },
208
575
  },
209
576
  };
210
577
  /** Commands that take positional arguments, and what each means, in order. Every other command is positional-free. */
211
578
  const POSITIONAL_COMMANDS = {
212
579
  use: ['source-slug'],
580
+ // `sources scopes add <scope> <product>` is the longest form.
581
+ sources: ['subcommand', 'verb', 'product-or-scope', 'product-slug'],
213
582
  catalog: ['product-slug'],
214
583
  apps: ['subcommand', 'product-slug'],
215
584
  // `teams agents policy <team> <key> set <tool> auto` is the longest form.
@@ -225,6 +594,7 @@ const POSITIONAL_COMMANDS = {
225
594
  reviewers: ['subcommand', 'email'],
226
595
  routes: ['subcommand', 'product-or-event-id', 'value', 'team-slug'],
227
596
  skills: ['subcommand', 'slug-or-path'],
597
+ knowledge: ['subcommand', 'path-or-query'],
228
598
  runs: ['subcommand', 'agent-key-or-run-id'],
229
599
  approvals: ['subcommand', 'approval-id'],
230
600
  // `triggers <key> add event <trigger-key>` is the longest form: five tokens.
@@ -243,7 +613,8 @@ const USAGE = [
243
613
  ' appendix from a JSON reporter report; --raw skips that and',
244
614
  ' the reporter injection entirely.',
245
615
  ' login Authenticate against an Ekanos host in a browser (--host <url>;',
246
- ' offers to add the Ekanos docs to Claude Code: --claude|--no-claude)',
616
+ ' offers to add the Ekanos docs to Claude Code: --claude|--no-claude;',
617
+ ' a host you have never used asks first, or needs --trust-host)',
247
618
  ' logout Revoke and delete the stored session (--host <url> | --all)',
248
619
  ' whoami Print the identity the stored session authenticates as, plus',
249
620
  ' a compact list of the sources you hold a seat on',
@@ -251,53 +622,120 @@ const USAGE = [
251
622
  ' (--findings for full gate findings, --all to expand other',
252
623
  ' integrations in the source, --watch to block until this',
253
624
  " project's current version reaches a gate verdict)",
254
- ' sources List every source you hold a developer/source-admin seat on',
625
+ ' sources List every source you hold a developer/source-admin seat on |',
626
+ ' products [--source <slug>] (each product, Experience or',
627
+ ' Integration, and whether it is enabled for the Source) |',
628
+ ' products enable <product> | products disable <product> --yes',
629
+ ' (a super-admin grant: an "ekanos login" session is refused',
630
+ ' and pointed at /admin) | scopes (each product scope: its',
631
+ ' products and how many teams hold it; a team sees in the app',
632
+ " only its scopes' products) | scopes add <scope> <product> |",
633
+ ' scopes remove <scope> <product> --yes',
255
634
  ' catalog What a source can already use, and how to set it up',
256
635
  ' ("ekanos catalog [<product-slug>] --source <slug>", --json)',
257
636
  " apps The Source's app for an integration teams sign in through:",
258
637
  ' show <product> | set <product> (Source admin; takes the secret',
259
- ' at your terminal) | connect <product> [--no-wait] (one',
638
+ ' at your terminal, Enter keeps a saved value) | connect',
639
+ ' <product> [--no-wait] (one',
260
640
  " connection for the whole Source, used by the Source's provided",
261
- ' agents in every team) | disconnect <product> | list',
641
+ ' agents in every team) | disconnect <product> [--yes] (a',
642
+ ' script or agent run needs --yes) | list',
262
643
  " teams A Source's teams, for the Source admin:",
263
644
  ' create <name> [--slug <slug>] [--owner <email>] (you own it',
264
645
  ' unless --owner, who is invited as owner) |',
646
+ ' rename <team> <name> (the slug follows the name, as in the app) |',
265
647
  ' members list <team> | members invite <team> <email> [--role',
266
648
  ' <role>] (default member; sends the invitation email) |',
267
649
  ' members role <team> <email> <role> | members remove <team>',
268
650
  " <email> --yes | roles list <team> (each role's permissions) |",
269
- ' connect <team> <product> (the link a member opens to sign in',
270
- ' an integration, and whether it is connected) |',
271
- ' list | show <team> <product> (a checklist: fields, callback',
651
+ ' connect <team> <product> [--connection <id>] [--as',
652
+ ' <member-email>] [--no-wait] [--no-open] [--link-only]',
653
+ ' [--reconnect] (connects',
654
+ " it: asks the form's fields at your terminal, or opens the",
655
+ ' sign-in for you or the member --as names and waits;',
656
+ " --connection agent installs the team's Linear agent (its own",
657
+ ' Linear app, as a member), which @mention and assign triggers',
658
+ " need; --link-only prints the team's page; --reconnect connects",
659
+ ' it again when it reads as connected but is broken: a revoked or',
660
+ ' expired sign-in) |',
661
+ ' connect <team> slack|linear-app [--as <member-email>] (installs',
662
+ ' Slack, or the Ekanos Agents app for Linear, which slack and',
663
+ ' linear triggers answer through; same link-and-wait) |',
664
+ " disconnect <team> <product> --yes (takes the team's connection",
665
+ " and its tokens down; its own app's saved fields stay) |",
666
+ ' list | show <team> (one screen: members, each integration and',
667
+ ' whose connection it is, each agent live or the next step) |',
668
+ ' show <team> <product> (a checklist: fields, callback',
272
669
  ' URLs, webhooks, connect link) | app set <team> <product>',
273
- ' [--connection <id>] (takes the secrets at your terminal) |',
274
- ' webhook <team> <product> [<webhook-id>] [--yes] (URL shown once)',
670
+ ' [--connection <id>] [--field <name>]… [--set <name>=<value>]…',
671
+ ' [--clear <name>]… (any one field at a time; a field you skip',
672
+ ' keeps its saved value; secrets only at your terminal) |',
673
+ ' webhook <team> <product> [<webhook-id>] [--yes] (URL saved to a 0600 file under ~/.ekanos/secrets, never printed) |',
674
+ ' events <team> [<product>] [--product <slug>] [--since 24h|7d|<date>]',
675
+ ' [--limit <n>] (the integration events recorded for the team, each',
676
+ ' with its status, last error and the runs it started, or why none did)',
677
+ ' | scope show <team> (the product scopes it holds and the products',
678
+ ' it sees in the app) | scope set <team> <scope> [<scope>…] [--yes]',
679
+ ' (replaces them; dropping one needs --yes)',
275
680
  " A team's agents, for the Source admin (teams <area> <verb> <team> …):",
276
- ' agents list <team> | agents enable <team> <key> [--as <member-email>]',
277
- ' | agents disable|unpin <team> <key> | agents pin <team> <key> <version>',
681
+ ' agents list <team> | agents show <team> <key> (is it live, and if',
682
+ ' not, why: each connection and whose it is, each trigger and how its',
683
+ ' events arrive, last event and run, one Next command)',
684
+ ' | agents enable <team> <key> [--as <member-email>]',
685
+ ' [--draft] [--confirm-access] (--confirm-access: runs as you and',
686
+ ' confirms it may use your connections) | agents confirm <team> <key>',
687
+ ' (you, the person it runs as, confirm it may use your connections;',
688
+ ' nobody else can; on an agent held on an earlier version it moves',
689
+ ' it to the version waiting) | agents disable <team> <key> --yes | agents',
690
+ ' unpin <team> <key> | agents pin <team> <key> <version> | agents pin',
691
+ ' <team> <key> --draft (this team only runs the unpublished draft, by',
692
+ ' hand, every write waiting; the next publish puts it back to',
693
+ ' following)',
278
694
  ' | agents policy <team> <key> [set <tool> auto|approve | unset <tool>]',
279
- ' [--reads auto|approve] [--writes auto|approve]',
280
- ' triggers list|add|remove <team> <key> … | links list|add|remove',
695
+ ' [--reads auto|approve] [--writes auto|approve] [--confirm-access]',
696
+ ' | agents skills <team> <key> [add|remove <skill>] (where the',
697
+ ' template allows team skills; its own skills stay)',
698
+ ' triggers list|add|remove <team> <key> … (add … event <id>',
699
+ " [--filter field=a,b]… or --filter '<json>'; remove <trigger-id|key> --yes;",
700
+ ' add … slack --channel',
701
+ ' <#name|id>… [--dms] --yes | add … linear [--label <name>]…',
702
+ ' [--no-mention], once installed with teams connect) | triggers',
703
+ " rotate <team> <key> <trigger-key|id> [--yes] (a webhook trigger's",
704
+ ' new token, the old one revoked; saved to a 0600 file, never printed) | links list|add|remove',
281
705
  ' <team> <from-key> [<to-key> --kind talk|delegate --mode await|handoff]',
282
- ' | mcp list|add|status|token|effect <team> [<server-key> …]',
283
- ' (see "ekanos teams --help")',
706
+ ' (remove <link-id|to-key> [--kind <kind>] --yes)',
707
+ ' | mcp list|add|status|token|effect|remove <team> [<server-key> …]',
708
+ ' (remove needs --yes)',
709
+ ' Every area also takes the team as --team <slug>.',
710
+ ' ("ekanos teams --help" spells out each area)',
284
711
  ' reviewers Who decides agent reply drafts, for the Source admin:',
285
712
  ' list | add <email> --reach source|team [--team <slug>] |',
286
- ' remove <email> [--team <slug>]',
713
+ ' remove <email> [--team <slug>] --yes',
287
714
  " routes Route a Source connection's events to teams, for the Source admin:",
288
715
  ' list [<product>] | options <product> | set <product> <value> <team>',
289
- ' [--label <name>] | remove <product> <value> | webhook <product>',
290
- ' [--webhook <id>] (URL shown once) | unrouted [<product>] |',
716
+ ' [--label <name>] | remove <product> <value> --yes | webhook',
717
+ ' <product> [--webhook <id>] [--yes] (URL saved to a 0600 file, never printed; --yes',
718
+ ' replaces a live one, revoking it) | unrouted [<product>] |',
291
719
  ' replay <event-id>',
292
720
  " agents The source's agent templates, held by Ekanos:",
293
721
  ' init [<dir>] [--claude|--no-claude] (skills + docs MCP for designing',
294
722
  ' agents with Claude; offers to add the docs to Claude Code)',
295
- ' list | show <key> | pull <key> [--out <dir>]',
723
+ ' list [--all] (--all: archived agents too) | show <key> |',
724
+ ' pull <key> [--out <dir>] [--force] (--force: overwrite local',
725
+ ' files that differ; without it the pull refuses and writes nothing)',
296
726
  ' apply <dir|file> [--base-version <n>] [--overwrite]',
297
- ' publish <key> [--to <n>] --yes | rollback <key> --to <n> --yes',
727
+ ' archive <key> --yes (refused while any team has it on; lists',
728
+ ' each team and the command to turn it off) | unarchive <key>',
729
+ ' publish <key> [--to <n>] [--confirm-access] --yes | rollback <key>',
730
+ ' --to <n> [--confirm-access] --yes (a team whose person confirmed less',
731
+ ' than the version reaches is held on its earlier version until they',
732
+ ' confirm; --confirm-access: you confirm, and move, every team where',
733
+ ' it runs as you; the output lists every other team held, and the command)',
298
734
  ' test <key> --team <slug> (--prompt "…" | --input <file.json>)',
299
735
  ' [--follow] (runs the latest, unpublished version on one team;',
300
- ' same grant, policy, approvals and audit; marked as a test)',
736
+ ' same grant, policy, approvals and audit; marked as a test; to',
737
+ ' test a tool the draft adds, first "teams agents pin <team> <key>',
738
+ ' --draft", which also works before anything is published)',
301
739
  ' apply also pushes <dir>/skills/<slug>/ folders first and names',
302
740
  ' them in the agent (--no-skills skips that)',
303
741
  ' Edit one template in place (stores a draft version; --publish',
@@ -305,7 +743,8 @@ const USAGE = [
305
743
  ' policy <key> [set <tool> auto|approve | unset <tool>]',
306
744
  ' [--reads auto|approve] [--writes auto|approve]',
307
745
  ' triggers <key> [list | add event <k> --event <slug.event>',
308
- ' [--filter field=a,b]... | add schedule <k> --cron "<expr>"',
746
+ " [--filter field=a,b]... (or one --filter '<json>') | add",
747
+ ' schedule <k> --cron "<expr>"',
309
748
  ' [--timezone <tz>] --prompt <text> | add webhook <k> --prompt',
310
749
  ' <text> | remove <k>] [--prompt <text>] [--disabled]',
311
750
  ' links <key> [list | add <to-key> --kind talk|delegate',
@@ -318,11 +757,29 @@ const USAGE = [
318
757
  ' push <dir> [--team <t>] (a new version, live at once; created',
319
758
  ' on first push, "unchanged" when identical; --team: that team\'s',
320
759
  ' own skill, no other team sees it) | pull <slug> [--out <dir>]',
760
+ ' [--force] (refuses to overwrite a local file that differs)',
761
+ ' | archive <slug> [--team <t>] --yes (refused while an agent',
762
+ ' names it or a team agent has it attached; lists each and the',
763
+ ' command that stops it) | unarchive <slug> [--team <t>]',
764
+ " knowledge The Source's Knowledge base (markdown articles), for the Source admin:",
765
+ ' list [--team <t>] | push <dir> [--team <t>] (each .md file an',
766
+ ' article: title from frontmatter or the first heading, tags from',
767
+ ' frontmatter; upserts by path, "unchanged" when identical;',
768
+ ' --prune [--yes] also deletes the articles an earlier push of',
769
+ ' this folder made whose files are gone, never ones written or',
770
+ ' edited in the app) |',
771
+ " search <query> --team <t> (as that team's agents search: its",
772
+ ' articles plus General) | pull [--team <t>] [--out <dir>]',
773
+ ' [--force] (refuses to overwrite a local file that differs)',
774
+ " Without --team: General, which every team's agents granted",
775
+ ' search_knowledge read.',
321
776
  " runs Run and inspect a team's agents, as the Source admin:",
322
777
  ' start <agent-key> --team <slug> (--prompt "…" | --input',
323
- ' <file.json>) [--follow] | list --team <slug> [--agent <key>] |',
778
+ ' <file.json>) [--follow] | list --team <slug> [--agent <key>]',
779
+ ' [--status <status>] [--since 24h|7d|<date>] |',
324
780
  ' show <run-id> --team <slug> (--json: transcript, tool calls,',
325
- ' how each was authorized, approvals)',
781
+ ' how each was authorized, approvals) | cancel <run-id> --team',
782
+ ' <slug> [--yes] (stops a live run; asks first, --yes in a script)',
326
783
  " approvals What a team's agent runs wait on: list --team <slug> |",
327
784
  ' approve|reject <id> --team <slug> [--reason "…"] |',
328
785
  ' edit <id> --team <slug> --body <file> (edit & send a reply draft)',
@@ -340,12 +797,15 @@ const USAGE = [
340
797
  ' bare "ekanos upgrade" MUTATES the project (installs the target versions,',
341
798
  ' verifies with validate + the test script, rolls back on any failure).',
342
799
  '',
343
- 'Host resolution (logout, whoami, status, sources, catalog, apps, teams, reviewers, routes, agents, skills, runs, approvals, use, publish, upgrade):',
800
+ 'Host resolution (logout, whoami, status, sources, catalog, apps, teams, reviewers, routes, agents, skills, knowledge, runs, approvals, use, publish, upgrade):',
344
801
  ' --host, else EKANOS_HOST, else the "host" field in ekanos.json, else the',
345
802
  ' sole stored login. "ekanos publish" saves host and source to ekanos.json.',
346
803
  ' "ekanos login" never reads ekanos.json for a host (--host, else',
347
804
  ' EKANOS_HOST, else the sole stored login) — it is the verb that creates',
348
- ' credentials, so a committed file must never choose where they go.',
805
+ ' credentials, so a committed file must never choose where they go. A',
806
+ ' host you have never logged in to is shown with its sign-in domain and',
807
+ ' asked about first (--trust-host when nobody can be asked). A remote',
808
+ ' http:// host is refused by every verb that sends credentials.',
349
809
  '',
350
810
  'Publish targeting (see "ekanos sources" / "ekanos use"):',
351
811
  ' publish refuses (FORBIDDEN) a source you hold no seat on. With multiple',
@@ -457,6 +917,8 @@ export async function run(argv, options = {}) {
457
917
  host: parsed.flags.host,
458
918
  force: parsed.flags.force === true,
459
919
  claude: parsed.flags.claude,
920
+ trustHost: parsed.flags['trust-host'] === true,
921
+ confirmHost: options.confirm,
460
922
  cwd,
461
923
  env,
462
924
  stdinIsTTY: options.stdinIsTTY,
@@ -488,6 +950,9 @@ export async function run(argv, options = {}) {
488
950
  case 'sources':
489
951
  return await runSources(ctx, {
490
952
  host: parsed.flags.host,
953
+ positionals: parsed.positionals,
954
+ source: parsed.flags.source,
955
+ yes: parsed.flags.yes === true,
491
956
  dir,
492
957
  env,
493
958
  });
@@ -512,6 +977,7 @@ export async function run(argv, options = {}) {
512
977
  askSecret: options.askSecret,
513
978
  confirm: options.confirm,
514
979
  noWait: parsed.flags['no-wait'] === true,
980
+ yes: parsed.flags.yes === true,
515
981
  });
516
982
  case 'teams':
517
983
  return await runTeams(ctx, {
@@ -523,12 +989,18 @@ export async function run(argv, options = {}) {
523
989
  owner: parsed.flags.owner,
524
990
  role: parsed.flags.role,
525
991
  yes: parsed.flags.yes === true,
992
+ noWait: parsed.flags['no-wait'] === true,
993
+ noOpen: parsed.flags.open === false,
994
+ linkOnly: parsed.flags['link-only'] === true,
995
+ reconnect: parsed.flags.reconnect === true,
526
996
  flags: parsed.flags,
527
997
  dir,
528
998
  env,
529
999
  stdinIsTTY: options.stdinIsTTY,
530
1000
  ask: options.ask,
531
1001
  askSecret: options.askSecret,
1002
+ openUrl: options.openUrl,
1003
+ pollDelayMs: options.pollDelayMs,
532
1004
  });
533
1005
  case 'reviewers':
534
1006
  return await runReviewers(ctx, {
@@ -537,6 +1009,7 @@ export async function run(argv, options = {}) {
537
1009
  positionals: parsed.positionals,
538
1010
  reach: parsed.flags.reach,
539
1011
  team: parsed.flags.team,
1012
+ yes: parsed.flags.yes === true,
540
1013
  dir,
541
1014
  env,
542
1015
  });
@@ -547,6 +1020,7 @@ export async function run(argv, options = {}) {
547
1020
  positionals: parsed.positionals,
548
1021
  label: parsed.flags.label,
549
1022
  webhook: parsed.flags.webhook,
1023
+ yes: parsed.flags.yes === true,
550
1024
  dir,
551
1025
  env,
552
1026
  });
@@ -561,6 +1035,7 @@ export async function run(argv, options = {}) {
561
1035
  overwrite: parsed.flags.overwrite === true,
562
1036
  out: parsed.flags.out,
563
1037
  yes: parsed.flags.yes === true,
1038
+ all: parsed.flags.all === true,
564
1039
  force: parsed.flags.force === true,
565
1040
  claude: parsed.flags.claude,
566
1041
  skills: parsed.flags.skills,
@@ -575,6 +1050,7 @@ export async function run(argv, options = {}) {
575
1050
  reads: parsed.flags.reads,
576
1051
  writes: parsed.flags.writes,
577
1052
  publish: parsed.flags.publish === true,
1053
+ confirmAccess: parsed.flags['confirm-access'] === true,
578
1054
  dir,
579
1055
  env,
580
1056
  stdinIsTTY: options.stdinIsTTY,
@@ -592,14 +1068,19 @@ export async function run(argv, options = {}) {
592
1068
  positionals: parsed.positionals,
593
1069
  team: parsed.flags.team,
594
1070
  agent: parsed.flags.agent,
1071
+ status: parsed.flags.status,
1072
+ since: parsed.flags.since,
595
1073
  prompt: parsed.flags.prompt,
596
1074
  input: parsed.flags.input,
597
1075
  follow: parsed.flags.follow === true,
598
1076
  reason: parsed.flags.reason,
599
1077
  body: parsed.flags.body,
1078
+ yes: parsed.flags.yes === true,
600
1079
  dir,
601
1080
  env,
602
1081
  sleep: options.sleep,
1082
+ stdinIsTTY: options.stdinIsTTY,
1083
+ confirm: options.confirm,
603
1084
  });
604
1085
  case 'skills':
605
1086
  return await runSkills(ctx, {
@@ -609,8 +1090,28 @@ export async function run(argv, options = {}) {
609
1090
  target: parsed.positionals[1],
610
1091
  team: parsed.flags.team,
611
1092
  out: parsed.flags.out,
1093
+ force: parsed.flags.force === true,
1094
+ yes: parsed.flags.yes === true,
1095
+ dir,
1096
+ env,
1097
+ stdinIsTTY: options.stdinIsTTY,
1098
+ confirm: options.confirm,
1099
+ });
1100
+ case 'knowledge':
1101
+ return await runKnowledge(ctx, {
1102
+ host: parsed.flags.host,
1103
+ source: parsed.flags.source,
1104
+ subcommand: parsed.positionals[0],
1105
+ target: parsed.positionals[1],
1106
+ team: parsed.flags.team,
1107
+ out: parsed.flags.out,
1108
+ force: parsed.flags.force === true,
1109
+ prune: parsed.flags.prune === true,
1110
+ yes: parsed.flags.yes === true,
612
1111
  dir,
613
1112
  env,
1113
+ stdinIsTTY: options.stdinIsTTY,
1114
+ confirm: options.confirm,
614
1115
  });
615
1116
  case 'use':
616
1117
  return await runUse(ctx, {
@@ -729,12 +1230,83 @@ function emitHelp(ctx) {
729
1230
  });
730
1231
  return ctx.succeed({ usage: USAGE, commands: COMMANDS, docs: SDK_DOCS_URL }, `${USAGE}${DOCS_FOOTER}`);
731
1232
  }
1233
+ /** The per-area usage a command's own parser already gives, shown in its help too. */
1234
+ const COMMAND_DETAILS = {
1235
+ teams: () => TEAM_AGENT_AREAS.map(usageFor),
1236
+ agents: () => Object.entries(TEMPLATE_EDIT_HELP).map(([area, examples]) => `${area}: ${examples}`),
1237
+ };
1238
+ /** A command's block of the top-level usage: its line and the indented ones under it. */
1239
+ function commandUsage(command) {
1240
+ const lines = USAGE.split('\n');
1241
+ const start = lines.findIndex((line) => line.startsWith(` ${command} `));
1242
+ if (start === -1)
1243
+ return '';
1244
+ const end = lines.findIndex((line, index) => index > start && !line.startsWith(' '));
1245
+ return lines.slice(start, end === -1 ? undefined : end).join('\n');
1246
+ }
1247
+ function optionLabel(name, spec) {
1248
+ if (spec.type === 'boolean')
1249
+ return `--${name}`;
1250
+ return `--${name} <value>${spec.repeatable ? '…' : ''}`;
1251
+ }
1252
+ /**
1253
+ * `ekanos <command> --help`: the command's block of the usage (its
1254
+ * subcommands, each described), the per-area usage its parser gives, and
1255
+ * every flag with its description. The JSON envelope carries the same, so an
1256
+ * agent reading it is not left with flag names only.
1257
+ */
732
1258
  function emitCommandHelp(ctx, command) {
733
1259
  var _a;
734
- const specs = (_a = COMMAND_SPECS[command]) !== null && _a !== void 0 ? _a : {};
1260
+ var _b, _c, _d;
1261
+ const specs = (_b = COMMAND_SPECS[command]) !== null && _b !== void 0 ? _b : {};
735
1262
  const flags = Object.keys(specs).map((name) => `--${name}`);
736
- const help = `Usage: ekanos ${command} [${flags.join('] [')}]`;
737
- ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags });
738
- return ctx.succeed({ command, flags }, help);
1263
+ const usage = commandUsage(command);
1264
+ const details = (_c = (_a = COMMAND_DETAILS[command]) === null || _a === void 0 ? void 0 : _a.call(COMMAND_DETAILS)) !== null && _c !== void 0 ? _c : [];
1265
+ const options = [
1266
+ ...Object.entries(specs),
1267
+ ...Object.entries(GLOBAL_SPECS),
1268
+ ].map(([name, spec]) => { var _a; return ({
1269
+ flag: `--${name}`,
1270
+ label: optionLabel(name, spec),
1271
+ takesValue: spec.type === 'string',
1272
+ repeatable: spec.repeatable === true,
1273
+ description: (_a = spec.description) !== null && _a !== void 0 ? _a : '',
1274
+ }); });
1275
+ const positionals = (_d = POSITIONAL_COMMANDS[command]) !== null && _d !== void 0 ? _d : [];
1276
+ const synopsis = [
1277
+ `ekanos ${command}`,
1278
+ positionals.length === 0
1279
+ ? ''
1280
+ : positionals.length === 1
1281
+ ? ` <${positionals[0]}>`
1282
+ : ` <${positionals[0]}> …`,
1283
+ ' [options]',
1284
+ ].join('');
1285
+ const docs = docsPage('cli/commands', command);
1286
+ const width = Math.min(26, Math.max(...options.map((option) => option.label.length)));
1287
+ const human = [
1288
+ `Usage: ${synopsis}`,
1289
+ ...(usage ? ['', usage] : []),
1290
+ ...(details.length > 0 ? ['', ...details] : []),
1291
+ '',
1292
+ 'Options:',
1293
+ ...options.map((option) => option.label.length > width
1294
+ ? ` ${option.label}\n ${' '.repeat(width)} ${option.description}`
1295
+ : ` ${option.label.padEnd(width)} ${option.description}`),
1296
+ '',
1297
+ `Docs: ${docs}`,
1298
+ ].join('\n');
1299
+ ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags, usage });
1300
+ return ctx.succeed({
1301
+ command,
1302
+ flags,
1303
+ usage,
1304
+ details,
1305
+ options: options.map((_a) => {
1306
+ var { label: _label } = _a, option = __rest(_a, ["label"]);
1307
+ return option;
1308
+ }),
1309
+ docs,
1310
+ }, human);
739
1311
  }
740
1312
  //# sourceMappingURL=index.js.map