@oh-my-pi/pi-coding-agent 14.9.1 → 14.9.3
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/CHANGELOG.md +60 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +3 -3
- package/src/config/prompt-templates.ts +0 -5
- package/src/config/settings-schema.ts +38 -0
- package/src/eval/eval.lark +10 -31
- package/src/eval/index.ts +1 -0
- package/src/eval/parse.ts +156 -255
- package/src/eval/sniff.ts +28 -0
- package/src/export/html/template.css +38 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +209 -15
- package/src/extensibility/extensions/runner.ts +173 -177
- package/src/hashline/apply.ts +8 -24
- package/src/hashline/constants.ts +20 -0
- package/src/hashline/execute.ts +0 -1
- package/src/hashline/grammar.lark +16 -27
- package/src/hashline/hash.ts +4 -34
- package/src/hashline/input.ts +16 -2
- package/src/hashline/parser.ts +12 -40
- package/src/hashline/types.ts +1 -2
- package/src/internal-urls/agent-protocol.ts +1 -0
- package/src/internal-urls/artifact-protocol.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +2 -1
- package/src/internal-urls/jobs-protocol.ts +1 -0
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/internal-urls/mcp-protocol.ts +1 -0
- package/src/internal-urls/memory-protocol.ts +1 -0
- package/src/internal-urls/pi-protocol.ts +1 -0
- package/src/internal-urls/router.ts +2 -1
- package/src/internal-urls/rule-protocol.ts +1 -0
- package/src/internal-urls/skill-protocol.ts +1 -0
- package/src/internal-urls/types.ts +18 -2
- package/src/mcp/transports/http.ts +49 -47
- package/src/prompts/system/custom-system-prompt.md +0 -2
- package/src/prompts/system/now-prompt.md +7 -0
- package/src/prompts/system/project-prompt.md +2 -0
- package/src/prompts/system/subagent-system-prompt.md +18 -9
- package/src/prompts/system/subagent-user-prompt.md +1 -10
- package/src/prompts/system/system-prompt.md +154 -233
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/eval.md +26 -13
- package/src/prompts/tools/hashline.md +1 -4
- package/src/sdk.ts +12 -22
- package/src/session/agent-session.ts +49 -17
- package/src/system-prompt.ts +38 -104
- package/src/task/executor.ts +15 -9
- package/src/task/index.ts +38 -33
- package/src/task/render.ts +4 -2
- package/src/tools/bash.ts +15 -41
- package/src/tools/eval.ts +13 -36
- package/src/tools/index.ts +0 -3
- package/src/tools/path-utils.ts +21 -1
- package/src/tools/read.ts +71 -49
- package/src/tools/search.ts +13 -1
- package/src/utils/file-display-mode.ts +11 -5
- package/src/workspace-tree.ts +210 -410
- package/src/task/template.ts +0 -47
- package/src/tools/bash-normalize.ts +0 -107
|
@@ -839,9 +839,11 @@
|
|
|
839
839
|
|
|
840
840
|
function renderEdit(name, args, result, ctx) {
|
|
841
841
|
const filePath = str(args.file_path == null ? args.path : args.file_path);
|
|
842
|
-
const pathHtml = filePath
|
|
842
|
+
const pathHtml = filePath ? escapeHtml(shortenPath(filePath)) : '';
|
|
843
843
|
let html = toolHead('edit', pathHtml);
|
|
844
|
-
if (
|
|
844
|
+
if (typeof args.input === 'string' && args.input.length) {
|
|
845
|
+
html += codeBlock(args.input, null);
|
|
846
|
+
} else if (Array.isArray(args.edits)) {
|
|
845
847
|
html += '<div class="tool-args">';
|
|
846
848
|
for (const e of args.edits) {
|
|
847
849
|
const op = e && typeof e.op === 'string' ? e.op : '?';
|
|
@@ -867,7 +869,8 @@
|
|
|
867
869
|
|
|
868
870
|
function renderAstEdit(name, args, result, ctx) {
|
|
869
871
|
const lang = args.lang || null;
|
|
870
|
-
const
|
|
872
|
+
const paths = Array.isArray(args.paths) ? args.paths.map(p => shortenPath(String(p))).join(', ') : (args.path ? shortenPath(String(args.path)) : '');
|
|
873
|
+
const pathHtml = paths ? escapeHtml(paths) : '';
|
|
871
874
|
const badges = [];
|
|
872
875
|
if (lang) badges.push(lang);
|
|
873
876
|
if (args.glob) badges.push('glob=' + args.glob);
|
|
@@ -931,10 +934,12 @@
|
|
|
931
934
|
}
|
|
932
935
|
|
|
933
936
|
function renderFind(name, args, result, ctx) {
|
|
934
|
-
const
|
|
935
|
-
const patHtml =
|
|
936
|
-
const badges =
|
|
937
|
-
|
|
937
|
+
const paths = Array.isArray(args.paths) ? args.paths.map(p => shortenPath(String(p))).join(', ') : (str(args.pattern) || '.');
|
|
938
|
+
const patHtml = paths ? escapeHtml(paths) : invalidArgHtml();
|
|
939
|
+
const badges = [];
|
|
940
|
+
if (args.limit) badges.push('limit=' + args.limit);
|
|
941
|
+
if (args.hidden === false) badges.push('no-hidden');
|
|
942
|
+
let html = toolHead('find', '<span class="tool-pattern">' + patHtml + '</span>', badges.length ? badges : null);
|
|
938
943
|
if (result) {
|
|
939
944
|
const output = ctx.getResultText();
|
|
940
945
|
if (output) html += formatExpandableOutput(output, 10);
|
|
@@ -1168,14 +1173,18 @@
|
|
|
1168
1173
|
}
|
|
1169
1174
|
|
|
1170
1175
|
function renderGh(name, args, result, ctx) {
|
|
1176
|
+
const op = str(args.op);
|
|
1171
1177
|
const badges = [];
|
|
1178
|
+
if (op) badges.push(op);
|
|
1172
1179
|
if (args.repo) badges.push(String(args.repo));
|
|
1173
1180
|
if (args.issue) badges.push('#' + args.issue);
|
|
1174
|
-
if (args.pr) badges.push('PR ' + args.pr);
|
|
1181
|
+
if (args.pr) badges.push(Array.isArray(args.pr) ? 'PRs ' + args.pr.join(',') : 'PR ' + args.pr);
|
|
1175
1182
|
if (args.branch) badges.push('branch=' + args.branch);
|
|
1176
|
-
if (args.query) badges.push('query=' + args.query);
|
|
1183
|
+
if (args.query) badges.push('query=' + truncate(String(args.query), 60));
|
|
1177
1184
|
if (args.run) badges.push('run=' + args.run);
|
|
1185
|
+
if (args.title) badges.push('title=' + truncate(String(args.title), 40));
|
|
1178
1186
|
let html = toolHead(name, '', badges);
|
|
1187
|
+
if (args.body) html += '<div class="tool-output"><div>' + escapeHtml(truncate(String(args.body), 400)) + '</div></div>';
|
|
1179
1188
|
if (result) {
|
|
1180
1189
|
const output = ctx.getResultText();
|
|
1181
1190
|
if (output) html += formatExpandableOutput(output, 12, 'markdown');
|
|
@@ -1249,6 +1258,178 @@
|
|
|
1249
1258
|
return html;
|
|
1250
1259
|
}
|
|
1251
1260
|
|
|
1261
|
+
// Parse `*** Begin <LANG>` cell headers (canonical) and the legacy
|
|
1262
|
+
// `===== <info> =====` headers used by older transcripts. Cells emitted
|
|
1263
|
+
// before the format cutover still need to render in HTML exports.
|
|
1264
|
+
function parseEvalCells(input) {
|
|
1265
|
+
const text = String(input);
|
|
1266
|
+
if (/^[*]{2,}\s*Begin\b/im.test(text)) return parseEvalCellsNew(text);
|
|
1267
|
+
return parseEvalCellsLegacy(text);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function evalLangAlias(token) {
|
|
1271
|
+
const t = String(token || '').toUpperCase();
|
|
1272
|
+
if (t === 'PY' || t === 'PYTHON' || t === 'IPY' || t === 'IPYTHON') return 'py';
|
|
1273
|
+
if (t === 'JS' || t === 'JAVASCRIPT') return 'js';
|
|
1274
|
+
if (t === 'TS' || t === 'TYPESCRIPT') return 'ts';
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
function parseEvalCellsNew(text) {
|
|
1279
|
+
const STARS = '\\*{2,}';
|
|
1280
|
+
const BEGIN = new RegExp('^' + STARS + '\\s*Begin\\b\\s*(\\S+)?\\s*$', 'i');
|
|
1281
|
+
const END = new RegExp('^' + STARS + '\\s*End\\b.*$', 'i');
|
|
1282
|
+
const TITLE = new RegExp('^' + STARS + '\\s*Title\\s*:\\s*(.+?)\\s*$', 'i');
|
|
1283
|
+
const TIMEOUT = new RegExp('^' + STARS + '\\s*Timeout\\s*:\\s*(\\S+)\\s*$', 'i');
|
|
1284
|
+
const RESET = new RegExp('^' + STARS + '\\s*Reset\\s*$', 'i');
|
|
1285
|
+
const lines = text.split('\n');
|
|
1286
|
+
if (lines.length && lines[lines.length - 1] === '') lines.pop();
|
|
1287
|
+
const cells = [];
|
|
1288
|
+
let i = 0;
|
|
1289
|
+
while (i < lines.length && lines[i].trim() === '') i++;
|
|
1290
|
+
while (i < lines.length) {
|
|
1291
|
+
const beginMatch = BEGIN.exec(lines[i]);
|
|
1292
|
+
if (!beginMatch) { i++; continue; }
|
|
1293
|
+
const lang = evalLangAlias(beginMatch[1]) || 'py';
|
|
1294
|
+
i++;
|
|
1295
|
+
let title = '';
|
|
1296
|
+
const attrs = [];
|
|
1297
|
+
while (i < lines.length) {
|
|
1298
|
+
const tm = TITLE.exec(lines[i]);
|
|
1299
|
+
if (tm) { if (!title) title = tm[1]; i++; continue; }
|
|
1300
|
+
const to = TIMEOUT.exec(lines[i]);
|
|
1301
|
+
if (to) { attrs.push('t=' + to[1]); i++; continue; }
|
|
1302
|
+
if (RESET.test(lines[i])) { attrs.push('rst'); i++; continue; }
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
const codeLines = [];
|
|
1306
|
+
while (i < lines.length) {
|
|
1307
|
+
if (END.test(lines[i])) { i++; break; }
|
|
1308
|
+
if (BEGIN.test(lines[i])) break;
|
|
1309
|
+
codeLines.push(lines[i]);
|
|
1310
|
+
i++;
|
|
1311
|
+
}
|
|
1312
|
+
while (codeLines.length && codeLines[codeLines.length - 1].trim() === '') codeLines.pop();
|
|
1313
|
+
cells.push({ lang, title, attrs, code: codeLines.join('\n') });
|
|
1314
|
+
while (i < lines.length && lines[i].trim() === '') i++;
|
|
1315
|
+
}
|
|
1316
|
+
return cells;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function parseEvalCellsLegacy(input) {
|
|
1320
|
+
const HEADER = /^={5,}\s*(.*?)\s*={5,}\s*$/;
|
|
1321
|
+
const lines = String(input).split('\n');
|
|
1322
|
+
const cells = [];
|
|
1323
|
+
let inheritedLang = 'py';
|
|
1324
|
+
let current = null;
|
|
1325
|
+
for (const line of lines) {
|
|
1326
|
+
const m = line.match(HEADER);
|
|
1327
|
+
if (m) {
|
|
1328
|
+
if (current) cells.push(current);
|
|
1329
|
+
const info = m[1] || '';
|
|
1330
|
+
let lang = inheritedLang;
|
|
1331
|
+
let title = '';
|
|
1332
|
+
const langMatch = info.match(/^(py|js|ts)(?::"([^"]*)")?/);
|
|
1333
|
+
if (langMatch) {
|
|
1334
|
+
lang = langMatch[1];
|
|
1335
|
+
if (langMatch[2]) title = langMatch[2];
|
|
1336
|
+
}
|
|
1337
|
+
if (!title) {
|
|
1338
|
+
const idMatch = info.match(/id:"([^"]*)"/);
|
|
1339
|
+
if (idMatch) title = idMatch[1];
|
|
1340
|
+
}
|
|
1341
|
+
inheritedLang = lang;
|
|
1342
|
+
const attrs = [];
|
|
1343
|
+
const tMatch = info.match(/(?:^|\s)t:(\S+)/);
|
|
1344
|
+
if (tMatch) attrs.push('t=' + tMatch[1]);
|
|
1345
|
+
if (/(?:^|\s)rst(?:\s|$)/.test(info)) attrs.push('rst');
|
|
1346
|
+
current = { lang, title, attrs, code: '' };
|
|
1347
|
+
} else {
|
|
1348
|
+
if (!current) current = { lang: inheritedLang, title: '', attrs: [], code: '' };
|
|
1349
|
+
current.code += (current.code ? '\n' : '') + line;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (current) cells.push(current);
|
|
1353
|
+
return cells.map(c => ({ ...c, code: c.code.replace(/\s+$/, '') }));
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function evalLangToHljs(lang) {
|
|
1357
|
+
return lang === 'py' ? 'python' : lang === 'js' ? 'javascript' : lang === 'ts' ? 'typescript' : null;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
function renderEval(name, args, result, ctx) {
|
|
1361
|
+
let html = toolHead('eval');
|
|
1362
|
+
if (typeof args.input !== 'string') {
|
|
1363
|
+
html += '<div class="tool-error">[missing input]</div>';
|
|
1364
|
+
} else {
|
|
1365
|
+
const cells = parseEvalCells(args.input);
|
|
1366
|
+
if (cells.length === 0) {
|
|
1367
|
+
html += codeBlock(args.input, 'python');
|
|
1368
|
+
} else {
|
|
1369
|
+
for (const cell of cells) {
|
|
1370
|
+
html += '<div class="tool-cell">';
|
|
1371
|
+
const titleParts = [];
|
|
1372
|
+
if (cell.title) titleParts.push(cell.title);
|
|
1373
|
+
titleParts.push(cell.lang);
|
|
1374
|
+
if (cell.attrs && cell.attrs.length) titleParts.push(...cell.attrs);
|
|
1375
|
+
html += '<div class="tool-cell-title">' + escapeHtml(titleParts.join(' · ')) + '</div>';
|
|
1376
|
+
html += codeBlock(cell.code, evalLangToHljs(cell.lang));
|
|
1377
|
+
html += '</div>';
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (result) {
|
|
1382
|
+
html += ctx.renderResultImages();
|
|
1383
|
+
const output = ctx.getResultText();
|
|
1384
|
+
if (output) html += formatExpandableOutput(output, 12);
|
|
1385
|
+
}
|
|
1386
|
+
return html;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function renderSearch(name, args, result, ctx) {
|
|
1390
|
+
const pattern = str(args.pattern);
|
|
1391
|
+
const paths = Array.isArray(args.paths) ? args.paths.map(p => shortenPath(String(p))).join(', ') : (args.path ? shortenPath(String(args.path)) : '.');
|
|
1392
|
+
const patHtml = pattern === null ? invalidArgHtml() : escapeHtml(pattern);
|
|
1393
|
+
let head = '<span class="tool-name">search</span> <span class="tool-pattern">/' + patHtml + '/</span>';
|
|
1394
|
+
head += ' <span class="tool-arg-key">in</span> <span class="tool-path">' + escapeHtml(paths) + '</span>';
|
|
1395
|
+
const badges = [];
|
|
1396
|
+
if (args.i) badges.push('i');
|
|
1397
|
+
if (args.skip) badges.push('skip=' + args.skip);
|
|
1398
|
+
if (args.gitignore === false) badges.push('no-gitignore');
|
|
1399
|
+
for (const b of badges) head += ' <span class="tool-badge">' + escapeHtml(b) + '</span>';
|
|
1400
|
+
let html = '<div class="tool-header">' + head + '</div>';
|
|
1401
|
+
if (result) {
|
|
1402
|
+
const output = ctx.getResultText();
|
|
1403
|
+
if (output) html += formatExpandableOutput(output, 12);
|
|
1404
|
+
}
|
|
1405
|
+
return html;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function renderRecipe(name, args, result, ctx) {
|
|
1409
|
+
const op = str(args.op) || '?';
|
|
1410
|
+
let html = toolHead('recipe', '<span class="tool-arg-val">' + escapeHtml(op) + '</span>');
|
|
1411
|
+
if (result) {
|
|
1412
|
+
html += ctx.renderResultImages();
|
|
1413
|
+
const output = ctx.getResultText();
|
|
1414
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
1415
|
+
}
|
|
1416
|
+
return html;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function renderIrc(name, args, result, ctx) {
|
|
1420
|
+
const op = str(args.op) || '?';
|
|
1421
|
+
const badges = [op];
|
|
1422
|
+
if (args.to) badges.push('to=' + args.to);
|
|
1423
|
+
if (args.awaitReply === false) badges.push('no-reply');
|
|
1424
|
+
let html = toolHead('irc', '', badges);
|
|
1425
|
+
if (args.message) html += '<div class="tool-output"><div>' + escapeHtml(String(args.message)) + '</div></div>';
|
|
1426
|
+
if (result) {
|
|
1427
|
+
const output = ctx.getResultText();
|
|
1428
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
1429
|
+
}
|
|
1430
|
+
return html;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1252
1433
|
|
|
1253
1434
|
function renderGenericTool(name, args, result, ctx) {
|
|
1254
1435
|
let html = toolHead(name);
|
|
@@ -1266,6 +1447,7 @@
|
|
|
1266
1447
|
|
|
1267
1448
|
const TOOL_RENDERERS = {
|
|
1268
1449
|
bash: renderBash,
|
|
1450
|
+
eval: renderEval,
|
|
1269
1451
|
js: renderJsLike,
|
|
1270
1452
|
python: renderJsLike,
|
|
1271
1453
|
notebook: renderJsLike,
|
|
@@ -1275,6 +1457,7 @@
|
|
|
1275
1457
|
ast_edit: renderAstEdit,
|
|
1276
1458
|
ast_grep: renderAstGrep,
|
|
1277
1459
|
grep: renderGrep,
|
|
1460
|
+
search: renderSearch,
|
|
1278
1461
|
find: renderFind,
|
|
1279
1462
|
lsp: renderLsp,
|
|
1280
1463
|
todo_write: renderTodoWrite,
|
|
@@ -1300,16 +1483,25 @@
|
|
|
1300
1483
|
poll: renderJob,
|
|
1301
1484
|
cancel_job: renderJob,
|
|
1302
1485
|
job: renderJob,
|
|
1486
|
+
recipe: renderRecipe,
|
|
1487
|
+
irc: renderIrc,
|
|
1303
1488
|
};
|
|
1304
1489
|
|
|
1305
1490
|
function renderToolCall(call) {
|
|
1306
1491
|
const result = findToolResult(call.id);
|
|
1307
1492
|
const isError = result?.isError || false;
|
|
1308
1493
|
const statusClass = result ? (isError ? 'error' : 'success') : 'pending';
|
|
1309
|
-
const
|
|
1494
|
+
const rawArgs = call.arguments || {};
|
|
1495
|
+
const intent = typeof rawArgs._i === 'string' ? rawArgs._i.trim() : '';
|
|
1496
|
+
// Strip internal _i intent so renderers don't dump it as JSON.
|
|
1497
|
+
const args = {};
|
|
1498
|
+
for (const k of Object.keys(rawArgs)) {
|
|
1499
|
+
if (k !== '_i') args[k] = rawArgs[k];
|
|
1500
|
+
}
|
|
1310
1501
|
const name = call.name;
|
|
1311
1502
|
|
|
1312
1503
|
const ctx = {
|
|
1504
|
+
intent,
|
|
1313
1505
|
getResultText: () => {
|
|
1314
1506
|
if (!result) return '';
|
|
1315
1507
|
const textBlocks = result.content.filter(c => c.type === 'text');
|
|
@@ -1331,6 +1523,7 @@
|
|
|
1331
1523
|
|
|
1332
1524
|
const renderer = TOOL_RENDERERS[name] || renderGenericTool;
|
|
1333
1525
|
let html = '<div class="tool-execution ' + statusClass + '">';
|
|
1526
|
+
if (intent) html += '<div class="tool-intent">' + escapeHtml(intent) + '</div>';
|
|
1334
1527
|
try {
|
|
1335
1528
|
html += renderer(name, args, result, ctx);
|
|
1336
1529
|
} catch (err) {
|
|
@@ -1638,11 +1831,12 @@
|
|
|
1638
1831
|
}
|
|
1639
1832
|
|
|
1640
1833
|
if (tools && tools.length > 0) {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
</div>
|
|
1834
|
+
const namesHtml = tools.map(t => `<span class="tool-name-chip">${escapeHtml(t.name)}</span>`).join('');
|
|
1835
|
+
const fullHtml = tools.map(t => `<div class="tool-item"><span class="tool-item-name">${escapeHtml(t.name)}</span> - <span class="tool-item-desc">${escapeHtml(t.description)}</span></div>`).join('');
|
|
1836
|
+
html += `<div class="tools-list collapsed" onclick="this.classList.toggle('collapsed')">
|
|
1837
|
+
<div class="tools-header">Available Tools (${tools.length})</div>
|
|
1838
|
+
<div class="tools-collapsed">${namesHtml}</div>
|
|
1839
|
+
<div class="tools-content">${fullHtml}</div>
|
|
1646
1840
|
</div>`;
|
|
1647
1841
|
}
|
|
1648
1842
|
|