prior-cli 1.6.3 → 1.6.5
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/bin/prior.js +43 -1
- package/lib/agent.js +19 -0
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -45,6 +45,48 @@ let _spinIdx = 0;
|
|
|
45
45
|
let _spinStart = null;
|
|
46
46
|
let _spinLabel = '';
|
|
47
47
|
|
|
48
|
+
// ── Tool keyword hints ─────────────────────────────────────────
|
|
49
|
+
// Detects keywords in user input and prepends a hard directive so
|
|
50
|
+
// the model can't second-guess which tool to use.
|
|
51
|
+
const TOOL_HINTS = [
|
|
52
|
+
{
|
|
53
|
+
tool: 'ssl_check',
|
|
54
|
+
patterns: [
|
|
55
|
+
/\bssl\b/i, /\btls\b/i, /\bcertificate\b/i, /\bcert\b/i,
|
|
56
|
+
/\bhttps check\b/i, /\bcert expir/i, /\bssl expir/i,
|
|
57
|
+
],
|
|
58
|
+
hint: '[TOOL DIRECTIVE: You MUST call ssl_check — do NOT use zap_scan or zap_alerts]',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
tool: 'dns_lookup',
|
|
62
|
+
patterns: [
|
|
63
|
+
/\bdns\b/i, /\bmx record/i, /\bnameserver/i, /\bnslookup\b/i,
|
|
64
|
+
/\bdig\b/i, /\bdns record/i, /\btxt record/i, /\bcname\b/i,
|
|
65
|
+
/\bns record/i, /\baaaa record/i,
|
|
66
|
+
],
|
|
67
|
+
hint: '[TOOL DIRECTIVE: You MUST call dns_lookup]',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
tool: 'ip_lookup',
|
|
71
|
+
patterns: [
|
|
72
|
+
/\bip lookup\b/i, /\blook up.*ip\b/i, /\bwhere is .+ hosted\b/i,
|
|
73
|
+
/\bwho owns .+(ip|domain)\b/i, /\basn\b/i, /\bgeolocation\b/i,
|
|
74
|
+
/\bwhat ip\b/i, /\bresolve .+(domain|host)\b/i,
|
|
75
|
+
/\blookup\b.*\b(ip|domain|host)\b/i,
|
|
76
|
+
],
|
|
77
|
+
hint: '[TOOL DIRECTIVE: You MUST call ip_lookup]',
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
function injectToolHint(text) {
|
|
82
|
+
for (const { patterns, hint } of TOOL_HINTS) {
|
|
83
|
+
if (patterns.some(re => re.test(text))) {
|
|
84
|
+
return `${hint}\n${text}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return text;
|
|
88
|
+
}
|
|
89
|
+
|
|
48
90
|
function fmtElapsed(ms) {
|
|
49
91
|
const s = Math.floor(ms / 1000);
|
|
50
92
|
if (s < 60) return `${s}s`;
|
|
@@ -1285,7 +1327,7 @@ Be concise but thorough — this summary replaces the full history to save conte
|
|
|
1285
1327
|
|
|
1286
1328
|
_currentAbortController = new AbortController();
|
|
1287
1329
|
await runAgent({
|
|
1288
|
-
messages: [...chatHistory, { role: 'user', content: input }],
|
|
1330
|
+
messages: [...chatHistory, { role: 'user', content: injectToolHint(input) }],
|
|
1289
1331
|
model: currentModel,
|
|
1290
1332
|
cwd: process.cwd(),
|
|
1291
1333
|
projectContext,
|
package/lib/agent.js
CHANGED
|
@@ -77,6 +77,21 @@ function parseToolCalls(text) {
|
|
|
77
77
|
} catch { /* skip */ }
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Fallback: unclosed <tool>{...} (no closing </tool> — some models omit it)
|
|
81
|
+
const reUnclosed = /<tool>(\{[\s\S]*?\})\s*(?=$|\n|<)/g;
|
|
82
|
+
while ((m = reUnclosed.exec(text)) !== null) {
|
|
83
|
+
const alreadyCaptured = calls.some(c => m.index >= c.offset && m.index < c.offset + c.raw.length);
|
|
84
|
+
if (alreadyCaptured) continue;
|
|
85
|
+
try {
|
|
86
|
+
const fixed = fixJsonLiterals(m[1].trim());
|
|
87
|
+
const parsed = JSON.parse(fixed);
|
|
88
|
+
if (parsed && typeof parsed.name === 'string') {
|
|
89
|
+
const { name, args, ...rest } = parsed;
|
|
90
|
+
calls.push({ raw: m[0], offset: m.index, name, args: args || (Object.keys(rest).length > 0 ? rest : {}) });
|
|
91
|
+
}
|
|
92
|
+
} catch { /* skip */ }
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
// Primary variant: <tool name="X">{"args"}</tool> (name as attribute — used by some models)
|
|
81
96
|
const reAttr = /<tool\s+name="([^"]+)"[^>]*>([\s\S]*?)<\/tool>/g;
|
|
82
97
|
while ((m = reAttr.exec(text)) !== null) {
|
|
@@ -188,6 +203,10 @@ function truncateAtFakeTurn(text) {
|
|
|
188
203
|
function stripToolTags(text) {
|
|
189
204
|
// <tool>...</tool>
|
|
190
205
|
let out = text.replace(/<tool>[\s\S]*?<\/tool>/gi, '');
|
|
206
|
+
// Unclosed <tool>{...} (no closing tag)
|
|
207
|
+
out = out.replace(/<tool>\{[\s\S]*?\}\s*/gi, '');
|
|
208
|
+
// Bare <tool> with no content following
|
|
209
|
+
out = out.replace(/<\/?tool>/gi, '');
|
|
191
210
|
const namesPattern = TOOL_NAMES.join('|');
|
|
192
211
|
// <tool_name ...>...</tool_name> (with or without attributes/JSON)
|
|
193
212
|
out = out.replace(new RegExp(`<(?:${namesPattern})[^>]*>[\\s\\S]*?<\\/(?:${namesPattern})>`, 'gi'), '');
|