@moda-ai/cli 1.19.2 → 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 +4 -1
- package/dist/harness.js +69 -1
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
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
|
|
@@ -880,13 +882,14 @@ export const commandRegistry = createCommandRegistry([
|
|
|
880
882
|
},
|
|
881
883
|
{
|
|
882
884
|
name: 'harness',
|
|
883
|
-
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',
|
|
884
886
|
examples: [
|
|
885
887
|
'moda harness analyze --summary',
|
|
886
888
|
'moda harness analyze --remote',
|
|
887
889
|
'moda harness pull --sync',
|
|
888
890
|
'moda harness approve --yes',
|
|
889
891
|
'moda harness sync --from-report .moda/harness-report.json',
|
|
892
|
+
'moda harness delete my-harness-key --yes',
|
|
890
893
|
'moda harness explain --agent=agent_support',
|
|
891
894
|
],
|
|
892
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/package.json
CHANGED