@moda-ai/cli 1.1.1 → 1.3.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.
Files changed (59) hide show
  1. package/README.md +36 -0
  2. package/dist/agent.d.ts +5 -0
  3. package/dist/agent.js +477 -0
  4. package/dist/analyst-tui.d.ts +81 -0
  5. package/dist/analyst-tui.js +314 -0
  6. package/dist/api-client.d.ts +4 -0
  7. package/dist/api-client.js +20 -3
  8. package/dist/auth.d.ts +22 -3
  9. package/dist/auth.js +230 -66
  10. package/dist/cli.d.ts +20 -3
  11. package/dist/cli.js +745 -93
  12. package/dist/command-context.d.ts +34 -0
  13. package/dist/command-context.js +38 -0
  14. package/dist/command-registry.d.ts +22 -0
  15. package/dist/command-registry.js +15 -0
  16. package/dist/config.d.ts +40 -1
  17. package/dist/config.js +70 -3
  18. package/dist/doctor.d.ts +79 -0
  19. package/dist/doctor.js +1111 -0
  20. package/dist/errors.d.ts +5 -0
  21. package/dist/errors.js +9 -0
  22. package/dist/harness-report.d.ts +138 -0
  23. package/dist/harness-report.js +1464 -0
  24. package/dist/harness.d.ts +233 -0
  25. package/dist/harness.js +3264 -0
  26. package/dist/init/index.d.ts +30 -1
  27. package/dist/init/index.js +389 -23
  28. package/dist/init/rules.d.ts +5 -1
  29. package/dist/init/rules.js +22 -16
  30. package/dist/init/sdk-guide.d.ts +3 -0
  31. package/dist/init/sdk-guide.js +397 -0
  32. package/dist/init/tenant.js +12 -3
  33. package/dist/keys.d.ts +47 -0
  34. package/dist/keys.js +136 -0
  35. package/dist/output.d.ts +118 -0
  36. package/dist/output.js +228 -0
  37. package/dist/production-intelligence.d.ts +134 -0
  38. package/dist/production-intelligence.js +1113 -0
  39. package/dist/profile-commands.d.ts +10 -0
  40. package/dist/profile-commands.js +252 -0
  41. package/dist/profiles.d.ts +90 -0
  42. package/dist/profiles.js +432 -0
  43. package/dist/prompts.d.ts +6 -0
  44. package/dist/prompts.js +368 -0
  45. package/dist/schemas.d.ts +4 -4
  46. package/dist/skills.d.ts +25 -0
  47. package/dist/skills.js +468 -0
  48. package/dist/sync.d.ts +5 -0
  49. package/dist/sync.js +27 -0
  50. package/dist/telemetry.d.ts +18 -0
  51. package/dist/telemetry.js +63 -0
  52. package/dist/tui.d.ts +40 -0
  53. package/dist/tui.js +387 -0
  54. package/dist/workspace.d.ts +12 -0
  55. package/dist/workspace.js +1294 -0
  56. package/hooks/moda-snapshot.sh +41 -0
  57. package/package.json +10 -7
  58. package/skills/moda-cli/SKILL.md +48 -4
  59. package/skills/integrate-moda/SKILL.md +0 -63
@@ -1,2 +1,31 @@
1
- export declare function runInit(flags: Record<string, unknown>): Promise<void>;
1
+ export interface InitResult {
2
+ apiKey: string;
3
+ tenantName: string;
4
+ phases: InitPhaseResult[];
5
+ setupPlan: InitSetupAction[];
6
+ }
7
+ export interface InitPhaseResult {
8
+ id: 'welcome' | 'auth' | 'workspace' | 'harness' | 'plan' | 'apply' | 'validate';
9
+ title: string;
10
+ status: 'completed' | 'skipped' | 'warning';
11
+ details?: string;
12
+ }
13
+ export interface InitSetupAction {
14
+ category: 'sdk' | 'harness' | 'hooks' | 'prompts' | 'skills' | 'ci-cd' | 'validation';
15
+ title: string;
16
+ status: 'planned' | 'applied' | 'skipped' | 'manual';
17
+ approved: boolean;
18
+ files: string[];
19
+ command?: string;
20
+ reason?: string;
21
+ }
22
+ export interface InitProgressEvent {
23
+ phase: InitPhaseResult['id'];
24
+ message: string;
25
+ status?: InitPhaseResult['status'];
26
+ }
27
+ export interface InitRunOptions {
28
+ onEvent?: (event: InitProgressEvent) => void;
29
+ }
30
+ export declare function runInit(flags: Record<string, unknown>, options?: InitRunOptions): Promise<InitResult>;
2
31
  //# sourceMappingURL=index.d.ts.map
@@ -1,16 +1,55 @@
1
1
  // src/init/index.ts — Full moda init wizard orchestrator
2
- import { select } from '@inquirer/prompts';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { confirm, select } from '@inquirer/prompts';
3
5
  import { login, loadToken } from '../auth.js';
4
6
  import { selectTenantAndCreateKey } from './tenant.js';
5
7
  import { detectAgents } from './mcp.js';
6
8
  import { installRules } from './rules.js';
7
- export async function runInit(flags) {
8
- const yes = flags.yes === true || flags.y === true;
9
- console.log('\n Moda — LLM Observability for Agents\n');
10
- // Step 1: Authentication
11
- console.log(' Step 1/3: Authentication');
9
+ import { initPrompts } from '../prompts.js';
10
+ import { pullSkills } from '../skills.js';
11
+ // Coding-agent capture is temporarily disabled — see the hooks phase below.
12
+ // import { runHooksInstall } from '../workspace.js';
13
+ import { SDK_GUIDE_PATH, writeSdkIntegrationGuide } from './sdk-guide.js';
14
+ import { createCommandContext } from '../command-context.js';
15
+ import { CliInputError } from '../errors.js';
16
+ import { runHarnessCommand } from '../harness.js';
17
+ import { runDoctorCommand } from '../doctor.js';
18
+ import { renderInitBanner, renderInitComplete, renderInitNote, renderInitSetupPlan, renderInitStep, } from '../tui.js';
19
+ export async function runInit(flags, options = {}) {
20
+ const explicitYes = flags.yes === true ||
21
+ flags.yes === 'true' ||
22
+ flags.y === true ||
23
+ flags.y === 'true';
24
+ const json = flags.json === true || flags.json === 'true';
25
+ const agent = flags.agent === true || flags.agent === 'true';
26
+ const human = !json && !agent;
27
+ const yes = explicitYes || json || agent;
28
+ const phases = [];
29
+ const log = (message) => {
30
+ if (human)
31
+ console.log(message);
32
+ };
33
+ // `warn` is only used by the temporarily-disabled hooks phase below; keep the
34
+ // underscore-prefixed binding so re-enabling it is a rename away.
35
+ const _warn = (message) => {
36
+ if (human)
37
+ console.error(message);
38
+ };
39
+ const emit = (phase, message, status) => {
40
+ options.onEvent?.({ phase, message, status });
41
+ };
42
+ log(renderInitBanner());
43
+ log(renderInitStep(1, 7, 'Welcome', 'No environment variable files are written. File changes are listed before completion.'));
44
+ emit('welcome', 'Onboarding this project as a Moda agent harness.', 'completed');
45
+ phases.push({ id: 'welcome', title: 'Welcome', status: 'completed' });
46
+ // Step 2: Authentication
47
+ log(renderInitStep(2, 7, 'Authentication'));
12
48
  let token = loadToken();
13
49
  if (!token) {
50
+ if (json || agent) {
51
+ throw new CliInputError('Browser authentication is required before non-interactive init can continue.', 'Run `moda init` in a human terminal first, or provide an existing cached auth session.');
52
+ }
14
53
  let action = 'login';
15
54
  if (!yes) {
16
55
  action = await select({
@@ -21,32 +60,359 @@ export async function runInit(flags) {
21
60
  ],
22
61
  });
23
62
  }
24
- console.log(` Opening browser to ${action === 'signup' ? 'sign up' : 'log in'}...`);
63
+ log(renderInitNote(`Opening browser to ${action === 'signup' ? 'sign up' : 'log in'}...`, 'warning'));
64
+ emit('auth', `Opening browser to ${action}.`);
25
65
  token = await login(action);
26
- console.log(' Authentication successful');
66
+ log(renderInitNote('Authentication successful'));
27
67
  }
28
68
  else {
29
- console.log(' Already authenticated (cached token)');
69
+ log(renderInitNote('Already authenticated (cached token)'));
70
+ }
71
+ emit('auth', 'Authentication complete.', 'completed');
72
+ phases.push({ id: 'auth', title: 'Authentication', status: 'completed' });
73
+ // Step 3: Tenant + API Key
74
+ log(renderInitStep(3, 7, 'Workspace & Profile'));
75
+ emit('workspace', 'Selecting workspace and creating API key.');
76
+ const { apiKey, tenantName } = await selectTenantAndCreateKey();
77
+ log(renderInitNote(`Active workspace: ${tenantName}`));
78
+ emit('workspace', `Active workspace: ${tenantName}`, 'completed');
79
+ phases.push({ id: 'workspace', title: 'Workspace & Profile', status: 'completed', details: tenantName });
80
+ // Step 4: Harness analyst report.
81
+ log(renderInitStep(4, 7, 'Harness Discovery', 'Moda Analyst investigates the harness and writes cited local artifacts.'));
82
+ emit('harness', 'Running Moda Analyst harness discovery.');
83
+ const harnessResult = await runHarnessCommand(createCommandContext({
84
+ command: 'harness',
85
+ positional: 'analyze',
86
+ positionals: ['analyze'],
87
+ flags: commandFlags({
88
+ summary: true,
89
+ yes,
90
+ json: json || agent,
91
+ agent,
92
+ analyst: stringFlag(flags.analyst) ?? 'auto',
93
+ quiet: true,
94
+ progress: true,
95
+ 'no-telemetry': true,
96
+ }),
97
+ }));
98
+ if (harnessResult.exitCode !== 0) {
99
+ throw new CliInputError('Moda Analyst produced a harness report that did not pass validation.', 'Review .moda/harness-report-validation.json, then rerun `moda init --analyst=claude|cursor|codex` or `moda harness analyze --analyst=local-scan --summary`.');
30
100
  }
31
- // Step 2: Tenant + API Key
32
- console.log('\n Step 2/3: Select Organization');
33
- const { tenantName } = await selectTenantAndCreateKey();
34
- // Step 3: Install SDK integration guide for coding agents
35
- console.log('\n Step 3/3: Install SDK Guide');
101
+ log(renderInitNote('Wrote or verified .moda/harness-report.json, .moda/harness.json, and .moda/HARNESS.md'));
102
+ if (human)
103
+ printHarnessDetectionSummary();
104
+ emit('harness', 'Harness discovery complete.', 'completed');
105
+ phases.push({ id: 'harness', title: 'Harness Discovery', status: 'completed' });
106
+ // Step 5: Setup plan
107
+ log(renderInitStep(5, 7, 'Setup Plan', 'Every write/sync action is explicit before it is applied.'));
108
+ const setupPlan = buildSetupPlan({
109
+ // Coding-agent capture is temporarily disabled; never prompt for or install trace hooks.
110
+ // installHooks: await shouldIngestCodingAgentTraces(flags, yes),
111
+ installHooks: false,
112
+ installSdkGuide: await shouldCreateSdkGuide(flags, yes),
113
+ configureCiCd: await shouldConfigureCiCd(flags, yes),
114
+ });
115
+ log(renderInitSetupPlan(setupPlan));
116
+ emit('plan', 'Setup plan ready.', 'completed');
117
+ phases.push({ id: 'plan', title: 'Setup Plan', status: 'completed' });
118
+ // Step 6: Apply approved setup changes.
119
+ log(renderInitStep(6, 7, 'Apply Approved Changes'));
120
+ emit('apply', 'Applying approved setup changes.');
121
+ // Coding-agent capture is temporarily disabled; the hooks phase never runs.
122
+ // const ingestCodingAgentTraces = setupPlan.find((action) => action.category === 'hooks')?.approved === true;
123
+ const ingestCodingAgentTraces = false;
124
+ // if (ingestCodingAgentTraces) {
125
+ // try {
126
+ // await runHooksInstall({
127
+ // command: 'hooks',
128
+ // positional: 'install',
129
+ // positionals: ['install'],
130
+ // flags: { agent: 'all', ...(human ? {} : { json: 'true' }) },
131
+ // });
132
+ // } catch (err) {
133
+ // const message = err instanceof Error ? err.message : String(err);
134
+ // warn(renderInitNote(`hook/telemetry install failed: ${message}`, 'warning'));
135
+ // warn(' Run `moda hooks install --agent=all` manually to enable traces.');
136
+ // }
137
+ // } else {
138
+ // log(renderInitNote('Skipped coding-agent trace ingestion', 'skipped'));
139
+ // }
36
140
  const agents = detectAgents();
37
141
  if (agents.length > 0) {
38
- console.log(` Detected: ${agents.join(', ')}`);
39
- await installRules(agents);
142
+ log(renderInitNote(`Detected coding agents: ${agents.join(', ')}`));
143
+ await installRules(agents, { yes, silent: !human });
144
+ // Pull any skills the pipeline has already generated for this tenant into
145
+ // the local agent skill dirs. Best-effort: a brand-new tenant has none yet,
146
+ // and a transient ingest/network error must not fail setup.
147
+ try {
148
+ const pulled = await pullSkills({ silent: true });
149
+ if (pulled.wrote + pulled.updated > 0) {
150
+ log(renderInitNote(`Pulled ${pulled.wrote + pulled.updated} learned skill(s) into agent skills`));
151
+ }
152
+ }
153
+ catch {
154
+ // Silent: skills are additive; never block init on them.
155
+ }
156
+ }
157
+ else {
158
+ log(renderInitNote('No coding agents detected, skipping', 'skipped'));
159
+ }
160
+ await initPrompts({ silent: true });
161
+ log(renderInitNote('Created or verified .moda/prompts.yml'));
162
+ const installSdkGuide = setupPlan.find((action) => action.category === 'sdk')?.approved === true;
163
+ if (installSdkGuide) {
164
+ const guidePath = writeSdkIntegrationGuide();
165
+ log(renderInitNote(`Wrote ${guidePath}`));
166
+ markApplied(setupPlan, 'sdk');
40
167
  }
41
168
  else {
42
- console.log(' No coding agents detected, skipping');
169
+ log(renderInitNote('Skipped SDK integration guide', 'skipped'));
170
+ markApplied(setupPlan, 'sdk', 'User skipped SDK integration guide.');
171
+ }
172
+ const configureCiCd = setupPlan.find((action) => action.category === 'ci-cd')?.approved === true;
173
+ if (configureCiCd) {
174
+ const workflowPath = writeCiCdWorkflow();
175
+ log(renderInitNote(`Wrote ${workflowPath}`));
176
+ markApplied(setupPlan, 'ci-cd');
43
177
  }
178
+ log(renderInitNote('Reusing approved Step 4 harness report; no second analyst run needed'));
179
+ markApplied(setupPlan, 'harness');
180
+ markApplied(setupPlan, 'hooks', ingestCodingAgentTraces ? undefined : 'Coding-agent trace capture is temporarily unavailable.');
181
+ markApplied(setupPlan, 'prompts');
182
+ markApplied(setupPlan, 'skills');
183
+ emit('apply', 'Approved changes applied.', 'completed');
184
+ phases.push({ id: 'apply', title: 'Apply Approved Changes', status: 'completed' });
185
+ log(renderInitStep(7, 7, 'Validate', 'Running local doctor checks so the setup leaves a clear state.'));
186
+ emit('validate', 'Running local Moda doctor diagnostics.');
187
+ await runDoctorCommand(createCommandContext({
188
+ command: 'doctor',
189
+ positional: undefined,
190
+ positionals: [],
191
+ flags: commandFlags({
192
+ json: true,
193
+ quiet: true,
194
+ offline: true,
195
+ 'no-telemetry': true,
196
+ }),
197
+ }));
198
+ markApplied(setupPlan, 'validation');
199
+ log(renderInitNote('Local validation checks completed'));
200
+ emit('validate', 'Local validation checks complete.', 'completed');
201
+ phases.push({ id: 'validate', title: 'Validate', status: 'completed' });
44
202
  // Done
45
- console.log(`\n Setup complete for ${tenantName}!\n`);
46
- console.log(' Now paste this into your coding agent to integrate Moda:\n');
47
- console.log(' Integrate the Moda SDK into this project. Follow the Moda SDK');
48
- console.log(' integration guide in the project rules.\n');
49
- console.log(' Dashboard: https://moda.dev');
50
- console.log('');
203
+ log(renderInitComplete({
204
+ tenantName,
205
+ filesChanged: changedFiles(setupPlan),
206
+ skipped: setupPlan.filter((item) => item.status === 'skipped' || item.status === 'manual'),
207
+ phases,
208
+ }));
209
+ return { apiKey, tenantName, phases, setupPlan };
210
+ }
211
+ function buildSetupPlan(options) {
212
+ return [
213
+ {
214
+ category: 'harness',
215
+ title: 'Analyze harness and write cited Moda report',
216
+ status: 'planned',
217
+ approved: true,
218
+ files: [
219
+ '.moda/harness-report.json',
220
+ '.moda/harness-report-validation.json',
221
+ '.moda/HARNESS_REPORT.md',
222
+ '.moda/harness.json',
223
+ '.moda/HARNESS.md',
224
+ ],
225
+ command: 'moda harness analyze --analyst=auto --summary',
226
+ },
227
+ {
228
+ category: 'hooks',
229
+ title: 'Install coding-agent trace hooks',
230
+ status: options.installHooks ? 'planned' : 'skipped',
231
+ approved: options.installHooks,
232
+ files: ['~/.claude/settings.json', '~/.codex/hooks.json', '~/.cursor/hooks.json'],
233
+ command: 'moda hooks install --agent=all',
234
+ reason: options.installHooks ? undefined : 'Coding-agent trace capture is temporarily unavailable.',
235
+ },
236
+ {
237
+ category: 'prompts',
238
+ title: 'Initialize prompt registry manifest',
239
+ status: 'planned',
240
+ approved: true,
241
+ files: ['.moda/prompts.yml'],
242
+ command: 'moda prompts init',
243
+ },
244
+ {
245
+ category: 'skills',
246
+ title: 'Pull approved Moda skills when available',
247
+ status: 'planned',
248
+ approved: true,
249
+ files: ['.claude/skills', '.cursor/rules'],
250
+ command: 'moda skills pull',
251
+ },
252
+ {
253
+ category: 'sdk',
254
+ title: 'Write SDK integration guide',
255
+ status: options.installSdkGuide ? 'planned' : 'manual',
256
+ approved: options.installSdkGuide,
257
+ files: [SDK_GUIDE_PATH],
258
+ command: 'open .moda/SDK_INTEGRATION.md',
259
+ reason: options.installSdkGuide
260
+ ? 'Guide only; package installs and source edits remain human-approved/manual.'
261
+ : 'SDK package installation and code instrumentation stay human-approved/manual.',
262
+ },
263
+ {
264
+ category: 'ci-cd',
265
+ title: 'CI/CD validation workflow',
266
+ status: options.configureCiCd ? 'planned' : 'manual',
267
+ approved: options.configureCiCd,
268
+ files: ['.github/workflows/moda-validation.yml'],
269
+ command: 'moda doctor --online --json',
270
+ reason: options.configureCiCd ? undefined : 'No CI/CD files are changed until a project-specific plan is approved.',
271
+ },
272
+ {
273
+ category: 'validation',
274
+ title: 'Run local Moda doctor diagnostics',
275
+ status: 'planned',
276
+ approved: true,
277
+ files: [],
278
+ command: 'moda doctor',
279
+ },
280
+ ];
281
+ }
282
+ function markApplied(setupPlan, category, skipReason) {
283
+ const action = setupPlan.find((item) => item.category === category);
284
+ if (!action)
285
+ return;
286
+ if (skipReason) {
287
+ action.status = 'skipped';
288
+ action.reason = skipReason;
289
+ return;
290
+ }
291
+ if (action.approved)
292
+ action.status = 'applied';
293
+ }
294
+ function changedFiles(setupPlan) {
295
+ return [...new Set(setupPlan
296
+ .filter((action) => action.status === 'applied')
297
+ .flatMap((action) => action.files))].filter((file) => !file.includes('.env'));
298
+ }
299
+ function writeCiCdWorkflow(cwd = process.cwd()) {
300
+ const relativePath = '.github/workflows/moda-validation.yml';
301
+ const path = join(cwd, relativePath);
302
+ mkdirSync(dirname(path), { recursive: true });
303
+ writeFileSync(path, `${[
304
+ 'name: Moda Validation',
305
+ '',
306
+ 'on:',
307
+ ' pull_request:',
308
+ ' push:',
309
+ ' branches: [main]',
310
+ '',
311
+ 'jobs:',
312
+ ' moda-doctor:',
313
+ ' runs-on: ubuntu-latest',
314
+ ' env:',
315
+ ' MODA_API_KEY: ${{ secrets.MODA_API_KEY }}',
316
+ ' MODA_TENANT_ID: ${{ secrets.MODA_TENANT_ID }}',
317
+ ' steps:',
318
+ ' - uses: actions/checkout@v4',
319
+ ' - uses: actions/setup-node@v4',
320
+ ' with:',
321
+ ' node-version: 22',
322
+ ' - name: Validate Moda setup',
323
+ ' run: npx -y @moda-ai/cli@latest moda doctor --online --json',
324
+ '',
325
+ ].join('\n')}`);
326
+ return relativePath;
327
+ }
328
+ function printHarnessDetectionSummary(cwd = process.cwd()) {
329
+ const manifestPath = join(cwd, '.moda', 'harness.json');
330
+ if (!existsSync(manifestPath))
331
+ return;
332
+ try {
333
+ const graph = JSON.parse(readFileSync(manifestPath, 'utf8'));
334
+ const frameworkNames = (graph.agentFrameworks ?? [])
335
+ .map((framework) => typeof framework.name === 'string' ? framework.name : undefined)
336
+ .filter((name) => Boolean(name));
337
+ const providerNames = (graph.modelProviders ?? [])
338
+ .map((provider) => typeof provider.name === 'string' ? provider.name : undefined)
339
+ .filter((name) => Boolean(name));
340
+ console.log(` Detected harness: ${(graph.runtimeAgents ?? []).length} runtime agent(s), ` +
341
+ `${(graph.artifacts ?? []).length} artifact(s), ${(graph.relationships ?? []).length} relationship(s)`);
342
+ if (frameworkNames.length > 0)
343
+ console.log(` Frameworks: ${frameworkNames.join(', ')}`);
344
+ if (providerNames.length > 0)
345
+ console.log(` Model providers: ${providerNames.join(', ')}`);
346
+ }
347
+ catch {
348
+ console.log(' Detected harness summary is available in .moda/HARNESS.md');
349
+ }
350
+ }
351
+ function commandFlags(flags) {
352
+ return Object.fromEntries(Object.entries(flags)
353
+ .filter(([, value]) => value !== undefined && value !== false)
354
+ .map(([key, value]) => [key, value === true ? 'true' : String(value)]));
355
+ }
356
+ function stringFlag(value) {
357
+ return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
358
+ }
359
+ // Coding-agent capture is temporarily disabled; restore this prompt with the hooks phase.
360
+ // async function shouldIngestCodingAgentTraces(
361
+ // flags: Record<string, unknown>,
362
+ // yes: boolean,
363
+ // ): Promise<boolean> {
364
+ // const explicit = parseOptionalBoolean(flagValue(flags, 'coding-agent-traces', 'coding_agent_traces'));
365
+ // if (explicit !== undefined) return explicit;
366
+ // if (isTruthyFlag(flagValue(flags, 'no-coding-agent-traces', 'no_coding_agent_traces'))) return false;
367
+ // if (yes) return true;
368
+ // return confirm({
369
+ // message: 'Ingest coding-agent traces for Claude Code, Codex, and Cursor?',
370
+ // default: true,
371
+ // });
372
+ // }
373
+ async function shouldConfigureCiCd(flags, yes) {
374
+ const explicit = parseOptionalBoolean(flagValue(flags, 'ci-cd', 'ci_cd', 'ci'));
375
+ if (explicit !== undefined)
376
+ return explicit;
377
+ if (isTruthyFlag(flagValue(flags, 'no-ci-cd', 'no_ci_cd')))
378
+ return false;
379
+ if (yes)
380
+ return false;
381
+ return confirm({
382
+ message: 'Create a GitHub Actions workflow for Moda validation?',
383
+ default: false,
384
+ });
385
+ }
386
+ async function shouldCreateSdkGuide(flags, yes) {
387
+ const explicit = parseOptionalBoolean(flagValue(flags, 'sdk-guide', 'sdk_guide', 'sdk'));
388
+ if (explicit !== undefined)
389
+ return explicit;
390
+ if (isTruthyFlag(flagValue(flags, 'no-sdk-guide', 'no_sdk_guide')))
391
+ return false;
392
+ if (yes)
393
+ return true;
394
+ return confirm({
395
+ message: 'Create a Moda SDK integration guide? (No packages installed, no source files edited.)',
396
+ default: true,
397
+ });
398
+ }
399
+ function parseOptionalBoolean(value) {
400
+ if (value === undefined)
401
+ return undefined;
402
+ if (value === true || value === 'true' || value === '1' || value === 'yes')
403
+ return true;
404
+ if (value === false || value === 'false' || value === '0' || value === 'no')
405
+ return false;
406
+ return undefined;
407
+ }
408
+ function isTruthyFlag(value) {
409
+ return value === true || value === 'true' || value === '1' || value === 'yes';
410
+ }
411
+ function flagValue(flags, ...keys) {
412
+ for (const key of keys) {
413
+ if (flags[key] !== undefined)
414
+ return flags[key];
415
+ }
416
+ return undefined;
51
417
  }
52
418
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,6 @@
1
- export declare function installRules(detectedAgents: string[]): Promise<void>;
1
+ export declare function claudeSkillToCursorRule(skillContent: string): string;
2
+ export declare function installRules(detectedAgents: string[], options?: {
3
+ yes?: boolean;
4
+ silent?: boolean;
5
+ }): Promise<void>;
2
6
  //# sourceMappingURL=rules.d.ts.map
@@ -15,10 +15,6 @@ import { confirm } from '@inquirer/prompts';
15
15
  const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
16
16
  const SKILLS_SOURCE_DIR = join(PACKAGE_ROOT, 'skills');
17
17
  const SKILLS = [
18
- {
19
- id: 'integrate-moda',
20
- label: 'integrate-moda — instrument this project with the Moda SDK',
21
- },
22
18
  {
23
19
  id: 'moda-cli',
24
20
  label: 'moda-cli — let coding agents query Moda analytics via the CLI',
@@ -53,7 +49,7 @@ const ADAPTERS = {
53
49
  // the description out of it, and emit Cursor's variant.
54
50
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
55
51
  const DESCRIPTION_RE = /^description:\s*(.+?)\s*$/m;
56
- function claudeSkillToCursorRule(skillContent) {
52
+ export function claudeSkillToCursorRule(skillContent) {
57
53
  const match = FRONTMATTER_RE.exec(skillContent);
58
54
  if (!match) {
59
55
  // Body without frontmatter — pass through untouched rather than guess.
@@ -101,22 +97,26 @@ function installSkillForAgent(skill, adapter, content) {
101
97
  }
102
98
  }
103
99
  // --- Public entry point ---
104
- export async function installRules(detectedAgents) {
100
+ export async function installRules(detectedAgents, options = {}) {
105
101
  const adapters = detectedAgents
106
102
  .map((a) => ADAPTERS[a])
107
103
  .filter((a) => a !== undefined);
108
104
  if (adapters.length === 0) {
109
- console.log(' No supported coding agents detected');
105
+ if (!options.silent)
106
+ console.log(' No supported coding agents detected');
110
107
  return;
111
108
  }
112
109
  const agentList = adapters.map((a) => a.name).join(', ');
113
110
  const skillList = SKILLS.map((s) => `\n • ${s.label}`).join('');
114
- const shouldInstall = await confirm({
115
- message: `Install Moda skills for ${agentList}?${skillList}\n Continue?`,
116
- default: true,
117
- });
111
+ const shouldInstall = options.yes
112
+ ? true
113
+ : await confirm({
114
+ message: `Install Moda skills for ${agentList}?${skillList}\n Continue?`,
115
+ default: true,
116
+ });
118
117
  if (!shouldInstall) {
119
- console.log(' Skipped');
118
+ if (!options.silent)
119
+ console.log(' Skipped');
120
120
  return;
121
121
  }
122
122
  let wrote = 0;
@@ -137,15 +137,20 @@ export async function installRules(detectedAgents) {
137
137
  const outcome = installSkillForAgent(skill, adapter, content);
138
138
  switch (outcome.status) {
139
139
  case 'wrote':
140
- console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
140
+ if (!options.silent)
141
+ console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
141
142
  wrote++;
142
143
  break;
143
144
  case 'exists':
144
- console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
145
+ if (!options.silent) {
146
+ console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
147
+ }
145
148
  skipped++;
146
149
  break;
147
150
  case 'failed':
148
- console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
151
+ if (!options.silent) {
152
+ console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
153
+ }
149
154
  failed++;
150
155
  break;
151
156
  }
@@ -159,7 +164,8 @@ export async function installRules(detectedAgents) {
159
164
  .filter(Boolean)
160
165
  .join(', ');
161
166
  if (summary) {
162
- console.log(` Done — ${summary}`);
167
+ if (!options.silent)
168
+ console.log(` Done — ${summary}`);
163
169
  }
164
170
  }
165
171
  //# sourceMappingURL=rules.js.map
@@ -0,0 +1,3 @@
1
+ export declare const SDK_GUIDE_PATH = ".moda/SDK_INTEGRATION.md";
2
+ export declare function writeSdkIntegrationGuide(cwd?: string): string;
3
+ //# sourceMappingURL=sdk-guide.d.ts.map