@fiodos/cli 0.1.21 → 0.1.23
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/package.json +2 -1
- package/src/aiAnalyze.js +151 -274
- package/src/changeRegistry.js +2 -5
- package/src/index.js +202 -2
- package/src/wireHandlers.js +98 -34
- package/src/wireReactNative.js +69 -4
- package/src/wireWebMount.js +4 -0
- package/src/llm.js +0 -144
package/src/llm.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 3 — EXPLICIT LLM calls inside the pipeline.
|
|
3
|
-
*
|
|
4
|
-
* The static layers (1+2) extract facts; this layer makes judgment calls the
|
|
5
|
-
* code cannot express, and every call is LOGGED so the final report can show
|
|
6
|
-
* exactly which decisions came from the model vs from static analysis:
|
|
7
|
-
*
|
|
8
|
-
* 1. classifyApp — app type vs the pattern catalog
|
|
9
|
-
* 2. curateActions — which detected callables deserve agent exposure,
|
|
10
|
-
* category, sensitivity, parameters, label
|
|
11
|
-
* 3. writeRouteMeta — intent ids, labels and example phrases for routes
|
|
12
|
-
* 4. patternGapFill — given the matched pattern, propose what's missing
|
|
13
|
-
*
|
|
14
|
-
* Model: gpt-4o-mini via the OpenAI REST API (OPENAI_API_KEY env var).
|
|
15
|
-
*/
|
|
16
|
-
'use strict';
|
|
17
|
-
|
|
18
|
-
const LLM_LOG = [];
|
|
19
|
-
|
|
20
|
-
function getLog() {
|
|
21
|
-
return LLM_LOG;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function callLLM(taskName, system, user, { maxTokens = 2000 } = {}) {
|
|
25
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
26
|
-
if (!apiKey) throw new Error('OPENAI_API_KEY is required for the LLM layer');
|
|
27
|
-
|
|
28
|
-
const t0 = Date.now();
|
|
29
|
-
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
30
|
-
method: 'POST',
|
|
31
|
-
headers: {
|
|
32
|
-
'Content-Type': 'application/json',
|
|
33
|
-
Authorization: `Bearer ${apiKey}`,
|
|
34
|
-
},
|
|
35
|
-
body: JSON.stringify({
|
|
36
|
-
model: process.env.AUTO_MANIFEST_MODEL || 'gpt-4o-mini',
|
|
37
|
-
messages: [
|
|
38
|
-
{ role: 'system', content: system },
|
|
39
|
-
{ role: 'user', content: user },
|
|
40
|
-
],
|
|
41
|
-
temperature: 0.2,
|
|
42
|
-
max_tokens: maxTokens,
|
|
43
|
-
response_format: { type: 'json_object' },
|
|
44
|
-
}),
|
|
45
|
-
});
|
|
46
|
-
if (!res.ok) {
|
|
47
|
-
const body = await res.text();
|
|
48
|
-
throw new Error(`LLM call '${taskName}' failed: ${res.status} ${body.slice(0, 300)}`);
|
|
49
|
-
}
|
|
50
|
-
const data = await res.json();
|
|
51
|
-
const content = data.choices?.[0]?.message?.content || '{}';
|
|
52
|
-
let parsed;
|
|
53
|
-
try {
|
|
54
|
-
parsed = JSON.parse(content);
|
|
55
|
-
} catch {
|
|
56
|
-
throw new Error(`LLM call '${taskName}' returned non-JSON output`);
|
|
57
|
-
}
|
|
58
|
-
LLM_LOG.push({
|
|
59
|
-
task: taskName,
|
|
60
|
-
ms: Date.now() - t0,
|
|
61
|
-
promptChars: system.length + user.length,
|
|
62
|
-
usage: data.usage || null,
|
|
63
|
-
});
|
|
64
|
-
return parsed;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** 1. Classify the app against the available pattern catalog. */
|
|
68
|
-
async function classifyApp(appSummary, patternSummaries) {
|
|
69
|
-
const system =
|
|
70
|
-
'You classify a mobile app into one of the available integration patterns, or "none". ' +
|
|
71
|
-
'Answer JSON: {"pattern": "<id-or-none>", "confidence": 0..1, "appKind": "<free text>", "reason": "<short>"}';
|
|
72
|
-
const user =
|
|
73
|
-
`Available patterns:\n${patternSummaries.map((p) => `- id=${p.id}: ${p.summary}`).join('\n')}\n\n` +
|
|
74
|
-
`App evidence (from static analysis):\n${JSON.stringify(appSummary, null, 1).slice(0, 4000)}`;
|
|
75
|
-
return callLLM('classifyApp', system, user, { maxTokens: 300 });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 2. Curate detected callables into agent actions.
|
|
80
|
-
* Batched: large candidate lists are processed in chunks so no candidate is
|
|
81
|
-
* silently dropped by prompt-size truncation.
|
|
82
|
-
*/
|
|
83
|
-
async function curateActions(appKind, candidates) {
|
|
84
|
-
const BATCH = 14;
|
|
85
|
-
const all = [];
|
|
86
|
-
for (let i = 0; i < candidates.length; i += BATCH) {
|
|
87
|
-
const chunk = candidates.slice(i, i + BATCH);
|
|
88
|
-
const res = await curateActionsBatch(appKind, chunk);
|
|
89
|
-
all.push(...(res.actions || []));
|
|
90
|
-
}
|
|
91
|
-
return { actions: all };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function curateActionsBatch(appKind, candidates) {
|
|
95
|
-
const system =
|
|
96
|
-
'You design the action surface of an in-app voice agent. Input: callables detected by static ' +
|
|
97
|
-
'analysis of a mobile app (handlers, store actions, buttons, alerts). For EACH candidate decide:\n' +
|
|
98
|
-
'- expose: should the voice agent be able to trigger it? (skip pure-UI plumbing like toggling pickers, ' +
|
|
99
|
-
'rendering callbacks, backdrop handlers, text-input onChange)\n' +
|
|
100
|
-
'- intent: snake_case id; label: short human label (English); category: one of discovery|workout|history|account|navigation|other\n' +
|
|
101
|
-
'- requireConfirmation: true only for destructive/sensitive operations (deleting data, losing progress, signing out, spending money)\n' +
|
|
102
|
-
'- confirmationMessage: required question if requireConfirmation\n' +
|
|
103
|
-
'- parameters: object of {name:{type,required,description}} ONLY when the user must provide a value by voice (e.g. a search query)\n' +
|
|
104
|
-
'- requiresVisibleContent: true when the action operates on an item the user is looking at\n' +
|
|
105
|
-
'- examples: 3-4 natural English voice phrases\n' +
|
|
106
|
-
'- evidence_used: which input evidence justified your decision\n' +
|
|
107
|
-
'Be conservative: when a candidate looks like internal plumbing, expose=false. ' +
|
|
108
|
-
'Sign-in/sign-up/verification flows requiring typed credentials should be expose=false ' +
|
|
109
|
-
'(a voice agent cannot dictate passwords safely).\n' +
|
|
110
|
-
'Return a decision for EVERY candidate. "intent" MUST be a plain string.\n' +
|
|
111
|
-
'Answer JSON: {"actions": [{...candidate fields above, "source_name": "<input name>"}]}';
|
|
112
|
-
const user =
|
|
113
|
-
`App kind: ${appKind}\n\nCandidates (with static evidence):\n` +
|
|
114
|
-
JSON.stringify(candidates, null, 1).slice(0, 14000);
|
|
115
|
-
return callLLM('curateActions', system, user, { maxTokens: 4000 });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** 3. Route intents, labels, examples. */
|
|
119
|
-
async function writeRouteMeta(appKind, routes) {
|
|
120
|
-
const system =
|
|
121
|
-
'You name navigation intents for an in-app voice agent. For each route give: ' +
|
|
122
|
-
'intent (snake_case), label (short English), examples (3-4 natural English voice phrases). ' +
|
|
123
|
-
'Skip auth screens the agent should not navigate to mid-session if any seem like sign-in/sign-up (mark skip=true). ' +
|
|
124
|
-
'For dynamic routes ([param]) mark dynamic=true and skip=true (they need a runtime parameter the static manifest cannot provide). ' +
|
|
125
|
-
'Answer JSON: {"routes": [{"routePath": "...", "intent": "...", "label": "...", "examples": [...], "skip": false, "skipReason": ""}]}';
|
|
126
|
-
const user = `App kind: ${appKind}\n\nRoutes:\n${JSON.stringify(routes, null, 1)}`;
|
|
127
|
-
return callLLM('writeRouteMeta', system, user, { maxTokens: 2500 });
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** 4. Pattern gap-fill: what does the matched pattern expect that we missed? */
|
|
131
|
-
async function patternGapFill(patternId, patternText, draftManifestSummary) {
|
|
132
|
-
const system =
|
|
133
|
-
'You compare a draft voice-agent manifest against an integration pattern document. ' +
|
|
134
|
-
'List actions/routes the pattern suggests SHOULD usually exist for this app type but are missing or ' +
|
|
135
|
-
'under-specified in the draft. Only suggest things genuinely implied by the pattern; do not invent. ' +
|
|
136
|
-
'Answer JSON: {"suggestions": [{"kind": "action"|"route", "intent": "...", "label": "...", "why": "...", ' +
|
|
137
|
-
'"confidence": 0..1}], "patternUseful": true|false, "notes": "<honest assessment of how much this pattern helped>"}';
|
|
138
|
-
const user =
|
|
139
|
-
`Pattern (${patternId}):\n${patternText.slice(0, 5000)}\n\nDraft manifest summary:\n` +
|
|
140
|
-
JSON.stringify(draftManifestSummary, null, 1).slice(0, 5000);
|
|
141
|
-
return callLLM('patternGapFill', system, user, { maxTokens: 1200 });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
module.exports = { classifyApp, curateActions, writeRouteMeta, patternGapFill, getLog };
|