nothumanallowed 13.2.80 → 13.2.82
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 +1 -1
- package/src/commands/ui.mjs +89 -26
- package/src/constants.mjs +1 -1
- package/src/services/github.mjs +79 -0
- package/src/services/web-ui.mjs +10 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.2.
|
|
3
|
+
"version": "13.2.82",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -2692,7 +2692,7 @@ export async function cmdUI(args) {
|
|
|
2692
2692
|
|
|
2693
2693
|
// ── Fast keyword-based planning (no LLM call needed for common patterns) ──────
|
|
2694
2694
|
const taskLow = task.toLowerCase();
|
|
2695
|
-
const hasPdf = !!(body.hasPdf) || /pdf|allegat|catalogo|scheda\s*tecnic
|
|
2695
|
+
const hasPdf = !!(body.hasPdf) || /pdf|allegat|catalogo|scheda\s*tecnic/i.test(taskLow);
|
|
2696
2696
|
const hasEmail = /email|mail|inbox|posta/i.test(taskLow);
|
|
2697
2697
|
const hasCalendar = /calendar|agenda|calendari|eventi|schedule/i.test(taskLow);
|
|
2698
2698
|
const hasSearch = /cerca|search|notizie|news|ultime|latest|web|internet|tendenz|trend|acquista|compra|dove\s+trovare|where\s+to\s+buy|similar|simile/i.test(taskLow);
|
|
@@ -2958,12 +2958,30 @@ ${rawText.slice(0, 18000)}`;
|
|
|
2958
2958
|
// If there is document context from a previous step, ask the LLM to derive
|
|
2959
2959
|
// the optimal search queries. This is generic and works for any document/task.
|
|
2960
2960
|
let searchQueries = [stepPrompt.slice(0, 120)];
|
|
2961
|
-
|
|
2961
|
+
// Only use LLM query generation when context is a PDF/document (not previous agent text output).
|
|
2962
|
+
// When context is email/github output from a prior step, ignore it — use task + stepPrompt directly.
|
|
2963
|
+
const contextIsPdf = context && context.length > 50 && context.startsWith('## ATTACHED PDF');
|
|
2964
|
+
if (contextIsPdf) {
|
|
2962
2965
|
sendToken('[Building search queries from document...] ');
|
|
2963
2966
|
try {
|
|
2964
|
-
// Document context goes in system prompt — SENTINEL only scans user message
|
|
2965
2967
|
const queryPlanSys = `You are a search query generator. Given a document summary and a user task, output a JSON array of 1-3 concise web search queries (strings, max 80 chars each) that will find the best results. Output ONLY the JSON array, no explanation.\n\nDocument content:\n${context.slice(0, 3000)}`;
|
|
2966
|
-
const queryPlanUser = `User task: "${task.slice(0, 200)}". Generate search queries. If task asks for similar/alternative products use technical specs.
|
|
2968
|
+
const queryPlanUser = `User task: "${task.slice(0, 200)}". Generate search queries. If task asks for similar/alternative products use technical specs. Output: ["query1","query2",...]`;
|
|
2969
|
+
const planConfig2 = Object.assign({}, config, { thinking: 'off' });
|
|
2970
|
+
const queryRaw = await withTimeout(callLLM(planConfig2, queryPlanSys, queryPlanUser, { max_tokens: 200 }), 15000);
|
|
2971
|
+
const jsonMatch = queryRaw.match(/\[[\s\S]*?\]/);
|
|
2972
|
+
if (jsonMatch) {
|
|
2973
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2974
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
2975
|
+
searchQueries = parsed.filter(q => typeof q === 'string' && q.length > 2).slice(0, 3);
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
} catch {}
|
|
2979
|
+
} else {
|
|
2980
|
+
// No PDF — derive queries from task + stepPrompt using LLM for better queries
|
|
2981
|
+
sendToken('[Building search queries...] ');
|
|
2982
|
+
try {
|
|
2983
|
+
const queryPlanSys = `You are a search query generator. Given a user task and a search instruction, output a JSON array of 2-3 concise web search queries (strings, max 80 chars each). Focus on the specific topics in the task. Output ONLY the JSON array, no explanation.`;
|
|
2984
|
+
const queryPlanUser = `Task: "${task.slice(0, 300)}"\nSearch instruction: "${stepPrompt.slice(0, 200)}"\nOutput: ["query1","query2","query3"]`;
|
|
2967
2985
|
const planConfig2 = Object.assign({}, config, { thinking: 'off' });
|
|
2968
2986
|
const queryRaw = await withTimeout(callLLM(planConfig2, queryPlanSys, queryPlanUser, { max_tokens: 200 }), 15000);
|
|
2969
2987
|
const jsonMatch = queryRaw.match(/\[[\s\S]*?\]/);
|
|
@@ -2974,8 +2992,8 @@ ${rawText.slice(0, 18000)}`;
|
|
|
2974
2992
|
}
|
|
2975
2993
|
}
|
|
2976
2994
|
} catch {}
|
|
2977
|
-
sendToken(`[Queries: ${searchQueries.map(q => '"' + q + '"').join(', ')}] `);
|
|
2978
2995
|
}
|
|
2996
|
+
sendToken(`[Queries: ${searchQueries.map(q => '"' + q + '"').join(', ')}] `);
|
|
2979
2997
|
|
|
2980
2998
|
// Run all queries sequentially, accumulate results
|
|
2981
2999
|
for (let qi = 0; qi < searchQueries.length; qi++) {
|
|
@@ -3012,24 +3030,64 @@ ${rawText.slice(0, 18000)}`;
|
|
|
3012
3030
|
toolData = 'GitHub token not configured. Run: nha config set github-token YOUR_PAT';
|
|
3013
3031
|
} else {
|
|
3014
3032
|
const parts = [];
|
|
3015
|
-
//
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3033
|
+
// Extract repo from prompt or task (e.g. "owner/repo" pattern)
|
|
3034
|
+
const repoMatch = (stepPrompt + ' ' + task).match(/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
|
|
3035
|
+
const targetRepo = repoMatch ? repoMatch[1].replace(/[`'"]/g, '') : (config.github?.defaultRepo || '');
|
|
3036
|
+
|
|
3037
|
+
if (targetRepo) {
|
|
3038
|
+
sendToken(`[Analyzing ${targetRepo}...] `);
|
|
3039
|
+
// Repo metadata
|
|
3040
|
+
try {
|
|
3041
|
+
const info = await withTimeout(gh.getRepoInfo(config, targetRepo), 20000);
|
|
3042
|
+
parts.push(`## Repository: ${info.full_name}\n` +
|
|
3043
|
+
`- Description: ${info.description || 'none'}\n` +
|
|
3044
|
+
`- Stars: ${info.stars} | Forks: ${info.forks} | Watchers: ${info.watchers}\n` +
|
|
3045
|
+
`- Open issues: ${info.open_issues}\n` +
|
|
3046
|
+
`- Primary language: ${info.language}\n` +
|
|
3047
|
+
`- Topics: ${info.topics}\n` +
|
|
3048
|
+
`- License: ${info.license}\n` +
|
|
3049
|
+
`- Last push: ${info.pushed_at} | Created: ${info.created_at}\n` +
|
|
3050
|
+
`- Homepage: ${info.homepage || 'none'}\n` +
|
|
3051
|
+
`- Archived: ${info.archived}`);
|
|
3052
|
+
} catch (e) { parts.push(`## Repository ${targetRepo}\nCould not fetch repo info: ${e.message}`); }
|
|
3053
|
+
// Languages
|
|
3054
|
+
try {
|
|
3055
|
+
const langs = await withTimeout(gh.getRepoLanguages(config, targetRepo), 10000);
|
|
3056
|
+
if (langs) parts.push('## Languages\n' + langs);
|
|
3057
|
+
} catch {}
|
|
3058
|
+
// README
|
|
3059
|
+
try {
|
|
3060
|
+
const readme = await withTimeout(gh.getReadme(config, targetRepo), 15000);
|
|
3061
|
+
if (readme) parts.push('## README\n' + readme.slice(0, 3000));
|
|
3062
|
+
} catch {}
|
|
3063
|
+
// Recent commits
|
|
3023
3064
|
try {
|
|
3024
|
-
const
|
|
3025
|
-
if (
|
|
3026
|
-
} catch
|
|
3065
|
+
const commits = await withTimeout(gh.getRecentCommits(config, targetRepo, 10), 15000);
|
|
3066
|
+
if (commits) parts.push('## Recent Commits\n' + commits);
|
|
3067
|
+
} catch {}
|
|
3068
|
+
// Open issues
|
|
3027
3069
|
try {
|
|
3028
|
-
const
|
|
3029
|
-
if (
|
|
3030
|
-
} catch
|
|
3070
|
+
const issues = await withTimeout(gh.listIssues(config, targetRepo, 'open', 10), 15000);
|
|
3071
|
+
if (issues) parts.push('## Open Issues\n' + issues);
|
|
3072
|
+
} catch {}
|
|
3073
|
+
// Open PRs
|
|
3074
|
+
try {
|
|
3075
|
+
const prs = await withTimeout(gh.listPRs(config, targetRepo, 'open', 10), 15000);
|
|
3076
|
+
if (prs) parts.push('## Open Pull Requests\n' + prs);
|
|
3077
|
+
} catch {}
|
|
3078
|
+
// Contributors
|
|
3079
|
+
try {
|
|
3080
|
+
const contributors = await withTimeout(gh.getContributors(config, targetRepo, 10), 10000);
|
|
3081
|
+
if (contributors) parts.push('## Contributors\n' + contributors);
|
|
3082
|
+
} catch {}
|
|
3083
|
+
} else {
|
|
3084
|
+
// No specific repo — read notifications + user repos
|
|
3085
|
+
try {
|
|
3086
|
+
const notifs = await withTimeout(gh.listNotifications(config, 15), 15000);
|
|
3087
|
+
if (notifs) parts.push('## GitHub Notifications\n' + notifs);
|
|
3088
|
+
} catch {}
|
|
3031
3089
|
}
|
|
3032
|
-
toolData = parts.length > 0 ? parts.join('\n\n') : 'No GitHub data
|
|
3090
|
+
toolData = parts.length > 0 ? parts.join('\n\n') : 'No GitHub data could be retrieved.';
|
|
3033
3091
|
}
|
|
3034
3092
|
} catch (e) { toolData = `GitHub read failed: ${e.message}`; }
|
|
3035
3093
|
|
|
@@ -3154,6 +3212,11 @@ RULES:
|
|
|
3154
3212
|
userMsg = `Create a professional dashboard report for this data. Output ONLY the inner HTML body content (starting with <div class="header">):\n\n${canvasData}`;
|
|
3155
3213
|
} else if (isLiveDataAgent) {
|
|
3156
3214
|
// These agents fetched real data — use a focused prompt (no tool definitions to avoid JSON output)
|
|
3215
|
+
// Live data agents that fetched their own data: do NOT inject previous context
|
|
3216
|
+
// (prevents EmailAgent output from being repeated by GitHubAgent, CalendarAgent, etc.)
|
|
3217
|
+
const contextBlock = toolData
|
|
3218
|
+
? '' // Has own live data — ignore previous agent outputs to avoid repetition
|
|
3219
|
+
: (context ? `## OUTPUT FROM PREVIOUS AGENTS:\n${context}\n` : '');
|
|
3157
3220
|
const agentInstruction = `You are ${agent}, a specialist AI agent inside NHA Studio. Today is ${today}. Respond entirely in ${language}.
|
|
3158
3221
|
|
|
3159
3222
|
## OVERALL WORKFLOW GOAL:
|
|
@@ -3164,8 +3227,7 @@ Do NOT output JSON, tool calls, or code blocks. Write in plain text with markdow
|
|
|
3164
3227
|
Always apply your analysis specifically to the subject mentioned in the WORKFLOW GOAL.
|
|
3165
3228
|
|
|
3166
3229
|
${attachmentText ? `## ATTACHED FILE CONTENT:\n${attachmentText}\n` : ''}${toolData ? `## DATA FROM TOOLS:\n${toolData}\n` : '## DATA: No data was retrieved by this agent.\n'}
|
|
3167
|
-
${
|
|
3168
|
-
|
|
3230
|
+
${contextBlock}
|
|
3169
3231
|
Your task: ${stepPrompt}`;
|
|
3170
3232
|
sysPrompt = agentInstruction;
|
|
3171
3233
|
userMsg = toolData
|
|
@@ -3184,14 +3246,15 @@ ${task}
|
|
|
3184
3246
|
CRITICAL RULES:
|
|
3185
3247
|
- Do NOT output JSON, tool calls, function calls, or code blocks
|
|
3186
3248
|
- NEVER invent, fabricate, or hallucinate data, events, emails, meetings, or news
|
|
3187
|
-
- ONLY use
|
|
3188
|
-
-
|
|
3249
|
+
- ONLY use data from the DATA sections that is RELEVANT to your specific domain and the WORKFLOW GOAL
|
|
3250
|
+
- If the previous agents' output contains irrelevant personal data (e.g. unrelated emails, purchases, subscriptions) — IGNORE it entirely
|
|
3251
|
+
- ONLY reference data that directly relates to the subject of the WORKFLOW GOAL
|
|
3252
|
+
- If genuinely no relevant data exists for your domain, say so clearly — do NOT invent analysis
|
|
3189
3253
|
- Write in plain prose, structured with markdown headers (##) and bullet points (-)
|
|
3190
3254
|
- Be thorough and specific — this is for an executive briefing based on REAL data only
|
|
3191
|
-
- Always keep the OVERALL WORKFLOW GOAL in mind — apply your analysis specifically to the subject mentioned
|
|
3192
3255
|
|
|
3193
3256
|
${attachmentText ? `## ATTACHED FILE CONTENT:\n${attachmentText}\n` : ''}${toolData ? `## LIVE DATA FROM TOOLS:\n${toolData}\n` : '## LIVE DATA: No tool data was fetched for this step.\n'}
|
|
3194
|
-
${context ? `## OUTPUT FROM PREVIOUS AGENTS:\n${context}\n` : ''}`;
|
|
3257
|
+
${context ? `## OUTPUT FROM PREVIOUS AGENTS (use only what is RELEVANT to the workflow goal):\n${context}\n` : ''}`;
|
|
3195
3258
|
userMsg = hasRealData
|
|
3196
3259
|
? `Based ONLY on the real data above, complete this task specifically for the subject in the WORKFLOW GOAL: ${stepPrompt}`
|
|
3197
3260
|
: `No real data is available for "${task}". State this clearly and explain what data would be needed to complete: ${stepPrompt}`;
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.2.
|
|
8
|
+
export const VERSION = '13.2.82';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/github.mjs
CHANGED
|
@@ -185,6 +185,85 @@ export async function listUserRepos(config, maxResults = 30) {
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Get repository metadata: description, stars, forks, language, topics, license, last push.
|
|
190
|
+
*/
|
|
191
|
+
export async function getRepoInfo(config, repo) {
|
|
192
|
+
const data = await ghFetch(config, `/repos/${repo}`);
|
|
193
|
+
return {
|
|
194
|
+
full_name: data.full_name,
|
|
195
|
+
description: data.description || '',
|
|
196
|
+
stars: data.stargazers_count || 0,
|
|
197
|
+
forks: data.forks_count || 0,
|
|
198
|
+
watchers: data.watchers_count || 0,
|
|
199
|
+
open_issues: data.open_issues_count || 0,
|
|
200
|
+
language: data.language || 'N/A',
|
|
201
|
+
topics: (data.topics || []).join(', ') || 'none',
|
|
202
|
+
license: data.license?.name || 'none',
|
|
203
|
+
default_branch: data.default_branch || 'main',
|
|
204
|
+
pushed_at: data.pushed_at ? data.pushed_at.slice(0, 10) : 'unknown',
|
|
205
|
+
created_at: data.created_at ? data.created_at.slice(0, 10) : 'unknown',
|
|
206
|
+
homepage: data.homepage || '',
|
|
207
|
+
private: data.private,
|
|
208
|
+
archived: data.archived,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get programming languages breakdown for a repo.
|
|
214
|
+
*/
|
|
215
|
+
export async function getRepoLanguages(config, repo) {
|
|
216
|
+
const data = await ghFetch(config, `/repos/${repo}/languages`);
|
|
217
|
+
const total = Object.values(data).reduce((a, b) => a + b, 0) || 1;
|
|
218
|
+
return Object.entries(data)
|
|
219
|
+
.map(([lang, bytes]) => `${lang}: ${((bytes / total) * 100).toFixed(1)}%`)
|
|
220
|
+
.join(', ');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get recent commits for a repo (last N).
|
|
225
|
+
*/
|
|
226
|
+
export async function getRecentCommits(config, repo, maxResults = 10) {
|
|
227
|
+
const data = await ghFetch(config, `/repos/${repo}/commits?per_page=${maxResults}`);
|
|
228
|
+
if (!Array.isArray(data) || data.length === 0) return `No commits found in ${repo}.`;
|
|
229
|
+
return data.map((c, i) => {
|
|
230
|
+
const sha = c.sha ? c.sha.slice(0, 7) : '?';
|
|
231
|
+
const msg = (c.commit?.message || '').split('\n')[0].slice(0, 100);
|
|
232
|
+
const author = c.commit?.author?.name || c.author?.login || 'unknown';
|
|
233
|
+
const date = c.commit?.author?.date ? c.commit.author.date.slice(0, 10) : '';
|
|
234
|
+
return `${i + 1}. [${sha}] ${date} — ${author}: ${msg}`;
|
|
235
|
+
}).join('\n');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get README content for a repo.
|
|
240
|
+
*/
|
|
241
|
+
export async function getReadme(config, repo) {
|
|
242
|
+
try {
|
|
243
|
+
const data = await ghFetch(config, `/repos/${repo}/readme`);
|
|
244
|
+
if (data.content) {
|
|
245
|
+
const text = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
246
|
+
return text.slice(0, 6000); // cap at 6KB
|
|
247
|
+
}
|
|
248
|
+
return '';
|
|
249
|
+
} catch {
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get contributors for a repo.
|
|
256
|
+
*/
|
|
257
|
+
export async function getContributors(config, repo, maxResults = 10) {
|
|
258
|
+
try {
|
|
259
|
+
const data = await ghFetch(config, `/repos/${repo}/contributors?per_page=${maxResults}`);
|
|
260
|
+
if (!Array.isArray(data) || data.length === 0) return 'No contributors data.';
|
|
261
|
+
return data.map((c, i) => `${i + 1}. @${c.login} — ${c.contributions} commits`).join('\n');
|
|
262
|
+
} catch {
|
|
263
|
+
return 'Contributors data unavailable.';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
188
267
|
export async function markNotificationsRead(config) {
|
|
189
268
|
await ghFetch(config, '/notifications', {
|
|
190
269
|
method: 'PUT',
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -3660,7 +3660,7 @@ function renderStudioResult() {
|
|
|
3660
3660
|
var tokLine = (studioTokens && (studioTokens.in > 0 || studioTokens.out > 0))
|
|
3661
3661
|
? '<div style="margin-top:8px;font-size:11px;color:var(--dim);font-family:var(--mono)">⬆ ' + (studioTokens.in||0).toLocaleString() + ' token in ⬇ ' + (studioTokens.out||0).toLocaleString() + ' token out • <strong style="color:var(--green)">' + ((studioTokens.in||0)+(studioTokens.out||0)).toLocaleString() + '</strong> totale</div>'
|
|
3662
3662
|
: '';
|
|
3663
|
-
var dlBtn = '<div style="margin-top:12px;display:flex;align-items:center;gap:10px;flex-wrap:wrap">' +
|
|
3663
|
+
var dlBtn = '<div style="margin-top:14px;padding-top:12px;border-top:1px solid var(--border);display:flex;align-items:center;gap:10px;flex-wrap:wrap">' +
|
|
3664
3664
|
'<button onclick="downloadStudioPDF()" title="Scarica il workflow come PDF" style="display:inline-flex;align-items:center;gap:6px;padding:8px 18px;background:linear-gradient(135deg,#4f46e5,#2563eb);border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;letter-spacing:.3px;box-shadow:0 2px 8px rgba(79,70,229,.35)">⤓ Download PDF</button>' +
|
|
3665
3665
|
'<span style="font-size:11px;color:var(--dim)">Scarica il workflow completo come documento PDF</span>' +
|
|
3666
3666
|
'</div>';
|
|
@@ -3805,8 +3805,15 @@ async function runStudio() {
|
|
|
3805
3805
|
var parliamentActive = studioState.parliamentMode || (parliamentChk && parliamentChk.checked);
|
|
3806
3806
|
if (parliamentActive && studioState.nodes.length >= 1) {
|
|
3807
3807
|
var proposals = studioState.nodes
|
|
3808
|
-
.filter(function(n) {
|
|
3809
|
-
|
|
3808
|
+
.filter(function(n) {
|
|
3809
|
+
if (!n.output || n.output === \x27(no output)\x27) return false;
|
|
3810
|
+
if (n.agent === \x27CanvasAgent\x27 || n.agent === \x27DocumentReaderAgent\x27) return false;
|
|
3811
|
+
if (n.status === \x27error\x27) return false;
|
|
3812
|
+
// Exclude nodes whose output is a short error message (< 80 chars containing "error"/"could not")
|
|
3813
|
+
if (n.output.length < 120 && /error|could not|fallito|errore/i.test(n.output)) return false;
|
|
3814
|
+
return true;
|
|
3815
|
+
})
|
|
3816
|
+
.map(function(n) { return {agent: n.agent, label: n.label, icon: n.icon, output: n.output}; });
|
|
3810
3817
|
// Need at least 2 proposals for cross-reading; if only 1, include the full context as a second proposal
|
|
3811
3818
|
if (proposals.length === 1 && context) {
|
|
3812
3819
|
proposals.push({agent: \x27Context\x27, label: \x27Contesto workflow\x27, output: context});
|