@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 +4 -1
- package/dist/init/index.d.ts +1 -1
- package/dist/init/index.js +87 -1
- package/package.json +1 -1
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',
|
package/dist/init/index.d.ts
CHANGED
|
@@ -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;
|
package/dist/init/index.js
CHANGED
|
@@ -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
|
-
|
|
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)
|