@moda-ai/cli 1.19.1 → 1.20.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.
package/dist/cli.js CHANGED
@@ -143,6 +143,7 @@ Commands:
143
143
  harness scan Discover local runtime agents and write .moda/harness.json
144
144
  harness candidates Static (no-LLM) harness candidate pre-pass with file:line citations
145
145
  harness sync Sync the local harness graph to Moda Cloud
146
+ harness delete <id-or-key> Permanently delete a synced harness from Moda Cloud
146
147
  harness agents List detected runtime agents and families
147
148
  harness explain Explain the discovered harness graph
148
149
  agent context Emit local Moda context for coding agents
@@ -284,6 +285,7 @@ Examples:
284
285
  moda harness validate-report
285
286
  moda harness approve --yes
286
287
  moda harness sync --from-report .moda/harness-report.json
288
+ moda harness delete my-harness-key --yes
287
289
  moda --agent doctor
288
290
  moda agent context
289
291
  moda agent next-action
@@ -507,7 +509,7 @@ export async function runCommand(command, positional, flags, positionals = posit
507
509
  // reject an empty/whitespace query with the shared CLI input error before
508
510
  // it reaches the schema (which also guards via q.min(1)).
509
511
  if (!positional || positional.trim() === '') {
510
- throw new CliInputError('<query> is required', 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--time-range=RANGE] [--limit=N]');
512
+ throw new CliInputError('<query> is required', 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--time-range=RANGE] [--include-tool-io] [--limit=N]');
511
513
  }
512
514
  const args = flagsToArgs(flags, 'q', positional);
513
515
  const params = SearchSchema.parse(args);
@@ -521,6 +523,8 @@ export async function runCommand(command, positional, flags, positionals = posit
521
523
  query.set('time_range', params.time_range);
522
524
  if (params.limit)
523
525
  query.set('limit', params.limit.toString());
526
+ if (params.include_tool_io)
527
+ query.set('include_tool_io', 'true');
524
528
  const data = await callDataAPI(`/search?${query.toString()}`);
525
529
  recordSearchTelemetry(context, data);
526
530
  context.output.writeData(data);
@@ -878,13 +882,14 @@ export const commandRegistry = createCommandRegistry([
878
882
  },
879
883
  {
880
884
  name: 'harness',
881
- description: 'Analyze, validate, approve, sync, and explain the local multi-agent harness graph',
885
+ description: 'Analyze, validate, approve, sync, delete, and explain the local multi-agent harness graph',
882
886
  examples: [
883
887
  'moda harness analyze --summary',
884
888
  'moda harness analyze --remote',
885
889
  'moda harness pull --sync',
886
890
  'moda harness approve --yes',
887
891
  'moda harness sync --from-report .moda/harness-report.json',
892
+ 'moda harness delete my-harness-key --yes',
888
893
  'moda harness explain --agent=agent_support',
889
894
  ],
890
895
  auth: 'none',
package/dist/harness.js CHANGED
@@ -259,6 +259,47 @@ export async function runHarnessCommand(context) {
259
259
  });
260
260
  return { exitCode: 0 };
261
261
  }
262
+ case 'delete': {
263
+ const idOrKey = context.positionals[1]
264
+ ?? (context.flags.harness && context.flags.harness !== 'true' ? context.flags.harness : undefined);
265
+ if (!idOrKey) {
266
+ throw new CliInputError('Missing harness id or key to delete.', 'Usage: moda harness delete <harness-id-or-key> [--yes]');
267
+ }
268
+ const profileOptions = { profile: context.profile, env: context.env };
269
+ if (!resolveApiKey(profileOptions) && !context.dryRun) {
270
+ throw new CliInputError('No Moda API key found for harness delete.', 'Run `moda init` first, set MODA_API_KEY, or select a profile with an API key.');
271
+ }
272
+ // Identity banner (same pattern as sync): deletion is destructive and
273
+ // tenant-scoped, so the resolved credential and destination tenant must
274
+ // be visible BEFORE the user confirms — a shadowed or stale key would
275
+ // otherwise delete from the wrong tenant silently.
276
+ const identity = describeSyncIdentity(profileOptions);
277
+ if (identity) {
278
+ const label = identity.tenantSource === 'api-key'
279
+ ? describeSyncTenantLabel(identity)
280
+ : describeSyncTenantLabel({
281
+ tenantId: tenant?.tenantId ?? identity.tenantId,
282
+ tenantSource: 'config',
283
+ });
284
+ writeHumanProgress(context, `Deleting as ${identity.credential} → tenant ${label}`);
285
+ }
286
+ if (context.dryRun) {
287
+ context.output.writeData({
288
+ endpoint: `/api/harnesses/${idOrKey}`,
289
+ harnessKey: idOrKey,
290
+ deleted: false,
291
+ dryRun: true,
292
+ });
293
+ return { exitCode: 0 };
294
+ }
295
+ await confirmHarnessDelete(context, idOrKey);
296
+ const response = await callControlAPI(`/harnesses/${encodeURIComponent(idOrKey)}`, { method: 'DELETE' }, profileOptions);
297
+ writeHumanProgress(context, `Deleted harness ${response.harnessKey ?? idOrKey} from tenant ${response.tenantId ?? 'unknown'} ` +
298
+ `(${response.deleted?.versions ?? 0} versions, ${response.deleted?.agents ?? 0} agents, ` +
299
+ `${response.deleted?.artifacts ?? 0} artifacts, ${response.deleted?.relationships ?? 0} relationships).`);
300
+ context.output.writeData({ ...response, dryRun: false });
301
+ return { exitCode: 0 };
302
+ }
262
303
  case 'analyze': {
263
304
  await approveScanIfNeeded(scanPlan, {
264
305
  context,
@@ -494,7 +535,7 @@ export async function runHarnessCommand(context) {
494
535
  return { exitCode: 0 };
495
536
  }
496
537
  default:
497
- throw new CliInputError(`Unknown harness command '${subcommand}'.`, 'Usage: moda harness analyze|pull|validate-report|approve|scan|candidates|sync|agents|graph|explain|write-summary|status');
538
+ throw new CliInputError(`Unknown harness command '${subcommand}'.`, 'Usage: moda harness analyze|pull|validate-report|approve|scan|candidates|sync|delete|agents|graph|explain|write-summary|status');
498
539
  }
499
540
  }
500
541
  export function writeHarnessAnalyzeResult(context, payload) {
@@ -1916,6 +1957,33 @@ async function approveScanIfNeeded(scanPlan, options = {}) {
1916
1957
  throw new CliInputError(options.declinedMessage ?? 'Harness scan was not approved.', options.declinedHint ?? 'Run `moda harness scan --path=<dir>` for a narrower scan, or rerun with `--yes` after reviewing the read plan.');
1917
1958
  }
1918
1959
  }
1960
+ /**
1961
+ * Interactive confirmation gate for `harness delete`. `--yes` skips it; in
1962
+ * machine-readable modes or without a TTY the command refuses with a
1963
+ * resumable input-required error instead of prompting (same posture as the
1964
+ * broad-scan approval above) so agents/CI never hang on stdin.
1965
+ */
1966
+ async function confirmHarnessDelete(context, idOrKey) {
1967
+ if (context.yes)
1968
+ return;
1969
+ const message = `Permanently delete harness '${idOrKey}' and all of its synced versions from Moda Cloud?`;
1970
+ if (context.outputMode !== 'human' || process.stdin.isTTY !== true) {
1971
+ throw new CliInputRequiredError(message, {
1972
+ id: 'harness_delete_approval',
1973
+ question: message,
1974
+ kind: 'confirm',
1975
+ choices: [
1976
+ { value: 'yes', label: 'Delete harness', resume_flags: ['--yes'] },
1977
+ { value: 'no', label: 'Keep harness', resume_flags: [] },
1978
+ ],
1979
+ resume_command: ['moda', 'harness', 'delete', idOrKey, '--yes'],
1980
+ }, 'Re-run with `--yes` to confirm the deletion.');
1981
+ }
1982
+ const approved = await confirm({ message, default: false });
1983
+ if (!approved) {
1984
+ throw new CliInputError('Harness delete was not approved.', 'Re-run with `--yes` to skip the prompt, or leave the harness in place.');
1985
+ }
1986
+ }
1919
1987
  export function writeHumanProgress(context, message) {
1920
1988
  if (context.outputMode !== 'human')
1921
1989
  return;
package/dist/schemas.d.ts CHANGED
@@ -83,12 +83,12 @@ export declare const ContextSchema: z.ZodObject<{
83
83
  window: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
84
84
  }, "strip", z.ZodTypeAny, {
85
85
  conversation_id: string;
86
- msg_index?: number | undefined;
87
86
  window?: number | undefined;
87
+ msg_index?: number | undefined;
88
88
  }, {
89
89
  conversation_id: string;
90
- msg_index?: number | undefined;
91
90
  window?: number | undefined;
91
+ msg_index?: number | undefined;
92
92
  }>;
93
93
  export declare const FrustrationsSchema: z.ZodObject<{
94
94
  days_back: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
@@ -97,16 +97,16 @@ export declare const FrustrationsSchema: z.ZodObject<{
97
97
  include_window: z.ZodOptional<z.ZodEffects<z.ZodBoolean, boolean, unknown>>;
98
98
  window: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
99
99
  }, "strip", z.ZodTypeAny, {
100
+ window?: number | undefined;
100
101
  days_back?: number | undefined;
101
102
  limit?: number | undefined;
102
103
  offset?: number | undefined;
103
- window?: number | undefined;
104
104
  include_window?: boolean | undefined;
105
105
  }, {
106
+ window?: number | undefined;
106
107
  days_back?: number | undefined;
107
108
  limit?: number | undefined;
108
109
  offset?: number | undefined;
109
- window?: number | undefined;
110
110
  include_window?: unknown;
111
111
  }>;
112
112
  export declare const ToolFailuresSchema: z.ZodObject<{
@@ -126,19 +126,19 @@ export declare const ToolFailureDetailSchema: z.ZodObject<{
126
126
  window: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
127
127
  }, "strip", z.ZodTypeAny, {
128
128
  tool_name: string;
129
+ window?: number | undefined;
129
130
  subtype?: string | undefined;
130
131
  days_back?: number | undefined;
131
132
  limit?: number | undefined;
132
133
  offset?: number | undefined;
133
- window?: number | undefined;
134
134
  include_window?: boolean | undefined;
135
135
  }, {
136
136
  tool_name: string;
137
+ window?: number | undefined;
137
138
  subtype?: string | undefined;
138
139
  days_back?: number | undefined;
139
140
  limit?: number | undefined;
140
141
  offset?: number | undefined;
141
- window?: number | undefined;
142
142
  include_window?: unknown;
143
143
  }>;
144
144
  /**
@@ -172,17 +172,20 @@ export declare const SearchSchema: z.ZodObject<{
172
172
  user_id: z.ZodOptional<z.ZodString>;
173
173
  time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
174
174
  limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
175
+ include_tool_io: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
175
176
  }, "strip", z.ZodTypeAny, {
176
177
  q: string;
177
178
  mode?: "keyword" | "semantic" | "hybrid" | undefined;
178
179
  time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
179
180
  limit?: number | undefined;
180
181
  user_id?: string | undefined;
182
+ include_tool_io?: boolean | undefined;
181
183
  }, {
182
184
  q: string;
183
185
  mode?: "keyword" | "semantic" | "hybrid" | undefined;
184
186
  time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
185
187
  limit?: number | undefined;
186
188
  user_id?: string | undefined;
189
+ include_tool_io?: boolean | undefined;
187
190
  }>;
188
191
  //# sourceMappingURL=schemas.d.ts.map
package/dist/schemas.js CHANGED
@@ -91,5 +91,6 @@ export const SearchSchema = z.object({
91
91
  user_id: z.string().optional(),
92
92
  time_range: z.enum(TIME_RANGES).default('all').optional(),
93
93
  limit: z.number().min(1).max(100).default(20).optional(),
94
+ include_tool_io: z.boolean().default(false).optional(),
94
95
  });
95
96
  //# sourceMappingURL=schemas.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.19.1",
3
+ "version": "1.20.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-07-11T01:00:37.487Z",
4
- "cli_version": "1.19.1",
3
+ "bundled_at": "2026-07-11T21:49:32.595Z",
4
+ "cli_version": "1.20.0",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-node-anthropic",