@moda-ai/cli 1.55.9 → 1.55.11
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/agent-n0ch2j90.js +459 -0
- package/dist/cli-11fevr8y.js +811 -0
- package/dist/{cli-mx9r1689.js → cli-429zt7g4.js} +1 -65
- package/dist/cli-4qe6n8am.js +345 -0
- package/dist/cli-97fnztpy.js +140 -0
- package/dist/{cli-sjc132j8.js → cli-fq7kk77e.js} +18 -1733
- package/dist/cli-fwec3m42.js +946 -0
- package/dist/cli-g7ferymy.js +496 -0
- package/dist/cli-k5qqj2bp.js +70 -0
- package/dist/cli-m16taf4z.js +30 -0
- package/dist/{cli-4af3thx7.js → cli-m857n028.js} +195 -976
- package/dist/{cli-fhfdnvh4.js → cli-n9gexgma.js} +2 -2
- package/dist/{cli-tw8jj190.js → cli-t4jjhn3g.js} +5 -7
- package/dist/cli-tmqd1f0m.js +159 -0
- package/dist/cli.js +1410 -7193
- package/dist/doctor-spvy2zb3.js +23 -0
- package/dist/fix-ax6bbg37.js +1444 -0
- package/dist/{harness-github-actions-73a08nct.js → harness-github-actions-mtksqp3c.js} +7 -2
- package/dist/{harness-yshh2pgj.js → harness-jbqpbp6n.js} +7 -2
- package/dist/{index-f3k9jgrt.js → index-2d0q94sv.js} +35 -36
- package/dist/production-intelligence-1tf98n79.js +1802 -0
- package/dist/profile-commands-f6599bcx.js +481 -0
- package/dist/prompt-source-mcp.js +8 -3
- package/dist/prompts-y3xd9heh.js +40 -0
- package/dist/{provision-parsn3xg.js → provision-cjpsswa9.js} +7 -5
- package/dist/registry-1dvxsekd.js +1201 -0
- package/dist/skills-1k0hbkaf.js +19 -0
- package/dist/sync-by81xqk4.js +54 -0
- package/package.json +6 -7
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +2 -2
- package/hooks/moda-snapshot.sh +0 -41
|
@@ -1871,68 +1871,4 @@ function extractSuggestedCommands(text) {
|
|
|
1871
1871
|
return commands.length > 0 ? commands : undefined;
|
|
1872
1872
|
}
|
|
1873
1873
|
|
|
1874
|
-
|
|
1875
|
-
function flagEnabled(flags, key) {
|
|
1876
|
-
return flags[key] === "true";
|
|
1877
|
-
}
|
|
1878
|
-
var BOOLEAN_TRUE_WORDS = new Set(["true", "yes", "1", "on"]);
|
|
1879
|
-
var BOOLEAN_FALSE_WORDS = new Set(["false", "no", "0", "off"]);
|
|
1880
|
-
function parseBooleanFlagValue(value) {
|
|
1881
|
-
if (typeof value === "boolean")
|
|
1882
|
-
return value;
|
|
1883
|
-
if (typeof value === "number")
|
|
1884
|
-
return value === 1 ? true : value === 0 ? false : undefined;
|
|
1885
|
-
if (typeof value !== "string")
|
|
1886
|
-
return;
|
|
1887
|
-
const text = value.trim().toLowerCase();
|
|
1888
|
-
if (BOOLEAN_TRUE_WORDS.has(text))
|
|
1889
|
-
return true;
|
|
1890
|
-
if (BOOLEAN_FALSE_WORDS.has(text))
|
|
1891
|
-
return false;
|
|
1892
|
-
return;
|
|
1893
|
-
}
|
|
1894
|
-
function stringOption(value) {
|
|
1895
|
-
if (value === undefined || value === null)
|
|
1896
|
-
return;
|
|
1897
|
-
const text = String(value).trim();
|
|
1898
|
-
return text.length > 0 ? text : undefined;
|
|
1899
|
-
}
|
|
1900
|
-
function createCommandContext(input, options = {}) {
|
|
1901
|
-
const flags = options.commandFlags ?? input.flags;
|
|
1902
|
-
const rawFlags = options.rawFlags ?? input.flags;
|
|
1903
|
-
const outputMode = options.outputMode ?? outputModeFromFlags(flags);
|
|
1904
|
-
const quiet = flagEnabled(flags, "quiet");
|
|
1905
|
-
const interactive = outputMode === "human" && !quiet && !flagEnabled(flags, "yes") && process.stdin.isTTY === true && process.stderr.isTTY === true;
|
|
1906
|
-
const telemetryMeta = {};
|
|
1907
|
-
return {
|
|
1908
|
-
command: options.commandName ?? input.command,
|
|
1909
|
-
positional: input.positional,
|
|
1910
|
-
positionals: input.positionals,
|
|
1911
|
-
flags,
|
|
1912
|
-
rawFlags,
|
|
1913
|
-
...input.bareFlags ? { bareFlags: input.bareFlags } : {},
|
|
1914
|
-
startedAt: options.startedAt ?? Date.now(),
|
|
1915
|
-
cwd: options.cwd ?? process.cwd(),
|
|
1916
|
-
env: options.env ?? process.env,
|
|
1917
|
-
outputMode,
|
|
1918
|
-
output: createCliOutput({
|
|
1919
|
-
command: options.commandName ?? input.command,
|
|
1920
|
-
mode: outputMode,
|
|
1921
|
-
quiet
|
|
1922
|
-
}),
|
|
1923
|
-
profile: flags.profile,
|
|
1924
|
-
dryRun: flagEnabled(flags, "dry-run"),
|
|
1925
|
-
yes: flagEnabled(flags, "yes"),
|
|
1926
|
-
quiet,
|
|
1927
|
-
verbose: flagEnabled(flags, "verbose"),
|
|
1928
|
-
interactive,
|
|
1929
|
-
recordTelemetry(meta) {
|
|
1930
|
-
Object.assign(telemetryMeta, meta);
|
|
1931
|
-
},
|
|
1932
|
-
getTelemetryMeta() {
|
|
1933
|
-
return telemetryMeta;
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, assertValidProfileName, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliCancellationError, isNetworkLikeError, isApiLikeError, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, recordApiRequest, getApiRequestCount, resetApiRequestCount, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, resolveOutputMode, createCliOutput, agentEnvelopeFits, agentMaxOutputBytes, parseBooleanFlagValue, stringOption, createCommandContext };
|
|
1874
|
+
export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliCancellationError, isNetworkLikeError, isApiLikeError, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, recordApiRequest, getApiRequestCount, resetApiRequestCount, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, agentMaxOutputBytes };
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// src/transforms.ts
|
|
2
|
+
function asRecord(value) {
|
|
3
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
function asString(value) {
|
|
9
|
+
return typeof value === "string" ? value : null;
|
|
10
|
+
}
|
|
11
|
+
function asNumber(value) {
|
|
12
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
13
|
+
}
|
|
14
|
+
function frustrationAnchorIndex(row) {
|
|
15
|
+
if (!row)
|
|
16
|
+
return null;
|
|
17
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
18
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
19
|
+
const resolved = asNumber(firstQuote?.msg_index) ?? asNumber(row.anchor_msg_index);
|
|
20
|
+
if (resolved !== null)
|
|
21
|
+
return resolved;
|
|
22
|
+
const keyTurns = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
23
|
+
const firstTurn = keyTurns.find((t) => typeof t === "number");
|
|
24
|
+
if (firstTurn !== undefined)
|
|
25
|
+
return firstTurn;
|
|
26
|
+
return asNumber(firstQuote?.turn);
|
|
27
|
+
}
|
|
28
|
+
function buildFrustrationAnchor(row) {
|
|
29
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
30
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
31
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
32
|
+
const keyTurnsRaw = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
33
|
+
const all_turns = keyTurnsRaw.filter((t) => typeof t === "number");
|
|
34
|
+
const msg_index = frustrationAnchorIndex(row);
|
|
35
|
+
let signal = null;
|
|
36
|
+
let quote_preview = null;
|
|
37
|
+
if (firstQuote) {
|
|
38
|
+
signal = asString(firstQuote.signal);
|
|
39
|
+
const quote = asString(firstQuote.quote);
|
|
40
|
+
if (quote !== null) {
|
|
41
|
+
let end = 0;
|
|
42
|
+
let points = 0;
|
|
43
|
+
for (const ch of quote) {
|
|
44
|
+
if (points++ === 200)
|
|
45
|
+
break;
|
|
46
|
+
end += ch.length;
|
|
47
|
+
}
|
|
48
|
+
quote_preview = quote.slice(0, end);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
kind: "frustration",
|
|
53
|
+
conversation_id,
|
|
54
|
+
msg_index,
|
|
55
|
+
signal,
|
|
56
|
+
quote_preview,
|
|
57
|
+
all_turns,
|
|
58
|
+
no_anchor: msg_index === null
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function buildToolFailureAnchor(row, toolName) {
|
|
62
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
63
|
+
const msg_index = asNumber(row.msg_index);
|
|
64
|
+
const tool_use_id = asString(row.tool_use_id);
|
|
65
|
+
const error_subtype = asString(row.error_subtype);
|
|
66
|
+
return {
|
|
67
|
+
kind: "tool_failure",
|
|
68
|
+
conversation_id,
|
|
69
|
+
msg_index,
|
|
70
|
+
tool_name: toolName,
|
|
71
|
+
tool_use_id,
|
|
72
|
+
error_subtype,
|
|
73
|
+
no_anchor: msg_index === null
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function frustrationsFromEmotions(response) {
|
|
77
|
+
if (!Array.isArray(response.detections) || Array.isArray(response.frustrations))
|
|
78
|
+
return response;
|
|
79
|
+
const summary = asRecord(response.summary) ?? {};
|
|
80
|
+
const pagination = asRecord(response.pagination) ?? {};
|
|
81
|
+
const byFamily = asRecord(summary.by_family) ?? {};
|
|
82
|
+
const totalAnalyzed = asNumber(summary.conversations_analyzed) ?? 0;
|
|
83
|
+
const byFamilyConversations = asRecord(summary.conversations_by_family);
|
|
84
|
+
const frustrated = asNumber(byFamilyConversations?.frustration) ?? asNumber(byFamily.frustration) ?? 0;
|
|
85
|
+
const frustrations = response.detections.map((item) => asRecord(item)).filter((row) => row !== null && (row.family === undefined || row.family === "frustration")).map(({ conversation_context, score, is_detected, ...row }) => ({
|
|
86
|
+
...row,
|
|
87
|
+
is_frustrated: is_detected === true,
|
|
88
|
+
frustration_score: score,
|
|
89
|
+
target: row.elicitor === "bot" || row.elicitor === "external" ? row.elicitor : "ambiguous",
|
|
90
|
+
conversation: conversation_context ?? null
|
|
91
|
+
}));
|
|
92
|
+
const atRisk = asNumber(pagination.at_risk) ?? Math.max(0, (asNumber(pagination.total) ?? 0) - frustrated);
|
|
93
|
+
const signalBreakdown = {};
|
|
94
|
+
for (const item of Array.isArray(response.signal_breakdown) ? response.signal_breakdown : []) {
|
|
95
|
+
const row = asRecord(item);
|
|
96
|
+
if (row?.family === "frustration" && typeof row.signal === "string") {
|
|
97
|
+
signalBreakdown[row.signal] = asNumber(row.count) ?? 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const { detections: _detections, ...rest } = response;
|
|
101
|
+
return {
|
|
102
|
+
...rest,
|
|
103
|
+
summary: {
|
|
104
|
+
total_analyzed: totalAnalyzed,
|
|
105
|
+
frustrated_count: frustrated,
|
|
106
|
+
at_risk_count: atRisk,
|
|
107
|
+
frustration_rate_pct: totalAnalyzed > 0 ? Math.round(frustrated / totalAnalyzed * 1000) / 10 : 0
|
|
108
|
+
},
|
|
109
|
+
signal_breakdown: signalBreakdown,
|
|
110
|
+
frustrations,
|
|
111
|
+
source: "emotion_detections"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function withFrustrationAnchors(response) {
|
|
115
|
+
if (!Array.isArray(response.frustrations))
|
|
116
|
+
return response;
|
|
117
|
+
const frustrations = response.frustrations.map((item) => {
|
|
118
|
+
const row = asRecord(item);
|
|
119
|
+
if (!row)
|
|
120
|
+
return item;
|
|
121
|
+
const anchor = buildFrustrationAnchor(row);
|
|
122
|
+
return { ...row, anchor };
|
|
123
|
+
});
|
|
124
|
+
return { ...response, frustrations };
|
|
125
|
+
}
|
|
126
|
+
var TOOL_FAILURE_CONTEXT_RADIUS = 2;
|
|
127
|
+
function shellWord(value) {
|
|
128
|
+
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
129
|
+
}
|
|
130
|
+
function trimFailureWindow(value, radius, fallbackCenter) {
|
|
131
|
+
const conv = asRecord(value);
|
|
132
|
+
if (!conv)
|
|
133
|
+
return value ?? null;
|
|
134
|
+
const { coverage: _coverage, context, ...convRest } = conv;
|
|
135
|
+
const ctx = asRecord(context);
|
|
136
|
+
if (!ctx)
|
|
137
|
+
return convRest;
|
|
138
|
+
const center = asNumber(ctx.center_index) ?? fallbackCenter;
|
|
139
|
+
const messages = (Array.isArray(ctx.messages) ? ctx.messages : []).map((message) => asRecord(message)).filter((message) => {
|
|
140
|
+
const index = asNumber(message?.index);
|
|
141
|
+
return message !== null && (center === null || index === null || Math.abs(index - center) <= radius);
|
|
142
|
+
}).map(({ content_blocks, ...message }) => {
|
|
143
|
+
const covered = typeof message.content === "string" && message.content.length > 0 || Array.isArray(message.tool_calls) && message.tool_calls.length > 0 || Array.isArray(message.tool_results) && message.tool_results.length > 0;
|
|
144
|
+
return covered || content_blocks === undefined ? message : { ...message, content_blocks };
|
|
145
|
+
});
|
|
146
|
+
const indexes = messages.map((message) => asNumber(message.index)).filter((index) => index !== null);
|
|
147
|
+
return {
|
|
148
|
+
...convRest,
|
|
149
|
+
context: {
|
|
150
|
+
...ctx,
|
|
151
|
+
...indexes.length > 0 ? { from_index: Math.min(...indexes), to_index: Math.max(...indexes) } : {},
|
|
152
|
+
messages
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function trimToolFailureConversations(response, opts) {
|
|
157
|
+
if (!Array.isArray(response.examples))
|
|
158
|
+
return response;
|
|
159
|
+
const examples = response.examples.map((item) => {
|
|
160
|
+
const row = asRecord(item);
|
|
161
|
+
if (!row)
|
|
162
|
+
return item;
|
|
163
|
+
const { conversation, window, ...rest } = row;
|
|
164
|
+
const conversationId = asString(row.conversation_id);
|
|
165
|
+
const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? asNumber(row.msg_index);
|
|
166
|
+
return {
|
|
167
|
+
...rest,
|
|
168
|
+
...conversationId && msgIndex !== null ? { context_command: `moda context ${shellWord(conversationId)} --msg-index=${msgIndex} --window=${TOOL_FAILURE_CONTEXT_RADIUS}` } : {},
|
|
169
|
+
...opts.drop ? {} : { conversation: trimFailureWindow(conversation, TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) },
|
|
170
|
+
..."window" in row ? { window: trimFailureWindow(window, opts.windowRadius ?? TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) } : {}
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
return { ...response, examples };
|
|
174
|
+
}
|
|
175
|
+
function withToolFailureAnchors(response) {
|
|
176
|
+
if (!Array.isArray(response.examples))
|
|
177
|
+
return response;
|
|
178
|
+
const toolName = asString(response.tool_name) ?? "";
|
|
179
|
+
const examples = response.examples.map((item) => {
|
|
180
|
+
const row = asRecord(item);
|
|
181
|
+
if (!row)
|
|
182
|
+
return item;
|
|
183
|
+
const anchor = buildToolFailureAnchor(row, toolName);
|
|
184
|
+
return { ...row, anchor };
|
|
185
|
+
});
|
|
186
|
+
return { ...response, examples };
|
|
187
|
+
}
|
|
188
|
+
function embeddedWindowSize(conversation) {
|
|
189
|
+
if (!conversation)
|
|
190
|
+
return null;
|
|
191
|
+
const ctx = asRecord(conversation.context);
|
|
192
|
+
if (!ctx)
|
|
193
|
+
return null;
|
|
194
|
+
const center = asNumber(ctx.center_index);
|
|
195
|
+
const from = asNumber(ctx.from_index);
|
|
196
|
+
const to = asNumber(ctx.to_index);
|
|
197
|
+
if (center === null || from === null || to === null)
|
|
198
|
+
return null;
|
|
199
|
+
return Math.min(center - from, to - center);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/intelligence-schema.ts
|
|
203
|
+
var INTELLIGENCE_SCHEMA_VERSION = "moda.intelligence.v1";
|
|
204
|
+
var provenanceSchema = { type: "string", enum: ["measured", "inferred"] };
|
|
205
|
+
var mutabilitySchema = {
|
|
206
|
+
type: "string",
|
|
207
|
+
enum: ["read", "write", "sync", "unknown"]
|
|
208
|
+
};
|
|
209
|
+
var confidenceSchema = {
|
|
210
|
+
type: "string",
|
|
211
|
+
enum: ["high", "medium", "low", "unknown"]
|
|
212
|
+
};
|
|
213
|
+
var MODA_INTELLIGENCE_V1_JSON_SCHEMA = {
|
|
214
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
215
|
+
$id: "moda.intelligence.v1",
|
|
216
|
+
title: "Moda Intelligence v1",
|
|
217
|
+
type: "object",
|
|
218
|
+
additionalProperties: false,
|
|
219
|
+
required: [
|
|
220
|
+
"schema_version",
|
|
221
|
+
"generated_at",
|
|
222
|
+
"command",
|
|
223
|
+
"source",
|
|
224
|
+
"degraded",
|
|
225
|
+
"question",
|
|
226
|
+
"summary",
|
|
227
|
+
"answer_markdown",
|
|
228
|
+
"answer_claims",
|
|
229
|
+
"findings",
|
|
230
|
+
"evidence_refs",
|
|
231
|
+
"next_commands",
|
|
232
|
+
"warnings"
|
|
233
|
+
],
|
|
234
|
+
properties: {
|
|
235
|
+
schema_version: { const: "moda.intelligence.v1" },
|
|
236
|
+
generated_at: { type: "string", format: "date-time" },
|
|
237
|
+
command: { type: "string" },
|
|
238
|
+
source: { type: "string", enum: ["cloud", "local_fallback"] },
|
|
239
|
+
degraded: { type: "boolean" },
|
|
240
|
+
question: { type: "string" },
|
|
241
|
+
summary: {
|
|
242
|
+
type: "object",
|
|
243
|
+
additionalProperties: false,
|
|
244
|
+
required: ["text", "confidence"],
|
|
245
|
+
properties: { text: { type: "string" }, confidence: confidenceSchema }
|
|
246
|
+
},
|
|
247
|
+
answer_markdown: { type: "string" },
|
|
248
|
+
answer_claims: {
|
|
249
|
+
type: "array",
|
|
250
|
+
items: {
|
|
251
|
+
type: "object",
|
|
252
|
+
additionalProperties: false,
|
|
253
|
+
required: ["text", "kind", "status", "evidence_ref_ids"],
|
|
254
|
+
properties: {
|
|
255
|
+
text: { type: "string" },
|
|
256
|
+
value: { type: "number" },
|
|
257
|
+
kind: { type: "string", enum: ["percentage", "count", "qualitative"] },
|
|
258
|
+
status: {
|
|
259
|
+
type: "string",
|
|
260
|
+
enum: ["grounded", "ungrounded", "unverifiable"]
|
|
261
|
+
},
|
|
262
|
+
evidence_ref_ids: { type: "array", items: { type: "string" } },
|
|
263
|
+
matched_path: { type: "string" }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
findings: {
|
|
268
|
+
type: "array",
|
|
269
|
+
items: {
|
|
270
|
+
type: "object",
|
|
271
|
+
additionalProperties: false,
|
|
272
|
+
required: ["id", "title", "evidence_ref_ids", "recommended_actions"],
|
|
273
|
+
properties: {
|
|
274
|
+
id: { type: "string" },
|
|
275
|
+
title: { type: "string" },
|
|
276
|
+
severity: {
|
|
277
|
+
type: "string",
|
|
278
|
+
enum: ["critical", "high", "medium", "low", "info"]
|
|
279
|
+
},
|
|
280
|
+
description: { type: "string" },
|
|
281
|
+
evidence_ref_ids: { type: "array", items: { type: "string" } },
|
|
282
|
+
recommended_actions: {
|
|
283
|
+
type: "array",
|
|
284
|
+
items: {
|
|
285
|
+
type: "object",
|
|
286
|
+
additionalProperties: false,
|
|
287
|
+
required: ["id", "title", "mutability", "requires_approval"],
|
|
288
|
+
properties: {
|
|
289
|
+
id: { type: "string" },
|
|
290
|
+
title: { type: "string" },
|
|
291
|
+
rationale: { type: "string" },
|
|
292
|
+
command: { type: "string" },
|
|
293
|
+
mutability: mutabilitySchema,
|
|
294
|
+
requires_approval: { type: "boolean" }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
evidence_refs: {
|
|
302
|
+
type: "array",
|
|
303
|
+
items: {
|
|
304
|
+
type: "object",
|
|
305
|
+
additionalProperties: false,
|
|
306
|
+
required: ["id", "kind", "label", "grounding"],
|
|
307
|
+
properties: {
|
|
308
|
+
id: { type: "string" },
|
|
309
|
+
kind: { type: "string" },
|
|
310
|
+
label: { type: "string" },
|
|
311
|
+
anchor: {
|
|
312
|
+
type: "object",
|
|
313
|
+
additionalProperties: false,
|
|
314
|
+
properties: {
|
|
315
|
+
conversation_id: { type: "string" },
|
|
316
|
+
message_index: { type: "number" },
|
|
317
|
+
run_id: { type: "string" },
|
|
318
|
+
tool_name: { type: "string" }
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
snippet: { type: "string" },
|
|
322
|
+
grounding: provenanceSchema
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
next_commands: {
|
|
327
|
+
type: "array",
|
|
328
|
+
items: {
|
|
329
|
+
type: "object",
|
|
330
|
+
additionalProperties: false,
|
|
331
|
+
required: ["command", "mutability", "requires_approval"],
|
|
332
|
+
properties: {
|
|
333
|
+
command: { type: "string" },
|
|
334
|
+
purpose: { type: "string" },
|
|
335
|
+
mutability: mutabilitySchema,
|
|
336
|
+
requires_approval: { type: "boolean" }
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
warnings: { type: "array", items: { type: "string" } },
|
|
341
|
+
meta: { type: "object" }
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export { asRecord, asString, asNumber, frustrationAnchorIndex, frustrationsFromEmotions, withFrustrationAnchors, shellWord, trimToolFailureConversations, withToolFailureAnchors, embeddedWindowSize, INTELLIGENCE_SCHEMA_VERSION, MODA_INTELLIGENCE_V1_JSON_SCHEMA };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/markdown-terminal.ts
|
|
2
|
+
function shouldColorTerminalOutput(opts = {}) {
|
|
3
|
+
const env = opts.env ?? process.env;
|
|
4
|
+
const isTTY = opts.isTTY ?? process.stdout.isTTY === true;
|
|
5
|
+
if (!isTTY)
|
|
6
|
+
return false;
|
|
7
|
+
if (env.NO_COLOR !== undefined)
|
|
8
|
+
return false;
|
|
9
|
+
if (env.TERM === "dumb")
|
|
10
|
+
return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
function terminalStyles(color) {
|
|
14
|
+
const paint = (open, close) => (text) => color ? `\x1B[${open}m${text}\x1B[${close}m` : text;
|
|
15
|
+
return {
|
|
16
|
+
bold: paint("1", "22"),
|
|
17
|
+
italic: paint("3", "23"),
|
|
18
|
+
dim: paint("2", "22"),
|
|
19
|
+
code: paint("36", "39"),
|
|
20
|
+
heading: paint("1;4", "22;24")
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
var FENCE_RE = /^\s*(`{3,}|~{3,})/;
|
|
24
|
+
var FENCE_CLOSE_RE = /^\s*(`{3,}|~{3,})\s*$/;
|
|
25
|
+
var TABLE_ROW_RE = /^\s*\|.*\S/;
|
|
26
|
+
var ANSI_RE = /\u001b(?:\[[0-9;?]*[ -/]*[@-~]|\][^\u0007\u001b]*(?:\u0007|\u001b\\)|[@-Z\\-_])/g;
|
|
27
|
+
function stripAnsi(text) {
|
|
28
|
+
return text.replace(ANSI_RE, "");
|
|
29
|
+
}
|
|
30
|
+
function parseFenceOpen(line) {
|
|
31
|
+
const match = FENCE_RE.exec(line);
|
|
32
|
+
if (!match)
|
|
33
|
+
return null;
|
|
34
|
+
return { char: match[1][0], len: match[1].length };
|
|
35
|
+
}
|
|
36
|
+
function isFenceClose(line, fence) {
|
|
37
|
+
const match = FENCE_CLOSE_RE.exec(line);
|
|
38
|
+
return match !== null && match[1][0] === fence.char && match[1].length >= fence.len;
|
|
39
|
+
}
|
|
40
|
+
var HEADER_RE = /^(#{1,6})\s+(.*?)\s*#*\s*$/;
|
|
41
|
+
var BULLET_RE = /^(\s*)([-*+])\s+(.*)$/;
|
|
42
|
+
var ORDERED_RE = /^(\s*)(\d+)[.)]\s+(.*)$/;
|
|
43
|
+
function renderMarkdownForTerminal(markdown, opts) {
|
|
44
|
+
const style = terminalStyles(opts.color);
|
|
45
|
+
const out = [];
|
|
46
|
+
const listStack = [];
|
|
47
|
+
let fence = null;
|
|
48
|
+
for (const raw of markdown.replace(/\r\n/g, `
|
|
49
|
+
`).split(`
|
|
50
|
+
`)) {
|
|
51
|
+
if (fence) {
|
|
52
|
+
out.push(stripAnsi(raw));
|
|
53
|
+
if (isFenceClose(raw, fence))
|
|
54
|
+
fence = null;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const opened = parseFenceOpen(raw);
|
|
58
|
+
if (opened) {
|
|
59
|
+
fence = opened;
|
|
60
|
+
listStack.length = 0;
|
|
61
|
+
out.push(stripAnsi(raw));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (TABLE_ROW_RE.test(raw)) {
|
|
65
|
+
out.push(stripAnsi(raw));
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const line = raw.replace(/\s+$/, "");
|
|
69
|
+
if (line === "") {
|
|
70
|
+
out.push("");
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const header = HEADER_RE.exec(line);
|
|
74
|
+
if (header) {
|
|
75
|
+
listStack.length = 0;
|
|
76
|
+
const text = renderInline(header[2], terminalStyles(false));
|
|
77
|
+
if (out.length > 0 && out[out.length - 1] !== "")
|
|
78
|
+
out.push("");
|
|
79
|
+
out.push(header[1].length <= 2 ? style.heading(text) : style.bold(text));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const bullet = BULLET_RE.exec(line);
|
|
83
|
+
const ordered = bullet ? null : ORDERED_RE.exec(line);
|
|
84
|
+
if (bullet || ordered) {
|
|
85
|
+
const match = bullet ?? ordered;
|
|
86
|
+
const level = resolveListLevel(match[1].length, listStack);
|
|
87
|
+
const indent = " ".repeat(level);
|
|
88
|
+
const text = renderInline(match[3], style);
|
|
89
|
+
if (bullet) {
|
|
90
|
+
out.push(`${indent}${level === 0 ? "•" : "-"} ${text}`);
|
|
91
|
+
} else {
|
|
92
|
+
out.push(`${indent}${match[2]}. ${text}`);
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!/^\s{2,}/.test(line))
|
|
97
|
+
listStack.length = 0;
|
|
98
|
+
out.push(renderInline(line, style));
|
|
99
|
+
}
|
|
100
|
+
return collapseBlankRuns(out).join(`
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
function resolveListLevel(indent, stack) {
|
|
104
|
+
while (stack.length > 0 && indent < stack[stack.length - 1] - 1)
|
|
105
|
+
stack.pop();
|
|
106
|
+
if (stack.length === 0 || indent > stack[stack.length - 1] + 1)
|
|
107
|
+
stack.push(indent);
|
|
108
|
+
return stack.length - 1;
|
|
109
|
+
}
|
|
110
|
+
function renderInline(text, style) {
|
|
111
|
+
const codeSpans = [];
|
|
112
|
+
let result = stripAnsi(text).replace(/`([^`]+)`/g, (_, code) => {
|
|
113
|
+
codeSpans.push(code);
|
|
114
|
+
return `\x00${codeSpans.length - 1}\x00`;
|
|
115
|
+
});
|
|
116
|
+
result = result.replace(/\*\*([^\s*](?:[^*]*[^\s*])?)\*\*/g, (_, inner) => style.bold(inner));
|
|
117
|
+
result = result.replace(/__([^\s_](?:[^_]*[^\s_])?)__/g, (_, inner) => style.bold(inner));
|
|
118
|
+
result = result.replace(/(?<![\w*])\*([^\s*](?:[^*\n]*[^\s*])?)\*(?![\w*])/g, (_, inner) => style.italic(inner));
|
|
119
|
+
result = result.replace(/(?<![\w_])_([^\s_](?:[^_\n]*[^\s_])?)_(?![\w_])/g, (_, inner) => style.italic(inner));
|
|
120
|
+
return result.replace(/\u0000(\d+)\u0000/g, (_, index) => style.code(codeSpans[Number(index)]));
|
|
121
|
+
}
|
|
122
|
+
function collapseBlankRuns(lines) {
|
|
123
|
+
const collapsed = [];
|
|
124
|
+
let blanks = 0;
|
|
125
|
+
for (const line of lines) {
|
|
126
|
+
if (line === "") {
|
|
127
|
+
blanks += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (blanks > 0)
|
|
131
|
+
collapsed.push("", ...blanks === 2 ? [""] : []);
|
|
132
|
+
blanks = 0;
|
|
133
|
+
collapsed.push(line);
|
|
134
|
+
}
|
|
135
|
+
if (blanks > 0)
|
|
136
|
+
collapsed.push("", ...blanks === 2 ? [""] : []);
|
|
137
|
+
return collapsed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { shouldColorTerminalOutput, terminalStyles, stripAnsi, renderMarkdownForTerminal };
|