@lexq/cli 0.1.30 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTEXT.md CHANGED
@@ -112,7 +112,7 @@ live)
112
112
 
113
113
  ### Simulation Statuses
114
114
 
115
- `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`
115
+ `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELED`
116
116
 
117
117
  ### Decision Statuses
118
118
 
package/dist/index.js CHANGED
@@ -300,13 +300,14 @@ function registerGroupCommands(program) {
300
300
  get Get group detail by ID
301
301
  create Create a new group
302
302
  update Update group settings
303
+ reorder Change group priorities (drag & drag equivalent)
303
304
  delete Archive a group
304
305
  ab-test Manage A/B tests (start, stop, adjust)
305
306
 
306
307
  Statuses: ACTIVE, DISABLED (emergency stop), ARCHIVED (soft delete)
307
308
  `
308
309
  );
309
- groups.command("list").description("List all policy groups").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
310
+ groups.command("list").description("List all policy groups").action(async () => {
310
311
  try {
311
312
  const globalOpts = program.opts();
312
313
  const format = globalOpts.format ?? "json";
@@ -314,13 +315,12 @@ function registerGroupCommands(program) {
314
315
  apiKey: globalOpts.apiKey,
315
316
  baseUrl: globalOpts.baseUrl,
316
317
  dryRun: globalOpts.dryRun,
317
- verbose: globalOpts.verbose,
318
- params: { page: opts.page, size: opts.size }
318
+ verbose: globalOpts.verbose
319
319
  });
320
320
  if (format === "table") {
321
321
  printTable(
322
322
  ["ID", "Name", "Status", "Priority", "Version", "Updated"],
323
- data.content.map((g) => [
323
+ data.map((g) => [
324
324
  g.id,
325
325
  g.name,
326
326
  g.status,
@@ -331,7 +331,7 @@ function registerGroupCommands(program) {
331
331
  { truncate: 24 }
332
332
  );
333
333
  console.log(`
334
- ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
334
+ ${data.length} total`);
335
335
  } else {
336
336
  printJson(data);
337
337
  }
@@ -362,19 +362,20 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
362
362
  Example:
363
363
  $ lexq groups create --json '{
364
364
  "name": "Payment Policy",
365
- "description": "Rules for payment processing",
366
- "priority": 0
365
+ "description": "Rules for payment processing"
367
366
  }'
368
367
 
369
368
  Fields:
370
369
  name string Group name (required, unique per tenant)
371
370
  description string Description (optional, max 255 chars)
372
- priority number Execution prioritylower runs first (required, min 0)
373
- activationGroup string Cross-group conflict resolution key (optional)
371
+ activationGroup string Execution Groupconflict-resolution cluster key (optional)
374
372
  activationMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
375
373
  activationStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT [default: FIRST_MATCH]
376
374
  executionLimit number Max rules to fire in MAX_N mode (optional)
377
375
  status string ACTIVE | DISABLED [default: ACTIVE]
376
+
377
+ priority is auto-assigned (appended last, tenant-wide). Use 'lexq groups reorder' to change order.
378
+ Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.
378
379
  `
379
380
  ).action(async (opts) => {
380
381
  try {
@@ -398,9 +399,9 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
398
399
  dedent3`
399
400
 
400
401
  Example:
401
- $ lexq groups update --id <groupId> --json '{"description": "Updated", "priority": 1}'
402
+ $ lexq groups update --id <groupId> --json '{"description": "Updated", "status": "DISABLED"}'
402
403
 
403
- All fields are optional — only provided fields are updated.
404
+ All fields are optional — only provided fields are updated. priority is not settable here — use 'lexq groups reorder'.
404
405
  `
405
406
  ).action(async (opts) => {
406
407
  try {
@@ -434,7 +435,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
434
435
  const answer = await rl.question(`Delete group ${opts.id}? [y/N] `);
435
436
  rl.close();
436
437
  if (answer.toLowerCase() !== "y") {
437
- console.log("Cancelled.");
438
+ console.log("Canceled.");
438
439
  return;
439
440
  }
440
441
  }
@@ -450,6 +451,41 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
450
451
  process.exit(1);
451
452
  }
452
453
  });
454
+ groups.command("reorder").description("Reorder policy groups by priority (drag & drop equivalent)").requiredOption("--group-ids <ids>", "Comma-separated group IDs in desired order").addHelpText(
455
+ "after",
456
+ dedent3`
457
+
458
+ Assigns priority 1, 2, 3, ... to groups in the order given (tenant-wide, 1...N continuous).
459
+ activationGroup (Execution Group) is NOT affected — reorder only changes priority.
460
+
461
+ Example:
462
+ $ lexq groups reorder --group-ids "id3,id1,id2"
463
+
464
+ Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
465
+ `
466
+ ).action(async (opts) => {
467
+ try {
468
+ const globalOpts = program.opts();
469
+ const groupIds = opts.groupIds.split(",").map((id) => id.trim());
470
+ const body = {
471
+ groups: groupIds.map((groupId, index) => ({
472
+ groupId,
473
+ priority: index + 1
474
+ }))
475
+ };
476
+ await apiRequest("PATCH", "policy-groups/reorder", {
477
+ apiKey: globalOpts.apiKey,
478
+ baseUrl: globalOpts.baseUrl,
479
+ dryRun: globalOpts.dryRun,
480
+ verbose: globalOpts.verbose,
481
+ body
482
+ });
483
+ console.log(`\u2713 ${groupIds.length} groups reordered.`);
484
+ } catch (error) {
485
+ printError(error);
486
+ process.exit(1);
487
+ }
488
+ });
453
489
  const abTest = groups.command("ab-test").description("A/B test management").addHelpText(
454
490
  "after",
455
491
  dedent3`
@@ -507,7 +543,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
507
543
  const answer = await rl.question(`Stop A/B test for group ${opts.groupId}? [y/N] `);
508
544
  rl.close();
509
545
  if (answer.toLowerCase() !== "y") {
510
- console.log("Cancelled.");
546
+ console.log("Canceled.");
511
547
  return;
512
548
  }
513
549
  }
@@ -719,7 +755,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
719
755
  const answer = await rl.question(`Delete version ${opts.id}? [y/N] `);
720
756
  rl.close();
721
757
  if (answer.toLowerCase() !== "y") {
722
- console.log("Cancelled.");
758
+ console.log("Canceled.");
723
759
  return;
724
760
  }
725
761
  }
@@ -803,7 +839,7 @@ function registerRuleCommands(program) {
803
839
  Only DRAFT versions allow rule modifications.
804
840
  `
805
841
  );
806
- rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
842
+ rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").action(async (opts) => {
807
843
  try {
808
844
  const globalOpts = program.opts();
809
845
  const format = globalOpts.format ?? "json";
@@ -814,14 +850,13 @@ function registerRuleCommands(program) {
814
850
  apiKey: globalOpts.apiKey,
815
851
  baseUrl: globalOpts.baseUrl,
816
852
  dryRun: globalOpts.dryRun,
817
- verbose: globalOpts.verbose,
818
- params: { page: opts.page, size: opts.size }
853
+ verbose: globalOpts.verbose
819
854
  }
820
855
  );
821
856
  if (format === "table") {
822
857
  printTable(
823
858
  ["ID", "Name", "Priority", "Conditions", "Actions", "Enabled"],
824
- data.content.map((r) => [
859
+ data.map((r) => [
825
860
  r.id,
826
861
  r.name,
827
862
  String(r.priority),
@@ -832,7 +867,7 @@ function registerRuleCommands(program) {
832
867
  { truncate: 24 }
833
868
  );
834
869
  console.log(`
835
- ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
870
+ ${data.length} total`);
836
871
  } else {
837
872
  printJson(data);
838
873
  }
@@ -867,7 +902,6 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
867
902
  Example:
868
903
  $ lexq rules create --group-id <gid> --version-id <vid> --json '{
869
904
  "name": "VIP 20% Discount",
870
- "priority": 0,
871
905
  "condition": {
872
906
  "type": "SINGLE",
873
907
  "field": "customer_tier",
@@ -891,8 +925,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
891
925
 
892
926
  Mutex (optional — rule-level conflict resolution):
893
927
  mutexGroup string Logical grouping key (e.g., "best-discount")
894
- mutexMode string NONE | EXCLUSIVE [default: NONE]
928
+ mutexMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
895
929
  mutexStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT
930
+ mutexLimit number Max rules to fire in MAX_N mode (required when MAX_N)
931
+
932
+ priority is auto-assigned (appended last). Use 'lexq rules reorder' to change order.
896
933
  `
897
934
  ).action(async (opts) => {
898
935
  try {
@@ -959,7 +996,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
959
996
  const answer = await rl.question(`Delete rule ${opts.id}? [y/N] `);
960
997
  rl.close();
961
998
  if (answer.toLowerCase() !== "y") {
962
- console.log("Cancelled.");
999
+ console.log("Canceled.");
963
1000
  return;
964
1001
  }
965
1002
  }
@@ -983,12 +1020,12 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
983
1020
  "after",
984
1021
  dedent5`
985
1022
 
986
- Assigns priority 0, 1, 2, ... to rules in the order given.
1023
+ Assigns priority 1, 2, 3, ... to rules in the order given (1-based, 1...N continuous).
987
1024
 
988
1025
  Example:
989
1026
  $ lexq rules reorder --group-id <gid> --version-id <vid> --rule-ids "id3,id1,id2"
990
1027
 
991
- Result: id3 → priority 0, id1 → priority 1, id2 → priority 2
1028
+ Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
992
1029
  `
993
1030
  ).action(async (opts) => {
994
1031
  try {
@@ -997,7 +1034,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
997
1034
  const body = {
998
1035
  rules: ruleIds.map((ruleId, index) => ({
999
1036
  ruleId,
1000
- priority: index
1037
+ priority: index + 1
1001
1038
  }))
1002
1039
  };
1003
1040
  await apiRequest(
@@ -1183,7 +1220,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1183
1220
  const answer = await rl.question(`Delete fact ${opts.id}? [y/N] `);
1184
1221
  rl.close();
1185
1222
  if (answer.toLowerCase() !== "y") {
1186
- console.log("Cancelled.");
1223
+ console.log("Canceled.");
1187
1224
  return;
1188
1225
  }
1189
1226
  }
@@ -1340,7 +1377,7 @@ function registerDeployCommands(program) {
1340
1377
  const answer = await rl.question(`Rollback group ${opts.groupId}? [y/N] `);
1341
1378
  rl.close();
1342
1379
  if (answer.toLowerCase() !== "y") {
1343
- console.log("Cancelled.");
1380
+ console.log("Canceled.");
1344
1381
  return;
1345
1382
  }
1346
1383
  }
@@ -1375,7 +1412,7 @@ function registerDeployCommands(program) {
1375
1412
  const answer = await rl.question(`Undeploy group ${opts.groupId}? [y/N] `);
1376
1413
  rl.close();
1377
1414
  if (answer.toLowerCase() !== "y") {
1378
- console.log("Cancelled.");
1415
+ console.log("Canceled.");
1379
1416
  return;
1380
1417
  }
1381
1418
  }
@@ -1830,10 +1867,7 @@ function registerAnalyticsCommands(program) {
1830
1867
  process.exit(1);
1831
1868
  }
1832
1869
  });
1833
- sim.command("list").description("List simulation history").option(
1834
- "--status <status>",
1835
- "Filter by status (PENDING, RUNNING, COMPLETED, FAILED, CANCELLED)"
1836
- ).option("--from <date>", "Start date (yyyy-MM-dd)").option("--to <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
1870
+ sim.command("list").description("List simulation history").option("--status <status>", "Filter by status (PENDING, RUNNING, COMPLETED, FAILED, CANCELED)").option("--from <date>", "Start date (yyyy-MM-dd)").option("--to <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
1837
1871
  try {
1838
1872
  const globalOpts = program.opts();
1839
1873
  const format = globalOpts.format ?? "json";
@@ -1880,7 +1914,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1880
1914
  "after",
1881
1915
  dedent8`
1882
1916
 
1883
- Only PENDING or RUNNING simulations can be cancelled.
1917
+ Only PENDING or RUNNING simulations can be canceled.
1884
1918
  `
1885
1919
  ).action(async (opts) => {
1886
1920
  try {
@@ -1891,7 +1925,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1891
1925
  const answer = await rl.question(`Cancel simulation ${opts.id}? [y/N] `);
1892
1926
  rl.close();
1893
1927
  if (answer.toLowerCase() !== "y") {
1894
- console.log("Cancelled.");
1928
+ console.log("Canceled.");
1895
1929
  return;
1896
1930
  }
1897
1931
  }
@@ -1901,7 +1935,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1901
1935
  dryRun: globalOpts.dryRun,
1902
1936
  verbose: globalOpts.verbose
1903
1937
  });
1904
- console.log(`\u2713 Simulation ${opts.id} cancelled.`);
1938
+ console.log(`\u2713 Simulation ${opts.id} canceled.`);
1905
1939
  } catch (error) {
1906
1940
  printError(error);
1907
1941
  process.exit(1);
@@ -2351,7 +2385,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2351
2385
  const answer = await rl.question(`Delete integration ${opts.id}? [y/N] `);
2352
2386
  rl.close();
2353
2387
  if (answer.toLowerCase() !== "y") {
2354
- console.log("Cancelled.");
2388
+ console.log("Canceled.");
2355
2389
  return;
2356
2390
  }
2357
2391
  }
@@ -2728,7 +2762,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2728
2762
  const answer = await rl.question(`Delete webhook subscription ${opts.id}? [y/N] `);
2729
2763
  rl.close();
2730
2764
  if (answer.toLowerCase() !== "y") {
2731
- console.log("Cancelled.");
2765
+ console.log("Canceled.");
2732
2766
  return;
2733
2767
  }
2734
2768
  }
@@ -2868,16 +2902,10 @@ function registerGroupTools(server, callApi) {
2868
2902
  "lexq_groups_list",
2869
2903
  {
2870
2904
  title: "List Policy Groups",
2871
- description: "List all policy groups with pagination.",
2872
- inputSchema: {
2873
- page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
2874
- size: z.number().int().min(1).max(100).default(20).describe("Page size")
2875
- }
2905
+ description: "List all policy groups (tenant-wide, priority ASC).",
2906
+ inputSchema: {}
2876
2907
  },
2877
- async ({ page, size }) => {
2878
- const params = paginationParams(page, size);
2879
- return callApi("GET", "policy-groups", { params });
2880
- }
2908
+ async () => callApi("GET", "policy-groups")
2881
2909
  );
2882
2910
  server.registerTool(
2883
2911
  "lexq_groups_get",
@@ -2894,10 +2922,9 @@ function registerGroupTools(server, callApi) {
2894
2922
  "lexq_groups_create",
2895
2923
  {
2896
2924
  title: "Create Policy Group",
2897
- description: "Create a new policy group. Requires name and priority. Optionally set conflict resolution, activation group, and description.",
2925
+ description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
2898
2926
  inputSchema: {
2899
2927
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
2900
- priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
2901
2928
  description: z.string().optional().describe("Group description"),
2902
2929
  activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2903
2930
  activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
@@ -2907,8 +2934,7 @@ function registerGroupTools(server, callApi) {
2907
2934
  },
2908
2935
  async (args) => {
2909
2936
  const body = {
2910
- name: args.name,
2911
- priority: args.priority
2937
+ name: args.name
2912
2938
  };
2913
2939
  if (args.description !== void 0) body.description = args.description;
2914
2940
  if (args.activationMode !== void 0) body.activationMode = args.activationMode;
@@ -2926,9 +2952,9 @@ function registerGroupTools(server, callApi) {
2926
2952
  inputSchema: {
2927
2953
  groupId: z.string().uuid().describe("Policy group ID"),
2928
2954
  name: z.string().optional().describe("New name"),
2929
- priority: z.number().int().min(0).optional().describe("New priority"),
2930
2955
  description: z.string().optional().describe("New description"),
2931
2956
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
2957
+ activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
2932
2958
  activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2933
2959
  activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
2934
2960
  executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
@@ -2947,6 +2973,23 @@ function registerGroupTools(server, callApi) {
2947
2973
  },
2948
2974
  async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
2949
2975
  );
2976
+ server.registerTool(
2977
+ "lexq_groups_reorder",
2978
+ {
2979
+ title: "Reorder Policy Groups",
2980
+ description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
2981
+ inputSchema: {
2982
+ groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
2983
+ }
2984
+ },
2985
+ async ({ groupIds }) => {
2986
+ const groups = groupIds.map((groupId, index) => ({
2987
+ groupId,
2988
+ priority: index + 1
2989
+ }));
2990
+ return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
2991
+ }
2992
+ );
2950
2993
  server.registerTool(
2951
2994
  "lexq_ab_test_start",
2952
2995
  {
@@ -3082,17 +3125,13 @@ function registerRuleTools(server, callApi) {
3082
3125
  "lexq_rules_list",
3083
3126
  {
3084
3127
  title: "List Rules",
3085
- description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
3128
+ description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
3086
3129
  inputSchema: {
3087
3130
  groupId: z3.string().uuid().describe("Policy group ID"),
3088
- versionId: z3.string().uuid().describe("Version ID"),
3089
- page: z3.number().int().min(0).default(0).describe("Page number"),
3090
- size: z3.number().int().min(1).max(100).default(20).describe("Page size")
3131
+ versionId: z3.string().uuid().describe("Version ID")
3091
3132
  }
3092
3133
  },
3093
- async ({ groupId, versionId, page, size }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`, {
3094
- params: paginationParams(page, size)
3095
- })
3134
+ async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
3096
3135
  );
3097
3136
  server.registerTool(
3098
3137
  "lexq_rules_get",
@@ -3112,7 +3151,7 @@ function registerRuleTools(server, callApi) {
3112
3151
  {
3113
3152
  title: "Create Rule",
3114
3153
  description: dedent13`
3115
- Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
3154
+ Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
3116
3155
 
3117
3156
  Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
3118
3157
  If a required key is missing, ask the user to confirm the type, isRequired, and description
@@ -3145,7 +3184,7 @@ function registerRuleTools(server, callApi) {
3145
3184
  groupId: z3.string().uuid().describe("Policy group ID"),
3146
3185
  versionId: z3.string().uuid().describe("Version ID"),
3147
3186
  rule: z3.string().describe(
3148
- "JSON string of CreateRuleRequest: { name, priority, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
3187
+ "JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
3149
3188
  )
3150
3189
  }
3151
3190
  },
@@ -3164,7 +3203,7 @@ function registerRuleTools(server, callApi) {
3164
3203
  versionId: z3.string().uuid().describe("Version ID"),
3165
3204
  ruleId: z3.string().uuid().describe("Rule ID"),
3166
3205
  rule: z3.string().describe(
3167
- "JSON string of UpdateRuleRequest: { name?, priority?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
3206
+ "JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
3168
3207
  )
3169
3208
  }
3170
3209
  },
@@ -3199,7 +3238,7 @@ function registerRuleTools(server, callApi) {
3199
3238
  "lexq_rules_reorder",
3200
3239
  {
3201
3240
  title: "Reorder Rules",
3202
- description: "Reorder rules by specifying rule IDs and their new priorities. Array index 0 = highest priority.",
3241
+ description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
3203
3242
  inputSchema: {
3204
3243
  groupId: z3.string().uuid().describe("Policy group ID"),
3205
3244
  versionId: z3.string().uuid().describe("Version ID"),
@@ -3209,7 +3248,7 @@ function registerRuleTools(server, callApi) {
3209
3248
  async ({ groupId, versionId, ruleIds }) => {
3210
3249
  const rules = ruleIds.map((ruleId, index) => ({
3211
3250
  ruleId,
3212
- priority: index
3251
+ priority: index + 1
3213
3252
  }));
3214
3253
  return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
3215
3254
  body: { rules }
@@ -3555,7 +3594,7 @@ function registerAnalyticsTools(server, callApi) {
3555
3594
  inputSchema: {
3556
3595
  page: z6.number().int().min(0).default(0).describe("Page number"),
3557
3596
  size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
3558
- status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELLED"]).optional().describe("Filter by status"),
3597
+ status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
3559
3598
  from: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
3560
3599
  to: z6.string().optional().describe("End date (yyyy-MM-dd)")
3561
3600
  }
@@ -4133,7 +4172,7 @@ function registerDomainTemplateCommands(program) {
4133
4172
  );
4134
4173
  rl.close();
4135
4174
  if (answer.toLowerCase() !== "y") {
4136
- console.log("Cancelled.");
4175
+ console.log("Canceled.");
4137
4176
  return;
4138
4177
  }
4139
4178
  }
@@ -13,38 +13,15 @@ function registerStatusTools(server, callApi) {
13
13
 
14
14
  // src/mcp/tools/groups.ts
15
15
  import { z } from "zod";
16
-
17
- // src/lib/config.ts
18
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
19
- import { homedir } from "os";
20
- import { join } from "path";
21
- var CONFIG_DIR = join(homedir(), ".lexq");
22
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
23
-
24
- // src/mcp/tools/_shared.ts
25
- function paginationParams(page, size) {
26
- const params = {};
27
- if (page !== void 0) params.page = String(page);
28
- if (size !== void 0) params.size = String(size);
29
- return params;
30
- }
31
-
32
- // src/mcp/tools/groups.ts
33
16
  function registerGroupTools(server, callApi) {
34
17
  server.registerTool(
35
18
  "lexq_groups_list",
36
19
  {
37
20
  title: "List Policy Groups",
38
- description: "List all policy groups with pagination.",
39
- inputSchema: {
40
- page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
41
- size: z.number().int().min(1).max(100).default(20).describe("Page size")
42
- }
21
+ description: "List all policy groups (tenant-wide, priority ASC).",
22
+ inputSchema: {}
43
23
  },
44
- async ({ page, size }) => {
45
- const params = paginationParams(page, size);
46
- return callApi("GET", "policy-groups", { params });
47
- }
24
+ async () => callApi("GET", "policy-groups")
48
25
  );
49
26
  server.registerTool(
50
27
  "lexq_groups_get",
@@ -61,10 +38,9 @@ function registerGroupTools(server, callApi) {
61
38
  "lexq_groups_create",
62
39
  {
63
40
  title: "Create Policy Group",
64
- description: "Create a new policy group. Requires name and priority. Optionally set conflict resolution, activation group, and description.",
41
+ description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
65
42
  inputSchema: {
66
43
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
67
- priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
68
44
  description: z.string().optional().describe("Group description"),
69
45
  activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
70
46
  activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
@@ -74,8 +50,7 @@ function registerGroupTools(server, callApi) {
74
50
  },
75
51
  async (args) => {
76
52
  const body = {
77
- name: args.name,
78
- priority: args.priority
53
+ name: args.name
79
54
  };
80
55
  if (args.description !== void 0) body.description = args.description;
81
56
  if (args.activationMode !== void 0) body.activationMode = args.activationMode;
@@ -93,9 +68,9 @@ function registerGroupTools(server, callApi) {
93
68
  inputSchema: {
94
69
  groupId: z.string().uuid().describe("Policy group ID"),
95
70
  name: z.string().optional().describe("New name"),
96
- priority: z.number().int().min(0).optional().describe("New priority"),
97
71
  description: z.string().optional().describe("New description"),
98
72
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
73
+ activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
99
74
  activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
100
75
  activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
101
76
  executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
@@ -114,6 +89,23 @@ function registerGroupTools(server, callApi) {
114
89
  },
115
90
  async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
116
91
  );
92
+ server.registerTool(
93
+ "lexq_groups_reorder",
94
+ {
95
+ title: "Reorder Policy Groups",
96
+ description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
97
+ inputSchema: {
98
+ groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
99
+ }
100
+ },
101
+ async ({ groupIds }) => {
102
+ const groups = groupIds.map((groupId, index) => ({
103
+ groupId,
104
+ priority: index + 1
105
+ }));
106
+ return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
107
+ }
108
+ );
117
109
  server.registerTool(
118
110
  "lexq_ab_test_start",
119
111
  {
@@ -154,6 +146,23 @@ function registerGroupTools(server, callApi) {
154
146
 
155
147
  // src/mcp/tools/versions.ts
156
148
  import { z as z2 } from "zod";
149
+
150
+ // src/lib/config.ts
151
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
152
+ import { homedir } from "os";
153
+ import { join } from "path";
154
+ var CONFIG_DIR = join(homedir(), ".lexq");
155
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
156
+
157
+ // src/mcp/tools/_shared.ts
158
+ function paginationParams(page, size) {
159
+ const params = {};
160
+ if (page !== void 0) params.page = String(page);
161
+ if (size !== void 0) params.size = String(size);
162
+ return params;
163
+ }
164
+
165
+ // src/mcp/tools/versions.ts
157
166
  function registerVersionTools(server, callApi) {
158
167
  server.registerTool(
159
168
  "lexq_versions_list",
@@ -249,17 +258,13 @@ function registerRuleTools(server, callApi) {
249
258
  "lexq_rules_list",
250
259
  {
251
260
  title: "List Rules",
252
- description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
261
+ description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
253
262
  inputSchema: {
254
263
  groupId: z3.string().uuid().describe("Policy group ID"),
255
- versionId: z3.string().uuid().describe("Version ID"),
256
- page: z3.number().int().min(0).default(0).describe("Page number"),
257
- size: z3.number().int().min(1).max(100).default(20).describe("Page size")
264
+ versionId: z3.string().uuid().describe("Version ID")
258
265
  }
259
266
  },
260
- async ({ groupId, versionId, page, size }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`, {
261
- params: paginationParams(page, size)
262
- })
267
+ async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
263
268
  );
264
269
  server.registerTool(
265
270
  "lexq_rules_get",
@@ -279,7 +284,7 @@ function registerRuleTools(server, callApi) {
279
284
  {
280
285
  title: "Create Rule",
281
286
  description: dedent`
282
- Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
287
+ Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
283
288
 
284
289
  Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
285
290
  If a required key is missing, ask the user to confirm the type, isRequired, and description
@@ -312,7 +317,7 @@ function registerRuleTools(server, callApi) {
312
317
  groupId: z3.string().uuid().describe("Policy group ID"),
313
318
  versionId: z3.string().uuid().describe("Version ID"),
314
319
  rule: z3.string().describe(
315
- "JSON string of CreateRuleRequest: { name, priority, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
320
+ "JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
316
321
  )
317
322
  }
318
323
  },
@@ -331,7 +336,7 @@ function registerRuleTools(server, callApi) {
331
336
  versionId: z3.string().uuid().describe("Version ID"),
332
337
  ruleId: z3.string().uuid().describe("Rule ID"),
333
338
  rule: z3.string().describe(
334
- "JSON string of UpdateRuleRequest: { name?, priority?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
339
+ "JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
335
340
  )
336
341
  }
337
342
  },
@@ -366,7 +371,7 @@ function registerRuleTools(server, callApi) {
366
371
  "lexq_rules_reorder",
367
372
  {
368
373
  title: "Reorder Rules",
369
- description: "Reorder rules by specifying rule IDs and their new priorities. Array index 0 = highest priority.",
374
+ description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
370
375
  inputSchema: {
371
376
  groupId: z3.string().uuid().describe("Policy group ID"),
372
377
  versionId: z3.string().uuid().describe("Version ID"),
@@ -376,7 +381,7 @@ function registerRuleTools(server, callApi) {
376
381
  async ({ groupId, versionId, ruleIds }) => {
377
382
  const rules = ruleIds.map((ruleId, index) => ({
378
383
  ruleId,
379
- priority: index
384
+ priority: index + 1
380
385
  }));
381
386
  return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
382
387
  body: { rules }
@@ -722,7 +727,7 @@ function registerAnalyticsTools(server, callApi) {
722
727
  inputSchema: {
723
728
  page: z6.number().int().min(0).default(0).describe("Page number"),
724
729
  size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
725
- status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELLED"]).optional().describe("Filter by status"),
730
+ status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
726
731
  from: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
727
732
  to: z6.string().optional().describe("End date (yyyy-MM-dd)")
728
733
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexq/cli",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "LexQ CLI — manage policies, simulate rules, and deploy from the terminal. Built for humans and AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -260,7 +260,7 @@ Simulation is async. Poll until `status` is `COMPLETED` or `FAILED`.
260
260
  | `RUNNING` | In progress (`progress` field shows 0–100) |
261
261
  | `COMPLETED` | Done — results available |
262
262
  | `FAILED` | Error occurred |
263
- | `CANCELLED` | Manually cancelled |
263
+ | `CANCELED` | Manually canceled |
264
264
 
265
265
  ### List Simulations
266
266