orizu 0.3.7 → 0.4.1

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 CHANGED
@@ -35,15 +35,56 @@ Install the CLI globally with your package manager:
35
35
  | pnpm | `pnpm add -g orizu` |
36
36
  | Bun | `bun add -g orizu` |
37
37
 
38
- Install the companion coding-agent skill from the CLI package:
38
+ Then run the guided setup — it signs you in, sets up your coding agents,
39
+ optionally creates a gitignored `.orizu/` workspace, and hands your agent a
40
+ prompt to plan repo-specific Orizu adoption:
39
41
 
40
42
  ```bash
41
- orizu install-skill --target agent-user --yes
43
+ orizu setup
42
44
  ```
43
45
 
44
- The npm package ships the skill alongside the CLI. Use `orizu install-skill --help`
45
- to see all supported targets, including Codex project, Claude user/project, and
46
- `AGENTS.md` installs.
46
+ Or install just the companion coding-agent skill:
47
+
48
+ ```bash
49
+ orizu install-skill --agent claude --agent codex --yes
50
+ ```
51
+
52
+ First-class plugin packages for Codex and Claude Code live under `plugins/`
53
+ in this repository. Plugins bundle the same skill; the CLI remains the runtime
54
+ either way.
55
+
56
+ ```bash
57
+ # Codex
58
+ codex plugin marketplace add <owner>/<this-repo>
59
+ codex plugin add orizu@orizu-plugins
60
+
61
+ # Claude Code (inside a session)
62
+ /plugin marketplace add <owner>/<this-repo>
63
+ /plugin install orizu@orizu
64
+ ```
65
+
66
+ The npm package ships the skill alongside the CLI. Run `orizu install-skill` in
67
+ a terminal for an interactive chooser (Claude Code and Codex preselected,
68
+ user-level installs by default), or use `orizu install-skill --help` for
69
+ project-scope installs, sync modes, and advanced targets including the managed
70
+ `AGENTS.md` section. Keep installs in sync after CLI upgrades with
71
+ `orizu skills status` and `orizu skills update`.
72
+
73
+ Coding agents can also read the bundled skill directly without installing it:
74
+
75
+ ```bash
76
+ orizu skills path --json
77
+ ```
78
+
79
+ This prints the skill location plus source metadata (`name`, `root`, `skillMd`,
80
+ `source`, `cliVersion`, `skillHash`), so an agent can go from `npx orizu --help`
81
+ to reading `SKILL.md` in two commands and verify the guidance matches the CLI
82
+ that supplied it.
83
+
84
+ Agents are first-class users of this CLI: **every command supports `--json`**
85
+ (as a prefix, `orizu --json teams list`, or a trailing flag) and emits a single
86
+ machine-readable JSON document instead of formatted text. Discover the full
87
+ command surface, including global options, with `orizu capabilities --json`.
47
88
 
48
89
  ## Authentication
49
90
 
@@ -82,6 +123,10 @@ orizu logout
82
123
  <td>Install the bundled agent skill</td>
83
124
  <td><code>orizu install-skill --target agent-user --yes</code></td>
84
125
  </tr>
126
+ <tr>
127
+ <td>Locate the bundled skill (read-only)</td>
128
+ <td><code>orizu skills path --json</code></td>
129
+ </tr>
85
130
  <tr>
86
131
  <td>Inspect CLI capabilities as JSON</td>
87
132
  <td><code>orizu capabilities --json</code></td>
@@ -121,7 +166,11 @@ orizu logout
121
166
  </tr>
122
167
  <tr>
123
168
  <td>List prompts</td>
124
- <td><code>orizu prompts list --project ops-eval/support-qa</code></td>
169
+ <td><code>orizu prompts list --project ops-eval/support-qa [--status active|archived|all]</code></td>
170
+ </tr>
171
+ <tr>
172
+ <td>Archive or restore a prompt</td>
173
+ <td><code>orizu prompts archive &lt;promptIdOrName&gt; --project ops-eval/support-qa</code><br><code>orizu prompts restore &lt;promptIdOrName&gt; --project ops-eval/support-qa</code></td>
125
174
  </tr>
126
175
  <tr>
127
176
  <td>Show prompt comments</td>
package/dist/banner.js ADDED
@@ -0,0 +1,20 @@
1
+ // Faceted rendering of the Orizu origami swan (public/orizu-swan.svg):
2
+ // each polygon is filled with block/triangle glyphs and shrunk slightly so
3
+ // the seams between origami facets stay visible, matching the brand mark.
4
+ // Printed uncolored so it renders in the terminal's default foreground.
5
+ export const ORIZU_BANNER = ` ▄▄
6
+ ▄▜▛ ██▙
7
+ ▟▀ ◥██▛
8
+ ▀▜▄
9
+ ▄▟█
10
+ ▄███
11
+ ▄████◤ ▄▄▄█▛
12
+ ▟█████◤ ▄▄█████▛
13
+ ▄▀███◤ ▄█████████▛
14
+ ◥█▄▀▛▐█▄◥▜███████▛ ███▀
15
+ ▜██ ████▄◥▜████▛ ██▀
16
+ █ ███████▄ ▜█▛ █▀
17
+ ▐█████████◣ ▀`;
18
+ export function renderBanner() {
19
+ return ORIZU_BANNER;
20
+ }
package/dist/csv.js CHANGED
@@ -1,4 +1,105 @@
1
- export function parseCSV(csvText) {
1
+ /**
2
+ * CSV parsing utilities with RFC 4180 compliance.
3
+ *
4
+ * This is the single canonical implementation shared by the web app and the
5
+ * published CLI: lib/csv-utils.ts re-exports from here. It lives inside
6
+ * packages/cli because the CLI is published standalone to npm and cannot
7
+ * import files outside its package root, while the app can import anything.
8
+ *
9
+ * Resource limits are enforced while parsing so a hostile CSV cannot exhaust
10
+ * memory. The numeric defaults are inlined here (this package cannot import
11
+ * @/lib/resource-limits) and mirror RESOURCE_LIMITS.CSV_* in that module —
12
+ * keep the two in sync.
13
+ */
14
+ export class CSVResourceLimitError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'CSVResourceLimitError';
18
+ }
19
+ }
20
+ // Mirrors RESOURCE_LIMITS.CSV_* in lib/resource-limits.ts.
21
+ const DEFAULT_CSV_MAX_BYTES = 10 * 1024 * 1024;
22
+ const DEFAULT_CSV_MAX_ROWS = 5_000;
23
+ const DEFAULT_CSV_MAX_FIELDS = 200;
24
+ const DEFAULT_CSV_MAX_FIELD_BYTES = 128 * 1024;
25
+ function getUtf8ByteLength(value) {
26
+ let bytes = 0;
27
+ for (let index = 0; index < value.length; index += 1) {
28
+ const codePoint = value.charCodeAt(index);
29
+ if (codePoint <= 0x7f) {
30
+ bytes += 1;
31
+ }
32
+ else if (codePoint <= 0x7ff) {
33
+ bytes += 2;
34
+ }
35
+ else if (codePoint >= 0xd800 &&
36
+ codePoint <= 0xdbff &&
37
+ index + 1 < value.length) {
38
+ const next = value.charCodeAt(index + 1);
39
+ if (next >= 0xdc00 && next <= 0xdfff) {
40
+ bytes += 4;
41
+ index += 1;
42
+ }
43
+ else {
44
+ bytes += 3;
45
+ }
46
+ }
47
+ else {
48
+ bytes += 3;
49
+ }
50
+ }
51
+ return bytes;
52
+ }
53
+ function enforceCsvTextLimit(csvText, options) {
54
+ const maxBytes = options.maxBytes ?? DEFAULT_CSV_MAX_BYTES;
55
+ if (getUtf8ByteLength(csvText) > maxBytes) {
56
+ throw new CSVResourceLimitError(`CSV data is too large (max ${maxBytes} bytes)`);
57
+ }
58
+ }
59
+ function enforceRowLimit(rowCount, options) {
60
+ const maxRows = options.maxRows ?? DEFAULT_CSV_MAX_ROWS;
61
+ if (rowCount > maxRows) {
62
+ throw new CSVResourceLimitError(`CSV has too many rows (max ${maxRows})`);
63
+ }
64
+ }
65
+ function enforceFieldLimits(row, options) {
66
+ const maxFields = options.maxFields ?? DEFAULT_CSV_MAX_FIELDS;
67
+ if (row.length > maxFields) {
68
+ throw new CSVResourceLimitError(`CSV row has too many fields (max ${maxFields})`);
69
+ }
70
+ const maxFieldBytes = options.maxFieldBytes ?? DEFAULT_CSV_MAX_FIELD_BYTES;
71
+ for (const field of row) {
72
+ if (getUtf8ByteLength(field) > maxFieldBytes) {
73
+ throw new CSVResourceLimitError(`CSV field is too large (max ${maxFieldBytes} bytes)`);
74
+ }
75
+ }
76
+ }
77
+ function addCsvRow(result, row, options) {
78
+ // Only keep rows with at least one non-whitespace field
79
+ if (!row.some(field => field.trim())) {
80
+ return;
81
+ }
82
+ enforceFieldLimits(row, options);
83
+ result.push(row);
84
+ enforceRowLimit(result.length, options);
85
+ }
86
+ /**
87
+ * Parses CSV text into a 2D array of strings, handling quoted fields, escaped
88
+ * quotes, multiline fields, and different line endings (CRLF, LF, CR).
89
+ * Completely empty rows are skipped.
90
+ *
91
+ * Resource limits (size, rows, fields, field size) are enforced during
92
+ * parsing; pass `options` to override the defaults. A violation throws
93
+ * {@link CSVResourceLimitError}.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const csv = 'name,age,city\nJohn,25,"New York"'
98
+ * parseCSV(csv) // [['name', 'age', 'city'], ['John', '25', 'New York']]
99
+ * ```
100
+ */
101
+ export function parseCSV(csvText, options = {}) {
102
+ enforceCsvTextLimit(csvText, options);
2
103
  const result = [];
3
104
  let currentRow = [];
4
105
  let currentField = '';
@@ -9,6 +110,7 @@ export function parseCSV(csvText) {
9
110
  const nextChar = csvText[i + 1];
10
111
  if (char === '"') {
11
112
  if (inQuotes && nextChar === '"') {
113
+ // Escaped quote - convert "" to "
12
114
  currentField += '"';
13
115
  i += 2;
14
116
  continue;
@@ -27,9 +129,7 @@ export function parseCSV(csvText) {
27
129
  if (char === '\n' || char === '\r') {
28
130
  if (currentField || currentRow.length > 0) {
29
131
  currentRow.push(currentField);
30
- if (currentRow.some(field => field.trim())) {
31
- result.push(currentRow);
32
- }
132
+ addCsvRow(result, currentRow, options);
33
133
  currentRow = [];
34
134
  currentField = '';
35
135
  }
@@ -45,16 +145,24 @@ export function parseCSV(csvText) {
45
145
  currentField += char;
46
146
  i++;
47
147
  }
148
+ // CSV might not end with a newline
48
149
  if (currentField || currentRow.length > 0) {
49
150
  currentRow.push(currentField);
50
- if (currentRow.some(field => field.trim())) {
51
- result.push(currentRow);
52
- }
151
+ addCsvRow(result, currentRow, options);
53
152
  }
54
153
  return result;
55
154
  }
56
- export function parseCSVToObjects(csvText) {
57
- const rows = parseCSV(csvText);
155
+ /**
156
+ * Parses CSV data into an array of objects using the first row as headers.
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * const csv = 'name,age\nJohn,25'
161
+ * parseCSVToObjects(csv) // [{ name: 'John', age: '25' }]
162
+ * ```
163
+ */
164
+ export function parseCSVToObjects(csvText, options = {}) {
165
+ const rows = parseCSV(csvText, options);
58
166
  if (rows.length < 2) {
59
167
  return [];
60
168
  }
package/dist/help.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const GLOBAL_OPTIONS = [
2
+ ['--json', 'Emit machine-readable JSON instead of human text. Available on every command, as a prefix (orizu --json <command>) or trailing flag.'],
2
3
  ['--local', 'Use http://localhost:3000.'],
3
4
  ['--server <url>', 'Use a specific server origin, for example https://preview.example.com.'],
4
5
  ['--version, -v', 'Print the orizu CLI version.'],
@@ -51,26 +52,111 @@ export const COMMAND_DOCS = [
51
52
  summary: 'Submit an optimization event payload.',
52
53
  group: 'Optimizations',
53
54
  },
55
+ {
56
+ path: ['setup'],
57
+ usage: 'orizu setup [--agent <claude|codex>]... [--scope global|project] [--mode auto|link|copy] [--workspace|--no-workspace] [--no-install] [--no-handoff] [--launch <claude|codex>] [--skip-login] [--yes] [--dry-run] [--no-input] [--json]',
58
+ summary: 'Guided onboarding: sign in, set up your coding agents, create a local workspace, and hand off to your agent.',
59
+ group: 'Agent setup',
60
+ options: [
61
+ { name: '--agent <agent>', help: 'Coding agent to set up. Repeat for multiple agents.', repeatable: true, choices: ['claude', 'codex'] },
62
+ { name: '--scope <scope>', help: 'global (default) installs for you across all projects; project installs into the current repo.', choices: ['global', 'project'] },
63
+ { name: '--mode <mode>', help: 'Skill install sync mode (auto, link, copy).', choices: ['auto', 'link', 'copy'] },
64
+ { name: '--workspace', help: 'Create the gitignored .orizu/ workspace without prompting.' },
65
+ { name: '--no-workspace', help: 'Skip workspace creation.' },
66
+ { name: '--no-install', help: 'Skip the agent integration phase.' },
67
+ { name: '--no-handoff', help: 'Skip printing the coding-agent setup prompt.' },
68
+ { name: '--launch <agent>', help: 'Launch a detected coding agent with the setup prompt (interactive terminals only).', choices: ['claude', 'codex'] },
69
+ { name: '--skip-login', help: 'Skip the authentication phase.' },
70
+ { name: '--yes', help: 'Apply changes without per-step confirmation prompts.' },
71
+ { name: '--dry-run', help: 'Preview every planned change without writing.' },
72
+ { name: '--no-input', help: 'Never prompt; behave as in a non-interactive terminal.' },
73
+ { name: '--json', help: 'Emit the setup summary as JSON.' },
74
+ ],
75
+ examples: [
76
+ 'orizu setup',
77
+ 'orizu setup --agent claude --agent codex --yes',
78
+ 'orizu setup --skip-login --no-workspace --dry-run',
79
+ ],
80
+ },
81
+ {
82
+ path: ['setup', 'prompt'],
83
+ usage: 'orizu setup prompt',
84
+ summary: 'Print the coding-agent setup prompt for repo-specific Orizu adoption.',
85
+ group: 'Agent setup',
86
+ examples: ['orizu setup prompt', 'claude "$(orizu setup prompt)"'],
87
+ },
54
88
  {
55
89
  path: ['install-skill'],
56
- usage: 'orizu install-skill [--target <target>]... [--yes] [--dry-run]',
57
- summary: 'Install the bundled Orizu CLI skill locally for coding agents.',
90
+ usage: 'orizu install-skill [--agent <claude|codex>]... [--scope global|project] [--mode auto|link|copy] [--target <target>]... [--yes] [--dry-run]',
91
+ summary: 'Set up the Orizu skill for your coding agents (Claude Code, Codex/Open Agent Skills, or project instructions).',
58
92
  group: 'Agent setup',
59
93
  aliases: [['skills', 'install']],
60
94
  options: [
95
+ {
96
+ name: '--agent <agent>',
97
+ help: 'Coding agent to set up. Repeat for multiple agents.',
98
+ repeatable: true,
99
+ choices: ['claude', 'codex'],
100
+ },
101
+ {
102
+ name: '--scope <scope>',
103
+ help: 'Where the install lives: global installs for you across all projects (default); project installs into the current repo.',
104
+ choices: ['global', 'project'],
105
+ },
106
+ {
107
+ name: '--mode <mode>',
108
+ help: 'How installs stay in sync with the CLI: auto (default) symlinks when the CLI install path is stable and copies otherwise; link forces a symlink; copy forces a full copy with sync metadata. Project-scope installs always copy.',
109
+ choices: ['auto', 'link', 'copy'],
110
+ },
61
111
  {
62
112
  name: '--target <target>',
63
- help: 'Install destination. Repeat for multiple targets.',
113
+ help: 'Advanced: explicit install destination ID. Repeat for multiple targets.',
64
114
  repeatable: true,
65
- choices: ['agent-user', 'codex-project', 'claude-user', 'claude-project', 'agents-md'],
115
+ choices: ['agent-user', 'agents-project', 'codex-project', 'claude-user', 'claude-project', 'agents-md'],
66
116
  },
67
117
  { name: '--yes', help: 'Replace an existing managed skill or AGENTS.md section without prompting.' },
68
118
  { name: '--dry-run', help: 'Show planned writes without changing files.' },
69
119
  ],
70
120
  examples: [
71
- 'orizu install-skill --target agent-user --yes',
72
- 'orizu install-skill --target codex-project --target agents-md',
73
- 'orizu skills install --target claude-user --dry-run',
121
+ 'orizu install-skill --agent claude --agent codex --yes',
122
+ 'orizu install-skill --agent claude --scope project --yes',
123
+ 'orizu install-skill --target agents-md --dry-run',
124
+ ],
125
+ },
126
+ {
127
+ path: ['skills', 'status'],
128
+ usage: 'orizu skills status [--json]',
129
+ summary: 'Report installed Orizu skill targets and whether each is in sync with the CLI.',
130
+ group: 'Agent setup',
131
+ options: [
132
+ { name: '--json', help: 'Emit machine-readable status for every known install target.' },
133
+ ],
134
+ examples: ['orizu skills status', 'orizu skills status --json'],
135
+ },
136
+ {
137
+ path: ['skills', 'update'],
138
+ usage: 'orizu skills update [--dry-run] [--json]',
139
+ summary: 'Refresh stale copied skill installs and repair broken symlinks.',
140
+ group: 'Agent setup',
141
+ options: [
142
+ { name: '--dry-run', help: 'Show which installs would be refreshed without changing files.' },
143
+ { name: '--json', help: 'Emit machine-readable update results.' },
144
+ ],
145
+ examples: ['orizu skills update', 'orizu skills update --dry-run'],
146
+ },
147
+ {
148
+ path: ['skills', 'path'],
149
+ usage: 'orizu skills path [--skill-md] [--json]',
150
+ summary: 'Print where the bundled Orizu skill lives so agents can read it without installing.',
151
+ group: 'Agent setup',
152
+ options: [
153
+ { name: '--skill-md', help: 'Print the full path to SKILL.md instead of the skill root directory.' },
154
+ { name: '--json', help: 'Emit machine-readable metadata: name, root, skillMd, source, cliVersion, skillHash.' },
155
+ ],
156
+ examples: [
157
+ 'orizu skills path',
158
+ 'orizu skills path --skill-md',
159
+ 'orizu skills path --json',
74
160
  ],
75
161
  },
76
162
  {
@@ -133,10 +219,22 @@ export const COMMAND_DOCS = [
133
219
  },
134
220
  {
135
221
  path: ['prompts', 'list'],
136
- usage: 'orizu prompts list --project <team/project>',
222
+ usage: 'orizu prompts list --project <team/project> [--status active|archived|all]',
137
223
  summary: 'List prompt artifacts in a project.',
138
224
  group: 'Prompts and judges',
139
225
  },
226
+ {
227
+ path: ['prompts', 'archive'],
228
+ usage: 'orizu prompts archive <prompt-id-or-name> --project <team/project> [--json]',
229
+ summary: 'Mark a prompt artifact as archived.',
230
+ group: 'Prompts and judges',
231
+ },
232
+ {
233
+ path: ['prompts', 'restore'],
234
+ usage: 'orizu prompts restore <prompt-id-or-name> --project <team/project> [--json]',
235
+ summary: 'Restore an archived prompt artifact to active status.',
236
+ group: 'Prompts and judges',
237
+ },
140
238
  {
141
239
  path: ['prompts', 'comments'],
142
240
  usage: 'orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]',
@@ -175,7 +273,7 @@ export const COMMAND_DOCS = [
175
273
  },
176
274
  {
177
275
  path: ['judges', 'list'],
178
- usage: 'orizu judges list --project <team/project>',
276
+ usage: 'orizu judges list --project <team/project> [--status active|archived|all]',
179
277
  summary: 'List judge prompt artifacts in a project.',
180
278
  group: 'Prompts and judges',
181
279
  },
@@ -259,7 +357,7 @@ export const COMMAND_DOCS = [
259
357
  },
260
358
  {
261
359
  path: ['optimizations', 'run-gepa'],
262
- usage: '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]',
360
+ usage: '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]',
263
361
  summary: 'Run the bundled GEPA-style optimizer locally and stream events.',
264
362
  group: 'Optimizations',
265
363
  },
@@ -352,20 +450,20 @@ export const COMMAND_DOCS = [
352
450
  },
353
451
  {
354
452
  path: ['tasks', 'create'],
355
- 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]',
356
- summary: 'Create a draft review task by default, or publish immediately with --publish and assignees.',
453
+ usage: 'orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> [--assignees <userIdOrEmail1,userIdOrEmail2> | --assignment-file <path>] [--publish] [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]',
454
+ summary: 'Create a draft review task by default, with auto distribution or an explicit row-assignment JSONL file.',
357
455
  group: 'Tasks',
358
456
  },
359
457
  {
360
458
  path: ['tasks', 'publish'],
361
- usage: 'orizu tasks publish --task <taskId> --assignees <userId1,userId2> [--json]',
459
+ usage: 'orizu tasks publish --task <taskId> (--assignees <userId1,userId2> | --assignment-file <path>) [--json]',
362
460
  summary: 'Publish an approved draft task and assign reviewers.',
363
461
  group: 'Tasks',
364
462
  },
365
463
  {
366
464
  path: ['tasks', 'assign'],
367
- usage: 'orizu tasks assign --task <taskId> --assignees <userId1,userId2>',
368
- summary: 'Assign a task to canonical user IDs.',
465
+ usage: 'orizu tasks assign --task <taskId> (--assignees <userId1,userId2> | --assignment-file <path>) [--replace-existing] [--json]',
466
+ summary: 'Assign a task with auto distribution or an explicit row-assignment JSONL file.',
369
467
  group: 'Tasks',
370
468
  },
371
469
  {
@@ -593,6 +691,7 @@ export function getCapabilities(version) {
593
691
  return {
594
692
  name: 'orizu',
595
693
  version,
694
+ globalOptions: GLOBAL_OPTIONS.map(([name, help]) => ({ name, help })),
596
695
  commands: COMMAND_DOCS.map(doc => ({
597
696
  name: commandKey(doc.path),
598
697
  usage: doc.usage,