@moda-ai/cli 1.1.1 → 1.2.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 +768 -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 +1102 -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 +383 -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
@@ -0,0 +1,34 @@
1
+ import { type CliOutput, type CliOutputMode } from './output.js';
2
+ export interface CommandContextInput {
3
+ command: string;
4
+ positional: string | undefined;
5
+ positionals: string[];
6
+ flags: Record<string, string>;
7
+ }
8
+ export interface CommandContext {
9
+ command: string;
10
+ positional: string | undefined;
11
+ positionals: string[];
12
+ flags: Record<string, string>;
13
+ rawFlags: Record<string, string>;
14
+ startedAt: number;
15
+ cwd: string;
16
+ env: NodeJS.ProcessEnv;
17
+ outputMode: CliOutputMode;
18
+ output: CliOutput;
19
+ profile: string | undefined;
20
+ dryRun: boolean;
21
+ yes: boolean;
22
+ quiet: boolean;
23
+ verbose: boolean;
24
+ interactive: boolean;
25
+ }
26
+ export declare function createCommandContext(input: CommandContextInput, options?: {
27
+ commandName?: string;
28
+ commandFlags?: Record<string, string>;
29
+ rawFlags?: Record<string, string>;
30
+ startedAt?: number;
31
+ cwd?: string;
32
+ env?: NodeJS.ProcessEnv;
33
+ }): CommandContext;
34
+ //# sourceMappingURL=command-context.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { createCliOutput, outputModeFromFlags } from './output.js';
2
+ function flagEnabled(flags, key) {
3
+ return flags[key] === 'true';
4
+ }
5
+ export function createCommandContext(input, options = {}) {
6
+ const flags = options.commandFlags ?? input.flags;
7
+ const rawFlags = options.rawFlags ?? input.flags;
8
+ const outputMode = outputModeFromFlags(flags);
9
+ const quiet = flagEnabled(flags, 'quiet');
10
+ const interactive = outputMode === 'human' &&
11
+ !quiet &&
12
+ !flagEnabled(flags, 'yes') &&
13
+ process.stdin.isTTY === true &&
14
+ process.stderr.isTTY === true;
15
+ return {
16
+ command: options.commandName ?? input.command,
17
+ positional: input.positional,
18
+ positionals: input.positionals,
19
+ flags,
20
+ rawFlags,
21
+ startedAt: options.startedAt ?? Date.now(),
22
+ cwd: options.cwd ?? process.cwd(),
23
+ env: options.env ?? process.env,
24
+ outputMode,
25
+ output: createCliOutput({
26
+ command: options.commandName ?? input.command,
27
+ mode: outputMode,
28
+ quiet,
29
+ }),
30
+ profile: flags.profile,
31
+ dryRun: flagEnabled(flags, 'dry-run'),
32
+ yes: flagEnabled(flags, 'yes'),
33
+ quiet,
34
+ verbose: flagEnabled(flags, 'verbose'),
35
+ interactive,
36
+ };
37
+ }
38
+ //# sourceMappingURL=command-context.js.map
@@ -0,0 +1,22 @@
1
+ import type { CliOutputMode } from './output.js';
2
+ export type CommandAuthRequirement = 'none' | 'api-key';
3
+ export type CommandMutability = 'read' | 'write';
4
+ export interface CommandMetadata {
5
+ name: string;
6
+ aliases?: string[];
7
+ description: string;
8
+ examples: string[];
9
+ auth: CommandAuthRequirement;
10
+ outputModes: readonly CliOutputMode[];
11
+ mutability: CommandMutability;
12
+ }
13
+ export interface CommandDefinition<TContext, TResult> extends CommandMetadata {
14
+ handler: (context: TContext) => Promise<TResult>;
15
+ }
16
+ export interface CommandRegistry<TContext, TResult, TDefinition extends CommandDefinition<TContext, TResult> = CommandDefinition<TContext, TResult>> {
17
+ all(): TDefinition[];
18
+ find(name: string): TDefinition | undefined;
19
+ has(name: string): boolean;
20
+ }
21
+ export declare function createCommandRegistry<TContext, TResult, TDefinition extends CommandDefinition<TContext, TResult>>(definitions: TDefinition[]): CommandRegistry<TContext, TResult, TDefinition>;
22
+ //# sourceMappingURL=command-registry.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function createCommandRegistry(definitions) {
2
+ const lookup = new Map();
3
+ for (const definition of definitions) {
4
+ lookup.set(definition.name, definition);
5
+ for (const alias of definition.aliases ?? []) {
6
+ lookup.set(alias, definition);
7
+ }
8
+ }
9
+ return {
10
+ all: () => [...definitions],
11
+ find: (name) => lookup.get(name),
12
+ has: (name) => lookup.has(name),
13
+ };
14
+ }
15
+ //# sourceMappingURL=command-registry.js.map
package/dist/config.d.ts CHANGED
@@ -1,5 +1,44 @@
1
+ import { type ProfileIOOptions } from './profiles.js';
1
2
  export declare const VERSION: string;
2
- export declare const MODA_API_KEY: string | undefined;
3
3
  export declare const MODA_BASE_URL: string;
4
+ /**
5
+ * Persisted CLI configuration written by `moda init` to `~/.moda/config.json`
6
+ * (mode 0600). This lets the snapshot/session-end hook authenticate without
7
+ * the user having to export MODA_API_KEY into whatever shell launched Claude
8
+ * (e.g. via Infisical, a login shell, or a `.claude/settings.json` env block).
9
+ * Mirrors the existing token persistence in `auth.ts`.
10
+ */
11
+ export interface CliConfig {
12
+ api_key?: string;
13
+ ingest_url?: string;
14
+ tenant_id?: string;
15
+ }
16
+ export declare function loadCliConfig(options?: ProfileIOOptions): CliConfig;
17
+ /**
18
+ * Merge-and-write the persisted config. Always chmod 0600 since it holds the
19
+ * API key. Best-effort: never throws on chmod failure (some filesystems).
20
+ */
21
+ export declare function saveCliConfig(update: CliConfig): void;
22
+ /**
23
+ * Resolve the API key: prefer the environment (so CI / explicit exports win),
24
+ * then fall back to the persisted `~/.moda/config.json`. Read lazily so a
25
+ * long-lived process (or a test) sees the current value.
26
+ */
27
+ export declare function resolveApiKey(options?: ProfileIOOptions): string | undefined;
28
+ /**
29
+ * Resolve the ingest worker base URL used by `moda workspace snapshot`.
30
+ * Env override -> persisted config -> production default.
31
+ */
32
+ export declare function resolveIngestUrl(options?: ProfileIOOptions): string;
33
+ /**
34
+ * Resolve the active tenant id: env override first, then the persisted
35
+ * `~/.moda/config.json` written by `moda init`. Used by the local dev
36
+ * orchestrator hook so the user doesn't have to export MODA_TENANT_ID.
37
+ */
38
+ export declare function resolveTenantId(options?: ProfileIOOptions): string | undefined;
39
+ export declare const MODA_API_KEY: string | undefined;
40
+ export declare const MODA_INGEST_URL: string;
41
+ export declare function resolveModaBaseUrl(options?: ProfileIOOptions): string;
4
42
  export declare function validateConfig(): void;
43
+ export declare function legacyConfigPath(): string;
5
44
  //# sourceMappingURL=config.d.ts.map
package/dist/config.js CHANGED
@@ -1,13 +1,80 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { DEFAULT_INGEST_URL, PROD_BASE_URL, getLegacyConfigPath, loadLegacyCliConfig, resolveApiKeyFromActiveProfile, resolveBaseUrl, resolveIngestUrlFromActiveProfile, resolveTenantIdFromActiveProfile, syncLegacyConfigToProfile, writeLegacyCliConfig, } from './profiles.js';
2
3
  const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
3
4
  export const VERSION = pkg.version;
5
+ // Canonical host is www.moda.dev. The apex `moda.dev` 308-redirects to it, and
6
+ // cross-host redirects strip the Authorization header (fetch/undici), so using
7
+ // the apex breaks `moda init`'s authenticated calls with a 401. Default to the
8
+ // canonical host directly.
9
+ export const MODA_BASE_URL = resolveBaseUrl();
10
+ export function loadCliConfig(options = {}) {
11
+ return loadLegacyCliConfig(options);
12
+ }
13
+ /**
14
+ * Merge-and-write the persisted config. Always chmod 0600 since it holds the
15
+ * API key. Best-effort: never throws on chmod failure (some filesystems).
16
+ */
17
+ export function saveCliConfig(update) {
18
+ const merged = writeLegacyCliConfig(update);
19
+ try {
20
+ syncLegacyConfigToProfile(merged);
21
+ }
22
+ catch {
23
+ // The legacy config write is the compatibility contract; profile sync is a
24
+ // best-effort migration aid until profile commands own the flow.
25
+ }
26
+ }
27
+ /**
28
+ * Resolve the API key: prefer the environment (so CI / explicit exports win),
29
+ * then fall back to the persisted `~/.moda/config.json`. Read lazily so a
30
+ * long-lived process (or a test) sees the current value.
31
+ */
32
+ export function resolveApiKey(options = {}) {
33
+ const env = options.env ?? process.env;
34
+ return (env.MODA_API_KEY ||
35
+ resolveApiKeyFromActiveProfile(options) ||
36
+ loadCliConfig(options).api_key ||
37
+ undefined);
38
+ }
39
+ /**
40
+ * Resolve the ingest worker base URL used by `moda workspace snapshot`.
41
+ * Env override -> persisted config -> production default.
42
+ */
43
+ export function resolveIngestUrl(options = {}) {
44
+ const env = options.env ?? process.env;
45
+ return (env.MODA_INGEST_URL ||
46
+ resolveIngestUrlFromActiveProfile(options) ||
47
+ loadCliConfig(options).ingest_url ||
48
+ DEFAULT_INGEST_URL);
49
+ }
50
+ /**
51
+ * Resolve the active tenant id: env override first, then the persisted
52
+ * `~/.moda/config.json` written by `moda init`. Used by the local dev
53
+ * orchestrator hook so the user doesn't have to export MODA_TENANT_ID.
54
+ */
55
+ export function resolveTenantId(options = {}) {
56
+ const env = options.env ?? process.env;
57
+ return (env.MODA_TENANT_ID ||
58
+ resolveTenantIdFromActiveProfile(options) ||
59
+ loadCliConfig(options).tenant_id ||
60
+ undefined);
61
+ }
62
+ // Back-compat exports. Prefer resolveApiKey()/resolveIngestUrl() for the lazy
63
+ // file fallback; these only reflect the environment at module-load time.
4
64
  export const MODA_API_KEY = process.env.MODA_API_KEY;
5
- export const MODA_BASE_URL = process.env.MODA_BASE_URL || 'https://moda.dev';
65
+ export const MODA_INGEST_URL = process.env.MODA_INGEST_URL || DEFAULT_INGEST_URL;
66
+ export function resolveModaBaseUrl(options = {}) {
67
+ return resolveBaseUrl(options) || PROD_BASE_URL;
68
+ }
6
69
  export function validateConfig() {
7
- if (!MODA_API_KEY) {
8
- console.error('Error: MODA_API_KEY environment variable is required');
70
+ if (!resolveApiKey()) {
71
+ console.error('Error: no Moda API key found. Set MODA_API_KEY or run "moda init".');
9
72
  console.error('Get your API key from https://moda.dev/settings');
10
73
  process.exit(1);
11
74
  }
12
75
  }
76
+ // Kept for callers/tests that need the legacy path during migration.
77
+ export function legacyConfigPath() {
78
+ return getLegacyConfigPath();
79
+ }
13
80
  //# sourceMappingURL=config.js.map
@@ -0,0 +1,79 @@
1
+ import type { CommandContext } from './command-context.js';
2
+ export type DoctorCheckStatus = 'pass' | 'warning' | 'fail' | 'skipped';
3
+ export type DoctorSeverity = 'info' | 'warning' | 'critical';
4
+ export interface DoctorEvidence {
5
+ label: string;
6
+ value: string;
7
+ }
8
+ export interface DoctorCheck {
9
+ id: string;
10
+ group: string;
11
+ title: string;
12
+ status: DoctorCheckStatus;
13
+ severity: DoctorSeverity;
14
+ evidence: DoctorEvidence[];
15
+ remediation?: string;
16
+ nextCommand?: string;
17
+ }
18
+ export interface DoctorReport {
19
+ schemaVersion: 'doctor.v0.1';
20
+ generatedAt: string;
21
+ profile: {
22
+ active: string;
23
+ requested?: string;
24
+ };
25
+ summary: {
26
+ status: DoctorCheckStatus;
27
+ pass: number;
28
+ warning: number;
29
+ fail: number;
30
+ skipped: number;
31
+ };
32
+ checks: DoctorCheck[];
33
+ nextCommands: string[];
34
+ fixPlan: Array<{
35
+ checkId: string;
36
+ command?: string;
37
+ action: string;
38
+ }>;
39
+ }
40
+ export interface StatusPayload {
41
+ schemaVersion: 'status.v0.1';
42
+ generatedAt: string;
43
+ status: DoctorCheckStatus;
44
+ activeProfile: string;
45
+ auth: {
46
+ apiKey: 'configured' | 'missing';
47
+ };
48
+ workspace: {
49
+ tenantId: string | null;
50
+ baseUrl: string;
51
+ ingestUrl: string;
52
+ };
53
+ dataFlow: DoctorCheckStatus;
54
+ harness: DoctorCheckStatus;
55
+ harnessSync: DoctorCheckStatus;
56
+ harnessReport: DoctorCheckStatus;
57
+ shadowSpark: DoctorCheckStatus;
58
+ prompts: DoctorCheckStatus;
59
+ skills: DoctorCheckStatus;
60
+ topBrokenCheck: {
61
+ id: string;
62
+ status: DoctorCheckStatus;
63
+ severity: DoctorSeverity;
64
+ nextCommand?: string;
65
+ } | null;
66
+ nextCommands: string[];
67
+ }
68
+ interface DoctorOptions {
69
+ offline: boolean;
70
+ }
71
+ export declare function runDoctorCommand(context: CommandContext): Promise<{
72
+ exitCode: number;
73
+ }>;
74
+ export declare function runStatusCommand(context: CommandContext): Promise<{
75
+ exitCode: number;
76
+ }>;
77
+ export declare function buildDoctorReport(context: CommandContext, options: DoctorOptions): Promise<DoctorReport>;
78
+ export {};
79
+ //# sourceMappingURL=doctor.d.ts.map