@moda-ai/cli 1.8.0 → 1.8.1

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/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { z } from 'zod';
3
3
  import { validateConfig, VERSION, resolveApiKey } from './config.js';
4
4
  import { callDataAPI, ApiError, getApiRequestCount, resetApiRequestCount, } from './api-client.js';
5
- import { OverviewSchema, ClustersSchema, ClusterConversationsSchema, ConversationsSchema, WorldStateSchema, ContextSchema, FrustrationsSchema, ToolFailuresSchema, ToolFailureDetailSchema, WorkflowsSchema, RunsSchema, RunDetailSchema, LoopsSchema, LoopDetailSchema, } from './schemas.js';
5
+ import { OverviewSchema, ClustersSchema, ClusterConversationsSchema, ConversationsSchema, SearchSchema, WorldStateSchema, ContextSchema, FrustrationsSchema, ToolFailuresSchema, ToolFailureDetailSchema, WorkflowsSchema, RunsSchema, RunDetailSchema, LoopsSchema, LoopDetailSchema, } from './schemas.js';
6
6
  import { withFrustrationAnchors, withToolFailureAnchors, embeddedWindowSize, asRecord, asString, asNumber } from './transforms.js';
7
7
  import { runPromptsCommand } from './prompts.js';
8
8
  // Coding-agent capture (shadow workspace / Shadow Spark / trace hooks) is temporarily
@@ -137,6 +137,7 @@ Commands:
137
137
  clusters Browse topic cluster hierarchy
138
138
  cluster-conversations <node_id> List conversations in a cluster
139
139
  conversations Search and filter conversations
140
+ search "<query>" Search conversation messages (keyword/semantic/hybrid)
140
141
  world-state <conversation_id> Get a conversation's world state (slots/threads/events)
141
142
  context <conversation_id> Get windowed conversation context
142
143
  frustrations Get user frustration detections
@@ -192,6 +193,12 @@ Conversations flags:
192
193
  --user-id=ID Filter to a single user
193
194
  --environment=ENV Filter by environment (all|development|staging|production)
194
195
 
196
+ Search flags:
197
+ --mode=keyword|semantic|hybrid Search mode (default hybrid)
198
+ --user-id=ID Filter to a single user
199
+ --time-range=RANGE Time range filter (all|1h|3d|7d|24h|30d|90d)
200
+ --limit=N Maximum results to return (1-100, default 20)
201
+
195
202
  World-state flags:
196
203
  --summary-only Return segment summaries only (omit the event stream)
197
204
  --event-limit=N Max world-state events to return (1-5000, default 2000)
@@ -249,6 +256,8 @@ Examples:
249
256
  moda conversations --search="error" --limit=5
250
257
  moda conversations --world-state="enterprise" --outcome=positive
251
258
  moda conversations --world-state="refund,billing" --include-world-state
259
+ moda search "billing error" --mode=hybrid
260
+ moda search "refund flow" --mode=semantic --time-range=7d --limit=10
252
261
  moda world-state <conversation_id> --summary-only
253
262
  moda frustrations --days-back=14 --limit=20
254
263
  moda context <conversation_id> --window=3
@@ -415,6 +424,29 @@ export async function runCommand(command, positional, flags, positionals = posit
415
424
  }
416
425
  break;
417
426
  }
427
+ case 'search': {
428
+ // Positional query mirrors world-state/context/cluster-conversations:
429
+ // reject an empty/whitespace query with the shared CLI input error before
430
+ // it reaches the schema (which also guards via q.min(1)).
431
+ if (!positional || positional.trim() === '') {
432
+ throw new CliInputError('<query> is required', 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--time-range=RANGE] [--limit=N]');
433
+ }
434
+ const args = flagsToArgs(flags, 'q', positional);
435
+ const params = SearchSchema.parse(args);
436
+ const query = new URLSearchParams();
437
+ query.set('q', params.q);
438
+ if (params.mode)
439
+ query.set('mode', params.mode);
440
+ if (params.user_id)
441
+ query.set('user_id', params.user_id);
442
+ if (params.time_range)
443
+ query.set('time_range', params.time_range);
444
+ if (params.limit)
445
+ query.set('limit', params.limit.toString());
446
+ const data = await callDataAPI(`/search?${query.toString()}`);
447
+ context.output.writeData(data);
448
+ break;
449
+ }
418
450
  case 'world-state': {
419
451
  if (!positional) {
420
452
  throw new CliInputError('<conversation_id> is required', 'Usage: moda world-state <conversation_id> [--summary-only] [--event-limit=N]');
@@ -856,6 +888,15 @@ export const commandRegistry = createCommandRegistry([
856
888
  ],
857
889
  ...dataApiDefaults,
858
890
  }),
891
+ legacyCommand({
892
+ name: 'search',
893
+ description: 'Search conversation messages (keyword, semantic, or hybrid)',
894
+ examples: [
895
+ 'moda search "billing error"',
896
+ 'moda search "refund flow" --mode=semantic --time-range=7d --limit=10',
897
+ ],
898
+ ...dataApiDefaults,
899
+ }),
859
900
  legacyCommand({
860
901
  name: 'world-state',
861
902
  description: "Get a conversation's world state (slots, threads, events)",
package/dist/schemas.d.ts CHANGED
@@ -141,6 +141,30 @@ export declare const ToolFailureDetailSchema: z.ZodObject<{
141
141
  include_window?: unknown;
142
142
  subtype?: string | undefined;
143
143
  }>;
144
+ /**
145
+ * moda search "<query>" — keyword/semantic/hybrid search across conversation
146
+ * messages. `q` arrives as a positional arg; the runCommand handler rejects an
147
+ * empty/whitespace query before parsing, and min(1) guards the schema path too.
148
+ */
149
+ export declare const SearchSchema: z.ZodObject<{
150
+ q: z.ZodString;
151
+ mode: z.ZodOptional<z.ZodDefault<z.ZodEnum<["keyword", "semantic", "hybrid"]>>>;
152
+ user_id: z.ZodOptional<z.ZodString>;
153
+ time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
154
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
155
+ }, "strip", z.ZodTypeAny, {
156
+ q: string;
157
+ mode?: "keyword" | "semantic" | "hybrid" | undefined;
158
+ time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
159
+ limit?: number | undefined;
160
+ user_id?: string | undefined;
161
+ }, {
162
+ q: string;
163
+ mode?: "keyword" | "semantic" | "hybrid" | undefined;
164
+ time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
165
+ limit?: number | undefined;
166
+ user_id?: string | undefined;
167
+ }>;
144
168
  /** moda workflows — recurring workflow catalog (read from agent_job_classes). */
145
169
  export declare const WorkflowsSchema: z.ZodObject<{
146
170
  time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
package/dist/schemas.js CHANGED
@@ -76,6 +76,19 @@ export const ToolFailureDetailSchema = z.object({
76
76
  // `moda workflows` lists the catalog; `moda runs` lists executions.
77
77
  const RUN_OUTCOMES = ['running', 'succeeded', 'partial', 'failed', 'loop', 'abandoned', 'unknown'];
78
78
  const TIME_RANGES = ['all', '1h', '3d', '7d', '24h', '30d', '90d'];
79
+ const SEARCH_MODES = ['keyword', 'semantic', 'hybrid'];
80
+ /**
81
+ * moda search "<query>" — keyword/semantic/hybrid search across conversation
82
+ * messages. `q` arrives as a positional arg; the runCommand handler rejects an
83
+ * empty/whitespace query before parsing, and min(1) guards the schema path too.
84
+ */
85
+ export const SearchSchema = z.object({
86
+ q: z.string().min(1).max(500),
87
+ mode: z.enum(SEARCH_MODES).default('hybrid').optional(),
88
+ user_id: z.string().optional(),
89
+ time_range: z.enum(TIME_RANGES).default('all').optional(),
90
+ limit: z.number().min(1).max(100).default(20).optional(),
91
+ });
79
92
  /** moda workflows — recurring workflow catalog (read from agent_job_classes). */
80
93
  export const WorkflowsSchema = z.object({
81
94
  time_range: z.enum(TIME_RANGES).default('all').optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {