@moda-ai/cli 1.10.2 → 1.12.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/dist/agent-schemas.js +60 -3
  2. package/dist/auth.js +5 -1
  3. package/dist/cli.js +124 -38
  4. package/dist/config.js +5 -3
  5. package/dist/errors.d.ts +32 -0
  6. package/dist/errors.js +40 -0
  7. package/dist/exit-funnel.d.ts +14 -0
  8. package/dist/exit-funnel.js +49 -0
  9. package/dist/harness.js +18 -6
  10. package/dist/init/index.js +57 -4
  11. package/dist/init/sdk-guide.d.ts +28 -1
  12. package/dist/init/sdk-guide.js +102 -98
  13. package/dist/init/sdk-integration.d.ts +23 -3
  14. package/dist/init/sdk-integration.js +106 -24
  15. package/dist/init/skill-resolver.d.ts +19 -0
  16. package/dist/init/skill-resolver.js +78 -0
  17. package/dist/init/tenant.d.ts +6 -1
  18. package/dist/init/tenant.js +35 -11
  19. package/dist/manifest.d.ts +5 -0
  20. package/dist/manifest.js +111 -0
  21. package/dist/output.d.ts +44 -4
  22. package/dist/output.js +337 -32
  23. package/dist/prompts.d.ts +2 -1
  24. package/dist/prompts.js +38 -13
  25. package/dist/redact.d.ts +2 -0
  26. package/dist/redact.js +81 -0
  27. package/dist/schemas.d.ts +2 -2
  28. package/dist/skills-bundled.d.ts +35 -0
  29. package/dist/skills-bundled.js +157 -0
  30. package/dist/skills-manifest.d.ts +33 -0
  31. package/dist/skills-manifest.js +244 -0
  32. package/dist/skills.d.ts +8 -2
  33. package/dist/skills.js +55 -161
  34. package/package.json +6 -4
  35. package/skills/integration/index.json +123 -0
  36. package/skills/integration/integration-node-anthropic/EXAMPLE.md +59 -0
  37. package/skills/integration/integration-node-anthropic/SKILL.md +150 -0
  38. package/skills/integration/integration-node-generic/EXAMPLE.md +77 -0
  39. package/skills/integration/integration-node-generic/SKILL.md +170 -0
  40. package/skills/integration/integration-node-langchain/EXAMPLE.md +59 -0
  41. package/skills/integration/integration-node-langchain/SKILL.md +144 -0
  42. package/skills/integration/integration-node-openai/EXAMPLE.md +55 -0
  43. package/skills/integration/integration-node-openai/SKILL.md +148 -0
  44. package/skills/integration/integration-node-vercel-ai-sdk/EXAMPLE.md +55 -0
  45. package/skills/integration/integration-node-vercel-ai-sdk/SKILL.md +179 -0
  46. package/skills/integration/integration-node-vercel-ai-sdk/references/prompt-attribution.md +31 -0
  47. package/skills/integration/integration-python-anthropic/EXAMPLE.md +66 -0
  48. package/skills/integration/integration-python-anthropic/SKILL.md +153 -0
  49. package/skills/integration/integration-python-claude-agent-sdk/EXAMPLE.md +61 -0
  50. package/skills/integration/integration-python-claude-agent-sdk/SKILL.md +129 -0
  51. package/skills/integration/integration-python-generic/EXAMPLE.md +46 -0
  52. package/skills/integration/integration-python-generic/SKILL.md +146 -0
  53. package/skills/integration/integration-python-langchain/EXAMPLE.md +63 -0
  54. package/skills/integration/integration-python-langchain/SKILL.md +141 -0
  55. package/skills/integration/integration-python-openai/EXAMPLE.md +65 -0
  56. package/skills/integration/integration-python-openai/SKILL.md +151 -0
  57. package/skills/moda-cli/SKILL.md +51 -7
  58. package/dist/init/detect.d.ts +0 -9
  59. package/dist/init/detect.js +0 -179
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: moda-cli
3
- version: 2.0.0
3
+ version: 2.1.0
4
4
  description: Query Moda's AI agent conversation analytics and manage code-first prompt versions from the terminal — semantic/keyword/hybrid message search, overview KPIs, topic clusters, message context, user frustration detections, tool failures, active loop detections, and moda prompts status/sync/promote. Use when the user asks about moda, modaflows, conversation analytics, prompt management, user frustrations, agent observability, tool failure debugging, wants to find conversations or tool calls about a topic, or wants to investigate how their AI agent is performing.
5
5
  ---
6
6
 
@@ -80,10 +80,14 @@ export MODA_API_KEY="moda_sk_..." # from moda.dev/settings
80
80
 
81
81
  ## Agent protocol
82
82
 
83
- `moda` is agent-safe: it picks an output mode automatically, signals how much
84
- to trust an answer through exit codes, and (in agent mode) emits a
85
- schema-versioned `moda.agent.v1` JSON envelope you can parse instead of
86
- scraping prose.
83
+ Run `moda manifest --json` first when integrating with the CLI. It returns the
84
+ hand-maintained `moda.manifest.v1` contract: commands, output modes, exit codes,
85
+ event catalog, env vars, schema ids, and a glossary.
86
+
87
+ `moda` is agent-safe: it picks an output mode automatically, signals the
88
+ orchestrator's next action through exit codes, and emits schema-versioned JSON
89
+ on stdout in agent modes. Errors also ride stdout envelopes/events in agent
90
+ mode; stderr is only for human-mode prose/progress.
87
91
 
88
92
  ### Output mode auto-selection
89
93
 
@@ -111,8 +115,11 @@ Branch on the exit code; it is stable and cheaper than parsing the payload:
111
115
  | Exit | Meaning | How to treat it |
112
116
  |---|---|---|
113
117
  | `0` | ok — answer is grounded in Moda Cloud intelligence | Trust it. |
118
+ | `1` | error — bad input, API/network failure, or unexpected failure | Read `errors[]`; retry only when the code is retryable. |
114
119
  | `3` | degraded — Moda Cloud was unavailable; answer synthesized from local Data API evidence | Answer is present but lower-trust; verify before acting. |
115
- | `1` | error — bad input, unknown command, or unexpected failure | Read `errors[]` in the envelope. |
120
+ | `4` | auth required | Run `auth.login_command`, then re-run `auth.resume_command` if present. |
121
+ | `5` | input required | Pick a choice, append its `resume_flags`, and re-run. |
122
+ | `130` | cancelled | Treat as interrupted or closed pipe; do not retry blindly. |
116
123
 
117
124
  ### The `moda.agent.v1` envelope
118
125
 
@@ -126,10 +133,44 @@ Fields an agent should read:
126
133
  - `next_commands[]` — suggested follow-up commands (each carries `mutability`
127
134
  and `requires_approval`).
128
135
  - `warnings[]`, `errors[]`.
136
+ - `meta.run_id` — correlates a non-stream envelope with stream events.
129
137
  - `data` — the raw command payload. For `ask`, `data.source` is `cloud` or
130
138
  `local_fallback` and `data.degraded` is a boolean — the canonical in-payload
131
139
  way to detect degradation (in addition to exit code 3).
132
140
 
141
+ `errors[].code` is one of `input_error`, `api_error`, `network_error`,
142
+ `auth_required`, `input_required`, `cancelled`, `command_error`,
143
+ `unknown_error`, or a documented domain code such as `cloud_unavailable`.
144
+
145
+ ### The `moda.agent_event.v1` stream
146
+
147
+ Use `--agent --stream` for commands whose progress matters (`init`, `harness
148
+ analyze`, and any command where liveness matters). The stream is NDJSON, one
149
+ event per stdout line.
150
+
151
+ Ordering guarantees:
152
+
153
+ - First event of every agent-stream run is `started`.
154
+ - `run_id` is constant within a run; `seq` starts at `1` and increments without
155
+ gaps.
156
+ - Exactly one terminal event ends every stream: `completed`, `error`,
157
+ `cancelled`, `needs_input`, or `auth_required` — on every exit path including
158
+ thrown errors, SIGINT, and refused input. Absence of a terminal event = the CLI
159
+ crashed.
160
+ - `heartbeat` fires every 10s (`MODA_HEARTBEAT_MS`, `0` disables) whenever no
161
+ other event is emitted. No heartbeat for more than 30s during a run is the
162
+ canonical stall signal.
163
+ - Secrets are redacted at the emit boundary; never expect full `moda_sk_`,
164
+ `moda_ct_`, JWT, bearer token, or `api_key` values in events.
165
+
166
+ Interaction is exit-and-reinvoke, not stdin:
167
+
168
+ - `needs_input` carries `input_request: { id, question, kind, choices,
169
+ resume_command }` and exits `5`. Choose a value and re-run with that choice's
170
+ `resume_flags`.
171
+ - `auth_required` carries `auth: { reason, login_command, resume_command }` and
172
+ exits `4`. Run the argv array in `login_command`, then resume.
173
+
133
174
  ### Detecting a degraded answer
134
175
 
135
176
  Treat an answer as degraded if **any** of these hold:
@@ -141,7 +182,8 @@ Treat an answer as degraded if **any** of these hold:
141
182
  ### Schema introspection
142
183
 
143
184
  ```bash
144
- moda --json-schemas # print the JSON Schemas for the agent output contract
185
+ moda manifest --json # discover commands/events/exit codes/glossary
186
+ moda --json-schemas # print JSON Schemas for the output contract
145
187
  ```
146
188
 
147
189
  ### Recommended agent usage
@@ -151,6 +193,8 @@ answer=$(moda ask "what should I fix first?")
151
193
  case $? in
152
194
  0) printf 'grounded answer:\n%s\n' "$answer" ;;
153
195
  3) printf 'DEGRADED (local fallback — verify before acting):\n%s\n' "$answer" ;;
196
+ 4) echo "$answer" | jq -r '.errors[0].message'; exit 4 ;;
197
+ 5) echo "$answer" | jq -r '.errors[0].message'; exit 5 ;;
154
198
  *) echo "moda ask failed:"; echo "$answer" | jq -r '.errors[].message'; exit 1 ;;
155
199
  esac
156
200
 
@@ -1,9 +0,0 @@
1
- export interface ProjectInfo {
2
- language: 'typescript' | 'python' | null;
3
- packageManager: string | null;
4
- llmProviders: string[];
5
- }
6
- export declare function detectProject(): Promise<ProjectInfo>;
7
- export declare function installSDK(project: ProjectInfo): Promise<boolean>;
8
- export declare function getInitSnippet(project: ProjectInfo): string;
9
- //# sourceMappingURL=detect.d.ts.map
@@ -1,179 +0,0 @@
1
- // src/init/detect.ts — Project detection and SDK installation
2
- import { existsSync, readFileSync, readdirSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { execSync } from 'node:child_process';
5
- import { select, confirm } from '@inquirer/prompts';
6
- const LOCKFILE_TO_PM = {
7
- 'package-lock.json': 'npm',
8
- 'yarn.lock': 'yarn',
9
- 'pnpm-lock.yaml': 'pnpm',
10
- 'bun.lockb': 'bun',
11
- 'bun.lock': 'bun',
12
- 'poetry.lock': 'poetry',
13
- 'uv.lock': 'uv',
14
- };
15
- const INSTALL_COMMANDS = {
16
- npm: 'npm install moda-ai',
17
- yarn: 'yarn add moda-ai',
18
- pnpm: 'pnpm add moda-ai',
19
- bun: 'bun add moda-ai',
20
- pip: 'pip install moda-ai',
21
- poetry: 'poetry add moda-ai',
22
- uv: 'uv add moda-ai',
23
- };
24
- const LLM_PATTERNS = [
25
- { pattern: /from openai|import openai|require\(['"]openai['"]\)/i, name: 'OpenAI' },
26
- { pattern: /from anthropic|import.*@anthropic-ai\/sdk|require\(['"]@anthropic-ai\/sdk['"]\)/i, name: 'Anthropic' },
27
- { pattern: /from ai |import.*@ai-sdk|require\(['"](ai|@ai-sdk)/i, name: 'Vercel AI SDK' },
28
- { pattern: /from langchain|import.*langchain/i, name: 'LangChain' },
29
- { pattern: /from claude_agent_sdk|import.*claude.agent/i, name: 'Claude Agent SDK' },
30
- ];
31
- export async function detectProject() {
32
- const cwd = process.cwd();
33
- const hasPackageJson = existsSync(join(cwd, 'package.json'));
34
- const hasPython = existsSync(join(cwd, 'pyproject.toml')) ||
35
- existsSync(join(cwd, 'requirements.txt')) ||
36
- existsSync(join(cwd, 'Pipfile'));
37
- let language = null;
38
- if (hasPackageJson && hasPython) {
39
- language = await select({
40
- message: 'Both TypeScript and Python detected. Which to configure?',
41
- choices: [
42
- { name: 'TypeScript / JavaScript', value: 'typescript' },
43
- { name: 'Python', value: 'python' },
44
- ],
45
- });
46
- }
47
- else if (hasPackageJson) {
48
- language = 'typescript';
49
- }
50
- else if (hasPython) {
51
- language = 'python';
52
- }
53
- // Detect package manager
54
- let packageManager = null;
55
- if (language === 'typescript') {
56
- for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {
57
- if (existsSync(join(cwd, lockfile))) {
58
- packageManager = pm;
59
- break;
60
- }
61
- }
62
- // No lockfile — detect which package manager is actually installed
63
- if (!packageManager) {
64
- for (const pm of ['bun', 'pnpm', 'yarn', 'npm']) {
65
- try {
66
- execSync(`${pm} --version`, { stdio: 'pipe' });
67
- packageManager = pm;
68
- break;
69
- }
70
- catch {
71
- // not installed
72
- }
73
- }
74
- if (!packageManager)
75
- packageManager = 'npm';
76
- }
77
- }
78
- else if (language === 'python') {
79
- if (existsSync(join(cwd, 'poetry.lock'))) {
80
- packageManager = 'poetry';
81
- }
82
- else if (existsSync(join(cwd, 'uv.lock'))) {
83
- packageManager = 'uv';
84
- }
85
- else {
86
- packageManager = 'pip';
87
- }
88
- }
89
- // Detect LLM providers (shallow scan)
90
- const llmProviders = detectLLMProviders(cwd, language);
91
- return { language, packageManager, llmProviders };
92
- }
93
- function detectLLMProviders(cwd, language) {
94
- if (!language)
95
- return [];
96
- const providers = new Set();
97
- const extensions = language === 'typescript' ? ['.ts', '.tsx', '.js', '.jsx', '.mjs'] : ['.py'];
98
- const dirs = ['src', 'app', 'lib', '.'];
99
- for (const dir of dirs) {
100
- const dirPath = join(cwd, dir);
101
- if (!existsSync(dirPath))
102
- continue;
103
- try {
104
- const files = readdirSync(dirPath, { withFileTypes: true });
105
- for (const file of files) {
106
- if (!file.isFile())
107
- continue;
108
- if (!extensions.some((ext) => file.name.endsWith(ext)))
109
- continue;
110
- try {
111
- const content = readFileSync(join(dirPath, file.name), 'utf8');
112
- for (const { pattern, name } of LLM_PATTERNS) {
113
- if (pattern.test(content)) {
114
- providers.add(name);
115
- }
116
- }
117
- }
118
- catch {
119
- // Skip unreadable files
120
- }
121
- }
122
- }
123
- catch {
124
- // Skip unreadable dirs
125
- }
126
- }
127
- return Array.from(providers);
128
- }
129
- export async function installSDK(project) {
130
- if (!project.packageManager)
131
- return false;
132
- const command = INSTALL_COMMANDS[project.packageManager];
133
- if (!command)
134
- return false;
135
- const shouldInstall = await confirm({
136
- message: `Install moda-ai SDK? (${command})`,
137
- default: true,
138
- });
139
- if (!shouldInstall) {
140
- console.log(` Skipped. Run manually: ${command}`);
141
- return false;
142
- }
143
- console.log(` Installing moda-ai (${project.packageManager})...`);
144
- try {
145
- execSync(command, { cwd: process.cwd(), stdio: 'pipe' });
146
- console.log(' Installed moda-ai');
147
- return true;
148
- }
149
- catch (err) {
150
- const message = err instanceof Error ? err.message : String(err);
151
- console.error(` Warning: SDK install failed: ${message}`);
152
- console.error(` Run manually: ${command}`);
153
- return false;
154
- }
155
- }
156
- export function getInitSnippet(project) {
157
- if (project.language === 'typescript') {
158
- return `
159
- import { Moda } from 'moda-ai';
160
-
161
- await Moda.init(process.env.MODA_API_KEY!);
162
- // Moda.conversationId = 'your-session-id';
163
- // ... your LLM calls are auto-instrumented ...
164
- await Moda.flush();
165
- `;
166
- }
167
- if (project.language === 'python') {
168
- return `
169
- import moda
170
-
171
- moda.init() # reads MODA_API_KEY from env
172
- # moda.conversation_id = 'your-session-id'
173
- # ... your LLM calls are auto-instrumented ...
174
- moda.flush()
175
- `;
176
- }
177
- return '';
178
- }
179
- //# sourceMappingURL=detect.js.map