orizu 0.5.24 → 0.5.26

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.
Files changed (57) hide show
  1. package/README.md +23 -3
  2. package/dist/credentials.d.ts +15 -0
  3. package/dist/credentials.d.ts.map +1 -1
  4. package/dist/credentials.js +279 -59
  5. package/dist/help.d.ts.map +1 -1
  6. package/dist/help.js +5 -2
  7. package/dist/http.d.ts +20 -0
  8. package/dist/http.d.ts.map +1 -1
  9. package/dist/http.js +239 -19
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +153 -213
  12. package/dist/keyboard-select.d.ts +7 -0
  13. package/dist/keyboard-select.d.ts.map +1 -0
  14. package/dist/keyboard-select.js +72 -0
  15. package/dist/logout-diagnostic.d.ts +4 -0
  16. package/dist/logout-diagnostic.d.ts.map +1 -0
  17. package/dist/logout-diagnostic.js +26 -0
  18. package/dist/setup-account.d.ts +11 -0
  19. package/dist/setup-account.d.ts.map +1 -0
  20. package/dist/setup-account.js +106 -0
  21. package/dist/setup-onboarding.d.ts +27 -1
  22. package/dist/setup-onboarding.d.ts.map +1 -1
  23. package/dist/setup-onboarding.js +95 -11
  24. package/dist/setup-summary.d.ts +12 -0
  25. package/dist/setup-summary.d.ts.map +1 -0
  26. package/dist/setup-summary.js +56 -0
  27. package/package.json +1 -1
  28. package/src/credentials.ts +326 -57
  29. package/src/help.ts +7 -2
  30. package/src/http.ts +325 -20
  31. package/src/index.ts +193 -279
  32. package/src/keyboard-select.ts +97 -0
  33. package/src/logout-diagnostic.ts +39 -0
  34. package/src/setup-account.ts +129 -0
  35. package/src/setup-onboarding.ts +133 -10
  36. package/src/setup-summary.ts +77 -0
  37. package/vendor/skills/orizu/SKILL.md +51 -111
  38. package/vendor/skills/orizu/flows/expand.md +27 -0
  39. package/vendor/skills/orizu/flows/first-win.md +31 -0
  40. package/vendor/skills/orizu/flows/onboard.md +31 -0
  41. package/vendor/skills/orizu/flows/promote.md +33 -0
  42. package/vendor/skills/orizu/flows/recurse.md +23 -0
  43. package/vendor/skills/orizu/flows/triage.md +37 -0
  44. package/vendor/skills/orizu/references/assess-and-plan.md +5 -5
  45. package/vendor/skills/orizu/references/authority-map.md +20 -0
  46. package/vendor/skills/orizu/references/building-apps.md +7 -1
  47. package/vendor/skills/orizu/references/building-judges.md +15 -16
  48. package/vendor/skills/orizu/references/cli-reference.md +17 -8
  49. package/vendor/skills/orizu/references/dataset-design.md +16 -6
  50. package/vendor/skills/orizu/references/eval-strategy.md +18 -15
  51. package/vendor/skills/orizu/references/instructions-after-prompts.md +2 -2
  52. package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +10 -6
  53. package/vendor/skills/orizu/references/optimization-reports.md +11 -11
  54. package/vendor/skills/orizu/references/optimization-with-gepa.md +4 -2
  55. package/vendor/skills/orizu/references/primer.md +18 -22
  56. package/vendor/skills/orizu/references/prompt-control-plane.md +5 -5
  57. package/vendor/skills/orizu/references/vocabulary.md +27 -0
@@ -0,0 +1,7 @@
1
+ interface KeyboardSelectOptions<T> {
2
+ onEscape?: () => T;
3
+ escapeLabel?: string;
4
+ }
5
+ export declare function promptKeyboardSelect<T>(title: string, items: T[], label: (item: T, index: number) => string, options?: KeyboardSelectOptions<T>): Promise<T>;
6
+ export {};
7
+ //# sourceMappingURL=keyboard-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-select.d.ts","sourceRoot":"","sources":["../src/keyboard-select.ts"],"names":[],"mappings":"AAUA,UAAU,qBAAqB,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAOD,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,EACzC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,CAAC,CAAC,CAuEZ"}
@@ -0,0 +1,72 @@
1
+ import { emitKeypressEvents } from 'readline';
2
+ import { stdin as input, stdout as output } from 'process';
3
+ import { sanitizeTerminalText } from './json-response.js';
4
+ export async function promptKeyboardSelect(title, items, label, options) {
5
+ if (items.length === 0) {
6
+ throw new Error(`No options available for ${title.toLowerCase()}`);
7
+ }
8
+ if (!input.isTTY || !output.isTTY) {
9
+ throw new Error(`${title} selection requires interactive terminal. Provide flags explicitly instead.`);
10
+ }
11
+ output.write(`\n${sanitizeTerminalText(title)}\n`);
12
+ output.write(`Use ↑/↓ to choose, Enter to confirm, or Esc to ${options?.escapeLabel || 'cancel'}.\n`);
13
+ let selected = 0;
14
+ let rendered = false;
15
+ const rawInput = input;
16
+ const wasRaw = Boolean(rawInput.isRaw);
17
+ emitKeypressEvents(input);
18
+ function render() {
19
+ if (rendered)
20
+ output.write(`\x1b[${items.length}A`);
21
+ items.forEach((item, index) => {
22
+ output.write('\x1b[2K');
23
+ const marker = index === selected ? '●' : '○';
24
+ output.write(` ${marker} ${sanitizeTerminalText(label(item, index))}\n`);
25
+ });
26
+ rendered = true;
27
+ }
28
+ render();
29
+ output.write('\x1b[?25l');
30
+ return new Promise((resolvePromise, rejectPromise) => {
31
+ function cleanup() {
32
+ input.off('keypress', onKeypress);
33
+ if (rawInput.setRawMode && !wasRaw)
34
+ rawInput.setRawMode(false);
35
+ input.pause();
36
+ output.write('\x1b[?25h\n');
37
+ }
38
+ function onKeypress(_chunk, key) {
39
+ if (key.ctrl && key.name === 'c') {
40
+ cleanup();
41
+ rejectPromise(new Error('Setup cancelled.'));
42
+ return;
43
+ }
44
+ if (key.name === 'escape') {
45
+ cleanup();
46
+ if (options?.onEscape)
47
+ resolvePromise(options.onEscape());
48
+ else
49
+ rejectPromise(new Error('Setup cancelled.'));
50
+ return;
51
+ }
52
+ if (key.name === 'up') {
53
+ selected = (selected - 1 + items.length) % items.length;
54
+ render();
55
+ return;
56
+ }
57
+ if (key.name === 'down') {
58
+ selected = (selected + 1) % items.length;
59
+ render();
60
+ return;
61
+ }
62
+ if (key.name === 'return' || key.name === 'enter') {
63
+ const item = items[selected];
64
+ cleanup();
65
+ resolvePromise(item);
66
+ }
67
+ }
68
+ input.on('keypress', onKeypress);
69
+ input.resume();
70
+ rawInput.setRawMode?.(true);
71
+ });
72
+ }
@@ -0,0 +1,4 @@
1
+ export declare const LOGOUT_DIAGNOSTIC_LIMIT = 180;
2
+ export declare function describeLogoutHttpFailure(response: Response, secrets: readonly string[]): Promise<string>;
3
+ export declare function describeLogoutTransportFailure(error: unknown, secrets: readonly string[], prefix?: string): string;
4
+ //# sourceMappingURL=logout-diagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout-diagnostic.d.ts","sourceRoot":"","sources":["../src/logout-diagnostic.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAQ1C,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAMR"}
@@ -0,0 +1,26 @@
1
+ import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
2
+ import { redactSecrets } from './secret-redaction.js';
3
+ export const LOGOUT_DIAGNOSTIC_LIMIT = 180;
4
+ function boundedLogoutDiagnostic(value, secrets) {
5
+ const bounded = sanitizeTerminalText(redactSecrets(value, { secrets }))
6
+ .slice(0, LOGOUT_DIAGNOSTIC_LIMIT);
7
+ return sanitizeHumanInlineText(sanitizeTerminalText, bounded).slice(0, LOGOUT_DIAGNOSTIC_LIMIT);
8
+ }
9
+ export async function describeLogoutHttpFailure(response, secrets) {
10
+ let body = '';
11
+ try {
12
+ body = await response.text();
13
+ }
14
+ catch (error) {
15
+ return describeLogoutTransportFailure(error, secrets, `HTTP ${response.status} body error`);
16
+ }
17
+ const detail = body.trim();
18
+ return boundedLogoutDiagnostic(detail ? `HTTP ${response.status}: ${detail}` : `HTTP ${response.status}`, secrets);
19
+ }
20
+ export function describeLogoutTransportFailure(error, secrets, prefix) {
21
+ const message = error instanceof Error ? error.message : String(error);
22
+ const isTimeout = error instanceof Error
23
+ && (error.name === 'AbortError' || error.name === 'TimeoutError');
24
+ const label = prefix ?? (isTimeout ? 'Timeout' : 'Network error');
25
+ return boundedLogoutDiagnostic(`${label}: ${message || 'remote logout failed'}`, secrets);
26
+ }
@@ -0,0 +1,11 @@
1
+ import { type AuthenticatedRequestContext } from './http.js';
2
+ export interface SetupAuthState {
3
+ state: 'signed-in' | 'signed-out';
4
+ baseUrl: string;
5
+ }
6
+ export declare function describeSetupAuthState(): SetupAuthState;
7
+ export declare function validateSetupIdentityEmail(value: unknown): string;
8
+ export declare function confirmExistingSetupAccount(context: AuthenticatedRequestContext, chooseAccount: (displayIdentity: string) => Promise<'continue' | 'switch'>, switchAccount: () => Promise<void>, options?: {
9
+ dryRun?: boolean;
10
+ }): Promise<'continued' | 'switched'>;
11
+ //# sourceMappingURL=setup-account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-account.d.ts","sourceRoot":"","sources":["../src/setup-account.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,WAAW,CAAA;AAElB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,GAAG,YAAY,CAAA;IACjC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,sBAAsB,IAAI,cAAc,CAOvD;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA2BjE;AA4DD,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,2BAA2B,EACpC,aAAa,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC,EAC1E,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAClC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,CAgBnC"}
@@ -0,0 +1,106 @@
1
+ import { hasResolvableAuth } from './credentials.js';
2
+ import { getBaseUrl, } from './http.js';
3
+ export function describeSetupAuthState() {
4
+ const baseUrl = getBaseUrl();
5
+ return {
6
+ // An environment bearer (ORIZU_TOKEN / ORIZU_TOKEN_FILE) is signed-in too.
7
+ state: hasResolvableAuth(baseUrl) ? 'signed-in' : 'signed-out',
8
+ baseUrl,
9
+ };
10
+ }
11
+ export function validateSetupIdentityEmail(value) {
12
+ if (typeof value !== 'string' || /[\u0000-\u001f\u007f-\u009f]/.test(value))
13
+ return '';
14
+ const email = value.trim();
15
+ if (!email || email.length > 254 || /\s/.test(email))
16
+ return '';
17
+ const parts = email.split('@');
18
+ if (parts.length !== 2)
19
+ return '';
20
+ const [localPart, domain] = parts;
21
+ if (!localPart
22
+ || localPart.length > 64
23
+ || localPart.startsWith('.')
24
+ || localPart.endsWith('.')
25
+ || localPart.includes('..')
26
+ || !/^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+$/.test(localPart))
27
+ return '';
28
+ if (!domain || domain.length > 253)
29
+ return '';
30
+ const labels = domain.split('.');
31
+ if (labels.some(label => !label
32
+ || label.length > 63
33
+ || !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(label)))
34
+ return '';
35
+ return email;
36
+ }
37
+ function identityRecovery(context) {
38
+ if (context.source === 'ORIZU_TOKEN') {
39
+ return 'Rotate or unset ORIZU_TOKEN and rerun `orizu setup`.';
40
+ }
41
+ if (context.source === 'ORIZU_TOKEN_FILE') {
42
+ return 'Fix, rotate, or unset ORIZU_TOKEN_FILE and rerun `orizu setup`.';
43
+ }
44
+ return 'Run `orizu login` and rerun `orizu setup`.';
45
+ }
46
+ function isRecord(value) {
47
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
48
+ }
49
+ function validateAgentUserId(value) {
50
+ if (typeof value !== 'string')
51
+ return '';
52
+ const userId = value.trim();
53
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(userId)
54
+ ? userId
55
+ : '';
56
+ }
57
+ function isCredentialStoreError(error) {
58
+ return error instanceof Error && /^ORIZU_CREDENTIALS_[A-Z_]+:/.test(error.message);
59
+ }
60
+ async function fetchSetupDisplayIdentity(context) {
61
+ let response;
62
+ try {
63
+ response = await context.fetch('/api/cli/auth/whoami');
64
+ }
65
+ catch (error) {
66
+ if (isCredentialStoreError(error))
67
+ throw error;
68
+ throw new Error(`Could not identify the authenticated account. ${identityRecovery(context)}`);
69
+ }
70
+ if (!response.ok) {
71
+ throw new Error(`Could not identify the authenticated account. ${identityRecovery(context)}`);
72
+ }
73
+ let data;
74
+ try {
75
+ data = await response.json();
76
+ }
77
+ catch {
78
+ throw new Error(`Could not identify the authenticated account. ${identityRecovery(context)}`);
79
+ }
80
+ if (!isRecord(data) || !isRecord(data.user)) {
81
+ throw new Error(`Could not identify the authenticated account. ${identityRecovery(context)}`);
82
+ }
83
+ const email = validateSetupIdentityEmail(data.user.email);
84
+ if (email)
85
+ return email;
86
+ if (data.user.email === null) {
87
+ const agentUserId = validateAgentUserId(data.user.id);
88
+ if (agentUserId)
89
+ return `Agent ${agentUserId}`;
90
+ throw new Error(`The authenticated account has no usable email or agent ID. ${identityRecovery(context)}`);
91
+ }
92
+ throw new Error(`The authenticated account has no usable email. ${identityRecovery(context)}`);
93
+ }
94
+ export async function confirmExistingSetupAccount(context, chooseAccount, switchAccount, options = {}) {
95
+ const choice = await chooseAccount(await fetchSetupDisplayIdentity(context));
96
+ if (choice === 'continue')
97
+ return 'continued';
98
+ if (options.dryRun) {
99
+ throw new Error('Account switching cannot be previewed. Rerun `orizu setup` without `--dry-run` to log in with a different account.');
100
+ }
101
+ if (context.source !== 'stored') {
102
+ throw new Error(`Authentication is supplied by ${context.source}. Unset ${context.source} and rerun \`orizu setup\` to log in with a different account.`);
103
+ }
104
+ await switchAccount();
105
+ return 'switched';
106
+ }
@@ -1,4 +1,23 @@
1
- export declare const LAUNCHABLE_AGENTS: Readonly<Record<string, string>>;
1
+ import { type SkillInstallAgent, type SkillInstallTarget } from './skill-installer.js';
2
+ export interface AgentLaunchSpec {
3
+ command: string;
4
+ displayLabel: string;
5
+ buildPromptArgs: (prompt: string) => string[];
6
+ }
7
+ /**
8
+ * Measured launch contracts:
9
+ * - Local `claude --help`, `codex --help`, and `pi --help` expose a direct
10
+ * positional prompt/message (observed 2026-08-30).
11
+ * - OpenCode's primary CLI reference documents `opencode run [message..]`.
12
+ * - Cursor's primary CLI overview and parameter reference document the Cursor
13
+ * Agent executable as `agent`, with direct `agent "prompt"` and
14
+ * `agent -p "prompt"` initial-prompt forms (retrieved 2026-08-30):
15
+ * https://cursor.com/docs/cli/overview
16
+ * https://cursor.com/docs/cli/reference/parameters
17
+ * Cursor is not installed locally; `cursor` is the editor command and is not
18
+ * detected as Cursor Agent.
19
+ */
20
+ export declare const AGENT_LAUNCH_SPECS: Readonly<Record<string, AgentLaunchSpec>>;
2
21
  export interface SetupSelectionArgs {
3
22
  team: string | null;
4
23
  createTeam: string | null;
@@ -13,4 +32,11 @@ export declare function validateSetupSelectionArgs(args: SetupSelectionArgs): vo
13
32
  export declare function validateSetupAgentArgs(agents: string[], launchAgent: string | null): void;
14
33
  export declare function setupLaunchPrompt(teamSlug?: string, projectSlug?: string): string;
15
34
  export declare function findExecutable(name: string, pathValue?: string): string | null;
35
+ export interface SetupSkillChoice {
36
+ label: string;
37
+ target: SkillInstallTarget;
38
+ }
39
+ export declare function setupNativeTargetForAgent(agent: SkillInstallAgent): SkillInstallTarget | null;
40
+ export declare function resolveSetupSkillHome(): string;
41
+ export declare function detectedSetupSkillChoices(homeDir: string): SetupSkillChoice[];
16
42
  //# sourceMappingURL=setup-onboarding.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup-onboarding.d.ts","sourceRoot":"","sources":["../src/setup-onboarding.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI9D,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,oBAAoB,EAAE,OAAO,CAAA;CAC9B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ5D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAWzE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAOzF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAyB,GAAG,MAAM,GAAG,IAAI,CAK9F"}
1
+ {"version":3,"file":"setup-onboarding.d.ts","sourceRoot":"","sources":["../src/setup-onboarding.ts"],"names":[],"mappings":"AAKA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;CAC9C;AAID;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAUxE,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,oBAAoB,EAAE,OAAO,CAAA;CAC9B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ5D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAWzE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAOzF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAyB,GAAG,MAAM,GAAG,IAAI,CAa9F;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,kBAAkB,CAAA;CAC3B;AAgBD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,IAAI,CAG7F;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAsBD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA6B7E"}
@@ -1,10 +1,32 @@
1
- import { existsSync } from 'fs';
2
- import { delimiter, join, resolve } from 'path';
3
- import { isSkillInstallAgent, SKILL_INSTALL_AGENTS } from './skill-installer.js';
4
- export const LAUNCHABLE_AGENTS = {
5
- claude: 'claude',
6
- codex: 'codex',
7
- pi: 'pi',
1
+ import { accessSync, constants, statSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { delimiter, join, relative, resolve, sep } from 'path';
4
+ import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
5
+ import { getSkillInstallPath, getTargetForAgent, isSkillInstallAgent, SKILL_INSTALL_AGENTS, } from './skill-installer.js';
6
+ const directPromptArgs = (prompt) => [prompt];
7
+ /**
8
+ * Measured launch contracts:
9
+ * - Local `claude --help`, `codex --help`, and `pi --help` expose a direct
10
+ * positional prompt/message (observed 2026-08-30).
11
+ * - OpenCode's primary CLI reference documents `opencode run [message..]`.
12
+ * - Cursor's primary CLI overview and parameter reference document the Cursor
13
+ * Agent executable as `agent`, with direct `agent "prompt"` and
14
+ * `agent -p "prompt"` initial-prompt forms (retrieved 2026-08-30):
15
+ * https://cursor.com/docs/cli/overview
16
+ * https://cursor.com/docs/cli/reference/parameters
17
+ * Cursor is not installed locally; `cursor` is the editor command and is not
18
+ * detected as Cursor Agent.
19
+ */
20
+ export const AGENT_LAUNCH_SPECS = {
21
+ claude: { command: 'claude', displayLabel: 'claude', buildPromptArgs: directPromptArgs },
22
+ codex: { command: 'codex', displayLabel: 'codex', buildPromptArgs: directPromptArgs },
23
+ pi: { command: 'pi', displayLabel: 'pi', buildPromptArgs: directPromptArgs },
24
+ opencode: {
25
+ command: 'opencode',
26
+ displayLabel: 'opencode',
27
+ buildPromptArgs: prompt => ['run', prompt],
28
+ },
29
+ cursor: { command: 'agent', displayLabel: 'cursor', buildPromptArgs: directPromptArgs },
8
30
  };
9
31
  export function validateSetupFlagValues(args) {
10
32
  const valueFlags = new Set(['--team', '--create-team', '--project', '--create-project', '--agent', '--launch', '--mode', '--service-origin', '--attach-workspace']);
@@ -42,8 +64,8 @@ export function validateSetupAgentArgs(agents, launchAgent) {
42
64
  if (!isSkillInstallAgent(agent))
43
65
  throw new Error(`Unknown agent '${agent}'. Available agents: ${SKILL_INSTALL_AGENTS.join(', ')}`);
44
66
  }
45
- if (launchAgent && !(launchAgent in LAUNCHABLE_AGENTS)) {
46
- throw new Error(`Unknown --launch agent '${launchAgent}'. Choices: ${Object.keys(LAUNCHABLE_AGENTS).join(', ')}.`);
67
+ if (launchAgent && !(launchAgent in AGENT_LAUNCH_SPECS)) {
68
+ throw new Error(`Unknown --launch agent '${launchAgent}'. Choices: ${Object.keys(AGENT_LAUNCH_SPECS).join(', ')}.`);
47
69
  }
48
70
  }
49
71
  export function setupLaunchPrompt(teamSlug, projectSlug) {
@@ -52,8 +74,70 @@ export function setupLaunchPrompt(teamSlug, projectSlug) {
52
74
  }
53
75
  export function findExecutable(name, pathValue = process.env.PATH || '') {
54
76
  for (const directory of pathValue.split(delimiter)) {
55
- if (directory && existsSync(join(directory, name)))
56
- return resolve(directory, name);
77
+ if (!directory)
78
+ continue;
79
+ const candidate = join(directory, name);
80
+ try {
81
+ if (!statSync(candidate).isFile())
82
+ continue;
83
+ accessSync(candidate, constants.X_OK);
84
+ return resolve(candidate);
85
+ }
86
+ catch {
87
+ // A stale PATH entry must not hide a later launchable command.
88
+ }
57
89
  }
58
90
  return null;
59
91
  }
92
+ const SETUP_NATIVE_SKILL_CHOICES = [
93
+ { agent: 'claude', label: 'Claude Code', directory: ['.claude'], binary: 'claude' },
94
+ { agent: 'devin', label: 'Devin', directory: ['.devin', 'skills'] },
95
+ { agent: 'droid', label: 'Droid', directory: ['.factory', 'skills'] },
96
+ { agent: 'grok', label: 'Grok Build', directory: ['.grok', 'skills'] },
97
+ { agent: 'windsurf', label: 'Windsurf', directory: ['.windsurf', 'skills'] },
98
+ { agent: 'opencode', label: 'OpenCode', directory: ['.config', 'opencode'], binary: 'opencode' },
99
+ ];
100
+ export function setupNativeTargetForAgent(agent) {
101
+ if (agent === 'codex' || agent === 'pi')
102
+ return null;
103
+ return getTargetForAgent(agent, 'global');
104
+ }
105
+ export function resolveSetupSkillHome() {
106
+ return resolve(process.env.HOME || homedir());
107
+ }
108
+ function isSetupConfigDirectory(path) {
109
+ try {
110
+ return statSync(path).isDirectory();
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ const SETUP_SKILL_CHOICE_MAX_WIDTH = 72;
117
+ function setupSkillChoiceLabel(name, target, homeDir) {
118
+ const destination = getSkillInstallPath(target, { homeDir });
119
+ const userFacingDestination = `~/${relative(homeDir, destination).split(sep).join('/')}`;
120
+ const label = sanitizeHumanInlineText(sanitizeTerminalText, `${name} — ${userFacingDestination}`);
121
+ if (label.length > SETUP_SKILL_CHOICE_MAX_WIDTH) {
122
+ throw new Error(`Setup skill destination label exceeds ${SETUP_SKILL_CHOICE_MAX_WIDTH} columns.`);
123
+ }
124
+ return label;
125
+ }
126
+ export function detectedSetupSkillChoices(homeDir) {
127
+ const detectedNativeChoices = SETUP_NATIVE_SKILL_CHOICES.filter(choice => isSetupConfigDirectory(join(homeDir, ...choice.directory))
128
+ || Boolean(choice.binary && findExecutable(choice.binary)));
129
+ const priority = (choice) => choice.agent === 'claude' ? 0 : choice.agent === 'opencode' ? 1 : 2;
130
+ const universalTarget = 'agent-user';
131
+ return [{
132
+ label: setupSkillChoiceLabel('Universal (.agents — Codex and others)', universalTarget, homeDir),
133
+ target: universalTarget,
134
+ }, ...detectedNativeChoices
135
+ .sort((left, right) => priority(left) - priority(right))
136
+ .map(choice => {
137
+ const target = getTargetForAgent(choice.agent, 'global');
138
+ return {
139
+ label: setupSkillChoiceLabel(choice.agent === 'claude' ? 'Claude' : choice.label, target, homeDir),
140
+ target,
141
+ };
142
+ })];
143
+ }
@@ -0,0 +1,12 @@
1
+ import type { SkillInstallTarget } from './skill-installer.js';
2
+ export interface SetupSkillSummaryOutcome {
3
+ target: SkillInstallTarget;
4
+ path: string;
5
+ action: 'created' | 'updated' | 'skipped' | 'failed';
6
+ error?: string;
7
+ }
8
+ export declare function setupSkillFriendlyName(target: SkillInstallTarget): string;
9
+ export declare function sanitizeSetupHumanInlineText(value: unknown): string;
10
+ export declare function formatSetupSkillDestination(destination: string, setupHome: string): string;
11
+ export declare function renderSetupSkillSummary(outcomes: SetupSkillSummaryOutcome[], setupHome: string): string[];
12
+ //# sourceMappingURL=setup-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-summary.d.ts","sourceRoot":"","sources":["../src/setup-summary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAkB9D,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,kBAAkB,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IACpD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAEzE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnE;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAY1F;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,wBAAwB,EAAE,EACpC,SAAS,EAAE,MAAM,GAChB,MAAM,EAAE,CAuBV"}
@@ -0,0 +1,56 @@
1
+ import { isAbsolute, relative, sep } from 'node:path';
2
+ import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
3
+ const SETUP_SKILL_FRIENDLY_NAMES = {
4
+ 'agent-user': 'Universal',
5
+ 'agents-project': 'Universal (project)',
6
+ 'codex-user': 'Codex',
7
+ 'codex-project': 'Codex (project)',
8
+ 'claude-user': 'Claude',
9
+ 'claude-project': 'Claude (project)',
10
+ 'devin-user': 'Devin',
11
+ 'droid-user': 'Droid',
12
+ 'grok-user': 'Grok Build',
13
+ 'windsurf-user': 'Windsurf',
14
+ 'opencode-user': 'OpenCode',
15
+ 'opencode-project': 'OpenCode (project)',
16
+ 'agents-md': 'Project instructions (AGENTS.md)',
17
+ };
18
+ export function setupSkillFriendlyName(target) {
19
+ return SETUP_SKILL_FRIENDLY_NAMES[target];
20
+ }
21
+ export function sanitizeSetupHumanInlineText(value) {
22
+ return sanitizeHumanInlineText(sanitizeTerminalText, value);
23
+ }
24
+ export function formatSetupSkillDestination(destination, setupHome) {
25
+ if (!isAbsolute(destination) || !isAbsolute(setupHome))
26
+ return destination;
27
+ const relativeDestination = relative(setupHome, destination);
28
+ if (relativeDestination === '')
29
+ return '~';
30
+ if (!isAbsolute(relativeDestination) &&
31
+ relativeDestination !== '..' &&
32
+ !relativeDestination.startsWith(`..${sep}`)) {
33
+ return `~/${relativeDestination.split(sep).join('/')}`;
34
+ }
35
+ return destination;
36
+ }
37
+ export function renderSetupSkillSummary(outcomes, setupHome) {
38
+ const successful = outcomes.filter(outcome => outcome.action === 'created' || outcome.action === 'updated');
39
+ const failures = outcomes.filter(outcome => outcome.action === 'failed');
40
+ const lines = [];
41
+ if (successful.length > 0) {
42
+ lines.push('Skills installed to the following directories:');
43
+ const nameWidth = Math.max(...successful.map(outcome => setupSkillFriendlyName(outcome.target).length));
44
+ for (const outcome of successful) {
45
+ const destination = formatSetupSkillDestination(outcome.path, setupHome);
46
+ lines.push(` ${setupSkillFriendlyName(outcome.target).padEnd(nameWidth)} ${sanitizeSetupHumanInlineText(destination)}`);
47
+ }
48
+ }
49
+ else {
50
+ lines.push('Skills not installed. To install them, run `orizu install-skill`.');
51
+ }
52
+ for (const failure of failures) {
53
+ lines.push(`${setupSkillFriendlyName(failure.target)} failed: ${sanitizeSetupHumanInlineText(failure.error || 'Unknown error')}`);
54
+ }
55
+ return lines;
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orizu",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",