@lexq/cli 0.1.14 → 0.1.16

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/dist/index.js CHANGED
@@ -910,7 +910,7 @@ function buildUpdateBody2(opts) {
910
910
  import "commander";
911
911
  function registerDeployCommands(program) {
912
912
  const deploy = program.command("deploy").description("Deployment lifecycle and history");
913
- deploy.command("publish").description("Publish a DRAFT version (DRAFT \u2192 ACTIVE)").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to publish").option("--memo <memo>", "Deployment memo").action(async (opts) => {
913
+ deploy.command("publish").description("Publish a DRAFT version (DRAFT \u2192 ACTIVE)").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to publish").requiredOption("--memo <memo>", "Publish Deployment memo").action(async (opts) => {
914
914
  try {
915
915
  const globalOpts = program.opts();
916
916
  await apiRequest(
@@ -921,7 +921,7 @@ function registerDeployCommands(program) {
921
921
  baseUrl: globalOpts.baseUrl,
922
922
  dryRun: globalOpts.dryRun,
923
923
  verbose: globalOpts.verbose,
924
- body: { memo: opts.memo ?? "" }
924
+ body: { memo: opts.memo }
925
925
  }
926
926
  );
927
927
  console.log(`\u2713 Version ${opts.versionId} published.`);
@@ -930,7 +930,7 @@ function registerDeployCommands(program) {
930
930
  process.exit(1);
931
931
  }
932
932
  });
933
- deploy.command("live").description("Deploy an ACTIVE version to live traffic").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to deploy").option("--memo <memo>", "Deployment memo").action(async (opts) => {
933
+ deploy.command("live").description("Deploy an ACTIVE version to live traffic").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to deploy").requiredOption("--memo <memo>", "Live Deployment memo").action(async (opts) => {
934
934
  try {
935
935
  const globalOpts = program.opts();
936
936
  await apiRequest("POST", `policy-groups/${opts.groupId}/deploy`, {
@@ -938,7 +938,7 @@ function registerDeployCommands(program) {
938
938
  baseUrl: globalOpts.baseUrl,
939
939
  dryRun: globalOpts.dryRun,
940
940
  verbose: globalOpts.verbose,
941
- body: { versionId: opts.versionId, memo: opts.memo ?? "" }
941
+ body: { versionId: opts.versionId, memo: opts.memo }
942
942
  });
943
943
  console.log(`\u2713 Version ${opts.versionId} deployed to live.`);
944
944
  } catch (error) {
@@ -946,7 +946,7 @@ function registerDeployCommands(program) {
946
946
  process.exit(1);
947
947
  }
948
948
  });
949
- deploy.command("rollback").description("Rollback to the previous deployed version").requiredOption("--group-id <groupId>", "Policy group ID").option("--memo <memo>", "Rollback reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
949
+ deploy.command("rollback").description("Rollback to the previous deployed version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--memo <memo>", "Rollback reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
950
950
  try {
951
951
  const globalOpts = program.opts();
952
952
  if (!opts.force) {
@@ -964,7 +964,7 @@ function registerDeployCommands(program) {
964
964
  baseUrl: globalOpts.baseUrl,
965
965
  dryRun: globalOpts.dryRun,
966
966
  verbose: globalOpts.verbose,
967
- body: { memo: opts.memo ?? "" }
967
+ body: { memo: opts.memo }
968
968
  });
969
969
  console.log(`\u2713 Group ${opts.groupId} rolled back.`);
970
970
  } catch (error) {
@@ -972,7 +972,7 @@ function registerDeployCommands(program) {
972
972
  process.exit(1);
973
973
  }
974
974
  });
975
- deploy.command("undeploy").description("Remove the live version from a group").requiredOption("--group-id <groupId>", "Policy group ID").option("--memo <memo>", "Undeploy reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
975
+ deploy.command("undeploy").description("Remove the live version from a group").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--memo <memo>", "Undeploy reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
976
976
  try {
977
977
  const globalOpts = program.opts();
978
978
  if (!opts.force) {
@@ -990,7 +990,7 @@ function registerDeployCommands(program) {
990
990
  baseUrl: globalOpts.baseUrl,
991
991
  dryRun: globalOpts.dryRun,
992
992
  verbose: globalOpts.verbose,
993
- body: { memo: opts.memo ?? "" }
993
+ body: { memo: opts.memo }
994
994
  });
995
995
  console.log(`\u2713 Group ${opts.groupId} undeployed.`);
996
996
  } catch (error) {
@@ -1553,7 +1553,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1553
1553
  process.exit(1);
1554
1554
  }
1555
1555
  });
1556
- history.command("get").description("Get execution detail").requiredOption("--id <executionId>", "Execution history ID").action(async (opts) => {
1556
+ history.command("get").description("Get execution detail").requiredOption("--id <traceId>", "Trace ID").action(async (opts) => {
1557
1557
  try {
1558
1558
  const globalOpts = program.opts();
1559
1559
  const data = await apiRequest(
@@ -1973,10 +1973,10 @@ function registerGroupTools(server, callApi) {
1973
1973
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
1974
1974
  priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
1975
1975
  description: z.string().optional().describe("Group description"),
1976
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
1977
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
1978
- maxSelections: z.number().int().min(1).optional().describe("Max selections (required when mode is MAX_N)"),
1979
- activationGroupId: z.string().uuid().optional().describe("Activation group ID")
1976
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
1977
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
1978
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions (required when mode is MAX_N)"),
1979
+ activationGroup: z.string().optional().describe("Activation group name")
1980
1980
  }
1981
1981
  },
1982
1982
  async (args) => {
@@ -1985,12 +1985,10 @@ function registerGroupTools(server, callApi) {
1985
1985
  priority: args.priority
1986
1986
  };
1987
1987
  if (args.description !== void 0) body.description = args.description;
1988
- if (args.conflictResolutionMode !== void 0)
1989
- body.conflictResolutionMode = args.conflictResolutionMode;
1990
- if (args.conflictResolutionStrategy !== void 0)
1991
- body.conflictResolutionStrategy = args.conflictResolutionStrategy;
1992
- if (args.maxSelections !== void 0) body.maxSelections = args.maxSelections;
1993
- if (args.activationGroupId !== void 0) body.activationGroupId = args.activationGroupId;
1988
+ if (args.activationMode !== void 0) body.activationMode = args.activationMode;
1989
+ if (args.activationStrategy !== void 0) body.activationStrategy = args.activationStrategy;
1990
+ if (args.executionLimit !== void 0) body.executionLimit = args.executionLimit;
1991
+ if (args.activationGroup !== void 0) body.activationGroup = args.activationGroup;
1994
1992
  return callApi("POST", "policy-groups", { body });
1995
1993
  }
1996
1994
  );
@@ -1998,16 +1996,16 @@ function registerGroupTools(server, callApi) {
1998
1996
  "lexq_groups_update",
1999
1997
  {
2000
1998
  title: "Update Policy Group",
2001
- description: "Update an existing policy group. Only provided fields are updated.",
1999
+ description: "Update a policy group. This is a full replacement \u2014 omitted optional fields will be set to null on the server.",
2002
2000
  inputSchema: {
2003
2001
  groupId: z.string().uuid().describe("Policy group ID"),
2004
2002
  name: z.string().optional().describe("New name"),
2005
2003
  priority: z.number().int().min(0).optional().describe("New priority"),
2006
2004
  description: z.string().optional().describe("New description"),
2007
2005
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
2008
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2009
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
2010
- maxSelections: z.number().int().min(1).optional().describe("Max selections")
2006
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2007
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
2008
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
2011
2009
  }
2012
2010
  },
2013
2011
  async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
@@ -2186,17 +2184,24 @@ function registerRuleTools(server, callApi) {
2186
2184
  "lexq_rules_create",
2187
2185
  {
2188
2186
  title: "Create Rule",
2189
- description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
2187
+ description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
2188
+
2189
+ Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
2190
+ If a required key is missing, ask the user to confirm the type, isRequired, and description
2191
+ before calling lexq_facts_create \u2014 registering facts enables type validation, Console UI
2192
+ autocomplete, and the dry-run requirements analyzer.
2193
+
2190
2194
  Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
2191
- Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
2195
+ Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
2192
2196
  Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
2193
-
2197
+
2194
2198
  Actions: [{ type, parameters }]
2195
-
2199
+
2196
2200
  Action parameter schemas:
2197
2201
  - DISCOUNT: { refVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT) }
2198
2202
  - POINT: { refVar: string, targetVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), integrationId: uuid }
2199
- - COUPON_ISSUE: { couponId: string, integrationId: uuid }- BLOCK: { reason: string }
2203
+ - COUPON_ISSUE: { couponId: string, integrationId: uuid }
2204
+ - BLOCK: { reason: string }
2200
2205
  - NOTIFICATION: { channel: "SMS"|"EMAIL"|"PUSH"|"KAKAO", targetVar: string, templateId: string, integrationId: uuid }
2201
2206
  - WEBHOOK: { url: string, method: "POST" }
2202
2207
  - SET_FACT: { key: string, value: string|number|boolean }
@@ -2367,7 +2372,7 @@ function registerDeployTools(server, callApi) {
2367
2372
  inputSchema: {
2368
2373
  groupId: z5.string().uuid().describe("Policy group ID"),
2369
2374
  versionId: z5.string().uuid().describe("Version ID to publish"),
2370
- memo: z5.string().default("").describe("Deployment memo")
2375
+ memo: z5.string().min(1).describe("Publish Deployment memo (required)")
2371
2376
  }
2372
2377
  },
2373
2378
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
@@ -2380,7 +2385,7 @@ function registerDeployTools(server, callApi) {
2380
2385
  inputSchema: {
2381
2386
  groupId: z5.string().uuid().describe("Policy group ID"),
2382
2387
  versionId: z5.string().uuid().describe("Version ID to deploy"),
2383
- memo: z5.string().default("").describe("Deployment memo")
2388
+ memo: z5.string().min(1).describe("Live Deployment memo (required)")
2384
2389
  }
2385
2390
  },
2386
2391
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
@@ -2464,10 +2469,8 @@ function registerAnalyticsTools(server, callApi) {
2464
2469
  {
2465
2470
  title: "Dry Run",
2466
2471
  description: `Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
2467
-
2468
- Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
2469
-
2470
- Always dry-run before publishing to validate rule behavior.`,
2472
+ Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
2473
+ Always dry-run before publishing to validate rule behavior.`,
2471
2474
  inputSchema: {
2472
2475
  versionId: z6.string().uuid().describe("Policy version ID to test against"),
2473
2476
  facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
@@ -2516,14 +2519,21 @@ Always dry-run before publishing to validate rule behavior.`,
2516
2519
  "lexq_simulation_start",
2517
2520
  {
2518
2521
  title: "Start Simulation",
2519
- description: `Start a batch simulation against historical execution data.
2520
-
2521
- Example body:
2522
- {
2523
- "policyVersionId": "<uuid>",
2524
- "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
2525
- "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
2526
- }`,
2522
+ description: `Start a batch simulation against historical or uploaded data.
2523
+
2524
+ dataset.type: "HISTORICAL" or "UPLOADED"
2525
+ dataset.source (when HISTORICAL): "EXECUTION_LOGS"
2526
+ dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
2527
+ options.maxRecords: number (max 100000, default 10000)
2528
+ options.baselinePolicyVersionId: uuid (optional, for comparison)
2529
+ options.includeRuleStats: boolean
2530
+
2531
+ Example body:
2532
+ {
2533
+ "policyVersionId": "<uuid>",
2534
+ "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
2535
+ "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
2536
+ }`,
2527
2537
  inputSchema: {
2528
2538
  body: z6.string().describe("JSON string of SimulationRequest")
2529
2539
  }
@@ -2595,16 +2605,15 @@ Example body:
2595
2605
  {
2596
2606
  title: "Upload Dataset",
2597
2607
  description: `Upload inline CSV or JSON content as a simulation dataset.
2598
- The content is uploaded to S3 and a path is returned.
2599
- Use this path in simulation start with dataset type UPLOADED.
2600
-
2601
- CSV example:
2602
- user_id,payment_amount
2603
- user_001,150000
2604
- user_002,50000
2605
-
2606
- JSON example:
2607
- [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
2608
+ The content is uploaded to S3 and a path is returned.
2609
+ Use this path in simulation start with dataset type UPLOADED.
2610
+
2611
+ CSV example:
2612
+ user_id,payment_amount
2613
+ user_001,150000
2614
+ user_002,50000
2615
+
2616
+ JSON example: [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
2608
2617
  inputSchema: {
2609
2618
  content: z6.string().describe("CSV or JSON content as string"),
2610
2619
  filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
@@ -2667,10 +2676,10 @@ function registerHistoryTools(server, callApi) {
2667
2676
  title: "Get Execution Detail",
2668
2677
  description: "Get full execution detail including request facts, result traces, and decision traces.",
2669
2678
  inputSchema: {
2670
- executionId: z7.string().uuid().describe("Execution history ID")
2679
+ traceId: z7.string().describe("Trace ID from execution history")
2671
2680
  }
2672
2681
  },
2673
- async ({ executionId }) => callApi("GET", `execution/history/${executionId}`)
2682
+ async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
2674
2683
  );
2675
2684
  server.registerTool(
2676
2685
  "lexq_history_stats",
@@ -2728,19 +2737,20 @@ function registerIntegrationTools(server, callApi) {
2728
2737
  "lexq_integrations_save",
2729
2738
  {
2730
2739
  title: "Save Integration",
2731
- description: "Create or update an integration. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
2740
+ description: "Create or update an integration. Provide id to update an existing one; omit id to create new. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
2732
2741
  inputSchema: {
2742
+ id: z8.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
2733
2743
  type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
2734
2744
  name: z8.string().describe("Integration name"),
2735
2745
  baseUrl: z8.string().describe("Base URL of the external service"),
2736
- apiKey: z8.string().optional().describe("API key or token for the service"),
2737
- isActive: z8.boolean().default(true).describe("Whether the integration is active"),
2738
- config: z8.string().optional().describe("JSON string of additional config key-value pairs")
2746
+ credential: z8.string().optional().describe("API key or token for the service"),
2747
+ additionalConfig: z8.string().optional().describe("JSON string of additional config key-value pairs"),
2748
+ isActive: z8.boolean().default(true).describe("Whether the integration is active")
2739
2749
  }
2740
2750
  },
2741
- async ({ config, ...rest }) => {
2751
+ async ({ additionalConfig, ...rest }) => {
2742
2752
  const body = { ...rest };
2743
- if (config) body.config = JSON.parse(config);
2753
+ if (additionalConfig) body.additionalConfig = JSON.parse(additionalConfig);
2744
2754
  return callApi("POST", "integrations", { body });
2745
2755
  }
2746
2756
  );
@@ -66,10 +66,10 @@ function registerGroupTools(server, callApi) {
66
66
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
67
67
  priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
68
68
  description: z.string().optional().describe("Group description"),
69
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
70
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
71
- maxSelections: z.number().int().min(1).optional().describe("Max selections (required when mode is MAX_N)"),
72
- activationGroupId: z.string().uuid().optional().describe("Activation group ID")
69
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
70
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
71
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions (required when mode is MAX_N)"),
72
+ activationGroup: z.string().optional().describe("Activation group name")
73
73
  }
74
74
  },
75
75
  async (args) => {
@@ -78,12 +78,10 @@ function registerGroupTools(server, callApi) {
78
78
  priority: args.priority
79
79
  };
80
80
  if (args.description !== void 0) body.description = args.description;
81
- if (args.conflictResolutionMode !== void 0)
82
- body.conflictResolutionMode = args.conflictResolutionMode;
83
- if (args.conflictResolutionStrategy !== void 0)
84
- body.conflictResolutionStrategy = args.conflictResolutionStrategy;
85
- if (args.maxSelections !== void 0) body.maxSelections = args.maxSelections;
86
- if (args.activationGroupId !== void 0) body.activationGroupId = args.activationGroupId;
81
+ if (args.activationMode !== void 0) body.activationMode = args.activationMode;
82
+ if (args.activationStrategy !== void 0) body.activationStrategy = args.activationStrategy;
83
+ if (args.executionLimit !== void 0) body.executionLimit = args.executionLimit;
84
+ if (args.activationGroup !== void 0) body.activationGroup = args.activationGroup;
87
85
  return callApi("POST", "policy-groups", { body });
88
86
  }
89
87
  );
@@ -91,16 +89,16 @@ function registerGroupTools(server, callApi) {
91
89
  "lexq_groups_update",
92
90
  {
93
91
  title: "Update Policy Group",
94
- description: "Update an existing policy group. Only provided fields are updated.",
92
+ description: "Update a policy group. This is a full replacement \u2014 omitted optional fields will be set to null on the server.",
95
93
  inputSchema: {
96
94
  groupId: z.string().uuid().describe("Policy group ID"),
97
95
  name: z.string().optional().describe("New name"),
98
96
  priority: z.number().int().min(0).optional().describe("New priority"),
99
97
  description: z.string().optional().describe("New description"),
100
98
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
101
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
102
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
103
- maxSelections: z.number().int().min(1).optional().describe("Max selections")
99
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
100
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
101
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
104
102
  }
105
103
  },
106
104
  async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
@@ -279,17 +277,24 @@ function registerRuleTools(server, callApi) {
279
277
  "lexq_rules_create",
280
278
  {
281
279
  title: "Create Rule",
282
- description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
280
+ description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
281
+
282
+ Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
283
+ If a required key is missing, ask the user to confirm the type, isRequired, and description
284
+ before calling lexq_facts_create \u2014 registering facts enables type validation, Console UI
285
+ autocomplete, and the dry-run requirements analyzer.
286
+
283
287
  Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
284
- Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
288
+ Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
285
289
  Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
286
-
290
+
287
291
  Actions: [{ type, parameters }]
288
-
292
+
289
293
  Action parameter schemas:
290
294
  - DISCOUNT: { refVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT) }
291
295
  - POINT: { refVar: string, targetVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), integrationId: uuid }
292
- - COUPON_ISSUE: { couponId: string, integrationId: uuid }- BLOCK: { reason: string }
296
+ - COUPON_ISSUE: { couponId: string, integrationId: uuid }
297
+ - BLOCK: { reason: string }
293
298
  - NOTIFICATION: { channel: "SMS"|"EMAIL"|"PUSH"|"KAKAO", targetVar: string, templateId: string, integrationId: uuid }
294
299
  - WEBHOOK: { url: string, method: "POST" }
295
300
  - SET_FACT: { key: string, value: string|number|boolean }
@@ -460,7 +465,7 @@ function registerDeployTools(server, callApi) {
460
465
  inputSchema: {
461
466
  groupId: z5.string().uuid().describe("Policy group ID"),
462
467
  versionId: z5.string().uuid().describe("Version ID to publish"),
463
- memo: z5.string().default("").describe("Deployment memo")
468
+ memo: z5.string().min(1).describe("Publish Deployment memo (required)")
464
469
  }
465
470
  },
466
471
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
@@ -473,7 +478,7 @@ function registerDeployTools(server, callApi) {
473
478
  inputSchema: {
474
479
  groupId: z5.string().uuid().describe("Policy group ID"),
475
480
  versionId: z5.string().uuid().describe("Version ID to deploy"),
476
- memo: z5.string().default("").describe("Deployment memo")
481
+ memo: z5.string().min(1).describe("Live Deployment memo (required)")
477
482
  }
478
483
  },
479
484
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
@@ -557,10 +562,8 @@ function registerAnalyticsTools(server, callApi) {
557
562
  {
558
563
  title: "Dry Run",
559
564
  description: `Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
560
-
561
- Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
562
-
563
- Always dry-run before publishing to validate rule behavior.`,
565
+ Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
566
+ Always dry-run before publishing to validate rule behavior.`,
564
567
  inputSchema: {
565
568
  versionId: z6.string().uuid().describe("Policy version ID to test against"),
566
569
  facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
@@ -609,14 +612,21 @@ Always dry-run before publishing to validate rule behavior.`,
609
612
  "lexq_simulation_start",
610
613
  {
611
614
  title: "Start Simulation",
612
- description: `Start a batch simulation against historical execution data.
613
-
614
- Example body:
615
- {
616
- "policyVersionId": "<uuid>",
617
- "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
618
- "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
619
- }`,
615
+ description: `Start a batch simulation against historical or uploaded data.
616
+
617
+ dataset.type: "HISTORICAL" or "UPLOADED"
618
+ dataset.source (when HISTORICAL): "EXECUTION_LOGS"
619
+ dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
620
+ options.maxRecords: number (max 100000, default 10000)
621
+ options.baselinePolicyVersionId: uuid (optional, for comparison)
622
+ options.includeRuleStats: boolean
623
+
624
+ Example body:
625
+ {
626
+ "policyVersionId": "<uuid>",
627
+ "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
628
+ "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
629
+ }`,
620
630
  inputSchema: {
621
631
  body: z6.string().describe("JSON string of SimulationRequest")
622
632
  }
@@ -688,16 +698,15 @@ Example body:
688
698
  {
689
699
  title: "Upload Dataset",
690
700
  description: `Upload inline CSV or JSON content as a simulation dataset.
691
- The content is uploaded to S3 and a path is returned.
692
- Use this path in simulation start with dataset type UPLOADED.
693
-
694
- CSV example:
695
- user_id,payment_amount
696
- user_001,150000
697
- user_002,50000
698
-
699
- JSON example:
700
- [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
701
+ The content is uploaded to S3 and a path is returned.
702
+ Use this path in simulation start with dataset type UPLOADED.
703
+
704
+ CSV example:
705
+ user_id,payment_amount
706
+ user_001,150000
707
+ user_002,50000
708
+
709
+ JSON example: [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
701
710
  inputSchema: {
702
711
  content: z6.string().describe("CSV or JSON content as string"),
703
712
  filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
@@ -760,10 +769,10 @@ function registerHistoryTools(server, callApi) {
760
769
  title: "Get Execution Detail",
761
770
  description: "Get full execution detail including request facts, result traces, and decision traces.",
762
771
  inputSchema: {
763
- executionId: z7.string().uuid().describe("Execution history ID")
772
+ traceId: z7.string().describe("Trace ID from execution history")
764
773
  }
765
774
  },
766
- async ({ executionId }) => callApi("GET", `execution/history/${executionId}`)
775
+ async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
767
776
  );
768
777
  server.registerTool(
769
778
  "lexq_history_stats",
@@ -821,19 +830,20 @@ function registerIntegrationTools(server, callApi) {
821
830
  "lexq_integrations_save",
822
831
  {
823
832
  title: "Save Integration",
824
- description: "Create or update an integration. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
833
+ description: "Create or update an integration. Provide id to update an existing one; omit id to create new. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
825
834
  inputSchema: {
835
+ id: z8.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
826
836
  type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
827
837
  name: z8.string().describe("Integration name"),
828
838
  baseUrl: z8.string().describe("Base URL of the external service"),
829
- apiKey: z8.string().optional().describe("API key or token for the service"),
830
- isActive: z8.boolean().default(true).describe("Whether the integration is active"),
831
- config: z8.string().optional().describe("JSON string of additional config key-value pairs")
839
+ credential: z8.string().optional().describe("API key or token for the service"),
840
+ additionalConfig: z8.string().optional().describe("JSON string of additional config key-value pairs"),
841
+ isActive: z8.boolean().default(true).describe("Whether the integration is active")
832
842
  }
833
843
  },
834
- async ({ config, ...rest }) => {
844
+ async ({ additionalConfig, ...rest }) => {
835
845
  const body = { ...rest };
836
- if (config) body.config = JSON.parse(config);
846
+ if (additionalConfig) body.additionalConfig = JSON.parse(additionalConfig);
837
847
  return callApi("POST", "integrations", { body });
838
848
  }
839
849
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexq/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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": {