orizu 0.3.5 → 0.3.7
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/README.md +25 -3
- package/dist/help.js +611 -0
- package/dist/index.js +268 -15
- package/dist/skill-installer.js +180 -0
- package/package.json +3 -3
- package/scripts/vendor-orizu-skill.mjs +54 -0
- package/src/help.ts +676 -0
- package/src/index.ts +353 -15
- package/src/skill-installer.ts +260 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/__init__.py +4 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +24 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +469 -21
- package/vendor/skills/orizu-cli/SKILL.md +186 -0
- package/vendor/skills/orizu-cli/agents/openai.yaml +4 -0
- package/vendor/skills/orizu-cli/references/building-apps.md +1461 -0
- package/vendor/skills/orizu-cli/references/building-judges.md +218 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +325 -0
- package/vendor/skills/orizu-cli/references/optimization-reports.md +174 -0
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +291 -0
- package/vendor/skills/orizu-cli/references/primer.md +245 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +832 -0
- package/vendor/skills/orizu-cli/scripts/test-app.mjs +492 -0
package/dist/index.js
CHANGED
|
@@ -13,9 +13,12 @@ import { clearServerCredentials, getServerCredentials, saveServerCredentials } f
|
|
|
13
13
|
import { parseDatasetFile } from './file-parser.js';
|
|
14
14
|
import { streamJsonlRowChunks } from './jsonl-stream.js';
|
|
15
15
|
import { parseDatasetReference } from './dataset-download.js';
|
|
16
|
+
import { extractErrorMessage } from './error-response.js';
|
|
16
17
|
import { parseGlobalFlags } from './global-flags.js';
|
|
18
|
+
import { getCapabilities, renderHelpForArgs, renderRootHelp } from './help.js';
|
|
17
19
|
import { runLocalAppPreview } from './preview-runtime.js';
|
|
18
20
|
import { assertSecureTokenTransport, authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags, } from './http.js';
|
|
21
|
+
import { getSkillInstallPath, installSkillTarget, isSkillInstallTarget, SKILL_INSTALL_TARGETS, targetNeedsOverwrite, } from './skill-installer.js';
|
|
19
22
|
import { formatTaskCreateError } from './task-create-error.js';
|
|
20
23
|
const MAX_README_LENGTH = 200_000;
|
|
21
24
|
function getCliVersion() {
|
|
@@ -32,16 +35,16 @@ function printVersion() {
|
|
|
32
35
|
printLine(`orizu ${getCliVersion()}`);
|
|
33
36
|
}
|
|
34
37
|
function printUsage() {
|
|
35
|
-
printLine(
|
|
36
|
-
printPreviewUsage();
|
|
38
|
+
printLine(renderRootHelp());
|
|
37
39
|
}
|
|
38
40
|
function printPreviewUsage() {
|
|
39
41
|
printLine(`\nLocal app preview:\n\n orizu apps preview --file <path> --input-schema <json-path> --output-schema <json-path> --sample-row <json-path> [--screenshot <png-path>] [--headed] [--keep-open] [--component <name>]`);
|
|
40
42
|
}
|
|
41
43
|
function printOptimizationUsage() {
|
|
42
|
-
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--json]`);
|
|
44
|
+
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--num-threads auto|N] [--log-dir logs] [--no-skip-perfect-parent-reflection]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--json]`);
|
|
43
45
|
}
|
|
44
46
|
let cliArgs = process.argv.slice(2);
|
|
47
|
+
let cliJsonOutput = false;
|
|
45
48
|
function getArg(name) {
|
|
46
49
|
const index = cliArgs.indexOf(name);
|
|
47
50
|
if (index === -1 || index + 1 >= cliArgs.length) {
|
|
@@ -49,6 +52,26 @@ function getArg(name) {
|
|
|
49
52
|
}
|
|
50
53
|
return cliArgs[index + 1];
|
|
51
54
|
}
|
|
55
|
+
function getArgs(name) {
|
|
56
|
+
const values = [];
|
|
57
|
+
for (let index = 0; index < cliArgs.length; index += 1) {
|
|
58
|
+
if (cliArgs[index] !== name) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const value = cliArgs[index + 1];
|
|
62
|
+
if (!value || value.startsWith('-')) {
|
|
63
|
+
throw new Error(`Usage: ${name} <value>`);
|
|
64
|
+
}
|
|
65
|
+
values.push(value);
|
|
66
|
+
index += 1;
|
|
67
|
+
}
|
|
68
|
+
return values;
|
|
69
|
+
}
|
|
70
|
+
function rejectDashPrefixedOptionValue(name, value) {
|
|
71
|
+
if (value && value.startsWith('-')) {
|
|
72
|
+
throw new Error(`Invalid value for ${name}: option values cannot start with a dash`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
52
75
|
export function normalizeSlugInput(slug) {
|
|
53
76
|
return slug.trim().toLowerCase();
|
|
54
77
|
}
|
|
@@ -1569,7 +1592,7 @@ async function execScorer() {
|
|
|
1569
1592
|
printLine(`Wrote scorer result to ${sanitizeTerminalText(outPath)}`);
|
|
1570
1593
|
}
|
|
1571
1594
|
}
|
|
1572
|
-
const
|
|
1595
|
+
const MARKDOWN_REPORT_MAX_BYTES = 2 * 1024 * 1024;
|
|
1573
1596
|
function parseOptionalNumberFlag(name) {
|
|
1574
1597
|
const value = getArg(name);
|
|
1575
1598
|
if (!value) {
|
|
@@ -1584,9 +1607,11 @@ function parseOptionalNumberFlag(name) {
|
|
|
1584
1607
|
function hasObjectKeys(value) {
|
|
1585
1608
|
return Object.keys(value).length > 0;
|
|
1586
1609
|
}
|
|
1587
|
-
function
|
|
1610
|
+
function readMarkdownReportInput(reportLabel) {
|
|
1588
1611
|
const report = getArg('--report');
|
|
1589
1612
|
const reportFile = getArg('--report-file');
|
|
1613
|
+
rejectDashPrefixedOptionValue('--report', report);
|
|
1614
|
+
rejectDashPrefixedOptionValue('--report-file', reportFile);
|
|
1590
1615
|
if (report && reportFile) {
|
|
1591
1616
|
throw new Error('Use either --report or --report-file, not both');
|
|
1592
1617
|
}
|
|
@@ -1611,13 +1636,19 @@ function readOptimizationReportInput() {
|
|
|
1611
1636
|
sourceName = 'inline';
|
|
1612
1637
|
}
|
|
1613
1638
|
if (!markdown.trim()) {
|
|
1614
|
-
throw new Error(
|
|
1639
|
+
throw new Error(`${reportLabel} report markdown must not be blank`);
|
|
1615
1640
|
}
|
|
1616
|
-
if (Buffer.byteLength(markdown, 'utf8') >
|
|
1617
|
-
throw new Error(
|
|
1641
|
+
if (Buffer.byteLength(markdown, 'utf8') > MARKDOWN_REPORT_MAX_BYTES) {
|
|
1642
|
+
throw new Error(`${reportLabel} report exceeds ${MARKDOWN_REPORT_MAX_BYTES} bytes`);
|
|
1618
1643
|
}
|
|
1619
1644
|
return { markdown, sourceName };
|
|
1620
1645
|
}
|
|
1646
|
+
function readOptimizationReportInput() {
|
|
1647
|
+
return readMarkdownReportInput('Optimization');
|
|
1648
|
+
}
|
|
1649
|
+
function readTaskReportInput() {
|
|
1650
|
+
return readMarkdownReportInput('Task');
|
|
1651
|
+
}
|
|
1621
1652
|
async function startOptimizationRun() {
|
|
1622
1653
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1623
1654
|
const optimizerVersionId = getArg('--optimizer-version');
|
|
@@ -1922,11 +1953,106 @@ function readSourceBytes(pathArg) {
|
|
|
1922
1953
|
}
|
|
1923
1954
|
}
|
|
1924
1955
|
function hasJsonFlag() {
|
|
1925
|
-
return hasArg('--json');
|
|
1956
|
+
return cliJsonOutput || hasArg('--json');
|
|
1926
1957
|
}
|
|
1927
1958
|
function printJson(value) {
|
|
1928
1959
|
printLine(JSON.stringify(value));
|
|
1929
1960
|
}
|
|
1961
|
+
function printCapabilities() {
|
|
1962
|
+
const capabilities = getCapabilities(getCliVersion());
|
|
1963
|
+
if (hasJsonFlag()) {
|
|
1964
|
+
printLine(JSON.stringify(capabilities));
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
printLine('orizu capabilities');
|
|
1968
|
+
printLine('');
|
|
1969
|
+
printLine('Run `orizu capabilities --json` for a machine-readable command manifest.');
|
|
1970
|
+
printLine('');
|
|
1971
|
+
for (const command of capabilities.commands) {
|
|
1972
|
+
printLine(` ${command.name.padEnd(34)} ${command.help}`);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
async function askYesNo(question, defaultYes) {
|
|
1976
|
+
const rl = createInterface({ input, output });
|
|
1977
|
+
try {
|
|
1978
|
+
const suffix = defaultYes ? 'Y/n' : 'y/N';
|
|
1979
|
+
const answer = (await rl.question(`${question} (${suffix}) `)).trim().toLowerCase();
|
|
1980
|
+
if (!answer) {
|
|
1981
|
+
return defaultYes;
|
|
1982
|
+
}
|
|
1983
|
+
return answer === 'y' || answer === 'yes';
|
|
1984
|
+
}
|
|
1985
|
+
finally {
|
|
1986
|
+
rl.close();
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async function promptSkillInstallTargets() {
|
|
1990
|
+
if (!isInteractiveTerminal()) {
|
|
1991
|
+
throw new Error(`Usage: orizu install-skill --target <${SKILL_INSTALL_TARGETS.join('|')}> [--yes] [--dry-run]`);
|
|
1992
|
+
}
|
|
1993
|
+
printLine('Where should the Orizu CLI skill be installed?');
|
|
1994
|
+
const targets = [];
|
|
1995
|
+
for (const target of SKILL_INSTALL_TARGETS) {
|
|
1996
|
+
const defaultYes = target === 'agent-user';
|
|
1997
|
+
if (await askYesNo(` Install to ${target}?`, defaultYes)) {
|
|
1998
|
+
targets.push(target);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
return targets;
|
|
2002
|
+
}
|
|
2003
|
+
function parseSkillInstallTargets() {
|
|
2004
|
+
const rawTargets = getArgs('--target');
|
|
2005
|
+
const targets = [];
|
|
2006
|
+
for (const target of rawTargets) {
|
|
2007
|
+
if (!isSkillInstallTarget(target)) {
|
|
2008
|
+
throw new Error(`Unknown skill install target '${target}'. Available targets: ${SKILL_INSTALL_TARGETS.join(', ')}`);
|
|
2009
|
+
}
|
|
2010
|
+
if (!targets.includes(target)) {
|
|
2011
|
+
targets.push(target);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return targets;
|
|
2015
|
+
}
|
|
2016
|
+
function formatSkillInstallAction(action) {
|
|
2017
|
+
if (action === 'created')
|
|
2018
|
+
return 'Wrote';
|
|
2019
|
+
if (action === 'updated')
|
|
2020
|
+
return 'Updated';
|
|
2021
|
+
if (action === 'would-create')
|
|
2022
|
+
return 'Would write';
|
|
2023
|
+
return 'Would update';
|
|
2024
|
+
}
|
|
2025
|
+
async function installSkillCommand() {
|
|
2026
|
+
const skipConfirm = hasArg('--yes');
|
|
2027
|
+
const dryRun = hasArg('--dry-run');
|
|
2028
|
+
let targets = parseSkillInstallTargets();
|
|
2029
|
+
if (targets.length === 0) {
|
|
2030
|
+
targets = await promptSkillInstallTargets();
|
|
2031
|
+
}
|
|
2032
|
+
if (targets.length === 0) {
|
|
2033
|
+
printLine('No targets selected; nothing to do.');
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
for (const target of targets) {
|
|
2037
|
+
let overwrite = skipConfirm;
|
|
2038
|
+
const installPath = getSkillInstallPath(target);
|
|
2039
|
+
if (!dryRun && !overwrite && targetNeedsOverwrite(target)) {
|
|
2040
|
+
if (!isInteractiveTerminal()) {
|
|
2041
|
+
throw new Error(`${installPath} already exists. Pass --yes to replace it.`);
|
|
2042
|
+
}
|
|
2043
|
+
overwrite = await askYesNo(`Replace ${installPath}?`, true);
|
|
2044
|
+
if (!overwrite) {
|
|
2045
|
+
printLine(`Skipped ${installPath}`);
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
const result = installSkillTarget(target, {
|
|
2050
|
+
overwrite,
|
|
2051
|
+
dryRun,
|
|
2052
|
+
});
|
|
2053
|
+
printLine(`${formatSkillInstallAction(result.action)} ${result.path}`);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
1930
2056
|
function readJsonFile(pathArg) {
|
|
1931
2057
|
const raw = readSourceFile(pathArg);
|
|
1932
2058
|
try {
|
|
@@ -2283,14 +2409,15 @@ async function createTask() {
|
|
|
2283
2409
|
const appId = getArg('--app');
|
|
2284
2410
|
const title = getArg('--title');
|
|
2285
2411
|
const assignees = parseCommaSeparated(getArg('--assignees'));
|
|
2412
|
+
const publish = hasArg('--publish');
|
|
2286
2413
|
const versionArg = getArg('--version');
|
|
2287
2414
|
const instructions = getArg('--instructions');
|
|
2288
2415
|
const labelsPerItemArg = getArg('--labels-per-item');
|
|
2289
2416
|
const labelsPerItem = labelsPerItemArg ? Number(labelsPerItemArg) : 1;
|
|
2290
2417
|
const parsedVersionNum = versionArg ? Number(versionArg) : Number.NaN;
|
|
2291
2418
|
const versionNum = Number.isInteger(parsedVersionNum) && parsedVersionNum > 0 ? parsedVersionNum : null;
|
|
2292
|
-
if (!projectSlug || !datasetId || !appId || !title || assignees.length === 0) {
|
|
2293
|
-
throw new Error('Usage: orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]');
|
|
2419
|
+
if (!projectSlug || !datasetId || !appId || !title || (publish && assignees.length === 0)) {
|
|
2420
|
+
throw new Error('Usage: orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> [--assignees <userIdOrEmail1,userIdOrEmail2>] [--publish] [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]');
|
|
2294
2421
|
}
|
|
2295
2422
|
if (versionArg && versionNum === null) {
|
|
2296
2423
|
throw new Error('--version must be a positive integer');
|
|
@@ -2305,6 +2432,7 @@ async function createTask() {
|
|
|
2305
2432
|
versionNum,
|
|
2306
2433
|
title,
|
|
2307
2434
|
memberIds: assignees,
|
|
2435
|
+
publish,
|
|
2308
2436
|
instructions,
|
|
2309
2437
|
requiredAssignmentsPerRow: labelsPerItem,
|
|
2310
2438
|
}),
|
|
@@ -2332,6 +2460,13 @@ async function createTask() {
|
|
|
2332
2460
|
status: data.task.status,
|
|
2333
2461
|
requiredAssignmentsPerRow: data.task.requiredAssignmentsPerRow,
|
|
2334
2462
|
assignmentsCreated: data.assignmentsCreated,
|
|
2463
|
+
draft: data.task.status === 'draft',
|
|
2464
|
+
...(data.task.status === 'draft'
|
|
2465
|
+
? {
|
|
2466
|
+
message: 'Draft created. Test it manually before assigning.',
|
|
2467
|
+
publishCommand: `orizu tasks publish --task ${data.task.id} --assignees <userId1,userId2>`,
|
|
2468
|
+
}
|
|
2469
|
+
: {}),
|
|
2335
2470
|
...(data.assignmentShortfall !== undefined ? { assignmentShortfall: data.assignmentShortfall } : {}),
|
|
2336
2471
|
...(data.warning ? { warning: data.warning } : {}),
|
|
2337
2472
|
}, null, 2));
|
|
@@ -2339,14 +2474,56 @@ async function createTask() {
|
|
|
2339
2474
|
}
|
|
2340
2475
|
const baseUrl = getBaseUrl();
|
|
2341
2476
|
const taskUrl = `${baseUrl}/d/${projectSlug}/tasks/${data.task.id}`;
|
|
2342
|
-
|
|
2477
|
+
const isDraft = data.task.status === 'draft';
|
|
2478
|
+
printLine(`Created ${isDraft ? 'draft ' : ''}task ${sanitizeTerminalText(data.task.title)} (${sanitizeTerminalText(data.task.id)}) [${sanitizeTerminalText(data.task.status)}]` +
|
|
2343
2479
|
`\n Task ID: ${sanitizeTerminalText(data.task.id)}` +
|
|
2344
2480
|
`\n Dataset ID: ${sanitizeTerminalText(datasetId)}` +
|
|
2345
2481
|
`\n Version: v${data.task.versionNum} (${sanitizeTerminalText(data.task.versionId)})` +
|
|
2346
2482
|
`\n Labels/row: ${data.task.requiredAssignmentsPerRow}` +
|
|
2347
2483
|
`\n Assignments: ${data.assignmentsCreated}` +
|
|
2348
2484
|
(data.warning ? `\n Warning: ${sanitizeTerminalText(data.warning)}` : '') +
|
|
2349
|
-
`\n URL: ${sanitizeTerminalText(taskUrl)}`
|
|
2485
|
+
`\n URL: ${sanitizeTerminalText(taskUrl)}` +
|
|
2486
|
+
(isDraft
|
|
2487
|
+
? `\n\nThis task is a draft. Test it manually before assigning.\nPublish after approval: orizu tasks publish --task ${sanitizeTerminalText(data.task.id)} --assignees <userId1,userId2>`
|
|
2488
|
+
: ''));
|
|
2489
|
+
}
|
|
2490
|
+
async function publishTask() {
|
|
2491
|
+
const taskId = getArg('--task');
|
|
2492
|
+
const assignees = parseCommaSeparated(getArg('--assignees'));
|
|
2493
|
+
if (!taskId || assignees.length === 0) {
|
|
2494
|
+
throw new Error('Usage: orizu tasks publish --task <taskId> --assignees <userId1,userId2> [--json]');
|
|
2495
|
+
}
|
|
2496
|
+
const response = await authedFetch(`/api/cli/tasks/${encodeURIComponent(taskId)}/publish`, {
|
|
2497
|
+
method: 'POST',
|
|
2498
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2499
|
+
body: JSON.stringify({ memberIds: assignees }),
|
|
2500
|
+
});
|
|
2501
|
+
if (!response.ok) {
|
|
2502
|
+
throw new Error(`Failed to publish task: ${await extractErrorMessage(response)}`);
|
|
2503
|
+
}
|
|
2504
|
+
const data = await parseJsonResponse(response, 'Task publish');
|
|
2505
|
+
if (hasArg('--json')) {
|
|
2506
|
+
printLine(JSON.stringify({
|
|
2507
|
+
taskId: data.task.id,
|
|
2508
|
+
status: data.task.status,
|
|
2509
|
+
assignmentsCreated: data.assignmentsCreated,
|
|
2510
|
+
assignmentsProcessed: data.assignmentsProcessed,
|
|
2511
|
+
...(data.assignmentsRemoved !== undefined
|
|
2512
|
+
? { assignmentsRemoved: data.assignmentsRemoved }
|
|
2513
|
+
: {}),
|
|
2514
|
+
...(data.assignmentShortfall !== undefined
|
|
2515
|
+
? { assignmentShortfall: data.assignmentShortfall }
|
|
2516
|
+
: {}),
|
|
2517
|
+
...(data.warning ? { warning: data.warning } : {}),
|
|
2518
|
+
}, null, 2));
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
printLine(`Published task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]` +
|
|
2522
|
+
`\n Assignments: ${data.assignmentsCreated}` +
|
|
2523
|
+
(data.assignmentsRemoved !== undefined
|
|
2524
|
+
? `\n Replaced: ${data.assignmentsRemoved} old assignments removed`
|
|
2525
|
+
: '') +
|
|
2526
|
+
(data.warning ? `\n Warning: ${sanitizeTerminalText(data.warning)}` : ''));
|
|
2350
2527
|
}
|
|
2351
2528
|
async function assignTask() {
|
|
2352
2529
|
const taskId = getArg('--task');
|
|
@@ -2425,6 +2602,35 @@ async function updateTaskStatus(targetStatus) {
|
|
|
2425
2602
|
const action = targetStatus === 'paused' ? 'Paused' : 'Unpaused';
|
|
2426
2603
|
printLine(`${action} task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]`);
|
|
2427
2604
|
}
|
|
2605
|
+
async function setTaskReport() {
|
|
2606
|
+
const taskId = getArg('--task');
|
|
2607
|
+
rejectDashPrefixedOptionValue('--task', taskId);
|
|
2608
|
+
if (!taskId) {
|
|
2609
|
+
throw new Error('Usage: orizu tasks report set --task <taskId> (--report <markdown|@file> | --report-file <path>) [--json]');
|
|
2610
|
+
}
|
|
2611
|
+
const report = readTaskReportInput();
|
|
2612
|
+
if (!report) {
|
|
2613
|
+
throw new Error('Usage: orizu tasks report set --task <taskId> (--report <markdown|@file> | --report-file <path>) [--json]');
|
|
2614
|
+
}
|
|
2615
|
+
const response = await authedFetch(`/api/cli/tasks/${encodeURIComponent(taskId)}/report`, {
|
|
2616
|
+
method: 'PATCH',
|
|
2617
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2618
|
+
body: JSON.stringify({
|
|
2619
|
+
reportMarkdown: report.markdown,
|
|
2620
|
+
reportSourceName: report.sourceName,
|
|
2621
|
+
}),
|
|
2622
|
+
});
|
|
2623
|
+
if (!response.ok) {
|
|
2624
|
+
throw new Error(`Failed to update task report: ${await response.text()}`);
|
|
2625
|
+
}
|
|
2626
|
+
const data = await parseJsonResponse(response, 'Task report update');
|
|
2627
|
+
if (hasJsonFlag()) {
|
|
2628
|
+
printJson(data);
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
printLine(`Uploaded task report for ${sanitizeTerminalText(data.task.id)} ` +
|
|
2632
|
+
`[${sanitizeTerminalText(data.task.status)}]`);
|
|
2633
|
+
}
|
|
2428
2634
|
function defaultAppExportFilename(appName, versionNum) {
|
|
2429
2635
|
const safeName = appName
|
|
2430
2636
|
.trim()
|
|
@@ -3293,10 +3499,37 @@ async function runnersExec() {
|
|
|
3293
3499
|
materializedRunner.cleanup();
|
|
3294
3500
|
}
|
|
3295
3501
|
}
|
|
3502
|
+
function isHelpToken(value) {
|
|
3503
|
+
return value === '--help' || value === '-h';
|
|
3504
|
+
}
|
|
3505
|
+
function extractHelpTarget(args) {
|
|
3506
|
+
if (args[0] === 'help') {
|
|
3507
|
+
return helpTargetFromArgs(args.slice(1));
|
|
3508
|
+
}
|
|
3509
|
+
if (!args.some(isHelpToken)) {
|
|
3510
|
+
return null;
|
|
3511
|
+
}
|
|
3512
|
+
return helpTargetFromArgs(args);
|
|
3513
|
+
}
|
|
3514
|
+
function helpTargetFromArgs(args) {
|
|
3515
|
+
const targetArgs = args.filter(arg => !isHelpToken(arg) && arg !== '--json');
|
|
3516
|
+
const optionIndex = targetArgs.findIndex(arg => arg.startsWith('-'));
|
|
3517
|
+
return optionIndex === -1 ? targetArgs : targetArgs.slice(0, optionIndex);
|
|
3518
|
+
}
|
|
3296
3519
|
export async function main(rawArgs = process.argv.slice(2)) {
|
|
3297
3520
|
const parsed = parseGlobalFlags(rawArgs);
|
|
3298
3521
|
setGlobalFlags(parsed.flags);
|
|
3299
3522
|
cliArgs = parsed.args;
|
|
3523
|
+
cliJsonOutput = false;
|
|
3524
|
+
if (cliArgs[0] === '--json') {
|
|
3525
|
+
cliJsonOutput = true;
|
|
3526
|
+
cliArgs = cliArgs.slice(1);
|
|
3527
|
+
}
|
|
3528
|
+
const helpTarget = extractHelpTarget(cliArgs);
|
|
3529
|
+
if (helpTarget !== null) {
|
|
3530
|
+
printLine(renderHelpForArgs(helpTarget));
|
|
3531
|
+
return;
|
|
3532
|
+
}
|
|
3300
3533
|
const command = cliArgs[0];
|
|
3301
3534
|
const subcommand = cliArgs[1];
|
|
3302
3535
|
if (command === '--version' || command === '-v') {
|
|
@@ -3305,9 +3538,20 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3305
3538
|
}
|
|
3306
3539
|
if (!command) {
|
|
3307
3540
|
printUsage();
|
|
3308
|
-
printOptimizationUsage();
|
|
3309
3541
|
process.exit(1);
|
|
3310
3542
|
}
|
|
3543
|
+
if (command === 'capabilities') {
|
|
3544
|
+
printCapabilities();
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3547
|
+
if (command === 'install-skill') {
|
|
3548
|
+
await installSkillCommand();
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
if (command === 'skills' && subcommand === 'install') {
|
|
3552
|
+
await installSkillCommand();
|
|
3553
|
+
return;
|
|
3554
|
+
}
|
|
3311
3555
|
if (command === 'login') {
|
|
3312
3556
|
await login();
|
|
3313
3557
|
return;
|
|
@@ -3498,6 +3742,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3498
3742
|
await createTask();
|
|
3499
3743
|
return;
|
|
3500
3744
|
}
|
|
3745
|
+
if (command === 'tasks' && subcommand === 'publish') {
|
|
3746
|
+
await publishTask();
|
|
3747
|
+
return;
|
|
3748
|
+
}
|
|
3501
3749
|
if (command === 'tasks' && subcommand === 'assign') {
|
|
3502
3750
|
await assignTask();
|
|
3503
3751
|
return;
|
|
@@ -3506,6 +3754,12 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3506
3754
|
await taskStatus();
|
|
3507
3755
|
return;
|
|
3508
3756
|
}
|
|
3757
|
+
if (command === 'tasks' &&
|
|
3758
|
+
subcommand === 'report' &&
|
|
3759
|
+
(cliArgs[2] === 'set' || cliArgs[2] === 'upload')) {
|
|
3760
|
+
await setTaskReport();
|
|
3761
|
+
return;
|
|
3762
|
+
}
|
|
3509
3763
|
if (command === 'tasks' && subcommand === 'pause') {
|
|
3510
3764
|
await updateTaskStatus('paused');
|
|
3511
3765
|
return;
|
|
@@ -3568,7 +3822,6 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3568
3822
|
return;
|
|
3569
3823
|
}
|
|
3570
3824
|
printUsage();
|
|
3571
|
-
printOptimizationUsage();
|
|
3572
3825
|
process.exit(1);
|
|
3573
3826
|
}
|
|
3574
3827
|
function isCliEntrypoint() {
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from 'fs';
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
export const SKILL_NAME = 'orizu-cli';
|
|
6
|
+
export const AGENTS_START_MARKER = '<!-- orizu-cli:start -->';
|
|
7
|
+
export const AGENTS_END_MARKER = '<!-- orizu-cli:end -->';
|
|
8
|
+
export const SKILL_INSTALL_TARGETS = [
|
|
9
|
+
'agent-user',
|
|
10
|
+
'codex-project',
|
|
11
|
+
'claude-user',
|
|
12
|
+
'claude-project',
|
|
13
|
+
'agents-md',
|
|
14
|
+
];
|
|
15
|
+
function getCliRoot() {
|
|
16
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
}
|
|
18
|
+
function isSkillDir(path) {
|
|
19
|
+
return existsSync(resolve(path, 'SKILL.md'));
|
|
20
|
+
}
|
|
21
|
+
export function isSafeRelativeSkillPath(rel) {
|
|
22
|
+
return rel !== '..' && !rel.startsWith('../') && !isAbsolute(rel);
|
|
23
|
+
}
|
|
24
|
+
export function formatMissingSkillSourceError(paths) {
|
|
25
|
+
return `Unable to find bundled ${SKILL_NAME} skill. Reinstall the orizu CLI or run from the Orizu repository.\n`
|
|
26
|
+
+ `Searched:\n- ${paths.join('\n- ')}`;
|
|
27
|
+
}
|
|
28
|
+
export function resolveSkillSourceDir() {
|
|
29
|
+
const override = process.env.ORIZU_SKILL_SOURCE_DIR;
|
|
30
|
+
if (override) {
|
|
31
|
+
const resolvedOverride = resolve(override);
|
|
32
|
+
if (!isSkillDir(resolvedOverride)) {
|
|
33
|
+
throw new Error(`ORIZU_SKILL_SOURCE_DIR is not an Orizu skill directory: ${resolvedOverride}`);
|
|
34
|
+
}
|
|
35
|
+
return resolvedOverride;
|
|
36
|
+
}
|
|
37
|
+
const cliRoot = getCliRoot();
|
|
38
|
+
const packagedSkill = resolve(cliRoot, 'vendor', 'skills', SKILL_NAME);
|
|
39
|
+
if (isSkillDir(packagedSkill)) {
|
|
40
|
+
return packagedSkill;
|
|
41
|
+
}
|
|
42
|
+
const repoSkill = resolve(cliRoot, '..', '..', 'skills', SKILL_NAME);
|
|
43
|
+
if (isSkillDir(repoSkill)) {
|
|
44
|
+
return repoSkill;
|
|
45
|
+
}
|
|
46
|
+
const searchedPaths = [
|
|
47
|
+
`packaged skill: ${packagedSkill}`,
|
|
48
|
+
`repository fallback: ${repoSkill}`,
|
|
49
|
+
];
|
|
50
|
+
throw new Error(formatMissingSkillSourceError(searchedPaths));
|
|
51
|
+
}
|
|
52
|
+
function resolveHome(options) {
|
|
53
|
+
return resolve(options?.homeDir || process.env.HOME || homedir());
|
|
54
|
+
}
|
|
55
|
+
function resolveCwd(options) {
|
|
56
|
+
return resolve(options?.cwd || process.cwd());
|
|
57
|
+
}
|
|
58
|
+
export function getSkillInstallPath(target, options) {
|
|
59
|
+
const home = resolveHome(options);
|
|
60
|
+
const cwd = resolveCwd(options);
|
|
61
|
+
if (target === 'agent-user') {
|
|
62
|
+
return resolve(home, '.agents', 'skills', SKILL_NAME);
|
|
63
|
+
}
|
|
64
|
+
if (target === 'codex-project') {
|
|
65
|
+
return resolve(cwd, '.codex', 'skills', SKILL_NAME);
|
|
66
|
+
}
|
|
67
|
+
if (target === 'claude-user') {
|
|
68
|
+
return resolve(home, '.claude', 'skills', SKILL_NAME);
|
|
69
|
+
}
|
|
70
|
+
if (target === 'claude-project') {
|
|
71
|
+
return resolve(cwd, '.claude', 'skills', SKILL_NAME);
|
|
72
|
+
}
|
|
73
|
+
return resolve(cwd, 'AGENTS.md');
|
|
74
|
+
}
|
|
75
|
+
function shouldCopySkillPath(sourceRoot, sourcePath) {
|
|
76
|
+
const rel = relative(sourceRoot, sourcePath).replace(/\\/g, '/');
|
|
77
|
+
if (!rel) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (!isSafeRelativeSkillPath(rel)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const parts = rel.split('/');
|
|
84
|
+
return !parts.some(part => part === '.DS_Store' ||
|
|
85
|
+
part === '__pycache__' ||
|
|
86
|
+
part === '.pytest_cache') && !rel.endsWith('.pyc') && !rel.endsWith('.pyo');
|
|
87
|
+
}
|
|
88
|
+
function copySkillTree(sourceDir, targetDir) {
|
|
89
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
90
|
+
mkdirSync(dirname(targetDir), { recursive: true });
|
|
91
|
+
cpSync(sourceDir, targetDir, {
|
|
92
|
+
recursive: true,
|
|
93
|
+
filter: sourcePath => shouldCopySkillPath(sourceDir, sourcePath),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function stripFrontmatter(markdown) {
|
|
97
|
+
const match = markdown.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);
|
|
98
|
+
if (!match) {
|
|
99
|
+
return markdown;
|
|
100
|
+
}
|
|
101
|
+
return markdown.slice(match[0].length).replace(/^[\r\n]+/, '');
|
|
102
|
+
}
|
|
103
|
+
function buildAgentsSection(sourceDir) {
|
|
104
|
+
const skillMarkdown = readFileSync(resolve(sourceDir, 'SKILL.md'), 'utf8');
|
|
105
|
+
const body = stripFrontmatter(skillMarkdown).trimEnd();
|
|
106
|
+
return `${AGENTS_START_MARKER}\n${body}\n${AGENTS_END_MARKER}\n`;
|
|
107
|
+
}
|
|
108
|
+
function installAgentsMd(sourceDir, targetPath, options) {
|
|
109
|
+
const section = buildAgentsSection(sourceDir);
|
|
110
|
+
const exists = existsSync(targetPath);
|
|
111
|
+
const existing = exists ? readFileSync(targetPath, 'utf8') : '';
|
|
112
|
+
const hasManagedSection = existing.includes(AGENTS_START_MARKER)
|
|
113
|
+
&& existing.includes(AGENTS_END_MARKER);
|
|
114
|
+
const action = exists ? 'updated' : 'created';
|
|
115
|
+
if (options?.dryRun) {
|
|
116
|
+
return {
|
|
117
|
+
target: 'agents-md',
|
|
118
|
+
path: targetPath,
|
|
119
|
+
action: action === 'updated' ? 'would-update' : 'would-create',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (hasManagedSection && !options?.overwrite) {
|
|
123
|
+
throw new Error(`${targetPath} already contains an Orizu CLI section. Pass --yes to replace it.`);
|
|
124
|
+
}
|
|
125
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
126
|
+
if (!exists) {
|
|
127
|
+
writeFileSync(targetPath, section, 'utf8');
|
|
128
|
+
return { target: 'agents-md', path: targetPath, action: 'created' };
|
|
129
|
+
}
|
|
130
|
+
if (hasManagedSection) {
|
|
131
|
+
const pattern = new RegExp(`${escapeRegExp(AGENTS_START_MARKER)}[\\s\\S]*?${escapeRegExp(AGENTS_END_MARKER)}\\n?`);
|
|
132
|
+
writeFileSync(targetPath, existing.replace(pattern, section), 'utf8');
|
|
133
|
+
return { target: 'agents-md', path: targetPath, action: 'updated' };
|
|
134
|
+
}
|
|
135
|
+
const existingPrefix = existing.trim().length === 0 ? '' : existing;
|
|
136
|
+
const separator = existingPrefix.endsWith('\n') ? '\n' : '\n\n';
|
|
137
|
+
writeFileSync(targetPath, `${existingPrefix}${existingPrefix ? separator : ''}${section}`, 'utf8');
|
|
138
|
+
return { target: 'agents-md', path: targetPath, action: 'updated' };
|
|
139
|
+
}
|
|
140
|
+
function escapeRegExp(value) {
|
|
141
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
142
|
+
}
|
|
143
|
+
export function targetNeedsOverwrite(target, options) {
|
|
144
|
+
const targetPath = getSkillInstallPath(target, options);
|
|
145
|
+
if (!existsSync(targetPath)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (target !== 'agents-md') {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
const existing = readFileSync(targetPath, 'utf8');
|
|
152
|
+
return existing.includes(AGENTS_START_MARKER) && existing.includes(AGENTS_END_MARKER);
|
|
153
|
+
}
|
|
154
|
+
export function installSkillTarget(target, options) {
|
|
155
|
+
const sourceDir = resolveSkillSourceDir();
|
|
156
|
+
const targetPath = getSkillInstallPath(target, options);
|
|
157
|
+
const exists = existsSync(targetPath);
|
|
158
|
+
if (target === 'agents-md') {
|
|
159
|
+
return installAgentsMd(sourceDir, targetPath, options);
|
|
160
|
+
}
|
|
161
|
+
if (options?.dryRun) {
|
|
162
|
+
return {
|
|
163
|
+
target,
|
|
164
|
+
path: targetPath,
|
|
165
|
+
action: exists ? 'would-update' : 'would-create',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (exists && !options?.overwrite) {
|
|
169
|
+
throw new Error(`${targetPath} already exists. Pass --yes to replace it.`);
|
|
170
|
+
}
|
|
171
|
+
copySkillTree(sourceDir, targetPath);
|
|
172
|
+
return {
|
|
173
|
+
target,
|
|
174
|
+
path: targetPath,
|
|
175
|
+
action: exists ? 'updated' : 'created',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export function isSkillInstallTarget(value) {
|
|
179
|
+
return SKILL_INSTALL_TARGETS.includes(value);
|
|
180
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orizu",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc -p tsconfig.json",
|
|
19
|
-
"prepack": "node scripts/vendor-gepa-python.mjs",
|
|
20
|
-
"postpack": "node scripts/vendor-gepa-python.mjs --clean"
|
|
19
|
+
"prepack": "node scripts/vendor-gepa-python.mjs && node scripts/vendor-orizu-skill.mjs",
|
|
20
|
+
"postpack": "node scripts/vendor-gepa-python.mjs --clean && node scripts/vendor-orizu-skill.mjs --clean"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@playwright/test": "^1.60.0",
|