@moda-ai/cli 1.6.0 → 1.7.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
@@ -198,6 +198,8 @@ Init flags:
198
198
  --no-sdk-integrate Skip SDK integration (alias: --no-sdk-guide)
199
199
  --ci-cd Create GitHub Actions Moda validation workflow
200
200
  --no-ci-cd Skip CI/CD workflow creation
201
+ --autosync Create GitHub Actions workflow to sync prompts + skills to Moda on push to main
202
+ --no-autosync Skip autosync workflow creation
201
203
  --offline Run final validation offline (skip the online telemetry check)
202
204
 
203
205
  Examples:
@@ -228,6 +230,7 @@ Examples:
228
230
  moda harness scan --json
229
231
  moda harness explain --agent=agent_support
230
232
  moda init --ci-cd
233
+ moda init --autosync
231
234
  moda overview --days-back=30
232
235
  moda clusters --time-range=7d
233
236
  moda conversations --search="error" --limit=5
@@ -581,7 +584,7 @@ export const commandRegistry = createCommandRegistry([
581
584
  {
582
585
  name: 'init',
583
586
  description: 'Set up Moda in your project',
584
- examples: ['moda init', 'moda init --sdk-integrate', 'moda init --ci-cd'],
587
+ examples: ['moda init', 'moda init --sdk-integrate', 'moda init --ci-cd', 'moda init --autosync'],
585
588
  auth: 'none',
586
589
  outputModes: ['human', 'json', 'agent'],
587
590
  mutability: 'write',
@@ -11,7 +11,7 @@ export interface InitPhaseResult {
11
11
  details?: string;
12
12
  }
13
13
  export interface InitSetupAction {
14
- category: 'sdk' | 'harness' | 'hooks' | 'prompts' | 'skills' | 'ci-cd' | 'validation';
14
+ category: 'sdk' | 'harness' | 'hooks' | 'prompts' | 'skills' | 'ci-cd' | 'autosync' | 'validation';
15
15
  title: string;
16
16
  status: 'planned' | 'applied' | 'skipped' | 'manual';
17
17
  approved: boolean;
@@ -13,6 +13,7 @@ import { runSkillSync } from '../skills.js';
13
13
  import { SDK_GUIDE_PATH, writeSdkIntegrationGuide } from './sdk-guide.js';
14
14
  import { runSdkIntegration } from './sdk-integration.js';
15
15
  import { createCommandContext } from '../command-context.js';
16
+ import { VERSION } from '../config.js';
16
17
  import { CliInputError } from '../errors.js';
17
18
  import { runHarnessCommand } from '../harness.js';
18
19
  import { buildDoctorReport } from '../doctor.js';
@@ -136,6 +137,7 @@ export async function runInit(flags, options = {}) {
136
137
  installHooks: false,
137
138
  integrateSdk: await shouldIntegrateSdk(flags, yes),
138
139
  configureCiCd: await shouldConfigureCiCd(flags, yes),
140
+ configureAutosync: await shouldConfigureAutosync(flags, yes),
139
141
  });
140
142
  log(renderInitSetupPlan(setupPlan));
141
143
  emit('plan', 'Setup plan ready.', 'completed');
@@ -235,6 +237,12 @@ export async function runInit(flags, options = {}) {
235
237
  log(renderInitNote(`Wrote ${workflowPath}`));
236
238
  markApplied(setupPlan, 'ci-cd');
237
239
  }
240
+ const configureAutosync = setupPlan.find((action) => action.category === 'autosync')?.approved === true;
241
+ if (configureAutosync) {
242
+ const workflowPath = writeAutosyncWorkflow();
243
+ log(renderInitNote(`Wrote ${workflowPath}`));
244
+ markApplied(setupPlan, 'autosync');
245
+ }
238
246
  log(renderInitNote('Reusing approved Step 4 harness report; no second analyst run needed'));
239
247
  markApplied(setupPlan, 'harness');
240
248
  markApplied(setupPlan, 'hooks', ingestCodingAgentTraces ? undefined : 'Coding-agent trace capture is temporarily unavailable.');
@@ -337,6 +345,15 @@ function buildSetupPlan(options) {
337
345
  command: 'moda doctor --online --json',
338
346
  reason: options.configureCiCd ? undefined : 'No CI/CD files are changed until a project-specific plan is approved.',
339
347
  },
348
+ {
349
+ category: 'autosync',
350
+ title: 'Autosync prompts and skills to Moda on push to main',
351
+ status: options.configureAutosync ? 'planned' : 'manual',
352
+ approved: options.configureAutosync,
353
+ files: ['.github/workflows/moda-autosync.yml'],
354
+ command: 'moda prompts sync && moda skills sync',
355
+ reason: options.configureAutosync ? undefined : 'No autosync workflow is written until it is explicitly approved (--autosync).',
356
+ },
340
357
  {
341
358
  category: 'validation',
342
359
  title: 'Run Moda doctor online to confirm telemetry is reaching Moda',
@@ -364,6 +381,13 @@ function changedFiles(setupPlan) {
364
381
  .filter((action) => action.status === 'applied')
365
382
  .flatMap((action) => action.files))].filter((file) => !file.includes('.env'));
366
383
  }
384
+ // The CLI version generated workflows pin to. Pinning (rather than `@latest`,
385
+ // which resolves at CI run time) keeps a future compromised `@moda-ai/cli`
386
+ // publish from silently executing in adopters' CI with their MODA_API_KEY /
387
+ // MODA_TENANT_ID present — especially important for autosync, which *writes* to
388
+ // the Moda registry. The pin tracks the CLI that generated the file, so each
389
+ // `moda init` upgrade is an explicit, reviewable bump of the workflow.
390
+ const GENERATED_WORKFLOW_CLI = `@moda-ai/cli@${VERSION}`;
367
391
  function writeCiCdWorkflow(cwd = process.cwd()) {
368
392
  const relativePath = '.github/workflows/moda-validation.yml';
369
393
  const path = join(cwd, relativePath);
@@ -376,6 +400,11 @@ function writeCiCdWorkflow(cwd = process.cwd()) {
376
400
  ' push:',
377
401
  ' branches: [main]',
378
402
  '',
403
+ '# Least privilege: these jobs only read the checked-out repo and talk to',
404
+ '# Moda over the API key; they never write back to the repository.',
405
+ 'permissions:',
406
+ ' contents: read',
407
+ '',
379
408
  'jobs:',
380
409
  ' moda-doctor:',
381
410
  ' runs-on: ubuntu-latest',
@@ -388,7 +417,51 @@ function writeCiCdWorkflow(cwd = process.cwd()) {
388
417
  ' with:',
389
418
  ' node-version: 22',
390
419
  ' - name: Validate Moda setup',
391
- ' run: npx -y @moda-ai/cli@latest moda doctor --online --json',
420
+ ` run: npx -y ${GENERATED_WORKFLOW_CLI} moda doctor --online --json`,
421
+ '',
422
+ ].join('\n')}`);
423
+ return relativePath;
424
+ }
425
+ // Generates a push-to-main autosync workflow that keeps the Moda registry in
426
+ // sync with the repo: it pushes prompt and skill changes after they merge. It
427
+ // only runs on `main` (never on pull_request) so unmerged versions are never
428
+ // published. Keep its env in step with writeCiCdWorkflow().
429
+ function writeAutosyncWorkflow(cwd = process.cwd()) {
430
+ const relativePath = '.github/workflows/moda-autosync.yml';
431
+ const path = join(cwd, relativePath);
432
+ mkdirSync(dirname(path), { recursive: true });
433
+ writeFileSync(path, `${[
434
+ 'name: Moda Autosync',
435
+ '',
436
+ 'on:',
437
+ ' push:',
438
+ ' branches: [main]',
439
+ '',
440
+ '# Least privilege: this job only reads the checked-out repo and writes to',
441
+ '# Moda over the API key; it never writes back to the repository.',
442
+ 'permissions:',
443
+ ' contents: read',
444
+ '',
445
+ '# A single in-flight sync at a time; newer pushes supersede older runs.',
446
+ 'concurrency:',
447
+ ' group: moda-autosync',
448
+ ' cancel-in-progress: true',
449
+ '',
450
+ 'jobs:',
451
+ ' moda-sync:',
452
+ ' runs-on: ubuntu-latest',
453
+ ' env:',
454
+ ' MODA_API_KEY: ${{ secrets.MODA_API_KEY }}',
455
+ ' MODA_TENANT_ID: ${{ secrets.MODA_TENANT_ID }}',
456
+ ' steps:',
457
+ ' - uses: actions/checkout@v4',
458
+ ' - uses: actions/setup-node@v4',
459
+ ' with:',
460
+ ' node-version: 22',
461
+ ` - name: Sync prompts to Moda`,
462
+ ` run: npx -y ${GENERATED_WORKFLOW_CLI} moda prompts sync`,
463
+ ` - name: Sync skills to Moda`,
464
+ ` run: npx -y ${GENERATED_WORKFLOW_CLI} moda skills sync`,
392
465
  '',
393
466
  ].join('\n')}`);
394
467
  return relativePath;
@@ -451,6 +524,19 @@ async function shouldConfigureCiCd(flags, yes) {
451
524
  default: false,
452
525
  });
453
526
  }
527
+ async function shouldConfigureAutosync(flags, yes) {
528
+ const explicit = parseOptionalBoolean(flagValue(flags, 'autosync', 'auto-sync', 'auto_sync'));
529
+ if (explicit !== undefined)
530
+ return explicit;
531
+ if (isTruthyFlag(flagValue(flags, 'no-autosync', 'no_autosync', 'no-auto-sync', 'no_auto_sync')))
532
+ return false;
533
+ if (yes)
534
+ return false;
535
+ return confirm({
536
+ message: 'Create a GitHub Actions workflow to autosync prompts and skills to Moda on push to main?',
537
+ default: false,
538
+ });
539
+ }
454
540
  async function shouldIntegrateSdk(flags, yes) {
455
541
  const explicit = parseOptionalBoolean(flagValue(flags, 'sdk-integrate', 'sdk_integrate', 'sdk-guide', 'sdk_guide', 'sdk'));
456
542
  if (explicit !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {