orizu 0.3.7 → 0.4.0
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 +55 -6
- package/dist/banner.js +20 -0
- package/dist/csv.js +117 -9
- package/dist/help.js +109 -10
- package/dist/index.js +736 -44
- package/dist/setup-prompt.js +22 -0
- package/dist/skill-installer.js +243 -17
- package/dist/workspace.js +76 -0
- package/package.json +5 -1
- package/src/banner.ts +21 -0
- package/src/csv.ts +140 -10
- package/src/help.ts +109 -10
- package/src/index.ts +815 -46
- package/src/setup-prompt.ts +27 -0
- package/src/skill-installer.ts +343 -17
- package/src/workspace.ts +112 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/__init__.py +2 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +57 -3
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +37 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +233 -17
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +98 -17
- package/vendor/skills/orizu-cli/SKILL.md +8 -3
- package/vendor/skills/orizu-cli/references/cli-reference.md +25 -4
- package/vendor/skills/orizu-cli/references/optimization-reports.md +2 -2
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +1 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +17 -5
package/dist/index.js
CHANGED
|
@@ -18,8 +18,11 @@ import { parseGlobalFlags } from './global-flags.js';
|
|
|
18
18
|
import { getCapabilities, renderHelpForArgs, renderRootHelp } from './help.js';
|
|
19
19
|
import { runLocalAppPreview } from './preview-runtime.js';
|
|
20
20
|
import { assertSecureTokenTransport, authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags, } from './http.js';
|
|
21
|
-
import { getSkillInstallPath, installSkillTarget, isSkillInstallTarget, SKILL_INSTALL_TARGETS, targetNeedsOverwrite, } from './skill-installer.js';
|
|
21
|
+
import { computeSkillContentHash, getSkillInstallPath, getSkillTargetStatus, getTargetForAgent, installSkillTarget, isSkillInstallAgent, isSkillInstallTarget, resolveSkillSource, SKILL_INSTALL_AGENTS, SKILL_INSTALL_TARGETS, targetNeedsOverwrite, } from './skill-installer.js';
|
|
22
|
+
import { renderBanner } from './banner.js';
|
|
22
23
|
import { formatTaskCreateError } from './task-create-error.js';
|
|
24
|
+
import { renderAgentSetupPrompt } from './setup-prompt.js';
|
|
25
|
+
import { getWorkspaceRoot, initOrizuWorkspace, WORKSPACE_DIR_NAME, workspaceExists, } from './workspace.js';
|
|
23
26
|
const MAX_README_LENGTH = 200_000;
|
|
24
27
|
function getCliVersion() {
|
|
25
28
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
@@ -34,14 +37,22 @@ function printLine(message = '') {
|
|
|
34
37
|
function printVersion() {
|
|
35
38
|
printLine(`orizu ${getCliVersion()}`);
|
|
36
39
|
}
|
|
40
|
+
function printBannerIfInteractive() {
|
|
41
|
+
if (!process.stdout.isTTY) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
printLine(renderBanner());
|
|
45
|
+
printLine('');
|
|
46
|
+
}
|
|
37
47
|
function printUsage() {
|
|
48
|
+
printBannerIfInteractive();
|
|
38
49
|
printLine(renderRootHelp());
|
|
39
50
|
}
|
|
40
51
|
function printPreviewUsage() {
|
|
41
52
|
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>]`);
|
|
42
53
|
}
|
|
43
54
|
function printOptimizationUsage() {
|
|
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]`);
|
|
55
|
+
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] [--budget auto|light|medium|heavy | --max-metric-calls <n> | --max-full-evals <n> | --max-iterations <n>] [--num-threads auto|N] [--reflection-retry-attempts N] [--reflection-http-timeout-seconds 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]`);
|
|
45
56
|
}
|
|
46
57
|
let cliArgs = process.argv.slice(2);
|
|
47
58
|
let cliJsonOutput = false;
|
|
@@ -204,14 +215,16 @@ function printPromptSummaries(items, emptyMessage) {
|
|
|
204
215
|
id: sanitizeTerminalText(item.id),
|
|
205
216
|
name: sanitizeTerminalText(item.name),
|
|
206
217
|
role: sanitizeTerminalText(item.role),
|
|
218
|
+
status: sanitizeTerminalText(item.status || 'active'),
|
|
207
219
|
}));
|
|
208
220
|
const idWidth = Math.max('ID'.length, ...rows.map(row => row.id.length));
|
|
209
221
|
const nameWidth = Math.max('NAME'.length, ...rows.map(row => row.name.length));
|
|
210
222
|
const roleWidth = Math.max('ROLE'.length, ...rows.map(row => row.role.length));
|
|
211
|
-
|
|
212
|
-
printLine(`${'
|
|
223
|
+
const statusWidth = Math.max('STATUS'.length, ...rows.map(row => row.status.length));
|
|
224
|
+
printLine(`${'ID'.padEnd(idWidth)} ${'NAME'.padEnd(nameWidth)} ${'ROLE'.padEnd(roleWidth)} ${'STATUS'.padEnd(statusWidth)}`);
|
|
225
|
+
printLine(`${'-'.repeat(idWidth)} ${'-'.repeat(nameWidth)} ${'-'.repeat(roleWidth)} ${'-'.repeat(statusWidth)}`);
|
|
213
226
|
rows.forEach(row => {
|
|
214
|
-
printLine(`${row.id.padEnd(idWidth)} ${row.name.padEnd(nameWidth)} ${row.role.padEnd(roleWidth)}`);
|
|
227
|
+
printLine(`${row.id.padEnd(idWidth)} ${row.name.padEnd(nameWidth)} ${row.role.padEnd(roleWidth)} ${row.status.padEnd(statusWidth)}`);
|
|
215
228
|
});
|
|
216
229
|
}
|
|
217
230
|
function printScorerSummaries(items) {
|
|
@@ -703,6 +716,10 @@ async function login() {
|
|
|
703
716
|
const baseUrl = hasArg('--no-prompt-if-logged-in') ? getBaseUrl() : resolveLoginBaseUrl();
|
|
704
717
|
assertSecureTokenTransport(baseUrl);
|
|
705
718
|
if (hasArg('--no-prompt-if-logged-in') && getServerCredentials(baseUrl)) {
|
|
719
|
+
if (hasJsonFlag()) {
|
|
720
|
+
printJson({ status: 'already-logged-in', server: baseUrl });
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
706
723
|
printLine(`Already logged in to ${sanitizeTerminalText(baseUrl)}.`);
|
|
707
724
|
return;
|
|
708
725
|
}
|
|
@@ -781,6 +798,14 @@ async function login() {
|
|
|
781
798
|
credentialType: 'pat',
|
|
782
799
|
apiKey: loginData.apiKey,
|
|
783
800
|
});
|
|
801
|
+
if (hasJsonFlag()) {
|
|
802
|
+
printJson({
|
|
803
|
+
status: 'logged-in',
|
|
804
|
+
server: baseUrl,
|
|
805
|
+
user: { id: loginData.user.id, email: loginData.user.email ?? null },
|
|
806
|
+
});
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
784
809
|
printLine(`Logged in as ${sanitizeTerminalText(loginData.user.email ?? loginData.user.id)}`);
|
|
785
810
|
}
|
|
786
811
|
async function whoami() {
|
|
@@ -789,12 +814,20 @@ async function whoami() {
|
|
|
789
814
|
throw new Error(`whoami failed: ${await response.text()}`);
|
|
790
815
|
}
|
|
791
816
|
const data = await response.json();
|
|
817
|
+
if (hasJsonFlag()) {
|
|
818
|
+
printJson({ user: data.user, server: getBaseUrl() });
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
792
821
|
printLine(sanitizeTerminalText(data.user.email ?? data.user.id));
|
|
793
822
|
}
|
|
794
823
|
async function logout() {
|
|
795
824
|
const baseUrl = getBaseUrl();
|
|
796
825
|
const credentials = getServerCredentials(baseUrl);
|
|
797
826
|
if (!credentials) {
|
|
827
|
+
if (hasJsonFlag()) {
|
|
828
|
+
printJson({ status: 'already-logged-out', server: baseUrl });
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
798
831
|
printLine(`Already logged out for ${sanitizeTerminalText(baseUrl)}.`);
|
|
799
832
|
return;
|
|
800
833
|
}
|
|
@@ -820,6 +853,10 @@ async function logout() {
|
|
|
820
853
|
remoteLogoutError = sanitizeTerminalText(message).slice(0, 180);
|
|
821
854
|
}
|
|
822
855
|
clearServerCredentials(baseUrl);
|
|
856
|
+
if (hasJsonFlag()) {
|
|
857
|
+
printJson({ status: 'logged-out', server: baseUrl, remoteLogoutError });
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
823
860
|
if (remoteLogoutError) {
|
|
824
861
|
console.warn(`Warning: remote logout failed: ${remoteLogoutError}`);
|
|
825
862
|
}
|
|
@@ -832,6 +869,15 @@ async function printEnv() {
|
|
|
832
869
|
const project = projectArg ? await resolveProjectSelection(projectArg) : null;
|
|
833
870
|
const projectId = project?.id || getArg('--project-id') || process.env.ORIZU_PROJECT_ID || '';
|
|
834
871
|
const projectSlug = project ? `${project.teamSlug}/${project.slug}` : process.env.ORIZU_PROJECT || '';
|
|
872
|
+
if (hasJsonFlag()) {
|
|
873
|
+
printJson({
|
|
874
|
+
ORIZU_API_URL: baseUrl,
|
|
875
|
+
ORIZU_TOKEN: token,
|
|
876
|
+
ORIZU_PROJECT_ID: projectId,
|
|
877
|
+
...(projectSlug ? { ORIZU_PROJECT: projectSlug } : {}),
|
|
878
|
+
});
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
835
881
|
printLine(`export ORIZU_API_URL=${shellQuote(baseUrl)}`);
|
|
836
882
|
printLine(`export ORIZU_TOKEN=${shellQuote(token)}`);
|
|
837
883
|
printLine(`export ORIZU_PROJECT_ID=${shellQuote(projectId)}`);
|
|
@@ -868,24 +914,46 @@ async function logOptimizationEvent() {
|
|
|
868
914
|
throw new Error(`Failed to log event: ${await response.text()}`);
|
|
869
915
|
}
|
|
870
916
|
const data = await parseJsonResponse(response, 'Optimization event log');
|
|
917
|
+
if (hasJsonFlag()) {
|
|
918
|
+
printJson({ eventId: data.eventId });
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
871
921
|
printLine(`Logged event ${sanitizeTerminalText(data.eventId)}`);
|
|
872
922
|
}
|
|
873
923
|
async function listPrompts() {
|
|
874
924
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
875
|
-
const
|
|
925
|
+
const status = getArg('--status') || 'active';
|
|
926
|
+
if (!['active', 'archived', 'all'].includes(status)) {
|
|
927
|
+
throw new Error('Usage: orizu prompts list --project <team/project> [--status active|archived|all]');
|
|
928
|
+
}
|
|
929
|
+
const params = new URLSearchParams({ project, status });
|
|
930
|
+
const response = await authedFetch(`/api/cli/prompts?${params.toString()}`);
|
|
876
931
|
if (!response.ok) {
|
|
877
932
|
throw new Error(`Failed to fetch prompts: ${await response.text()}`);
|
|
878
933
|
}
|
|
879
934
|
const data = await parseJsonResponse(response, 'Prompts list');
|
|
935
|
+
if (hasJsonFlag()) {
|
|
936
|
+
printJson(data);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
880
939
|
printPromptSummaries(data.prompts, 'No prompts found.');
|
|
881
940
|
}
|
|
882
941
|
async function listJudges() {
|
|
883
942
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
884
|
-
const
|
|
943
|
+
const status = getArg('--status') || 'active';
|
|
944
|
+
if (!['active', 'archived', 'all'].includes(status)) {
|
|
945
|
+
throw new Error('Usage: orizu judges list --project <team/project> [--status active|archived|all]');
|
|
946
|
+
}
|
|
947
|
+
const params = new URLSearchParams({ project, status });
|
|
948
|
+
const response = await authedFetch(`/api/cli/judges?${params.toString()}`);
|
|
885
949
|
if (!response.ok) {
|
|
886
950
|
throw new Error(`Failed to fetch judges: ${await response.text()}`);
|
|
887
951
|
}
|
|
888
952
|
const data = await parseJsonResponse(response, 'Judges list');
|
|
953
|
+
if (hasJsonFlag()) {
|
|
954
|
+
printJson(data);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
889
957
|
printPromptSummaries(data.judges, 'No judges found.');
|
|
890
958
|
}
|
|
891
959
|
async function listPromptComments() {
|
|
@@ -922,6 +990,10 @@ async function listScorers() {
|
|
|
922
990
|
throw new Error(`Failed to fetch scorers: ${await response.text()}`);
|
|
923
991
|
}
|
|
924
992
|
const data = await parseJsonResponse(response, 'Scorers list');
|
|
993
|
+
if (hasJsonFlag()) {
|
|
994
|
+
printJson(data);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
925
997
|
printScorerSummaries(data.scorers);
|
|
926
998
|
}
|
|
927
999
|
async function registerScorer() {
|
|
@@ -1203,6 +1275,28 @@ async function setPromptLabel() {
|
|
|
1203
1275
|
}
|
|
1204
1276
|
printLine(`Moved ${sanitizeTerminalText(label)} to ${sanitizeTerminalText(promptVersionId)}`);
|
|
1205
1277
|
}
|
|
1278
|
+
async function setPromptArchivedState(archived) {
|
|
1279
|
+
const promptRef = getPositionalArg(2);
|
|
1280
|
+
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1281
|
+
const command = archived ? 'archive' : 'restore';
|
|
1282
|
+
if (!promptRef) {
|
|
1283
|
+
throw new Error(`Usage: orizu prompts ${command} <prompt-id-or-name> --project <team/project> [--json]`);
|
|
1284
|
+
}
|
|
1285
|
+
const response = await authedFetch(`/api/cli/prompts/${encodeURIComponent(promptRef)}?project=${encodeURIComponent(project)}`, {
|
|
1286
|
+
method: 'PATCH',
|
|
1287
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1288
|
+
body: JSON.stringify({ archived }),
|
|
1289
|
+
});
|
|
1290
|
+
if (!response.ok) {
|
|
1291
|
+
throw new Error(`Failed to ${command} prompt: ${await response.text()}`);
|
|
1292
|
+
}
|
|
1293
|
+
const data = await parseJsonResponse(response, `Prompt ${command}`);
|
|
1294
|
+
if (hasJsonFlag()) {
|
|
1295
|
+
printJson(data);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
printLine(`${archived ? 'Archived' : 'Restored'} prompt ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)})`);
|
|
1299
|
+
}
|
|
1206
1300
|
async function bindPromptScorer(role) {
|
|
1207
1301
|
const promptId = getPositionalArg(3);
|
|
1208
1302
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
@@ -1400,6 +1494,10 @@ async function submitRunResults() {
|
|
|
1400
1494
|
throw new Error(`Failed to submit run: ${await response.text()}`);
|
|
1401
1495
|
}
|
|
1402
1496
|
const data = await parseJsonResponse(response, 'Run submit');
|
|
1497
|
+
if (hasJsonFlag()) {
|
|
1498
|
+
printJson({ run: data.run });
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1403
1501
|
printLine(`Submitted run ${sanitizeTerminalText(data.run.id)} ` +
|
|
1404
1502
|
`(${formatPercent(data.run.aggregateScore)}) -> ` +
|
|
1405
1503
|
sanitizeTerminalText(data.run.perRowResultsStoragePath));
|
|
@@ -1857,9 +1955,17 @@ async function runGepaOptimization() {
|
|
|
1857
1955
|
if (result.status !== 0) {
|
|
1858
1956
|
throw new Error(`orizu-gepa failed with exit code ${result.status}`);
|
|
1859
1957
|
}
|
|
1958
|
+
if (hasJsonFlag()) {
|
|
1959
|
+
printJson({ status: 'completed', exitCode: 0 });
|
|
1960
|
+
}
|
|
1860
1961
|
}
|
|
1861
1962
|
async function listTeams() {
|
|
1862
|
-
|
|
1963
|
+
const teams = await fetchTeams();
|
|
1964
|
+
if (hasJsonFlag()) {
|
|
1965
|
+
printJson({ teams });
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
printTeams(teams);
|
|
1863
1969
|
}
|
|
1864
1970
|
async function resolveTeamSlug(teamSlugArg) {
|
|
1865
1971
|
if (teamSlugArg) {
|
|
@@ -1891,12 +1997,21 @@ async function createTeam() {
|
|
|
1891
1997
|
throw new Error(`Failed to create team: ${await response.text()}`);
|
|
1892
1998
|
}
|
|
1893
1999
|
const data = await parseJsonResponse(response, 'Team create');
|
|
2000
|
+
if (hasJsonFlag()) {
|
|
2001
|
+
printJson({ team: data.team });
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
1894
2004
|
printLine(`Created team: ${sanitizeTerminalText(data.team.name)} (${sanitizeTerminalText(data.team.slug)})`);
|
|
1895
2005
|
}
|
|
1896
2006
|
async function listProjects() {
|
|
1897
2007
|
const teamSlugArg = getArg('--team');
|
|
1898
2008
|
const teamSlug = teamSlugArg ? normalizeSlugInput(teamSlugArg) : null;
|
|
1899
|
-
|
|
2009
|
+
const projects = await fetchProjects(teamSlug || undefined);
|
|
2010
|
+
if (hasJsonFlag()) {
|
|
2011
|
+
printJson({ projects });
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
printProjects(projects);
|
|
1900
2015
|
}
|
|
1901
2016
|
async function createProject() {
|
|
1902
2017
|
const name = getArg('--name');
|
|
@@ -1918,15 +2033,29 @@ async function createProject() {
|
|
|
1918
2033
|
throw new Error(`Failed to create project: ${await response.text()}`);
|
|
1919
2034
|
}
|
|
1920
2035
|
const data = await parseJsonResponse(response, 'Project create');
|
|
2036
|
+
if (hasJsonFlag()) {
|
|
2037
|
+
printJson({ project: data.project });
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
1921
2040
|
printLine(`Created project ${sanitizeTerminalText(`${data.project.teamSlug}/${data.project.slug}`)}`);
|
|
1922
2041
|
}
|
|
1923
2042
|
async function listTasks() {
|
|
1924
2043
|
const project = getArg('--project');
|
|
1925
|
-
|
|
2044
|
+
const tasks = await fetchTasks(project || undefined);
|
|
2045
|
+
if (hasJsonFlag()) {
|
|
2046
|
+
printJson({ tasks });
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
printTasks(tasks);
|
|
1926
2050
|
}
|
|
1927
2051
|
async function listApps() {
|
|
1928
2052
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1929
|
-
|
|
2053
|
+
const apps = await fetchApps(project);
|
|
2054
|
+
if (hasJsonFlag()) {
|
|
2055
|
+
printJson({ apps });
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
printApps(apps);
|
|
1930
2059
|
}
|
|
1931
2060
|
function readSourceFile(pathArg) {
|
|
1932
2061
|
const expandedPath = expandHomePath(pathArg);
|
|
@@ -1986,24 +2115,82 @@ async function askYesNo(question, defaultYes) {
|
|
|
1986
2115
|
rl.close();
|
|
1987
2116
|
}
|
|
1988
2117
|
}
|
|
1989
|
-
|
|
2118
|
+
const SKILL_INSTALL_USAGE = `Usage: orizu install-skill [--agent <${SKILL_INSTALL_AGENTS.join('|')}>]... [--scope global|project] [--mode auto|link|copy] [--target <${SKILL_INSTALL_TARGETS.join('|')}>]... [--yes] [--dry-run]`;
|
|
2119
|
+
function describeSkillTarget(target) {
|
|
2120
|
+
if (target === 'claude-user')
|
|
2121
|
+
return 'Claude Code, for you across all projects';
|
|
2122
|
+
if (target === 'claude-project')
|
|
2123
|
+
return 'Claude Code, only for this project';
|
|
2124
|
+
if (target === 'agent-user')
|
|
2125
|
+
return 'Codex / Open Agent Skills, for you across all projects';
|
|
2126
|
+
if (target === 'agents-project')
|
|
2127
|
+
return 'Codex / Open Agent Skills, only for this project';
|
|
2128
|
+
if (target === 'codex-project')
|
|
2129
|
+
return 'Codex legacy project folder (.codex/skills)';
|
|
2130
|
+
return 'Project instructions, managed Orizu section in AGENTS.md';
|
|
2131
|
+
}
|
|
2132
|
+
async function promptSkillInstallTargets(scope) {
|
|
1990
2133
|
if (!isInteractiveTerminal()) {
|
|
1991
|
-
throw new Error(
|
|
2134
|
+
throw new Error(SKILL_INSTALL_USAGE);
|
|
1992
2135
|
}
|
|
1993
|
-
|
|
2136
|
+
const choices = [
|
|
2137
|
+
{ label: describeSkillTarget(getTargetForAgent('claude', scope)), target: getTargetForAgent('claude', scope), defaultYes: true },
|
|
2138
|
+
{ label: describeSkillTarget(getTargetForAgent('codex', scope)), target: getTargetForAgent('codex', scope), defaultYes: true },
|
|
2139
|
+
{ label: describeSkillTarget('agents-md'), target: 'agents-md', defaultYes: false },
|
|
2140
|
+
];
|
|
2141
|
+
printLine('Where should Orizu be available?');
|
|
1994
2142
|
const targets = [];
|
|
1995
|
-
for (const
|
|
1996
|
-
const
|
|
1997
|
-
if (await askYesNo(`
|
|
1998
|
-
targets.push(target);
|
|
2143
|
+
for (const choice of choices) {
|
|
2144
|
+
const installPath = getSkillInstallPath(choice.target);
|
|
2145
|
+
if (await askYesNo(` ${choice.label}\n ${installPath}?`, choice.defaultYes)) {
|
|
2146
|
+
targets.push(choice.target);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (await askYesNo(' Show advanced targets?', false)) {
|
|
2150
|
+
for (const target of SKILL_INSTALL_TARGETS) {
|
|
2151
|
+
if (targets.includes(target)) {
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
const installPath = getSkillInstallPath(target);
|
|
2155
|
+
if (await askYesNo(` ${describeSkillTarget(target)} [${target}]\n ${installPath}?`, false)) {
|
|
2156
|
+
targets.push(target);
|
|
2157
|
+
}
|
|
1999
2158
|
}
|
|
2000
2159
|
}
|
|
2001
2160
|
return targets;
|
|
2002
2161
|
}
|
|
2003
|
-
function
|
|
2004
|
-
const
|
|
2162
|
+
function parseSkillInstallScope() {
|
|
2163
|
+
const raw = getArg('--scope');
|
|
2164
|
+
if (raw === null || raw === 'global') {
|
|
2165
|
+
return 'global';
|
|
2166
|
+
}
|
|
2167
|
+
if (raw === 'project') {
|
|
2168
|
+
return 'local';
|
|
2169
|
+
}
|
|
2170
|
+
throw new Error(`Unknown --scope '${raw}'. Choices: global, project.`);
|
|
2171
|
+
}
|
|
2172
|
+
function parseSkillInstallMode() {
|
|
2173
|
+
const raw = getArg('--mode');
|
|
2174
|
+
if (raw === null) {
|
|
2175
|
+
return 'auto';
|
|
2176
|
+
}
|
|
2177
|
+
if (raw !== 'auto' && raw !== 'link' && raw !== 'copy') {
|
|
2178
|
+
throw new Error(`Unknown --mode '${raw}'. Choices: auto, link, copy.`);
|
|
2179
|
+
}
|
|
2180
|
+
return raw;
|
|
2181
|
+
}
|
|
2182
|
+
function parseSkillInstallTargets(scope) {
|
|
2005
2183
|
const targets = [];
|
|
2006
|
-
for (const
|
|
2184
|
+
for (const agent of getArgs('--agent')) {
|
|
2185
|
+
if (!isSkillInstallAgent(agent)) {
|
|
2186
|
+
throw new Error(`Unknown agent '${agent}'. Available agents: ${SKILL_INSTALL_AGENTS.join(', ')}`);
|
|
2187
|
+
}
|
|
2188
|
+
const target = getTargetForAgent(agent, scope);
|
|
2189
|
+
if (!targets.includes(target)) {
|
|
2190
|
+
targets.push(target);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
for (const target of getArgs('--target')) {
|
|
2007
2194
|
if (!isSkillInstallTarget(target)) {
|
|
2008
2195
|
throw new Error(`Unknown skill install target '${target}'. Available targets: ${SKILL_INSTALL_TARGETS.join(', ')}`);
|
|
2009
2196
|
}
|
|
@@ -2022,36 +2209,412 @@ function formatSkillInstallAction(action) {
|
|
|
2022
2209
|
return 'Would write';
|
|
2023
2210
|
return 'Would update';
|
|
2024
2211
|
}
|
|
2025
|
-
|
|
2026
|
-
const
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
if (targets.length === 0) {
|
|
2030
|
-
targets = await promptSkillInstallTargets();
|
|
2212
|
+
function describePlannedWrite(target, action, mode, path) {
|
|
2213
|
+
const verb = action.includes('create') || action === 'created' ? 'create' : 'replace';
|
|
2214
|
+
if (target === 'agents-md') {
|
|
2215
|
+
return ` ${path} (${verb} managed Orizu section)`;
|
|
2031
2216
|
}
|
|
2032
|
-
if (
|
|
2033
|
-
|
|
2034
|
-
|
|
2217
|
+
if (mode === 'link') {
|
|
2218
|
+
return ` ${path} (${verb}, symlink to the CLI-managed skill)`;
|
|
2219
|
+
}
|
|
2220
|
+
return ` ${path} (${verb}, full copy with sync metadata)`;
|
|
2221
|
+
}
|
|
2222
|
+
async function applySkillInstallTargets(targets, options) {
|
|
2223
|
+
const cliVersion = getCliVersion();
|
|
2224
|
+
const jsonMode = hasJsonFlag();
|
|
2225
|
+
const planned = targets.map(target => installSkillTarget(target, { dryRun: true, mode: options.mode, cliVersion }));
|
|
2226
|
+
if (!jsonMode) {
|
|
2227
|
+
printLine('Planned writes:');
|
|
2228
|
+
for (const plan of planned) {
|
|
2229
|
+
printLine(describePlannedWrite(plan.target, plan.action, plan.mode, plan.path));
|
|
2230
|
+
}
|
|
2231
|
+
printLine('');
|
|
2035
2232
|
}
|
|
2233
|
+
if (options.dryRun) {
|
|
2234
|
+
if (!jsonMode) {
|
|
2235
|
+
printLine('Dry run: no files were changed.');
|
|
2236
|
+
}
|
|
2237
|
+
return planned.map(plan => ({
|
|
2238
|
+
target: plan.target,
|
|
2239
|
+
path: plan.path,
|
|
2240
|
+
action: 'skipped',
|
|
2241
|
+
mode: plan.mode,
|
|
2242
|
+
}));
|
|
2243
|
+
}
|
|
2244
|
+
const outcomes = [];
|
|
2036
2245
|
for (const target of targets) {
|
|
2037
|
-
let overwrite = skipConfirm;
|
|
2246
|
+
let overwrite = options.skipConfirm;
|
|
2038
2247
|
const installPath = getSkillInstallPath(target);
|
|
2039
|
-
if (!
|
|
2248
|
+
if (!overwrite && targetNeedsOverwrite(target)) {
|
|
2040
2249
|
if (!isInteractiveTerminal()) {
|
|
2041
2250
|
throw new Error(`${installPath} already exists. Pass --yes to replace it.`);
|
|
2042
2251
|
}
|
|
2043
2252
|
overwrite = await askYesNo(`Replace ${installPath}?`, true);
|
|
2044
2253
|
if (!overwrite) {
|
|
2045
|
-
|
|
2254
|
+
if (!jsonMode) {
|
|
2255
|
+
printLine(`Skipped ${installPath}`);
|
|
2256
|
+
}
|
|
2257
|
+
outcomes.push({ target, path: installPath, action: 'skipped', mode: null });
|
|
2046
2258
|
continue;
|
|
2047
2259
|
}
|
|
2048
2260
|
}
|
|
2261
|
+
try {
|
|
2262
|
+
const result = installSkillTarget(target, {
|
|
2263
|
+
overwrite,
|
|
2264
|
+
mode: options.mode,
|
|
2265
|
+
cliVersion,
|
|
2266
|
+
});
|
|
2267
|
+
if (!jsonMode) {
|
|
2268
|
+
const suffix = result.mode === 'link' ? ' (symlink)' : '';
|
|
2269
|
+
printLine(`${formatSkillInstallAction(result.action)} ${result.path}${suffix}`);
|
|
2270
|
+
}
|
|
2271
|
+
outcomes.push({
|
|
2272
|
+
target,
|
|
2273
|
+
path: result.path,
|
|
2274
|
+
action: result.action,
|
|
2275
|
+
mode: result.mode,
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
catch (error) {
|
|
2279
|
+
const message = error?.message || String(error);
|
|
2280
|
+
if (!jsonMode) {
|
|
2281
|
+
printLine(`Failed ${installPath}: ${message}`);
|
|
2282
|
+
}
|
|
2283
|
+
outcomes.push({ target, path: installPath, action: 'failed', mode: null, error: message });
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
return outcomes;
|
|
2287
|
+
}
|
|
2288
|
+
async function installSkillCommand() {
|
|
2289
|
+
const skipConfirm = hasArg('--yes');
|
|
2290
|
+
const dryRun = hasArg('--dry-run');
|
|
2291
|
+
const scope = parseSkillInstallScope();
|
|
2292
|
+
const mode = parseSkillInstallMode();
|
|
2293
|
+
let targets = parseSkillInstallTargets(scope);
|
|
2294
|
+
if (targets.length === 0) {
|
|
2295
|
+
targets = await promptSkillInstallTargets(scope);
|
|
2296
|
+
}
|
|
2297
|
+
if (targets.length === 0) {
|
|
2298
|
+
if (hasJsonFlag()) {
|
|
2299
|
+
printJson({ installs: [] });
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
printLine('No targets selected; nothing to do.');
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
const outcomes = await applySkillInstallTargets(targets, { mode, skipConfirm, dryRun });
|
|
2306
|
+
if (hasJsonFlag()) {
|
|
2307
|
+
printJson({ dryRun, installs: outcomes });
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
function describeSkillTargetState(status) {
|
|
2311
|
+
if (status.state === 'current')
|
|
2312
|
+
return 'current';
|
|
2313
|
+
if (status.state === 'stale')
|
|
2314
|
+
return 'stale (run `orizu skills update`)';
|
|
2315
|
+
if (status.state === 'broken-link')
|
|
2316
|
+
return 'broken symlink (run `orizu skills update`)';
|
|
2317
|
+
if (status.state === 'unmanaged')
|
|
2318
|
+
return 'exists without a managed Orizu section';
|
|
2319
|
+
return 'not installed';
|
|
2320
|
+
}
|
|
2321
|
+
function skillsStatusCommand() {
|
|
2322
|
+
const source = resolveSkillSource();
|
|
2323
|
+
const statuses = SKILL_INSTALL_TARGETS.map(target => getSkillTargetStatus(target));
|
|
2324
|
+
if (hasJsonFlag()) {
|
|
2325
|
+
printJson({
|
|
2326
|
+
name: source.name,
|
|
2327
|
+
root: source.root,
|
|
2328
|
+
source: source.source,
|
|
2329
|
+
cliVersion: getCliVersion(),
|
|
2330
|
+
sourceHash: computeSkillContentHash(source.root),
|
|
2331
|
+
targets: statuses.map(status => ({
|
|
2332
|
+
target: status.target,
|
|
2333
|
+
path: status.path,
|
|
2334
|
+
state: status.state,
|
|
2335
|
+
mode: status.mode,
|
|
2336
|
+
linkTarget: status.linkTarget,
|
|
2337
|
+
installedHash: status.installedHash,
|
|
2338
|
+
meta: status.meta,
|
|
2339
|
+
})),
|
|
2340
|
+
});
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
printLine(`Skill source: ${source.root} (${source.source})`);
|
|
2344
|
+
printLine('');
|
|
2345
|
+
for (const status of statuses) {
|
|
2346
|
+
const mode = status.mode ? `, ${status.mode}` : '';
|
|
2347
|
+
printLine(` ${status.target.padEnd(16)} ${describeSkillTargetState(status)}${mode}`);
|
|
2348
|
+
printLine(` ${''.padEnd(16)} ${status.path}`);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
async function skillsUpdateCommand() {
|
|
2352
|
+
const dryRun = hasArg('--dry-run');
|
|
2353
|
+
const cliVersion = getCliVersion();
|
|
2354
|
+
const updates = [];
|
|
2355
|
+
for (const target of SKILL_INSTALL_TARGETS) {
|
|
2356
|
+
const status = getSkillTargetStatus(target);
|
|
2357
|
+
if (status.state === 'missing' || status.state === 'unmanaged') {
|
|
2358
|
+
continue;
|
|
2359
|
+
}
|
|
2360
|
+
if (status.state === 'current') {
|
|
2361
|
+
updates.push({ target, path: status.path, action: 'already-current' });
|
|
2362
|
+
continue;
|
|
2363
|
+
}
|
|
2364
|
+
if (dryRun) {
|
|
2365
|
+
updates.push({ target, path: status.path, action: 'would-update' });
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
const refreshMode = status.mode === 'link' ? 'auto' : 'copy';
|
|
2049
2369
|
const result = installSkillTarget(target, {
|
|
2050
|
-
overwrite,
|
|
2051
|
-
|
|
2370
|
+
overwrite: true,
|
|
2371
|
+
mode: refreshMode,
|
|
2372
|
+
cliVersion,
|
|
2373
|
+
});
|
|
2374
|
+
updates.push({
|
|
2375
|
+
target,
|
|
2376
|
+
path: status.path,
|
|
2377
|
+
action: status.state === 'broken-link' ? 'relinked' : result.action,
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
if (hasJsonFlag()) {
|
|
2381
|
+
printJson({ updates });
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
if (updates.length === 0) {
|
|
2385
|
+
printLine('No Orizu skill installs found. Run `orizu install-skill` first.');
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
for (const update of updates) {
|
|
2389
|
+
if (update.action === 'already-current') {
|
|
2390
|
+
printLine(`Current ${update.path}`);
|
|
2391
|
+
}
|
|
2392
|
+
else if (update.action === 'would-update') {
|
|
2393
|
+
printLine(`Would update ${update.path}`);
|
|
2394
|
+
}
|
|
2395
|
+
else if (update.action === 'relinked') {
|
|
2396
|
+
printLine(`Relinked ${update.path}`);
|
|
2397
|
+
}
|
|
2398
|
+
else {
|
|
2399
|
+
printLine(`Updated ${update.path}`);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
function skillsPathCommand() {
|
|
2404
|
+
const source = resolveSkillSource();
|
|
2405
|
+
if (hasJsonFlag()) {
|
|
2406
|
+
printJson({
|
|
2407
|
+
name: source.name,
|
|
2408
|
+
root: source.root,
|
|
2409
|
+
skillMd: source.skillMd,
|
|
2410
|
+
source: source.source,
|
|
2411
|
+
cliVersion: getCliVersion(),
|
|
2412
|
+
skillHash: computeSkillContentHash(source.root),
|
|
2052
2413
|
});
|
|
2053
|
-
|
|
2414
|
+
return;
|
|
2054
2415
|
}
|
|
2416
|
+
printLine(hasArg('--skill-md') ? source.skillMd : source.root);
|
|
2417
|
+
}
|
|
2418
|
+
const LAUNCHABLE_AGENTS = {
|
|
2419
|
+
claude: 'claude',
|
|
2420
|
+
codex: 'codex',
|
|
2421
|
+
};
|
|
2422
|
+
function findExecutable(name) {
|
|
2423
|
+
const pathValue = process.env.PATH || '';
|
|
2424
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
2425
|
+
if (!dir) {
|
|
2426
|
+
continue;
|
|
2427
|
+
}
|
|
2428
|
+
const candidate = join(dir, name);
|
|
2429
|
+
if (existsSync(candidate)) {
|
|
2430
|
+
return candidate;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return null;
|
|
2434
|
+
}
|
|
2435
|
+
function describeSetupAuthState() {
|
|
2436
|
+
const baseUrl = getBaseUrl();
|
|
2437
|
+
return {
|
|
2438
|
+
state: getServerCredentials(baseUrl) ? 'signed-in' : 'signed-out',
|
|
2439
|
+
baseUrl,
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
async function setupCommand() {
|
|
2443
|
+
const dryRun = hasArg('--dry-run');
|
|
2444
|
+
const skipConfirm = hasArg('--yes');
|
|
2445
|
+
const noInput = hasArg('--no-input') || !isInteractiveTerminal();
|
|
2446
|
+
printBannerIfInteractive();
|
|
2447
|
+
printLine('Orizu setup');
|
|
2448
|
+
printLine('');
|
|
2449
|
+
// Phase 1: login
|
|
2450
|
+
printLine('1. Authentication');
|
|
2451
|
+
let auth = describeSetupAuthState();
|
|
2452
|
+
if (hasArg('--skip-login')) {
|
|
2453
|
+
printLine(' Skipped (--skip-login).');
|
|
2454
|
+
}
|
|
2455
|
+
else if (auth.state === 'signed-in') {
|
|
2456
|
+
printLine(` Already authenticated with ${sanitizeTerminalText(auth.baseUrl)}.`);
|
|
2457
|
+
}
|
|
2458
|
+
else if (noInput || dryRun) {
|
|
2459
|
+
printLine(' Not signed in. Run `orizu login` to authenticate.');
|
|
2460
|
+
}
|
|
2461
|
+
else if (await askYesNo(' Sign in to Orizu now?', true)) {
|
|
2462
|
+
await login();
|
|
2463
|
+
auth = describeSetupAuthState();
|
|
2464
|
+
}
|
|
2465
|
+
else {
|
|
2466
|
+
printLine(' Skipped. Run `orizu login` when ready.');
|
|
2467
|
+
}
|
|
2468
|
+
printLine('');
|
|
2469
|
+
// Phase 2: agent integration
|
|
2470
|
+
printLine('2. Agent integration');
|
|
2471
|
+
const scope = parseSkillInstallScope();
|
|
2472
|
+
const mode = parseSkillInstallMode();
|
|
2473
|
+
let targets = hasArg('--no-install') ? [] : parseSkillInstallTargets(scope);
|
|
2474
|
+
if (targets.length === 0 && !hasArg('--no-install')) {
|
|
2475
|
+
if (noInput) {
|
|
2476
|
+
printLine(' No agents selected. Pass --agent claude and/or --agent codex, or rerun interactively.');
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
targets = await promptSkillInstallTargets(scope);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
let installOutcomes = [];
|
|
2483
|
+
if (targets.length > 0) {
|
|
2484
|
+
installOutcomes = await applySkillInstallTargets(targets, { mode, skipConfirm, dryRun });
|
|
2485
|
+
}
|
|
2486
|
+
else if (hasArg('--no-install')) {
|
|
2487
|
+
printLine(' Skipped (--no-install).');
|
|
2488
|
+
}
|
|
2489
|
+
printLine('');
|
|
2490
|
+
// Phase 3: local workspace
|
|
2491
|
+
printLine('3. Local workspace');
|
|
2492
|
+
let workspaceState = 'skipped';
|
|
2493
|
+
if (hasArg('--no-workspace')) {
|
|
2494
|
+
printLine(' Skipped (--no-workspace).');
|
|
2495
|
+
}
|
|
2496
|
+
else if (workspaceExists()) {
|
|
2497
|
+
workspaceState = 'exists';
|
|
2498
|
+
printLine(` ${WORKSPACE_DIR_NAME}/ already initialized.`);
|
|
2499
|
+
}
|
|
2500
|
+
else {
|
|
2501
|
+
const wantsWorkspace = hasArg('--workspace') ||
|
|
2502
|
+
(!noInput && await askYesNo(` Create a gitignored ${WORKSPACE_DIR_NAME}/ workspace for local Orizu artifacts?`, true));
|
|
2503
|
+
if (wantsWorkspace) {
|
|
2504
|
+
const result = initOrizuWorkspace({
|
|
2505
|
+
baseUrl: auth.baseUrl,
|
|
2506
|
+
cliVersion: getCliVersion(),
|
|
2507
|
+
dryRun,
|
|
2508
|
+
});
|
|
2509
|
+
workspaceState = result.state === 'would-create' ? 'would-create' : result.state;
|
|
2510
|
+
for (const action of result.actions) {
|
|
2511
|
+
printLine(` ${dryRun ? 'Would ' : 'Did '}${action}`);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
else {
|
|
2515
|
+
printLine(` Skipped. Rerun with --workspace to create ${WORKSPACE_DIR_NAME}/.`);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
printLine('');
|
|
2519
|
+
// Phase 4: coding-agent handoff
|
|
2520
|
+
const handoffPrompt = renderAgentSetupPrompt({
|
|
2521
|
+
workspacePath: workspaceState === 'created' || workspaceState === 'exists'
|
|
2522
|
+
? getWorkspaceRoot()
|
|
2523
|
+
: null,
|
|
2524
|
+
});
|
|
2525
|
+
if (!hasArg('--no-handoff')) {
|
|
2526
|
+
printLine('4. Coding-agent handoff');
|
|
2527
|
+
printLine(' Give your coding agent this prompt to plan repo-specific Orizu adoption:');
|
|
2528
|
+
printLine('');
|
|
2529
|
+
printLine('--- prompt start ---');
|
|
2530
|
+
printLine(handoffPrompt);
|
|
2531
|
+
printLine('--- prompt end ---');
|
|
2532
|
+
printLine('');
|
|
2533
|
+
const detected = Object.keys(LAUNCHABLE_AGENTS).filter(agent => findExecutable(LAUNCHABLE_AGENTS[agent]));
|
|
2534
|
+
if (detected.length > 0) {
|
|
2535
|
+
printLine(` Detected agents: ${detected.join(', ')}. Launch one with the prompt, e.g.:`);
|
|
2536
|
+
for (const agent of detected) {
|
|
2537
|
+
printLine(` ${LAUNCHABLE_AGENTS[agent]} "$(orizu setup prompt)"`);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
const launchAgent = getArg('--launch');
|
|
2541
|
+
if (launchAgent) {
|
|
2542
|
+
if (!(launchAgent in LAUNCHABLE_AGENTS)) {
|
|
2543
|
+
throw new Error(`Unknown --launch agent '${launchAgent}'. Choices: ${Object.keys(LAUNCHABLE_AGENTS).join(', ')}.`);
|
|
2544
|
+
}
|
|
2545
|
+
const binary = findExecutable(LAUNCHABLE_AGENTS[launchAgent]);
|
|
2546
|
+
if (!isInteractiveTerminal()) {
|
|
2547
|
+
printLine(' Not launching: --launch requires an interactive terminal.');
|
|
2548
|
+
}
|
|
2549
|
+
else if (!binary) {
|
|
2550
|
+
printLine(` Not launching: '${LAUNCHABLE_AGENTS[launchAgent]}' was not found on PATH.`);
|
|
2551
|
+
}
|
|
2552
|
+
else if (dryRun) {
|
|
2553
|
+
printLine(` Would launch ${binary} with the setup prompt.`);
|
|
2554
|
+
}
|
|
2555
|
+
else if (skipConfirm || await askYesNo(` Launch ${LAUNCHABLE_AGENTS[launchAgent]} with the setup prompt now?`, true)) {
|
|
2556
|
+
printLine(` Launching ${binary}…`);
|
|
2557
|
+
spawnSync(binary, [handoffPrompt], { stdio: 'inherit' });
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
printLine('');
|
|
2561
|
+
}
|
|
2562
|
+
// Setup summary (success criteria checklist)
|
|
2563
|
+
const summaryTargets = installOutcomes.length > 0
|
|
2564
|
+
? installOutcomes
|
|
2565
|
+
.filter(outcome => outcome.action !== 'failed')
|
|
2566
|
+
.map(outcome => ({ outcome, status: getSkillTargetStatus(outcome.target) }))
|
|
2567
|
+
: [];
|
|
2568
|
+
const source = resolveSkillSource();
|
|
2569
|
+
if (hasJsonFlag()) {
|
|
2570
|
+
printJson({
|
|
2571
|
+
auth: { state: auth.state, server: auth.baseUrl },
|
|
2572
|
+
integrations: installOutcomes.map(outcome => ({
|
|
2573
|
+
target: outcome.target,
|
|
2574
|
+
path: outcome.path,
|
|
2575
|
+
action: outcome.action,
|
|
2576
|
+
mode: outcome.mode,
|
|
2577
|
+
error: outcome.error,
|
|
2578
|
+
})),
|
|
2579
|
+
skill: {
|
|
2580
|
+
root: source.root,
|
|
2581
|
+
source: source.source,
|
|
2582
|
+
cliVersion: getCliVersion(),
|
|
2583
|
+
skillHash: computeSkillContentHash(source.root),
|
|
2584
|
+
},
|
|
2585
|
+
workspace: { state: workspaceState, path: getWorkspaceRoot() },
|
|
2586
|
+
});
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
printLine('Setup summary');
|
|
2590
|
+
printLine(` Auth: ${auth.state === 'signed-in' ? `signed in (${sanitizeTerminalText(auth.baseUrl)})` : 'not signed in — run `orizu login`'}`);
|
|
2591
|
+
if (summaryTargets.length > 0) {
|
|
2592
|
+
printLine(' Integrations:');
|
|
2593
|
+
for (const { outcome, status } of summaryTargets) {
|
|
2594
|
+
const modeLabel = status.mode ? `, ${status.mode}` : '';
|
|
2595
|
+
printLine(` ${outcome.target.padEnd(16)} ${status.state}${modeLabel} ${outcome.path}`);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
else {
|
|
2599
|
+
printLine(' Integrations: none installed this run — `orizu install-skill` sets them up.');
|
|
2600
|
+
}
|
|
2601
|
+
const failed = installOutcomes.filter(outcome => outcome.action === 'failed');
|
|
2602
|
+
for (const failure of failed) {
|
|
2603
|
+
printLine(` ${failure.target.padEnd(16)} failed: ${failure.error}`);
|
|
2604
|
+
}
|
|
2605
|
+
printLine(` Skill source: ${source.root} (${source.source})`);
|
|
2606
|
+
printLine(` Workspace: ${workspaceState === 'skipped' ? 'skipped' : `${getWorkspaceRoot()} (${workspaceState})`}`);
|
|
2607
|
+
printLine(' Next: hand the prompt above to your coding agent to plan repo-specific Orizu adoption.');
|
|
2608
|
+
}
|
|
2609
|
+
function setupPromptCommand() {
|
|
2610
|
+
const prompt = renderAgentSetupPrompt({
|
|
2611
|
+
workspacePath: workspaceExists() ? getWorkspaceRoot() : null,
|
|
2612
|
+
});
|
|
2613
|
+
if (hasJsonFlag()) {
|
|
2614
|
+
printJson({ prompt });
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
printLine(prompt);
|
|
2055
2618
|
}
|
|
2056
2619
|
function readJsonFile(pathArg) {
|
|
2057
2620
|
const raw = readSourceFile(pathArg);
|
|
@@ -2281,6 +2844,10 @@ async function createAppFromFile() {
|
|
|
2281
2844
|
throw new Error(`Failed to create app: ${await response.text()}`);
|
|
2282
2845
|
}
|
|
2283
2846
|
const data = await parseJsonResponse(response, 'App create');
|
|
2847
|
+
if (hasJsonFlag()) {
|
|
2848
|
+
printJson({ app: data.app, warnings: data.warnings || [] });
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2284
2851
|
printLine(`Created app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) v${data.app.versionNum}`);
|
|
2285
2852
|
if (data.app.url) {
|
|
2286
2853
|
printLine(`View app: ${formatTerminalLink(data.app.url)}`);
|
|
@@ -2316,6 +2883,14 @@ async function previewAppFromFile() {
|
|
|
2316
2883
|
headed,
|
|
2317
2884
|
keepOpen,
|
|
2318
2885
|
});
|
|
2886
|
+
if (hasJsonFlag()) {
|
|
2887
|
+
printJson({
|
|
2888
|
+
url: result.url,
|
|
2889
|
+
screenshotPath: result.screenshotPath || null,
|
|
2890
|
+
warnings: result.warnings,
|
|
2891
|
+
});
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2319
2894
|
printLine(`Preview rendered: ${formatTerminalLink(result.url)}`);
|
|
2320
2895
|
if (result.screenshotPath) {
|
|
2321
2896
|
printLine(`Screenshot: ${sanitizeTerminalText(result.screenshotPath)}`);
|
|
@@ -2358,6 +2933,10 @@ async function updateAppFromFile() {
|
|
|
2358
2933
|
throw new Error(`Failed to update app: ${await response.text()}`);
|
|
2359
2934
|
}
|
|
2360
2935
|
const data = await parseJsonResponse(response, 'App update');
|
|
2936
|
+
if (hasJsonFlag()) {
|
|
2937
|
+
printJson({ app: data.app, warnings: data.warnings || [] });
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2361
2940
|
printLine(`Updated app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) to v${data.app.versionNum}`);
|
|
2362
2941
|
if (data.warnings?.length) {
|
|
2363
2942
|
printLine(`Warnings: ${sanitizeTerminalText(data.warnings.join('; '))}`);
|
|
@@ -2392,6 +2971,10 @@ async function linkAppDataset() {
|
|
|
2392
2971
|
throw new Error(`Failed to link dataset: ${await response.text()}`);
|
|
2393
2972
|
}
|
|
2394
2973
|
const data = await parseJsonResponse(response, 'App link dataset');
|
|
2974
|
+
if (hasJsonFlag()) {
|
|
2975
|
+
printJson({ app: data.app, linkedDataset: data.linkedDataset, versionNum: data.versionNum });
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2395
2978
|
printLine(`Linked dataset ${sanitizeTerminalText(data.linkedDataset.name)} (${sanitizeTerminalText(data.linkedDataset.id)}) to app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) version ${data.versionNum}`);
|
|
2396
2979
|
}
|
|
2397
2980
|
function parseCommaSeparated(value) {
|
|
@@ -2439,7 +3022,7 @@ async function createTask() {
|
|
|
2439
3022
|
});
|
|
2440
3023
|
if (!response.ok) {
|
|
2441
3024
|
const cliError = await formatTaskCreateError(response);
|
|
2442
|
-
if (
|
|
3025
|
+
if (hasJsonFlag()) {
|
|
2443
3026
|
const payload = cliError.structuredPayload ?? { error: cliError.message };
|
|
2444
3027
|
printLine(JSON.stringify({
|
|
2445
3028
|
...payload,
|
|
@@ -2449,7 +3032,7 @@ async function createTask() {
|
|
|
2449
3032
|
throw cliError;
|
|
2450
3033
|
}
|
|
2451
3034
|
const data = await parseJsonResponse(response, 'Task create');
|
|
2452
|
-
if (
|
|
3035
|
+
if (hasJsonFlag()) {
|
|
2453
3036
|
printLine(JSON.stringify({
|
|
2454
3037
|
taskId: data.task.id,
|
|
2455
3038
|
datasetId,
|
|
@@ -2502,7 +3085,7 @@ async function publishTask() {
|
|
|
2502
3085
|
throw new Error(`Failed to publish task: ${await extractErrorMessage(response)}`);
|
|
2503
3086
|
}
|
|
2504
3087
|
const data = await parseJsonResponse(response, 'Task publish');
|
|
2505
|
-
if (
|
|
3088
|
+
if (hasJsonFlag()) {
|
|
2506
3089
|
printLine(JSON.stringify({
|
|
2507
3090
|
taskId: data.task.id,
|
|
2508
3091
|
status: data.task.status,
|
|
@@ -2540,6 +3123,10 @@ async function assignTask() {
|
|
|
2540
3123
|
throw new Error(`Failed to assign task: ${await response.text()}`);
|
|
2541
3124
|
}
|
|
2542
3125
|
const data = await parseJsonResponse(response, 'Task assign');
|
|
3126
|
+
if (hasJsonFlag()) {
|
|
3127
|
+
printJson({ assignmentsCreated: data.assignmentsCreated });
|
|
3128
|
+
return;
|
|
3129
|
+
}
|
|
2543
3130
|
printLine(`Created ${data.assignmentsCreated} assignments.`);
|
|
2544
3131
|
}
|
|
2545
3132
|
async function taskStatus() {
|
|
@@ -2550,7 +3137,7 @@ async function taskStatus() {
|
|
|
2550
3137
|
const response = await authedFetch(`/api/cli/tasks/${encodeURIComponent(taskId)}/status`);
|
|
2551
3138
|
if (!response.ok) {
|
|
2552
3139
|
const rawBody = await response.text();
|
|
2553
|
-
if (
|
|
3140
|
+
if (hasJsonFlag()) {
|
|
2554
3141
|
let errorPayload = { error: rawBody };
|
|
2555
3142
|
try {
|
|
2556
3143
|
const parsed = JSON.parse(rawBody);
|
|
@@ -2577,7 +3164,7 @@ async function taskStatus() {
|
|
|
2577
3164
|
throw new Error(`Failed to fetch task status: ${errorMsg}`);
|
|
2578
3165
|
}
|
|
2579
3166
|
const data = await parseJsonResponse(response, 'Task status');
|
|
2580
|
-
if (
|
|
3167
|
+
if (hasJsonFlag()) {
|
|
2581
3168
|
printLine(JSON.stringify(data, null, 2));
|
|
2582
3169
|
return;
|
|
2583
3170
|
}
|
|
@@ -2599,6 +3186,10 @@ async function updateTaskStatus(targetStatus) {
|
|
|
2599
3186
|
throw new Error(`Failed to ${verb} task: ${await response.text()}`);
|
|
2600
3187
|
}
|
|
2601
3188
|
const data = await parseJsonResponse(response, 'Task status update');
|
|
3189
|
+
if (hasJsonFlag()) {
|
|
3190
|
+
printJson({ task: data.task });
|
|
3191
|
+
return;
|
|
3192
|
+
}
|
|
2602
3193
|
const action = targetStatus === 'paused' ? 'Paused' : 'Unpaused';
|
|
2603
3194
|
printLine(`${action} task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]`);
|
|
2604
3195
|
}
|
|
@@ -2656,7 +3247,7 @@ async function appDetail() {
|
|
|
2656
3247
|
if (!detail) {
|
|
2657
3248
|
throw new Error(`App '${appId}' not found in project '${projectSlug}'`);
|
|
2658
3249
|
}
|
|
2659
|
-
if (
|
|
3250
|
+
if (hasJsonFlag()) {
|
|
2660
3251
|
printLine(JSON.stringify(detail, null, 2));
|
|
2661
3252
|
return;
|
|
2662
3253
|
}
|
|
@@ -2709,11 +3300,24 @@ async function exportAppSource() {
|
|
|
2709
3300
|
? expandHomePath(outPathArg)
|
|
2710
3301
|
: defaultAppExportFilename(data.app.name, data.version.versionNum);
|
|
2711
3302
|
writeTextFileEnsuringDir(filename, data.version.code);
|
|
3303
|
+
if (hasJsonFlag()) {
|
|
3304
|
+
printJson({
|
|
3305
|
+
app: { id: data.app.id, name: data.app.name },
|
|
3306
|
+
versionNum: data.version.versionNum,
|
|
3307
|
+
savedTo: filename,
|
|
3308
|
+
});
|
|
3309
|
+
return;
|
|
3310
|
+
}
|
|
2712
3311
|
printLine(`Saved app ${sanitizeTerminalText(data.app.name)} v${data.version.versionNum} source to ${sanitizeTerminalText(filename)}`);
|
|
2713
3312
|
}
|
|
2714
3313
|
async function listTeamMembers() {
|
|
2715
3314
|
const teamSlug = await resolveTeamSlug(getArg('--team'));
|
|
2716
|
-
|
|
3315
|
+
const members = await fetchTeamMembers(teamSlug);
|
|
3316
|
+
if (hasJsonFlag()) {
|
|
3317
|
+
printJson({ team: teamSlug, members });
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
printTeamMembers(members);
|
|
2717
3321
|
}
|
|
2718
3322
|
async function addTeamMember() {
|
|
2719
3323
|
const teamSlug = await resolveTeamSlug(getArg('--team'));
|
|
@@ -2730,6 +3334,10 @@ async function addTeamMember() {
|
|
|
2730
3334
|
throw new Error(`Failed to add team member: ${await response.text()}`);
|
|
2731
3335
|
}
|
|
2732
3336
|
const data = await parseJsonResponse(response, 'Team member add');
|
|
3337
|
+
if (hasJsonFlag()) {
|
|
3338
|
+
printJson({ member: data.member });
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
2733
3341
|
printLine(`Added team member ${sanitizeTerminalText(data.member.email)} (${sanitizeTerminalText(data.member.id)})`);
|
|
2734
3342
|
}
|
|
2735
3343
|
async function removeTeamMember() {
|
|
@@ -2747,6 +3355,10 @@ async function removeTeamMember() {
|
|
|
2747
3355
|
if (!response.ok) {
|
|
2748
3356
|
throw new Error(`Failed to remove team member: ${await response.text()}`);
|
|
2749
3357
|
}
|
|
3358
|
+
if (hasJsonFlag()) {
|
|
3359
|
+
printJson({ removed: { id: member.id, email: member.email } });
|
|
3360
|
+
return;
|
|
3361
|
+
}
|
|
2750
3362
|
printLine(`Removed team member ${sanitizeTerminalText(member.email)}`);
|
|
2751
3363
|
}
|
|
2752
3364
|
async function changeTeamMemberRole() {
|
|
@@ -2773,6 +3385,10 @@ async function changeTeamMemberRole() {
|
|
|
2773
3385
|
if (!response.ok) {
|
|
2774
3386
|
throw new Error(`Failed to update member role: ${await response.text()}`);
|
|
2775
3387
|
}
|
|
3388
|
+
if (hasJsonFlag()) {
|
|
3389
|
+
printJson({ member: { id: member.id, email: member.email, role } });
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
2776
3392
|
printLine(`Updated ${sanitizeTerminalText(member.email)} role to ${sanitizeTerminalText(role)}`);
|
|
2777
3393
|
}
|
|
2778
3394
|
async function createDatasetFromRows(project, name, sourceType, rows, readmeMarkdown = null) {
|
|
@@ -2849,6 +3465,10 @@ async function uploadJsonlDatasetInChunks(file, project, datasetName, readmeMark
|
|
|
2849
3465
|
if (!dataset) {
|
|
2850
3466
|
throw new Error('Dataset file contains no rows');
|
|
2851
3467
|
}
|
|
3468
|
+
if (hasJsonFlag()) {
|
|
3469
|
+
printJson({ dataset });
|
|
3470
|
+
return;
|
|
3471
|
+
}
|
|
2852
3472
|
printLine(`Uploaded dataset ${sanitizeTerminalText(dataset.name)} (${sanitizeTerminalText(dataset.id)}) with ${dataset.rowCount} rows.`);
|
|
2853
3473
|
if (dataset.url) {
|
|
2854
3474
|
printLine(`View dataset: ${formatTerminalLink(dataset.url)}`);
|
|
@@ -2872,6 +3492,10 @@ async function uploadDataset() {
|
|
|
2872
3492
|
}
|
|
2873
3493
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2874
3494
|
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
3495
|
+
if (hasJsonFlag()) {
|
|
3496
|
+
printJson({ dataset: data.dataset });
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
2875
3499
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
2876
3500
|
if (data.dataset.url) {
|
|
2877
3501
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
@@ -2976,6 +3600,10 @@ async function downloadDataset() {
|
|
|
2976
3600
|
: `${datasetId}.${format}`;
|
|
2977
3601
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
2978
3602
|
writeFileSync(filename, bytes);
|
|
3603
|
+
if (hasJsonFlag()) {
|
|
3604
|
+
printJson({ datasetId, format, savedTo: filename });
|
|
3605
|
+
return;
|
|
3606
|
+
}
|
|
2979
3607
|
printLine(`Saved dataset ${sanitizeTerminalText(datasetId)} (${format.toUpperCase()}) to ${sanitizeTerminalText(filename)}`);
|
|
2980
3608
|
}
|
|
2981
3609
|
const MAX_INPUT_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
@@ -3101,6 +3729,10 @@ async function appendDatasetRows() {
|
|
|
3101
3729
|
}
|
|
3102
3730
|
}
|
|
3103
3731
|
if (lastResult) {
|
|
3732
|
+
if (hasJsonFlag()) {
|
|
3733
|
+
printJson({ dataset: lastResult.dataset, appendedCount: totalAppended });
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3104
3736
|
printLine(`Appended ${totalAppended} rows to dataset ${sanitizeTerminalText(lastResult.dataset.name)} (${sanitizeTerminalText(lastResult.dataset.id)}). New row count: ${lastResult.dataset.rowCount}`);
|
|
3105
3737
|
}
|
|
3106
3738
|
}
|
|
@@ -3147,6 +3779,10 @@ async function editDatasetRows() {
|
|
|
3147
3779
|
throw new Error(`Edit rows failed: ${await response.text()}`);
|
|
3148
3780
|
}
|
|
3149
3781
|
const data = await parseJsonResponse(response, 'Dataset edit rows');
|
|
3782
|
+
if (hasJsonFlag()) {
|
|
3783
|
+
printJson({ dataset: data.dataset, updatedCount: data.updatedCount });
|
|
3784
|
+
return;
|
|
3785
|
+
}
|
|
3150
3786
|
printLine(`Updated ${data.updatedCount} rows in dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}). Current row count: ${data.dataset.rowCount}`);
|
|
3151
3787
|
}
|
|
3152
3788
|
async function deleteDatasetRows() {
|
|
@@ -3175,6 +3811,10 @@ async function deleteDatasetRows() {
|
|
|
3175
3811
|
throw new Error(`Delete rows failed: ${await response.text()}`);
|
|
3176
3812
|
}
|
|
3177
3813
|
const data = await parseJsonResponse(response, 'Dataset delete rows');
|
|
3814
|
+
if (hasJsonFlag()) {
|
|
3815
|
+
printJson({ dataset: data.dataset, deletedCount: data.deletedCount });
|
|
3816
|
+
return;
|
|
3817
|
+
}
|
|
3178
3818
|
printLine(`Deleted ${data.deletedCount} rows from dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}). New row count: ${data.dataset.rowCount}`);
|
|
3179
3819
|
}
|
|
3180
3820
|
async function confirmDatasetDeletion(dataset) {
|
|
@@ -3213,6 +3853,10 @@ async function deleteDataset() {
|
|
|
3213
3853
|
throw new Error(`Delete failed: ${await response.text()}`);
|
|
3214
3854
|
}
|
|
3215
3855
|
const data = await parseJsonResponse(response, 'Dataset delete');
|
|
3856
|
+
if (hasJsonFlag()) {
|
|
3857
|
+
printJson({ dataset: data.dataset, deleted: true });
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3216
3860
|
printLine(`Deleted dataset ${sanitizeTerminalText(data.dataset.id)}.`);
|
|
3217
3861
|
}
|
|
3218
3862
|
async function lockDataset() {
|
|
@@ -3236,6 +3880,10 @@ async function lockDataset() {
|
|
|
3236
3880
|
throw new Error(`Lock failed: ${await response.text()}`);
|
|
3237
3881
|
}
|
|
3238
3882
|
const data = await parseJsonResponse(response, 'Dataset lock');
|
|
3883
|
+
if (hasJsonFlag()) {
|
|
3884
|
+
printJson({ dataset: data.dataset });
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3239
3887
|
printLine(`Locked dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) at ${sanitizeTerminalText(data.dataset.lockedAt)}. Row count: ${data.dataset.rowCount}`);
|
|
3240
3888
|
}
|
|
3241
3889
|
async function cloneDataset() {
|
|
@@ -3259,6 +3907,10 @@ async function cloneDataset() {
|
|
|
3259
3907
|
throw new Error(`Clone failed: ${await response.text()}`);
|
|
3260
3908
|
}
|
|
3261
3909
|
const data = await parseJsonResponse(response, 'Dataset clone');
|
|
3910
|
+
if (hasJsonFlag()) {
|
|
3911
|
+
printJson({ dataset: data.dataset });
|
|
3912
|
+
return;
|
|
3913
|
+
}
|
|
3262
3914
|
printLine(`Cloned dataset ${sanitizeTerminalText(data.dataset.parentDatasetId)} -> ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}). Row count: ${data.dataset.rowCount}`);
|
|
3263
3915
|
}
|
|
3264
3916
|
async function downloadAnnotations() {
|
|
@@ -3281,6 +3933,10 @@ async function downloadAnnotations() {
|
|
|
3281
3933
|
: fallbackName;
|
|
3282
3934
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3283
3935
|
writeFileSync(filename, bytes);
|
|
3936
|
+
if (hasJsonFlag()) {
|
|
3937
|
+
printJson({ taskId, format, savedTo: filename });
|
|
3938
|
+
return;
|
|
3939
|
+
}
|
|
3284
3940
|
printLine(`Saved ${format.toUpperCase()} export to ${sanitizeTerminalText(filename)}`);
|
|
3285
3941
|
}
|
|
3286
3942
|
function readRunnerManifest(runnerDir) {
|
|
@@ -3493,7 +4149,12 @@ async function runnersExec() {
|
|
|
3493
4149
|
else {
|
|
3494
4150
|
writeFileSync(outPath, resultJsonl);
|
|
3495
4151
|
}
|
|
3496
|
-
|
|
4152
|
+
if (hasJsonFlag()) {
|
|
4153
|
+
printJson({ rowCount: context.rows.length, out: outPath });
|
|
4154
|
+
}
|
|
4155
|
+
else {
|
|
4156
|
+
printLine(`Wrote ${context.rows.length} runner results to ${sanitizeTerminalText(outPath)}`);
|
|
4157
|
+
}
|
|
3497
4158
|
}
|
|
3498
4159
|
finally {
|
|
3499
4160
|
materializedRunner.cleanup();
|
|
@@ -3527,6 +4188,9 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3527
4188
|
}
|
|
3528
4189
|
const helpTarget = extractHelpTarget(cliArgs);
|
|
3529
4190
|
if (helpTarget !== null) {
|
|
4191
|
+
if (helpTarget.length === 0) {
|
|
4192
|
+
printBannerIfInteractive();
|
|
4193
|
+
}
|
|
3530
4194
|
printLine(renderHelpForArgs(helpTarget));
|
|
3531
4195
|
return;
|
|
3532
4196
|
}
|
|
@@ -3544,6 +4208,14 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3544
4208
|
printCapabilities();
|
|
3545
4209
|
return;
|
|
3546
4210
|
}
|
|
4211
|
+
if (command === 'setup' && subcommand === 'prompt') {
|
|
4212
|
+
setupPromptCommand();
|
|
4213
|
+
return;
|
|
4214
|
+
}
|
|
4215
|
+
if (command === 'setup') {
|
|
4216
|
+
await setupCommand();
|
|
4217
|
+
return;
|
|
4218
|
+
}
|
|
3547
4219
|
if (command === 'install-skill') {
|
|
3548
4220
|
await installSkillCommand();
|
|
3549
4221
|
return;
|
|
@@ -3552,6 +4224,18 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3552
4224
|
await installSkillCommand();
|
|
3553
4225
|
return;
|
|
3554
4226
|
}
|
|
4227
|
+
if (command === 'skills' && subcommand === 'path') {
|
|
4228
|
+
skillsPathCommand();
|
|
4229
|
+
return;
|
|
4230
|
+
}
|
|
4231
|
+
if (command === 'skills' && subcommand === 'status') {
|
|
4232
|
+
skillsStatusCommand();
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
if (command === 'skills' && subcommand === 'update') {
|
|
4236
|
+
await skillsUpdateCommand();
|
|
4237
|
+
return;
|
|
4238
|
+
}
|
|
3555
4239
|
if (command === 'login') {
|
|
3556
4240
|
await login();
|
|
3557
4241
|
return;
|
|
@@ -3621,6 +4305,14 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3621
4305
|
await pushPromptArtifact('prompt');
|
|
3622
4306
|
return;
|
|
3623
4307
|
}
|
|
4308
|
+
if (command === 'prompts' && subcommand === 'archive') {
|
|
4309
|
+
await setPromptArchivedState(true);
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
if (command === 'prompts' && subcommand === 'restore') {
|
|
4313
|
+
await setPromptArchivedState(false);
|
|
4314
|
+
return;
|
|
4315
|
+
}
|
|
3624
4316
|
if (command === 'prompts' && subcommand === 'labels' && cliArgs[2] === 'set') {
|
|
3625
4317
|
await setPromptLabel();
|
|
3626
4318
|
return;
|