tarsk 0.5.77 → 0.5.79
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/bundled-skills/skill-creator/eval-viewer/generate_review.js +183 -95
- package/dist/bundled-skills/skill-creator/scripts/__tests__/improve_description.test.js +77 -0
- package/dist/bundled-skills/skill-creator/scripts/__tests__/package_skill.test.js +203 -0
- package/dist/bundled-skills/skill-creator/scripts/__tests__/run_eval.test.js +125 -0
- package/dist/bundled-skills/skill-creator/scripts/__tests__/utils.test.js +149 -0
- package/dist/bundled-skills/skill-creator/scripts/aggregate_benchmark.js +2 -0
- package/dist/bundled-skills/skill-creator/scripts/improve_description.js +42 -22
- package/dist/bundled-skills/skill-creator/scripts/package_skill.js +68 -40
- package/dist/bundled-skills/skill-creator/scripts/run_eval.js +60 -32
- package/dist/bundled-skills/skill-creator/scripts/run_loop.js +224 -150
- package/dist/bundled-skills/skill-creator/scripts/utils.js +71 -41
- package/dist/index.js +175285 -165023
- package/dist/node_modules/@libsql/darwin-x64/README.md +3 -0
- package/dist/node_modules/@libsql/darwin-x64/index.node +0 -0
- package/dist/node_modules/@libsql/darwin-x64/package.json +36 -0
- package/dist/public/assets/account-view-DGfJrmbM.js +1 -0
- package/dist/public/assets/alert-dialog-D5UGH4qW.js +7 -0
- package/dist/public/assets/api-odZ7g9AN.js +2 -0
- package/dist/public/assets/{async-error-boundary-p7xL6pqw.js → async-error-boundary-CbAQza_Z.js} +1 -1
- package/dist/public/assets/browser-tab-DZUa8JpJ.js +12 -0
- package/dist/public/assets/button-B7hsXP2P.js +1 -0
- package/dist/public/assets/card-BTgRxBeB.js +1 -0
- package/dist/public/assets/chat-dock-context-CANpJk1z.js +1 -0
- package/dist/public/assets/commit-dialog-DAUjE4cb.js +1 -0
- package/dist/public/assets/commit-error-dialog-2tnhCseF.js +1 -0
- package/dist/public/assets/commit-method-fields-Dj2uqPrP.js +1 -0
- package/dist/public/assets/conversation-history-view-CpinBoLp.js +1 -0
- package/dist/public/assets/create-project-dialog-V4ZiwQkg.js +2 -0
- package/dist/public/assets/create-repo-dialog-DG9Ax-yf.js +1 -0
- package/dist/public/assets/delete-confirm-dialog-Dg2q6cGA.js +1 -0
- package/dist/public/assets/diff-view-KJQqPVMa.js +4 -0
- package/dist/public/assets/dist-B9o9ZI_U.js +1 -0
- package/dist/public/assets/dist-BF1fIO22.js +1 -0
- package/dist/public/assets/dist-CM7ef_Ef.js +1 -0
- package/dist/public/assets/{dist-bBjaCw3d.js → dist-Ms_QN0HI.js} +1 -1
- package/dist/public/assets/dist-R7ILWSDv.js +1 -0
- package/dist/public/assets/dist-T2H2nD62.js +1 -0
- package/dist/public/assets/dist-Xi9Q--PI.js +5 -0
- package/dist/public/assets/dist-bAGdBaoO.js +2 -0
- package/dist/public/assets/es2015-3o6GJKAC.js +41 -0
- package/dist/public/assets/{explorer-monaco-editor-DuU68YH9.js → explorer-monaco-editor-BUfO46YV.js} +1 -1
- package/dist/public/assets/explorer-tab-view-CXFHVIyO.js +2 -0
- package/dist/public/assets/explorer-tree-D3N9CgUw.js +1 -0
- package/dist/public/assets/explorer-view-Qla3PjXX.js +76 -0
- package/dist/public/assets/git-history-dialog-hQ3LY348.js +1 -0
- package/dist/public/assets/index-BQ4LyrcX.css +1 -0
- package/dist/public/assets/index-C1jToYjL.js +112 -0
- package/dist/public/assets/lucide-react-CNNzKD4G.js +1 -0
- package/dist/public/assets/main-tab-context-CNgxXqnH.js +1 -0
- package/dist/public/assets/model-list-controls-Dzi-bA4G.js +1 -0
- package/dist/public/assets/onboarding-dialog-CWHYidh3.js +1 -0
- package/dist/public/assets/pill-tabs-B-GZBSAx.js +1 -0
- package/dist/public/assets/progress-indicator-Dr6KLjL5.js +1 -0
- package/dist/public/assets/project-coding-view-XHObCz5c.js +1 -0
- package/dist/public/assets/project-danger-zone-view-DnUMqsVk.js +1 -0
- package/dist/public/assets/project-env-vars-view-CZ2H31qr.js +1 -0
- package/dist/public/assets/project-model-aliases-view-BDLn_rnO.js +1 -0
- package/dist/public/assets/project-settings-view-BiPB--It.js +1 -0
- package/dist/public/assets/project-tools-view-rHqvcYmB.js +1 -0
- package/dist/public/assets/providers-list-view-CgmQms1S.js +1 -0
- package/dist/public/assets/pull-request-dialog-B2IuZfiC.js +1 -0
- package/dist/public/assets/pull-with-changes-dialog-0GcFybrs.js +1 -0
- package/dist/public/assets/{push-before-pr-dialog-CxboQlmj.js → push-before-pr-dialog-BsHxhNkj.js} +1 -1
- package/dist/public/assets/scroll-area-fMQC4KJw.js +1 -0
- package/dist/public/assets/settings-general-tabs-view-DFH-XX7K.js +2 -0
- package/dist/public/assets/settings-general-view-DQTKXkvD.js +1 -0
- package/dist/public/assets/settings-list-COPIEKbu.js +1 -0
- package/dist/public/assets/settings-logs-view-DkZI0-PQ.js +5 -0
- package/dist/public/assets/settings-mcp-servers-view-DNUF0p87.js +5 -0
- package/dist/public/assets/settings-models-view-CAfXeeqi.js +1 -0
- package/dist/public/assets/settings-plugins-view-Wgp98zEC.js +2 -0
- package/dist/public/assets/settings-providers-view-CDXMVzWQ.js +2 -0
- package/dist/public/assets/settings-setup-view-gkQGOFn4.js +2 -0
- package/dist/public/assets/settings-skills-view-CcyTQlPl.js +2 -0
- package/dist/public/assets/settings-slack-view-idgTLLDo.js +1 -0
- package/dist/public/assets/settings-slash-commands-view-CPfpTFt5.js +1 -0
- package/dist/public/assets/settings-statistics-view-CewvaKLi.js +1 -0
- package/dist/public/assets/settings-storage-view-3HPIVaYG.js +1 -0
- package/dist/public/assets/settings-subagents-view-CWD4EWyU.js +2 -0
- package/dist/public/assets/settings-system-prompt-view-DitIaAmi.js +1 -0
- package/dist/public/assets/settings-tab-bar-BKrNKoYV.js +1 -0
- package/dist/public/assets/settings-view-C1rDwQOL.js +2 -0
- package/dist/public/assets/slug-utils-ZC2qISlV.js +1 -0
- package/dist/public/assets/store-DqAPgNUQ.js +9 -0
- package/dist/public/assets/suspense-resource-DzlMlIgR.js +1 -0
- package/dist/public/assets/table-DjVI02Kg.js +1 -0
- package/dist/public/assets/tasks-view-07DwlYR7.js +1 -0
- package/dist/public/assets/terminal-output-activity-B1bjjsl7.js +1 -0
- package/dist/public/assets/terminal-panel-B-B0Mjbv.js +38 -0
- package/dist/public/assets/toggle-group-Ti6E2rBa.js +1 -0
- package/dist/public/assets/tooltip-Dz8vH1DG.js +1 -0
- package/dist/public/assets/{use-active-dock-panel-instance-DjhNdiNZ.js → use-active-dock-panel-instance-C7H1mX2G.js} +1 -1
- package/dist/public/assets/use-installed-ai-files-CNN44_z3.js +1 -0
- package/dist/public/assets/use-settings-page-loading-CpaBd260.js +1 -0
- package/dist/public/assets/use-toast-xpb5jNtN.js +1 -0
- package/dist/public/assets/{useBaseQuery-BytIT4Si.js → useBaseQuery-DZ0hJ6lN.js} +1 -1
- package/dist/public/assets/utils-BW67ZVub.js +1 -0
- package/dist/public/assets/{web-C28hpsuV.js → web-BBOGW-Jp.js} +1 -1
- package/dist/public/assets/{web-BogjYrUi.js → web-CsFwi45J.js} +1 -1
- package/dist/public/assets/{web-HPHkrB3q.js → web-DmkLaAFF.js} +1 -1
- package/dist/public/assets/{web-CWmUhqaq.js → web-kuH5W1xE.js} +1 -1
- package/dist/public/browser-preview-rpc.js +123 -68
- package/dist/public/index.html +28 -24
- package/package.json +6 -5
- package/dist/public/assets/account-view-DqN6PuZx.js +0 -1
- package/dist/public/assets/api-BEi04Nbw.js +0 -2
- package/dist/public/assets/async-loading-fallback-cCtsF_26.js +0 -1
- package/dist/public/assets/browser-tab-D8O7Fv9w.js +0 -12
- package/dist/public/assets/button-D1ArHt_o.js +0 -1
- package/dist/public/assets/card-Bp7fZl_f.js +0 -1
- package/dist/public/assets/chat-dock-context-BlxEXa9b.js +0 -1
- package/dist/public/assets/commit-dialog-DY4D8-I1.js +0 -1
- package/dist/public/assets/commit-error-dialog-DBXZAW1l.js +0 -1
- package/dist/public/assets/commit-method-fields-C335Y5Id.js +0 -1
- package/dist/public/assets/context-menu-BUjcm0iJ.js +0 -1
- package/dist/public/assets/conversation-history-view-DQ_PXqZ9.js +0 -1
- package/dist/public/assets/create-project-dialog-fc3B-dqb.js +0 -2
- package/dist/public/assets/create-repo-dialog-DchKq-aa.js +0 -1
- package/dist/public/assets/delete-confirm-dialog-DEuqA4Wk.js +0 -1
- package/dist/public/assets/diff-view-CVlpWjUp.js +0 -4
- package/dist/public/assets/dist-BGVtJOIV.js +0 -2
- package/dist/public/assets/dist-BQOHvuU5.js +0 -1
- package/dist/public/assets/dist-BrrQ4RKZ.js +0 -1
- package/dist/public/assets/dist-CRRMWh4A.js +0 -5
- package/dist/public/assets/dist-DLuGyArC.js +0 -1
- package/dist/public/assets/dist-DSd8c-dh.js +0 -1
- package/dist/public/assets/dist-DTleMiQ4.js +0 -1
- package/dist/public/assets/dist-sGpOBecq.js +0 -1
- package/dist/public/assets/es2015-SVM3tAoD.js +0 -41
- package/dist/public/assets/explorer-tab-view-CHQ0mxMq.js +0 -2
- package/dist/public/assets/explorer-tree-BHqNnEtP.js +0 -1
- package/dist/public/assets/explorer-view-CgdDN1D-.js +0 -76
- package/dist/public/assets/git-history-dialog-BAWqdcQy.js +0 -1
- package/dist/public/assets/index-BHXl-V4W.js +0 -3665
- package/dist/public/assets/index-CLo6-Tya.css +0 -1
- package/dist/public/assets/main-tab-context-Byl1LybL.js +0 -1
- package/dist/public/assets/model-list-controls-BTd-p6XD.js +0 -1
- package/dist/public/assets/onboarding-dialog-DXg4wTyD.js +0 -1
- package/dist/public/assets/project-coding-view-R1jPpXkA.js +0 -1
- package/dist/public/assets/project-danger-zone-view-B8gSJxHw.js +0 -1
- package/dist/public/assets/project-env-vars-view-CHfsOYa_.js +0 -1
- package/dist/public/assets/project-model-aliases-view-DnEbS87g.js +0 -1
- package/dist/public/assets/project-settings-view-34BqFxN-.js +0 -1
- package/dist/public/assets/project-tools-view-DCBhdS_I.js +0 -1
- package/dist/public/assets/providers-list-view-m6xHKltt.js +0 -1
- package/dist/public/assets/pull-request-dialog-Bmtin2Mh.js +0 -1
- package/dist/public/assets/pull-with-changes-dialog-Cmh1vFmo.js +0 -1
- package/dist/public/assets/radio-group-DzcDTzWb.js +0 -1
- package/dist/public/assets/scroll-area-DgahW31C.js +0 -1
- package/dist/public/assets/settings-general-view-XHYGeoAk.js +0 -1
- package/dist/public/assets/settings-list-D-51AyVy.js +0 -1
- package/dist/public/assets/settings-logs-view-BiY8r9HI.js +0 -5
- package/dist/public/assets/settings-mcp-servers-view-DEmqI7is.js +0 -5
- package/dist/public/assets/settings-models-view-CB6htSan.js +0 -1
- package/dist/public/assets/settings-skills-view-QL-AL_vP.js +0 -3
- package/dist/public/assets/settings-slack-view-Bo1oDUFr.js +0 -1
- package/dist/public/assets/settings-slash-commands-view-CUY_SkzD.js +0 -1
- package/dist/public/assets/settings-statistics-view-B1iibWb7.js +0 -1
- package/dist/public/assets/settings-storage-view-BClnyDai.js +0 -1
- package/dist/public/assets/settings-subagents-view-D4OEJJk8.js +0 -2
- package/dist/public/assets/settings-system-prompt-view-e6CgLFVs.js +0 -1
- package/dist/public/assets/settings-view-Bk-knKOJ.js +0 -2
- package/dist/public/assets/slug-utils-DgUDtDRP.js +0 -1
- package/dist/public/assets/store-IJouqtOd.js +0 -9
- package/dist/public/assets/suspense-resource-BFrb_XJX.js +0 -1
- package/dist/public/assets/table-CYtkHxHy.js +0 -1
- package/dist/public/assets/tasks-view-DpGp9_WH.js +0 -1
- package/dist/public/assets/terminal-panel-OW4yyorA.js +0 -38
- package/dist/public/assets/toggle-group-rSptomuG.js +0 -1
- package/dist/public/assets/tooltip-BqGy0Y1-.js +0 -1
- package/dist/public/assets/use-installed-ai-files-BcrX9Nna.js +0 -1
- package/dist/public/assets/use-settings-page-loading-BTySlInF.js +0 -1
|
@@ -247,37 +247,47 @@ function embedFile(filePath) {
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
function
|
|
251
|
-
const result = {};
|
|
250
|
+
function readFeedbackMap(workspace) {
|
|
252
251
|
const feedbackMap = {};
|
|
253
252
|
const feedbackPath = path.join(workspace, "feedback.json");
|
|
254
|
-
if (fs.existsSync(feedbackPath))
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
if (!fs.existsSync(feedbackPath)) return feedbackMap;
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const data = JSON.parse(fs.readFileSync(feedbackPath, "utf-8"));
|
|
257
|
+
for (const review of data.reviews ?? []) {
|
|
258
|
+
if ((review.feedback ?? "").trim()) {
|
|
259
|
+
feedbackMap[review.run_id] = review.feedback;
|
|
261
260
|
}
|
|
262
|
-
} catch {
|
|
263
|
-
// ignore
|
|
264
261
|
}
|
|
262
|
+
} catch {
|
|
263
|
+
// Ignore missing or malformed previous feedback.
|
|
265
264
|
}
|
|
265
|
+
return feedbackMap;
|
|
266
|
+
}
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
|
|
268
|
+
function mapRunsToPreviousIteration(runs, feedbackMap) {
|
|
269
|
+
const result = {};
|
|
270
|
+
for (const run of runs) {
|
|
269
271
|
result[run.id] = {
|
|
270
272
|
feedback: feedbackMap[run.id] ?? "",
|
|
271
273
|
outputs: run.outputs ?? [],
|
|
272
274
|
};
|
|
273
275
|
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
274
278
|
|
|
279
|
+
function addOrphanedFeedback(result, feedbackMap) {
|
|
275
280
|
for (const [runId, feedback] of Object.entries(feedbackMap)) {
|
|
276
281
|
if (!result[runId]) {
|
|
277
282
|
result[runId] = { feedback, outputs: [] };
|
|
278
283
|
}
|
|
279
284
|
}
|
|
285
|
+
}
|
|
280
286
|
|
|
287
|
+
function loadPreviousIteration(workspace) {
|
|
288
|
+
const feedbackMap = readFeedbackMap(workspace);
|
|
289
|
+
const result = mapRunsToPreviousIteration(findRuns(workspace), feedbackMap);
|
|
290
|
+
addOrphanedFeedback(result, feedbackMap);
|
|
281
291
|
return result;
|
|
282
292
|
}
|
|
283
293
|
|
|
@@ -306,32 +316,80 @@ function generateHtml(runs, skillName, previous = null, benchmark = null) {
|
|
|
306
316
|
return template.replace("/*__EMBEDDED_DATA__*/", `const EMBEDDED_DATA = ${dataJson};`);
|
|
307
317
|
}
|
|
308
318
|
|
|
309
|
-
function
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
process.kill(parseInt(pidStr.trim(), 10), "SIGTERM");
|
|
319
|
-
} catch {
|
|
320
|
-
// ignore
|
|
321
|
-
}
|
|
319
|
+
function parseProcessIds(output) {
|
|
320
|
+
const processIds = new Set();
|
|
321
|
+
for (const line of (output ?? "").split("\n")) {
|
|
322
|
+
const candidate = line.trim();
|
|
323
|
+
if (!/^\d+$/.test(candidate)) continue;
|
|
324
|
+
|
|
325
|
+
const processId = Number.parseInt(candidate, 10);
|
|
326
|
+
if (Number.isSafeInteger(processId) && processId > 0) {
|
|
327
|
+
processIds.add(processId);
|
|
322
328
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
}
|
|
330
|
+
return [...processIds];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function discoverPortProcesses(port, runCommand) {
|
|
334
|
+
const result = runCommand("lsof", ["-ti", `:${port}`], {
|
|
335
|
+
encoding: "utf-8",
|
|
336
|
+
timeout: 5000,
|
|
337
|
+
});
|
|
338
|
+
if (result.error) throw result.error;
|
|
339
|
+
return parseProcessIds(result.stdout);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function terminateProcesses(processIds, terminateProcess) {
|
|
343
|
+
for (const processId of processIds) {
|
|
344
|
+
try {
|
|
345
|
+
terminateProcess(processId, "SIGTERM");
|
|
346
|
+
} catch {
|
|
347
|
+
// Ignore processes which have already exited or cannot be terminated.
|
|
331
348
|
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function busyWait(duration, getTime) {
|
|
353
|
+
const deadline = getTime() + duration;
|
|
354
|
+
while (getTime() < deadline) {
|
|
355
|
+
// Preserve the fallback when the sleep utility is unavailable.
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function pauseAfterTermination(runCommand, getTime) {
|
|
360
|
+
try {
|
|
361
|
+
const sleep = runCommand("sleep", ["0.5"], { stdio: "ignore" });
|
|
362
|
+
if (!sleep.error) return;
|
|
363
|
+
} catch {
|
|
364
|
+
// Fall back to a brief busy wait below.
|
|
365
|
+
}
|
|
366
|
+
busyWait(500, getTime);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function killProcess(processId, signal) {
|
|
370
|
+
process.kill(processId, signal);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function writeStandardError(message) {
|
|
374
|
+
process.stderr.write(message);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function killPort(
|
|
378
|
+
port,
|
|
379
|
+
{
|
|
380
|
+
runCommand = spawnSync,
|
|
381
|
+
terminateProcess = killProcess,
|
|
382
|
+
getTime = Date.now,
|
|
383
|
+
writeError = writeStandardError,
|
|
384
|
+
} = {},
|
|
385
|
+
) {
|
|
386
|
+
try {
|
|
387
|
+
const processIds = discoverPortProcesses(port, runCommand);
|
|
388
|
+
terminateProcesses(processIds, terminateProcess);
|
|
389
|
+
if (processIds.length > 0) pauseAfterTermination(runCommand, getTime);
|
|
332
390
|
} catch (error) {
|
|
333
391
|
if (error.message?.includes("ENOENT")) {
|
|
334
|
-
|
|
392
|
+
writeError("Note: lsof not found, cannot check if port is in use\n");
|
|
335
393
|
}
|
|
336
394
|
}
|
|
337
395
|
}
|
|
@@ -346,70 +404,98 @@ function openUrl(url) {
|
|
|
346
404
|
}
|
|
347
405
|
}
|
|
348
406
|
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
352
|
-
|
|
353
|
-
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) {
|
|
354
|
-
const runs = findRuns(workspace);
|
|
355
|
-
let benchmark = null;
|
|
356
|
-
if (benchmarkPath && fs.existsSync(benchmarkPath)) {
|
|
357
|
-
try {
|
|
358
|
-
benchmark = JSON.parse(fs.readFileSync(benchmarkPath, "utf-8"));
|
|
359
|
-
} catch {
|
|
360
|
-
// ignore
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
const html = generateHtml(runs, skillName, previous, benchmark);
|
|
364
|
-
const content = Buffer.from(html, "utf-8");
|
|
365
|
-
res.writeHead(200, {
|
|
366
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
367
|
-
"Content-Length": content.length,
|
|
368
|
-
});
|
|
369
|
-
res.end(content);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
407
|
+
function loadBenchmark(benchmarkPath) {
|
|
408
|
+
if (!benchmarkPath || !fs.existsSync(benchmarkPath)) return null;
|
|
372
409
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
res.end(data);
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
410
|
+
try {
|
|
411
|
+
return JSON.parse(fs.readFileSync(benchmarkPath, "utf-8"));
|
|
412
|
+
} catch {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
382
416
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
417
|
+
function sendResponse(res, status, contentType, content) {
|
|
418
|
+
res.writeHead(status, {
|
|
419
|
+
"Content-Type": contentType,
|
|
420
|
+
"Content-Length": content.length,
|
|
421
|
+
});
|
|
422
|
+
res.end(content);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function renderReviewPage(res, { workspace, skillName, previous, benchmarkPath }) {
|
|
426
|
+
const runs = findRuns(workspace);
|
|
427
|
+
const benchmark = loadBenchmark(benchmarkPath);
|
|
428
|
+
const html = generateHtml(runs, skillName, previous, benchmark);
|
|
429
|
+
sendResponse(res, 200, "text/html; charset=utf-8", Buffer.from(html, "utf-8"));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function sendFeedback(res, feedbackPath) {
|
|
433
|
+
const data = fs.existsSync(feedbackPath) ? fs.readFileSync(feedbackPath) : Buffer.from("{}");
|
|
434
|
+
sendResponse(res, 200, "application/json", data);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function persistFeedback(res, feedbackPath, chunks) {
|
|
438
|
+
let content;
|
|
439
|
+
let status = 200;
|
|
440
|
+
try {
|
|
441
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
442
|
+
const data = JSON.parse(body);
|
|
443
|
+
if (!data || typeof data !== "object" || !("reviews" in data)) {
|
|
444
|
+
throw new Error("Expected JSON object with 'reviews' key");
|
|
408
445
|
}
|
|
446
|
+
fs.writeFileSync(feedbackPath, `${JSON.stringify(data, null, 2)}\n`);
|
|
447
|
+
content = Buffer.from('{"ok":true}');
|
|
448
|
+
} catch (error) {
|
|
449
|
+
content = Buffer.from(JSON.stringify({ error: error.message }));
|
|
450
|
+
status = 500;
|
|
451
|
+
}
|
|
452
|
+
sendResponse(res, status, "application/json", content);
|
|
453
|
+
}
|
|
409
454
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
455
|
+
function receiveFeedback(req, res, feedbackPath) {
|
|
456
|
+
const chunks = [];
|
|
457
|
+
|
|
458
|
+
function collectChunk(chunk) {
|
|
459
|
+
chunks.push(chunk);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function finishFeedback() {
|
|
463
|
+
persistFeedback(res, feedbackPath, chunks);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
req.on("data", collectChunk);
|
|
467
|
+
req.on("end", finishFeedback);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function routeReviewRequest(req, res, options) {
|
|
471
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
472
|
+
const isFeedbackRequest = url.pathname === "/api/feedback";
|
|
473
|
+
|
|
474
|
+
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) {
|
|
475
|
+
renderReviewPage(res, options);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (req.method === "GET" && isFeedbackRequest) {
|
|
479
|
+
sendFeedback(res, options.feedbackPath);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (req.method === "POST" && isFeedbackRequest) {
|
|
483
|
+
receiveFeedback(req, res, options.feedbackPath);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
res.writeHead(404);
|
|
488
|
+
res.end();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function createReviewServer({ workspace, skillName, feedbackPath, previous, benchmarkPath }) {
|
|
492
|
+
const options = { workspace, skillName, feedbackPath, previous, benchmarkPath };
|
|
493
|
+
|
|
494
|
+
function handleReviewRequest(req, res) {
|
|
495
|
+
routeReviewRequest(req, res, options);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return http.createServer(handleReviewRequest);
|
|
413
499
|
}
|
|
414
500
|
|
|
415
501
|
function parseArgs(argv) {
|
|
@@ -540,6 +626,8 @@ function main() {
|
|
|
540
626
|
});
|
|
541
627
|
}
|
|
542
628
|
|
|
629
|
+
module.exports = { createReviewServer, killPort, loadPreviousIteration };
|
|
630
|
+
|
|
543
631
|
if (require.main === module) {
|
|
544
632
|
main();
|
|
545
633
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const assert = require("node:assert/strict");
|
|
2
|
+
const test = require("node:test");
|
|
3
|
+
|
|
4
|
+
const { buildHistoryText } = require("../improve_description.js");
|
|
5
|
+
|
|
6
|
+
const header =
|
|
7
|
+
"PREVIOUS ATTEMPTS (do NOT repeat these — try something structurally different):\n\n";
|
|
8
|
+
const longQuery = "q".repeat(81);
|
|
9
|
+
|
|
10
|
+
const cases = [
|
|
11
|
+
{
|
|
12
|
+
name: "returns an empty string for empty history",
|
|
13
|
+
history: [],
|
|
14
|
+
expected: "",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "renders legacy score fields",
|
|
18
|
+
history: [{ description: "Legacy description", passed: 2, total: 3 }],
|
|
19
|
+
expected: header + '<attempt train=2/3>\nDescription: "Legacy description"\n</attempt>\n\n',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "renders current train and test score fields",
|
|
23
|
+
history: [
|
|
24
|
+
{
|
|
25
|
+
description: "Current description",
|
|
26
|
+
train_passed: 4,
|
|
27
|
+
train_total: 5,
|
|
28
|
+
test_passed: 0,
|
|
29
|
+
test_total: 2,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
expected:
|
|
33
|
+
header + '<attempt train=4/5, test=0/2>\nDescription: "Current description"\n</attempt>\n\n',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "renders pass and fail results with truncated queries",
|
|
37
|
+
history: [
|
|
38
|
+
{
|
|
39
|
+
description: "Results description",
|
|
40
|
+
train_passed: 1,
|
|
41
|
+
train_total: 2,
|
|
42
|
+
results: [
|
|
43
|
+
{ pass: true, query: "short query", triggers: 2, runs: 2 },
|
|
44
|
+
{ pass: false, query: longQuery, triggers: 0, runs: 3 },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
expected:
|
|
49
|
+
header +
|
|
50
|
+
'<attempt train=1/2>\nDescription: "Results description"\nTrain results:\n' +
|
|
51
|
+
' [PASS] "short query" (triggered 2/2)\n' +
|
|
52
|
+
` [FAIL] "${"q".repeat(80)}" (triggered 0/3)\n` +
|
|
53
|
+
"</attempt>\n\n",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "renders notes and unknown test totals",
|
|
57
|
+
history: [
|
|
58
|
+
{
|
|
59
|
+
description: "Noted description",
|
|
60
|
+
train_passed: 3,
|
|
61
|
+
train_total: 3,
|
|
62
|
+
test_passed: 1,
|
|
63
|
+
note: "Try narrower wording.",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
expected:
|
|
67
|
+
header +
|
|
68
|
+
'<attempt train=3/3, test=1/?>\nDescription: "Noted description"\n' +
|
|
69
|
+
"Note: Try narrower wording.\n</attempt>\n\n",
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
for (const testCase of cases) {
|
|
74
|
+
test(testCase.name, () => {
|
|
75
|
+
assert.equal(buildHistoryText(testCase.history), testCase.expected);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const assert = require("node:assert/strict");
|
|
2
|
+
const fs = require("node:fs");
|
|
3
|
+
const os = require("node:os");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const test = require("node:test");
|
|
6
|
+
|
|
7
|
+
const { packageSkill } = require("../package_skill.js");
|
|
8
|
+
|
|
9
|
+
function withTemporaryDirectory(callback) {
|
|
10
|
+
const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "tarsk-package-skill-"));
|
|
11
|
+
try {
|
|
12
|
+
return callback(temporaryDirectory);
|
|
13
|
+
} finally {
|
|
14
|
+
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createSkill(rootDirectory, name = "example-skill") {
|
|
19
|
+
const skillPath = path.join(rootDirectory, name);
|
|
20
|
+
fs.mkdirSync(skillPath, { recursive: true });
|
|
21
|
+
fs.writeFileSync(
|
|
22
|
+
path.join(skillPath, "SKILL.md"),
|
|
23
|
+
"---\nname: example-skill\ndescription: Test\n---\n",
|
|
24
|
+
);
|
|
25
|
+
return skillPath;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function successfulValidation() {
|
|
29
|
+
return [true, "Skill is valid!"];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function unexpectedArchiveExecution() {
|
|
33
|
+
assert.fail("archive execution should not be reached");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function dependencies(overrides = {}) {
|
|
37
|
+
return {
|
|
38
|
+
validateSkill: successfulValidation,
|
|
39
|
+
executeArchive: unexpectedArchiveExecution,
|
|
40
|
+
...overrides,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test("rejects a missing skill path before validation", function () {
|
|
45
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
46
|
+
const missingPath = path.join(temporaryDirectory, "missing");
|
|
47
|
+
assert.equal(packageSkill(missingPath, null, dependencies()), null);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("rejects a skill path that is not a directory", function () {
|
|
52
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
53
|
+
const filePath = path.join(temporaryDirectory, "skill.txt");
|
|
54
|
+
fs.writeFileSync(filePath, "not a directory");
|
|
55
|
+
assert.equal(packageSkill(filePath, null, dependencies()), null);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("rejects a directory without SKILL.md", function () {
|
|
60
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
61
|
+
assert.equal(packageSkill(temporaryDirectory, null, dependencies()), null);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("returns null when skill validation fails", function () {
|
|
66
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
67
|
+
const skillPath = createSkill(temporaryDirectory);
|
|
68
|
+
const result = packageSkill(
|
|
69
|
+
skillPath,
|
|
70
|
+
null,
|
|
71
|
+
dependencies({
|
|
72
|
+
validateSkill: function validateInvalidSkill() {
|
|
73
|
+
return [false, "Invalid frontmatter"];
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
assert.equal(result, null);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("creates an archive in the current directory by default", function () {
|
|
82
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
83
|
+
const skillPath = createSkill(temporaryDirectory);
|
|
84
|
+
const archivePath = path.join(temporaryDirectory, "example-skill.skill");
|
|
85
|
+
const previousWorkingDirectory = process.cwd();
|
|
86
|
+
fs.writeFileSync(archivePath, "old archive");
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
process.chdir(temporaryDirectory);
|
|
90
|
+
const resolvedArchivePath = path.join(process.cwd(), "example-skill.skill");
|
|
91
|
+
const result = packageSkill(
|
|
92
|
+
skillPath,
|
|
93
|
+
null,
|
|
94
|
+
dependencies({
|
|
95
|
+
executeArchive: function createTestArchive(stagingDir, filename, skillName) {
|
|
96
|
+
assert.equal(filename, resolvedArchivePath);
|
|
97
|
+
assert.equal(skillName, "example-skill");
|
|
98
|
+
assert.equal(fs.existsSync(filename), false);
|
|
99
|
+
assert.equal(fs.existsSync(path.join(stagingDir, skillName, "SKILL.md")), true);
|
|
100
|
+
fs.writeFileSync(filename, "new archive");
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
assert.equal(result, resolvedArchivePath);
|
|
106
|
+
assert.equal(fs.readFileSync(archivePath, "utf8"), "new archive");
|
|
107
|
+
} finally {
|
|
108
|
+
process.chdir(previousWorkingDirectory);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("creates a missing custom output directory", function () {
|
|
114
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
115
|
+
const skillPath = createSkill(temporaryDirectory);
|
|
116
|
+
const outputDirectory = path.join(temporaryDirectory, "nested", "output");
|
|
117
|
+
const expectedArchive = path.join(outputDirectory, "example-skill.skill");
|
|
118
|
+
|
|
119
|
+
const result = packageSkill(
|
|
120
|
+
skillPath,
|
|
121
|
+
outputDirectory,
|
|
122
|
+
dependencies({
|
|
123
|
+
executeArchive: function createTestArchive(_stagingDir, filename) {
|
|
124
|
+
fs.writeFileSync(filename, "archive");
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
assert.equal(result, expectedArchive);
|
|
130
|
+
assert.equal(fs.existsSync(expectedArchive), true);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("excludes generated, dependency, metadata, and root eval files", function () {
|
|
135
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
136
|
+
const skillPath = createSkill(temporaryDirectory);
|
|
137
|
+
const files = {
|
|
138
|
+
"keep.txt": "keep",
|
|
139
|
+
"compiled.pyc": "exclude",
|
|
140
|
+
".DS_Store": "exclude",
|
|
141
|
+
"node_modules/package/index.js": "exclude",
|
|
142
|
+
"__pycache__/module.pyc": "exclude",
|
|
143
|
+
"evals/result.json": "exclude",
|
|
144
|
+
"nested/evals/keep.json": "keep",
|
|
145
|
+
};
|
|
146
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
147
|
+
const filename = path.join(skillPath, relativePath);
|
|
148
|
+
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
149
|
+
fs.writeFileSync(filename, content);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let stagedFiles = [];
|
|
153
|
+
const result = packageSkill(
|
|
154
|
+
skillPath,
|
|
155
|
+
temporaryDirectory,
|
|
156
|
+
dependencies({
|
|
157
|
+
executeArchive: function inspectStagedFiles(stagingDir, filename) {
|
|
158
|
+
stagedFiles = fs
|
|
159
|
+
.readdirSync(path.join(stagingDir, "example-skill"), {
|
|
160
|
+
recursive: true,
|
|
161
|
+
withFileTypes: true,
|
|
162
|
+
})
|
|
163
|
+
.filter(function selectFiles(entry) {
|
|
164
|
+
return entry.isFile();
|
|
165
|
+
})
|
|
166
|
+
.map(function relativeFilename(entry) {
|
|
167
|
+
return path.relative(stagingDir, path.join(entry.parentPath, entry.name));
|
|
168
|
+
})
|
|
169
|
+
.sort();
|
|
170
|
+
fs.writeFileSync(filename, "archive");
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
assert.equal(result, path.join(temporaryDirectory, "example-skill.skill"));
|
|
176
|
+
assert.deepEqual(stagedFiles, [
|
|
177
|
+
"example-skill/SKILL.md",
|
|
178
|
+
"example-skill/keep.txt",
|
|
179
|
+
"example-skill/nested/evals/keep.json",
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("returns null and removes staging files when archive execution fails", function () {
|
|
185
|
+
withTemporaryDirectory(function (temporaryDirectory) {
|
|
186
|
+
const skillPath = createSkill(temporaryDirectory);
|
|
187
|
+
let stagingDirectory;
|
|
188
|
+
|
|
189
|
+
const result = packageSkill(
|
|
190
|
+
skillPath,
|
|
191
|
+
temporaryDirectory,
|
|
192
|
+
dependencies({
|
|
193
|
+
executeArchive: function failArchive(stagingDir) {
|
|
194
|
+
stagingDirectory = stagingDir;
|
|
195
|
+
throw new Error("zip failed");
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
assert.equal(result, null);
|
|
201
|
+
assert.equal(fs.existsSync(stagingDirectory), false);
|
|
202
|
+
});
|
|
203
|
+
});
|