@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.
- package/README.md +36 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.js +477 -0
- package/dist/analyst-tui.d.ts +81 -0
- package/dist/analyst-tui.js +314 -0
- package/dist/api-client.d.ts +4 -0
- package/dist/api-client.js +20 -3
- package/dist/auth.d.ts +22 -3
- package/dist/auth.js +230 -66
- package/dist/cli.d.ts +20 -3
- package/dist/cli.js +745 -93
- package/dist/command-context.d.ts +34 -0
- package/dist/command-context.js +38 -0
- package/dist/command-registry.d.ts +22 -0
- package/dist/command-registry.js +15 -0
- package/dist/config.d.ts +40 -1
- package/dist/config.js +70 -3
- package/dist/doctor.d.ts +79 -0
- package/dist/doctor.js +1111 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +9 -0
- package/dist/harness-report.d.ts +138 -0
- package/dist/harness-report.js +1464 -0
- package/dist/harness.d.ts +233 -0
- package/dist/harness.js +3264 -0
- package/dist/init/index.d.ts +30 -1
- package/dist/init/index.js +389 -23
- package/dist/init/rules.d.ts +5 -1
- package/dist/init/rules.js +22 -16
- package/dist/init/sdk-guide.d.ts +3 -0
- package/dist/init/sdk-guide.js +397 -0
- package/dist/init/tenant.js +12 -3
- package/dist/keys.d.ts +47 -0
- package/dist/keys.js +136 -0
- package/dist/output.d.ts +118 -0
- package/dist/output.js +228 -0
- package/dist/production-intelligence.d.ts +134 -0
- package/dist/production-intelligence.js +1113 -0
- package/dist/profile-commands.d.ts +10 -0
- package/dist/profile-commands.js +252 -0
- package/dist/profiles.d.ts +90 -0
- package/dist/profiles.js +432 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.js +368 -0
- package/dist/schemas.d.ts +4 -4
- package/dist/skills.d.ts +25 -0
- package/dist/skills.js +468 -0
- package/dist/sync.d.ts +5 -0
- package/dist/sync.js +27 -0
- package/dist/telemetry.d.ts +18 -0
- package/dist/telemetry.js +63 -0
- package/dist/tui.d.ts +40 -0
- package/dist/tui.js +387 -0
- package/dist/workspace.d.ts +12 -0
- package/dist/workspace.js +1294 -0
- package/hooks/moda-snapshot.sh +41 -0
- package/package.json +10 -7
- package/skills/moda-cli/SKILL.md +48 -4
- package/skills/integrate-moda/SKILL.md +0 -63
package/dist/init/index.d.ts
CHANGED
|
@@ -1,2 +1,31 @@
|
|
|
1
|
-
export
|
|
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
|
package/dist/init/index.js
CHANGED
|
@@ -1,16 +1,55 @@
|
|
|
1
1
|
// src/init/index.ts — Full moda init wizard orchestrator
|
|
2
|
-
import {
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
+
log(renderInitNote('Authentication successful'));
|
|
27
67
|
}
|
|
28
68
|
else {
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
package/dist/init/rules.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
export declare function
|
|
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
|
package/dist/init/rules.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
140
|
+
if (!options.silent)
|
|
141
|
+
console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
|
|
141
142
|
wrote++;
|
|
142
143
|
break;
|
|
143
144
|
case 'exists':
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
167
|
+
if (!options.silent)
|
|
168
|
+
console.log(` Done — ${summary}`);
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
//# sourceMappingURL=rules.js.map
|