@ekanos/cli 0.16.0 → 0.17.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 (190) hide show
  1. package/README.md +62 -4
  2. package/dist/commands/add.d.ts +2 -0
  3. package/dist/commands/add.js +118 -0
  4. package/dist/commands/add.js.map +1 -0
  5. package/dist/commands/audiences.d.ts +31 -0
  6. package/dist/commands/audiences.js +239 -0
  7. package/dist/commands/audiences.js.map +1 -0
  8. package/dist/commands/build.d.ts +2 -0
  9. package/dist/commands/build.js +41 -0
  10. package/dist/commands/build.js.map +1 -0
  11. package/dist/commands/check.d.ts +2 -0
  12. package/dist/commands/check.js +43 -0
  13. package/dist/commands/check.js.map +1 -0
  14. package/dist/commands/codegen.d.ts +2 -0
  15. package/dist/commands/codegen.js +49 -0
  16. package/dist/commands/codegen.js.map +1 -0
  17. package/dist/commands/contract.d.ts +52 -0
  18. package/dist/commands/contract.js +300 -0
  19. package/dist/commands/contract.js.map +1 -0
  20. package/dist/commands/dev.d.ts +7 -0
  21. package/dist/commands/dev.js +186 -12
  22. package/dist/commands/dev.js.map +1 -1
  23. package/dist/commands/index-experience.d.ts +31 -0
  24. package/dist/commands/index-experience.js +21 -0
  25. package/dist/commands/index-experience.js.map +1 -0
  26. package/dist/commands/init.d.ts +7 -0
  27. package/dist/commands/init.js +12 -0
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/new.d.ts +2 -0
  30. package/dist/commands/new.js +39 -0
  31. package/dist/commands/new.js.map +1 -0
  32. package/dist/commands/publish.d.ts +59 -0
  33. package/dist/commands/publish.js +118 -65
  34. package/dist/commands/publish.js.map +1 -1
  35. package/dist/commands/scope-notice.d.ts +6 -0
  36. package/dist/commands/scope-notice.js +15 -0
  37. package/dist/commands/scope-notice.js.map +1 -1
  38. package/dist/commands/sources.js +2 -0
  39. package/dist/commands/sources.js.map +1 -1
  40. package/dist/commands/sync.d.ts +2 -0
  41. package/dist/commands/sync.js +150 -0
  42. package/dist/commands/sync.js.map +1 -0
  43. package/dist/commands/teams-audience.d.ts +12 -0
  44. package/dist/commands/teams-audience.js +144 -0
  45. package/dist/commands/teams-audience.js.map +1 -0
  46. package/dist/commands/teams-scope.js +4 -0
  47. package/dist/commands/teams-scope.js.map +1 -1
  48. package/dist/commands/teams.d.ts +1 -1
  49. package/dist/commands/teams.js +6 -1
  50. package/dist/commands/teams.js.map +1 -1
  51. package/dist/compatibility.d.ts +10 -1
  52. package/dist/compatibility.js +10 -1
  53. package/dist/compatibility.js.map +1 -1
  54. package/dist/dev-experience.d.ts +125 -0
  55. package/dist/dev-experience.js +128 -0
  56. package/dist/dev-experience.js.map +1 -0
  57. package/dist/experience/boundary.d.ts +12 -0
  58. package/dist/experience/boundary.js +260 -0
  59. package/dist/experience/boundary.js.map +1 -0
  60. package/dist/experience/build.d.ts +16 -0
  61. package/dist/experience/build.js +90 -0
  62. package/dist/experience/build.js.map +1 -0
  63. package/dist/experience/checks.d.ts +29 -0
  64. package/dist/experience/checks.js +133 -0
  65. package/dist/experience/checks.js.map +1 -0
  66. package/dist/experience/contract-sources.d.ts +36 -0
  67. package/dist/experience/contract-sources.js +111 -0
  68. package/dist/experience/contract-sources.js.map +1 -0
  69. package/dist/experience/definition-checks.d.ts +26 -0
  70. package/dist/experience/definition-checks.js +201 -0
  71. package/dist/experience/definition-checks.js.map +1 -0
  72. package/dist/experience/definition-shape.d.ts +60 -0
  73. package/dist/experience/definition-shape.js +2 -0
  74. package/dist/experience/definition-shape.js.map +1 -0
  75. package/dist/experience/init.d.ts +10 -0
  76. package/dist/experience/init.js +64 -0
  77. package/dist/experience/init.js.map +1 -0
  78. package/dist/experience/integrations-codegen.d.ts +17 -0
  79. package/dist/experience/integrations-codegen.js +51 -0
  80. package/dist/experience/integrations-codegen.js.map +1 -0
  81. package/dist/experience/json-schema-to-ts.d.ts +1 -0
  82. package/dist/experience/json-schema-to-ts.js +90 -0
  83. package/dist/experience/json-schema-to-ts.js.map +1 -0
  84. package/dist/experience/load-experience.d.ts +23 -0
  85. package/dist/experience/load-experience.js +115 -0
  86. package/dist/experience/load-experience.js.map +1 -0
  87. package/dist/experience/overrides.d.ts +8 -0
  88. package/dist/experience/overrides.js +21 -0
  89. package/dist/experience/overrides.js.map +1 -0
  90. package/dist/experience/pin-integration.d.ts +8 -0
  91. package/dist/experience/pin-integration.js +17 -0
  92. package/dist/experience/pin-integration.js.map +1 -0
  93. package/dist/experience/pin-ops.d.ts +12 -0
  94. package/dist/experience/pin-ops.js +36 -0
  95. package/dist/experience/pin-ops.js.map +1 -0
  96. package/dist/experience/pins.d.ts +35 -0
  97. package/dist/experience/pins.js +91 -0
  98. package/dist/experience/pins.js.map +1 -0
  99. package/dist/experience/project.d.ts +52 -0
  100. package/dist/experience/project.js +149 -0
  101. package/dist/experience/project.js.map +1 -0
  102. package/dist/experience/publish.d.ts +51 -0
  103. package/dist/experience/publish.js +144 -0
  104. package/dist/experience/publish.js.map +1 -0
  105. package/dist/experience/regenerate.d.ts +10 -0
  106. package/dist/experience/regenerate.js +33 -0
  107. package/dist/experience/regenerate.js.map +1 -0
  108. package/dist/experience/report.d.ts +9 -0
  109. package/dist/experience/report.js +23 -0
  110. package/dist/experience/report.js.map +1 -0
  111. package/dist/harness-scaffold.d.ts +52 -18
  112. package/dist/harness-scaffold.js +189 -9
  113. package/dist/harness-scaffold.js.map +1 -1
  114. package/dist/index.js +139 -7
  115. package/dist/index.js.map +1 -1
  116. package/dist/project-checks.d.ts +12 -0
  117. package/dist/project-checks.js +46 -0
  118. package/dist/project-checks.js.map +1 -1
  119. package/dist/project.d.ts +14 -0
  120. package/dist/project.js +10 -0
  121. package/dist/project.js.map +1 -1
  122. package/dist/repo/publish.d.ts +42 -0
  123. package/dist/repo/publish.js +225 -0
  124. package/dist/repo/publish.js.map +1 -0
  125. package/dist/repo/scaffold.d.ts +30 -0
  126. package/dist/repo/scaffold.js +165 -0
  127. package/dist/repo/scaffold.js.map +1 -0
  128. package/dist/teams-api.d.ts +37 -1
  129. package/dist/teams-api.js +62 -1
  130. package/dist/teams-api.js.map +1 -1
  131. package/dist/validate-findings.js +2 -1
  132. package/dist/validate-findings.js.map +1 -1
  133. package/package.json +2 -2
  134. package/templates/ekanos.json.tmpl +1 -0
  135. package/templates/experience/AGENTS.md.tmpl +22 -0
  136. package/templates/experience/ekanos.json.tmpl +7 -0
  137. package/templates/experience/ekanos.lock.json.tmpl +4 -0
  138. package/templates/experience/eslint.config.mjs.tmpl +25 -0
  139. package/templates/experience/gitignore.tmpl +17 -0
  140. package/templates/experience/package.json.tmpl +41 -0
  141. package/templates/experience/src/.ekanos/integrations.d.ts.tmpl +3 -0
  142. package/templates/experience/src/experience.ts.tmpl +30 -0
  143. package/templates/experience/src/widgets/hello-widget.tsx.tmpl +40 -0
  144. package/templates/experience/tsconfig.json.tmpl +20 -0
  145. package/templates/harness/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +9 -0
  146. package/templates/harness/app/[slug]/activation/page.tsx.tmpl +1 -1
  147. package/templates/harness/app/[slug]/page.tsx.tmpl +1 -1
  148. package/templates/harness/app/[slug]/tile/page.tsx.tmpl +1 -1
  149. package/templates/harness/app/[slug]/triggers/page.tsx.tmpl +1 -1
  150. package/templates/harness/app/[slug]/widgets/[widgetId]/page.tsx.tmpl +1 -1
  151. package/templates/harness/app/[slug]/widgets/page.tsx.tmpl +1 -1
  152. package/templates/harness/app/experience/layout.tsx.tmpl +17 -0
  153. package/templates/harness/app/experience/page.tsx.tmpl +1 -0
  154. package/templates/harness/app/experience/pages/[id]/page.tsx.tmpl +1 -0
  155. package/templates/harness/app/experience/review-queue/page.tsx.tmpl +1 -0
  156. package/templates/harness/app/experience/runs/page.tsx.tmpl +1 -0
  157. package/templates/harness/app/experience/widgets/[id]/page.tsx.tmpl +1 -0
  158. package/templates/harness/app/experience/widgets/page.tsx.tmpl +1 -0
  159. package/templates/harness/app/experience/workflows/[id]/page.tsx.tmpl +1 -0
  160. package/templates/harness/app/experience/workflows/page.tsx.tmpl +1 -0
  161. package/templates/harness/app/integrations/[slug]/connect/page.tsx.tmpl +1 -0
  162. package/templates/harness/app/integrations/[slug]/events/page.tsx.tmpl +1 -0
  163. package/templates/harness/app/integrations/[slug]/health/page.tsx.tmpl +1 -0
  164. package/templates/harness/app/integrations/[slug]/operations/[kind]/[name]/page.tsx.tmpl +1 -0
  165. package/templates/harness/app/integrations/[slug]/operations/page.tsx.tmpl +1 -0
  166. package/templates/harness/app/integrations/[slug]/page.tsx.tmpl +1 -0
  167. package/templates/harness/app/integrations/[slug]/preview/activation/page.tsx.tmpl +1 -0
  168. package/templates/harness/app/integrations/[slug]/preview/layout.tsx.tmpl +1 -0
  169. package/templates/harness/app/integrations/[slug]/preview/page.tsx.tmpl +1 -0
  170. package/templates/harness/app/integrations/[slug]/preview/tile/page.tsx.tmpl +1 -0
  171. package/templates/harness/app/integrations/[slug]/preview/triggers/page.tsx.tmpl +1 -0
  172. package/templates/harness/app/integrations/[slug]/preview/widgets/[widgetId]/page.tsx.tmpl +1 -0
  173. package/templates/harness/app/integrations/[slug]/preview/widgets/page.tsx.tmpl +1 -0
  174. package/templates/harness/app/integrations/[slug]/runs/page.tsx.tmpl +1 -0
  175. package/templates/harness/app/integrations/layout.tsx.tmpl +1 -0
  176. package/templates/harness/app/integrations/page.tsx.tmpl +1 -0
  177. package/templates/harness/app/page.experience.tsx.tmpl +5 -0
  178. package/templates/harness/repo/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +11 -0
  179. package/templates/harness/repo/app/(repo)/connections/page.tsx.tmpl +1 -0
  180. package/templates/harness/repo/app/(repo)/layout.tsx.tmpl +1 -0
  181. package/templates/harness/repo/app/(repo)/page.tsx.tmpl +1 -0
  182. package/templates/harness/repo/app/experiences/[name]/connect/page.tsx.tmpl +1 -0
  183. package/templates/harness/repo/app/experiences/[name]/layout.tsx.tmpl +9 -0
  184. package/templates/harness/repo/app/experiences/page.tsx.tmpl +5 -0
  185. package/templates/harness/repo/app/harness-shell.tsx.tmpl +9 -0
  186. package/templates/harness/repo/app/integrations/[slug]/layout.tsx.tmpl +1 -0
  187. package/templates/harness/repo/app/integrations/page.tsx.tmpl +5 -0
  188. package/templates/harness/repo/app/repo-experiences.ts.tmpl +5 -0
  189. package/templates/harness/styles/globals.css.tmpl +15 -1
  190. package/templates/package.json.tmpl +4 -4
package/dist/index.js CHANGED
@@ -9,12 +9,16 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
+ import { isRepoRoot } from '@ekanos/integration-schema/repo';
12
13
  import * as fs from 'node:fs';
14
+ import * as path from 'node:path';
13
15
  import { runAgents } from './commands/agents.js';
14
16
  import { TEMPLATE_EDIT_HELP } from './commands/agents-edit.js';
15
17
  import { runApps } from './commands/apps.js';
18
+ import { runAudiences } from './commands/audiences.js';
16
19
  import { runCatalog } from './commands/catalog.js';
17
20
  import { runDev } from './commands/dev.js';
21
+ import { EXPERIENCE_COMMANDS } from './commands/index-experience.js';
18
22
  import { runInit } from './commands/init.js';
19
23
  import { runKnowledge } from './commands/knowledge.js';
20
24
  import { runLogin } from './commands/login.js';
@@ -36,7 +40,7 @@ import { runWhoami } from './commands/whoami.js';
36
40
  import { runWorkflows } from './commands/workflows.js';
37
41
  import { CliContext } from './context.js';
38
42
  import { SDK_DOCS_URL, docsPage } from './docs-links.js';
39
- import { toCliError, usageError } from './errors.js';
43
+ import { preconditionError, toCliError, usageError } from './errors.js';
40
44
  import { detectInvocation, setInvocation } from './invocation.js';
41
45
  import { parseArgv } from './parse-argv.js';
42
46
  import { unknownCommand } from './unknown-command.js';
@@ -97,6 +101,10 @@ const COMMAND_SPECS = {
97
101
  type: 'string',
98
102
  description: 'Kebab-case id of the integration, e.g. acme-crm',
99
103
  },
104
+ kind: {
105
+ type: 'string',
106
+ description: 'What to scaffold: integration (default), experience (widgets and workflows that depend on integrations) or repo (one repo holding many of both)',
107
+ },
100
108
  dir: {
101
109
  type: 'string',
102
110
  description: 'Target directory (default ./<slug>; --dir . scaffolds in place)',
@@ -133,7 +141,11 @@ const COMMAND_SPECS = {
133
141
  },
134
142
  start: {
135
143
  type: 'boolean',
136
- description: 'Start the server even in JSON mode; --no-start only scaffolds',
144
+ description: 'Start the server even in JSON mode or under an agent (non-interactive runs only scaffold and print the command); --no-start only scaffolds',
145
+ },
146
+ 'contracts-dir': {
147
+ type: 'string',
148
+ description: 'Experiences: a directory of <integration>.contract.json files to resolve integrations from (offline registry)',
137
149
  },
138
150
  },
139
151
  test: {
@@ -232,6 +244,42 @@ const COMMAND_SPECS = {
232
244
  },
233
245
  page: { type: 'string', description: 'people: which page, from 1' },
234
246
  },
247
+ audiences: {
248
+ host: HOST,
249
+ source: SOURCE,
250
+ dir: DIR,
251
+ yes: {
252
+ type: 'boolean',
253
+ description: 'set: confirm dropping a product people then stop seeing',
254
+ },
255
+ name: { type: 'string', description: 'set: rename the audience' },
256
+ description: {
257
+ type: 'string',
258
+ description: 'create|set: what the audience is for',
259
+ },
260
+ products: {
261
+ type: 'string',
262
+ description: 'create|set: the products it shows, as a,b (set: replaces them; none empties it)',
263
+ },
264
+ widgets: {
265
+ type: 'string',
266
+ repeatable: true,
267
+ description: 'create|set: <product>=<id>,<id> shows only those widgets of the product (<product>= lifts the rule)',
268
+ },
269
+ tools: {
270
+ type: 'string',
271
+ repeatable: true,
272
+ description: 'create|set: <product>=<name>,<name> allows only those tools of the product (<product>= lifts the rule)',
273
+ },
274
+ default: {
275
+ type: 'boolean',
276
+ description: 'create|set: new teams of the Source start on it',
277
+ },
278
+ 'all-entitled': {
279
+ type: 'boolean',
280
+ description: 'create|set: show every product the Source has, now and later (--no-all-entitled turns it off)',
281
+ },
282
+ },
235
283
  catalog: {
236
284
  host: HOST,
237
285
  source: SOURCE,
@@ -264,6 +312,14 @@ const COMMAND_SPECS = {
264
312
  }, self: {
265
313
  type: 'boolean',
266
314
  description: 'members add: you join the team now, with no invitation email',
315
+ },
316
+ // `teams audience`.
317
+ member: {
318
+ type: 'string',
319
+ description: "audience: one member's own audience, by email",
320
+ }, inherit: {
321
+ type: 'boolean',
322
+ description: "audience --member: clear their own audience so the team's applies",
267
323
  } }, SECRET_INPUT), { clear: {
268
324
  type: 'string',
269
325
  repeatable: true,
@@ -677,6 +733,14 @@ const COMMAND_SPECS = {
677
733
  type: 'boolean',
678
734
  description: 'Publish to the ekanos.json source without asking (needed in a script with several seats)',
679
735
  },
736
+ 'dry-run': {
737
+ type: 'boolean',
738
+ description: 'Experiences and repos: gate, build and pack every package under .ekanos/publish/, publish nothing (publishing an experience is not supported by the host yet)',
739
+ },
740
+ all: {
741
+ type: 'boolean',
742
+ description: 'In a repo: publish every package',
743
+ },
680
744
  },
681
745
  upgrade: {
682
746
  dir: DIR,
@@ -700,9 +764,12 @@ const COMMAND_SPECS = {
700
764
  /** Commands that take positional arguments, and what each means, in order. Every other command is positional-free. */
701
765
  const POSITIONAL_COMMANDS = {
702
766
  use: ['source-slug'],
767
+ // In a repo: `publish <package>...` (repeatable, see VARIADIC_COMMANDS).
768
+ publish: ['package'],
703
769
  // `sources scopes add <scope> <product>` is the longest form.
704
770
  sources: ['subcommand', 'verb', 'product-or-scope', 'product-slug'],
705
771
  catalog: ['product-slug'],
772
+ audiences: ['subcommand', 'audience'],
706
773
  apps: ['subcommand', 'product-slug'],
707
774
  // `teams agents policy <team> <key> set <tool> auto` is the longest form.
708
775
  teams: [
@@ -725,12 +792,32 @@ const POSITIONAL_COMMANDS = {
725
792
  // `triggers <key> add event <trigger-key>` is the longest form: five tokens.
726
793
  agents: ['subcommand', 'key-or-path', 'action', 'arg', 'value'],
727
794
  };
795
+ for (const command of EXPERIENCE_COMMANDS) {
796
+ COMMAND_SPECS[command.name] = command.specs;
797
+ if (command.positionals)
798
+ POSITIONAL_COMMANDS[command.name] = command.positionals;
799
+ }
800
+ /** Commands that act on one package: refused at a repo root, with the way in. */
801
+ const REPO_ROOT_REFUSED = new Set([
802
+ 'validate',
803
+ 'test',
804
+ 'check',
805
+ 'build',
806
+ 'sync',
807
+ 'add',
808
+ 'codegen',
809
+ 'contract',
810
+ ]);
811
+ /** Commands whose last positional repeats. */
812
+ const VARIADIC_COMMANDS = new Set(['publish']);
728
813
  const COMMANDS = Object.keys(COMMAND_SPECS);
729
814
  const USAGE = [
730
815
  'Usage: ekanos <command> [options]',
731
816
  '',
732
817
  'Commands:',
733
- ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes)',
818
+ ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes);',
819
+ ' --kind experience for an experience, --kind repo for a repo of many',
820
+ ...EXPERIENCE_COMMANDS.flatMap((command) => command.usage),
734
821
  ' validate Validate ekanos.json + the integration definition (--json)',
735
822
  ' dev Scaffold .ekanos/harness and run it with your own Next',
736
823
  ' test Run the project test script via its package manager (--json).',
@@ -765,6 +852,14 @@ const USAGE = [
765
852
  " roles: never changed or deleted; a held role isn't deleted) |",
766
853
  ' people [--search <text>] [--page <n>] (everyone with access, one',
767
854
  ' row per person, with their teams and role on each)',
855
+ " audiences A source's audiences, for its Source admin (they replace product",
856
+ ' scopes): list | show <audience> | create <name> [--products a,b |',
857
+ ' --all-entitled] [--description <text>] [--default] [--widgets',
858
+ ' <product>=<id>,…]… [--tools <product>=<name>,…]… | set <audience>',
859
+ ' [--name] [--description] [--default] [--all-entitled] [--products',
860
+ ' a,b|none] [--widgets …]… [--tools …]… [--yes] (--products replaces;',
861
+ ' dropping a product needs --yes). A team or member is put on one',
862
+ ' with "teams audience <team> [<audience>] [--member <email>]"',
768
863
  ' catalog What a source can already use, and how to set it up:',
769
864
  ' Experiences, then Integrations; one product shows its tools',
770
865
  ' with their inputs and whose connection agent runs use',
@@ -838,7 +933,11 @@ const USAGE = [
838
933
  ' events <team> [<product>] [--product <slug>] [--since 24h|7d|<date>]',
839
934
  ' [--limit <n>] (the integration events recorded for the team, each',
840
935
  ' with its status, last error and the runs it started, or why none did)',
841
- ' | scope show <team> (the product scopes it holds and the products',
936
+ ' | audience <team> [<audience>] [--member <email> [--inherit]] [--yes]',
937
+ " (the audience a team starts its members on; a member's own, a Source",
938
+ " admin's only, overrides it; --inherit clears it; moving people to an",
939
+ ' audience that shows less needs --yes)',
940
+ ' | scope show <team> (DEPRECATED, use audience: the product scopes it holds and the products',
842
941
  ' it sees in the app) | scope set <team> <scope> [<scope>…] [--yes]',
843
942
  ' (replaces them; dropping one needs --yes)',
844
943
  " A team's agents, for the Source admin (teams <area> <verb> <team> …):",
@@ -988,7 +1087,9 @@ const USAGE = [
988
1087
  ' <run-id> | runs cancel <run-id> [--yes] | approvals list |',
989
1088
  ' approvals approve|reject <id> [--reason "…"]',
990
1089
  ' use Set this project\'s publish target ("ekanos use <source-slug>")',
991
- ' publish Validate, pack and submit the integration to an Ekanos host',
1090
+ ' publish Validate, pack and submit the integration to an Ekanos host.',
1091
+ ' In a repo: publish [<package>...] | --all [--dry-run] (integrations',
1092
+ ' first, each package gated, artifacts under .ekanos/publish/)',
992
1093
  ' upgrade Move this toolchain to a coherent target version, with',
993
1094
  ' verify-and-rollback (--check --yes --no-verify --host <url>)',
994
1095
  '',
@@ -1077,13 +1178,17 @@ export async function run(argv, options = {}) {
1077
1178
  if (positionalNames.length === 0 && parsed.positionals.length > 0) {
1078
1179
  throw usageError(`"${command}" takes no positional arguments (got "${parsed.positionals[0]}").`, `Pass options as flags, e.g. "--dir <path>". See "ekanos ${command} --help".`);
1079
1180
  }
1080
- if (parsed.positionals.length > positionalNames.length) {
1181
+ if (parsed.positionals.length > positionalNames.length &&
1182
+ !VARIADIC_COMMANDS.has(command)) {
1081
1183
  const usage = positionalNames.map((name) => `<${name}>`).join(' ');
1082
1184
  throw usageError(`"${command}" takes at most ${positionalNames.length} positional ` +
1083
1185
  `argument${positionalNames.length === 1 ? '' : 's'} (${usage}), ` +
1084
1186
  `got ${parsed.positionals.length}.`, `e.g. "ekanos ${command} ${usage}". See "ekanos ${command} --help".`);
1085
1187
  }
1086
1188
  const dir = (_f = parsed.flags.dir) !== null && _f !== void 0 ? _f : cwd;
1189
+ if (REPO_ROOT_REFUSED.has(command) && isRepoRoot(path.resolve(dir))) {
1190
+ throw preconditionError(`"${command}" works on one package, and ${dir} is a repo root.`, `Run it inside a package (cd integrations/<slug> or cd experiences/<slug>), or use "ekanos publish --all" for the whole repo.`);
1191
+ }
1087
1192
  switch (command) {
1088
1193
  case 'init':
1089
1194
  return await runInit(ctx, {
@@ -1093,6 +1198,7 @@ export async function run(argv, options = {}) {
1093
1198
  force: parsed.flags.force === true,
1094
1199
  yes: parsed.flags.yes === true,
1095
1200
  docs: parsed.flags.docs,
1201
+ kind: parsed.flags.kind,
1096
1202
  stdinIsTTY: options.stdinIsTTY,
1097
1203
  });
1098
1204
  case 'validate':
@@ -1104,6 +1210,7 @@ export async function run(argv, options = {}) {
1104
1210
  host: parsed.flags.host,
1105
1211
  force: parsed.flags.force === true,
1106
1212
  start: parsed.flags.start,
1213
+ contractsDir: parsed.flags['contracts-dir'],
1107
1214
  env,
1108
1215
  });
1109
1216
  case 'test':
@@ -1159,6 +1266,16 @@ export async function run(argv, options = {}) {
1159
1266
  stdinIsTTY: options.stdinIsTTY,
1160
1267
  ask: options.ask,
1161
1268
  });
1269
+ case 'audiences':
1270
+ return await runAudiences(ctx, {
1271
+ host: parsed.flags.host,
1272
+ source: parsed.flags.source,
1273
+ positionals: parsed.positionals,
1274
+ yes: parsed.flags.yes === true,
1275
+ flags: parsed.flags,
1276
+ dir,
1277
+ env,
1278
+ });
1162
1279
  case 'catalog':
1163
1280
  return await runCatalog(ctx, {
1164
1281
  host: parsed.flags.host,
@@ -1359,6 +1476,9 @@ export async function run(argv, options = {}) {
1359
1476
  source: parsed.flags.source,
1360
1477
  dir,
1361
1478
  yes: parsed.flags.yes === true,
1479
+ dryRun: parsed.flags['dry-run'] === true,
1480
+ packages: parsed.positionals,
1481
+ all: parsed.flags.all === true,
1362
1482
  env,
1363
1483
  stdinIsTTY: options.stdinIsTTY,
1364
1484
  confirm: options.confirm,
@@ -1374,9 +1494,21 @@ export async function run(argv, options = {}) {
1374
1494
  stdinIsTTY: options.stdinIsTTY,
1375
1495
  confirm: options.confirm,
1376
1496
  });
1377
- default:
1497
+ default: {
1498
+ const experienceCommand = EXPERIENCE_COMMANDS.find((candidate) => candidate.name === command);
1499
+ if (experienceCommand) {
1500
+ return await experienceCommand.run(ctx, {
1501
+ flags: parsed.flags,
1502
+ positionals: parsed.positionals,
1503
+ passthrough: parsed.passthrough,
1504
+ dir,
1505
+ cwd,
1506
+ env,
1507
+ });
1508
+ }
1378
1509
  // Unreachable — COMMANDS gate above — but keeps the switch total.
1379
1510
  throw usageError(`Unknown command "${command}".`, `Run one of: ${COMMANDS.join(', ')}.`);
1511
+ }
1380
1512
  }
1381
1513
  }
1382
1514
  catch (error) {