orizu 0.5.7 → 0.5.9
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 +13 -2
- package/dist/credentials.js +44 -0
- package/dist/help.js +77 -7
- package/dist/hosted-bootstrap.js +45 -0
- package/dist/hosted-harness-opencode.js +21 -1
- package/dist/hosted-harness.js +9 -0
- package/dist/hosted-harvest.js +121 -0
- package/dist/hosted-headless.js +163 -0
- package/dist/hosted-loop.js +119 -5
- package/dist/hosted-run-event-sink.js +21 -0
- package/dist/hosted-runtime-assets.js +27 -0
- package/dist/hosted-session-cli.js +51 -9
- package/dist/http.js +16 -1
- package/dist/index.js +54 -86
- package/dist/manifests-cli.js +26 -4
- package/dist/report-comments-cli.js +325 -0
- package/dist/skill-installer.js +16 -1
- package/package.json +1 -1
- package/src/credentials.ts +46 -0
- package/src/help.ts +77 -7
- package/src/hosted-bootstrap.ts +52 -0
- package/src/hosted-harness-opencode.ts +24 -2
- package/src/hosted-harness.ts +9 -0
- package/src/hosted-harvest.ts +164 -0
- package/src/hosted-headless.ts +178 -0
- package/src/hosted-loop.ts +133 -4
- package/src/hosted-run-event-sink.ts +28 -0
- package/src/hosted-runtime-assets.ts +31 -0
- package/src/hosted-session-cli.ts +50 -9
- package/src/http.ts +23 -1
- package/src/index.ts +74 -156
- package/src/manifests-cli.ts +28 -4
- package/src/report-comments-cli.ts +524 -0
- package/src/skill-installer.ts +18 -1
- package/vendor/skills/orizu-cli/SKILL.md +9 -2
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +1 -1
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
interface ReportCommentPerson {
|
|
2
|
+
name: string
|
|
3
|
+
initials?: string
|
|
4
|
+
color?: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface ReportCommentAuthor {
|
|
8
|
+
kind?: 'human'
|
|
9
|
+
person?: ReportCommentPerson
|
|
10
|
+
name?: string
|
|
11
|
+
initials?: string
|
|
12
|
+
color?: string
|
|
13
|
+
via?: string
|
|
14
|
+
actor?: 'agent'
|
|
15
|
+
onBehalfOf?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ReportCommentAnchor {
|
|
19
|
+
text: string | null
|
|
20
|
+
startLine: number | null
|
|
21
|
+
endLine: number | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ReportCommentReply {
|
|
25
|
+
id: string
|
|
26
|
+
body: string
|
|
27
|
+
author: ReportCommentAuthor
|
|
28
|
+
createdAt: string
|
|
29
|
+
updatedAt: string
|
|
30
|
+
editedAt?: string | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ReportCommentThread {
|
|
34
|
+
id: string
|
|
35
|
+
status: 'open' | 'resolved'
|
|
36
|
+
body: string
|
|
37
|
+
anchor: ReportCommentAnchor | null
|
|
38
|
+
author: ReportCommentAuthor
|
|
39
|
+
createdAt: string
|
|
40
|
+
updatedAt: string
|
|
41
|
+
editedAt?: string | null
|
|
42
|
+
createdByUserId?: string | null
|
|
43
|
+
resolvedAt: string | null
|
|
44
|
+
resolvedByUserId: string | null
|
|
45
|
+
replyCount: number
|
|
46
|
+
replies: ReportCommentReply[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ReportCommentMutationPayload {
|
|
50
|
+
comment: ReportCommentThread | ReportCommentReply
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ReportCommentsPayload {
|
|
54
|
+
subject?: {
|
|
55
|
+
type: 'prompt_version' | 'optimization_run_report' | 'task_report'
|
|
56
|
+
id: string
|
|
57
|
+
projectId: string
|
|
58
|
+
}
|
|
59
|
+
project?: {
|
|
60
|
+
id: string
|
|
61
|
+
name: string
|
|
62
|
+
slug: string
|
|
63
|
+
teamName: string | null
|
|
64
|
+
teamSlug: string | null
|
|
65
|
+
} | null
|
|
66
|
+
prompt?: {
|
|
67
|
+
id: string
|
|
68
|
+
name: string
|
|
69
|
+
role: string
|
|
70
|
+
description?: string | null
|
|
71
|
+
}
|
|
72
|
+
version?: {
|
|
73
|
+
id: string
|
|
74
|
+
versionNumber?: number
|
|
75
|
+
versionLabel?: string | null
|
|
76
|
+
status?: string | null
|
|
77
|
+
}
|
|
78
|
+
run?: {
|
|
79
|
+
id: string
|
|
80
|
+
}
|
|
81
|
+
task?: {
|
|
82
|
+
id: string
|
|
83
|
+
title?: string | null
|
|
84
|
+
}
|
|
85
|
+
summary: {
|
|
86
|
+
threadCount: number
|
|
87
|
+
openThreadCount: number
|
|
88
|
+
resolvedThreadCount: number
|
|
89
|
+
replyCount: number
|
|
90
|
+
}
|
|
91
|
+
comments: ReportCommentThread[]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ReportCommentsCliContext {
|
|
95
|
+
getArg: (name: string) => string | null
|
|
96
|
+
getPositionalArg: (index: number) => string | null
|
|
97
|
+
rejectDashPrefixedOptionValue: (name: string, value: string | null) => void
|
|
98
|
+
resolveProjectSlug: (preferredProject: string | null) => Promise<string>
|
|
99
|
+
readSourceFile: (pathArg: string) => string
|
|
100
|
+
authedFetch: (path: string, init?: RequestInit) => Promise<Response>
|
|
101
|
+
parseJsonResponse: <T>(response: Response, context: string) => Promise<T>
|
|
102
|
+
hasJsonFlag: () => boolean
|
|
103
|
+
printJson: (value: Record<string, unknown>) => void
|
|
104
|
+
printLine: (message?: string) => void
|
|
105
|
+
sanitizeTerminalText: (value: unknown) => string
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const COMMENTS_TARGET_USAGE =
|
|
109
|
+
'Use exactly one of --prompt <id-or-name>, --run <run-id>, or --task <task-id>'
|
|
110
|
+
|
|
111
|
+
function reportCommentsUsage(action: string): string {
|
|
112
|
+
const target =
|
|
113
|
+
'(--prompt <id-or-name> --project <team/project> [--label <label> | --version <id>] | --run <run-id> | --task <task-id>)'
|
|
114
|
+
if (action === 'list') {
|
|
115
|
+
return `Usage: orizu comments list ${target} [--json]`
|
|
116
|
+
}
|
|
117
|
+
if (action === 'add') {
|
|
118
|
+
return `Usage: orizu comments add ${target} --body <text|@file> [--anchor <text>] [--lines <start:end>] [--via <name>] [--json]`
|
|
119
|
+
}
|
|
120
|
+
if (action === 'reply') {
|
|
121
|
+
return 'Usage: orizu comments reply <comment-id> --body <text|@file> [--via <name>] [--json]'
|
|
122
|
+
}
|
|
123
|
+
if (action === 'edit') {
|
|
124
|
+
return 'Usage: orizu comments edit <comment-id> --body <text|@file> [--json]'
|
|
125
|
+
}
|
|
126
|
+
return `Usage: orizu comments ${action} <comment-id> [--json]`
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function compactTerminalText(
|
|
130
|
+
ctx: ReportCommentsCliContext,
|
|
131
|
+
value: string,
|
|
132
|
+
maxLength = 160
|
|
133
|
+
): string {
|
|
134
|
+
const compact = ctx.sanitizeTerminalText(value).replace(/\s+/g, ' ').trim()
|
|
135
|
+
if (compact.length <= maxLength) {
|
|
136
|
+
return compact
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return `${compact.slice(0, Math.max(0, maxLength - 1)).trim()}…`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function readReportCommentError(
|
|
143
|
+
ctx: ReportCommentsCliContext,
|
|
144
|
+
response: Response
|
|
145
|
+
): Promise<string> {
|
|
146
|
+
const status = response.status
|
|
147
|
+
? `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ''}`
|
|
148
|
+
: 'request failed'
|
|
149
|
+
const contentType = response.headers.get('content-type') || ''
|
|
150
|
+
|
|
151
|
+
if (!contentType.toLowerCase().includes('application/json')) {
|
|
152
|
+
return status
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const parsed = await response.clone().json()
|
|
157
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
158
|
+
const error = (parsed as { error?: unknown }).error
|
|
159
|
+
if (typeof error === 'string' && error.trim()) {
|
|
160
|
+
return compactTerminalText(ctx, error, 220)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
// Fall through to the status-only message for malformed JSON errors.
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return status
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printIndentedBody(
|
|
171
|
+
ctx: ReportCommentsCliContext,
|
|
172
|
+
value: string,
|
|
173
|
+
indent: string
|
|
174
|
+
) {
|
|
175
|
+
const lines = ctx.sanitizeTerminalText(value || '').split(/\r?\n/)
|
|
176
|
+
for (const line of lines) {
|
|
177
|
+
ctx.printLine(`${indent}${line}`)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function formatReportCommentAnchor(
|
|
182
|
+
ctx: ReportCommentsCliContext,
|
|
183
|
+
anchor: ReportCommentAnchor | null
|
|
184
|
+
): string | null {
|
|
185
|
+
if (!anchor) {
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const lineLabel = anchor.startLine && anchor.endLine
|
|
190
|
+
? anchor.startLine === anchor.endLine
|
|
191
|
+
? `line ${anchor.startLine}`
|
|
192
|
+
: `lines ${anchor.startLine}-${anchor.endLine}`
|
|
193
|
+
: null
|
|
194
|
+
const selectedText = anchor.text ? `"${compactTerminalText(ctx, anchor.text, 180)}"` : null
|
|
195
|
+
|
|
196
|
+
if (lineLabel && selectedText) {
|
|
197
|
+
return `${lineLabel}: ${selectedText}`
|
|
198
|
+
}
|
|
199
|
+
return lineLabel || selectedText
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function reportCommentAuthorName(author: ReportCommentAuthor | null | undefined): string {
|
|
203
|
+
const personName = author?.person?.name
|
|
204
|
+
const name = personName || author?.name || 'Unknown'
|
|
205
|
+
const via = author?.via ? ` via ${author.via}` : ''
|
|
206
|
+
const agent = author?.actor === 'agent'
|
|
207
|
+
? author.onBehalfOf
|
|
208
|
+
? ` (agent on behalf of ${author.onBehalfOf})`
|
|
209
|
+
: ' (agent)'
|
|
210
|
+
: ''
|
|
211
|
+
return `${name}${via}${agent}`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function printReportCommentSubject(
|
|
215
|
+
ctx: ReportCommentsCliContext,
|
|
216
|
+
data: ReportCommentsPayload
|
|
217
|
+
) {
|
|
218
|
+
if (data.prompt && data.version) {
|
|
219
|
+
const versionLabel = data.version.versionLabel || (
|
|
220
|
+
data.version.versionNumber ? `v${data.version.versionNumber}` : data.version.id
|
|
221
|
+
)
|
|
222
|
+
ctx.printLine(`Prompt: ${ctx.sanitizeTerminalText(data.prompt.name)} (${ctx.sanitizeTerminalText(data.prompt.id)})`)
|
|
223
|
+
ctx.printLine(`Version: ${ctx.sanitizeTerminalText(String(versionLabel))} (${ctx.sanitizeTerminalText(data.version.id)})`)
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (data.run) {
|
|
228
|
+
ctx.printLine(`Optimization run: ${ctx.sanitizeTerminalText(data.run.id)}`)
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (data.task) {
|
|
233
|
+
const title = data.task.title ? `${ctx.sanitizeTerminalText(data.task.title)} ` : ''
|
|
234
|
+
ctx.printLine(`Task: ${title}(${ctx.sanitizeTerminalText(data.task.id)})`)
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (data.subject) {
|
|
239
|
+
ctx.printLine(`Subject: ${ctx.sanitizeTerminalText(data.subject.type)} ${ctx.sanitizeTerminalText(data.subject.id)}`)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function printReportComments(
|
|
244
|
+
ctx: ReportCommentsCliContext,
|
|
245
|
+
data: ReportCommentsPayload
|
|
246
|
+
) {
|
|
247
|
+
printReportCommentSubject(ctx, data)
|
|
248
|
+
ctx.printLine(
|
|
249
|
+
`Comments: ${data.summary.threadCount} thread${data.summary.threadCount === 1 ? '' : 's'} ` +
|
|
250
|
+
`(${data.summary.openThreadCount} open, ${data.summary.resolvedThreadCount} resolved, ${data.summary.replyCount} replies)`
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if (data.comments.length === 0) {
|
|
254
|
+
ctx.printLine('\nNo comments found for this report.')
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
for (const [index, thread] of data.comments.entries()) {
|
|
259
|
+
const authorName = reportCommentAuthorName(thread.author)
|
|
260
|
+
const anchor = formatReportCommentAnchor(ctx, thread.anchor)
|
|
261
|
+
ctx.printLine('')
|
|
262
|
+
ctx.printLine(
|
|
263
|
+
`${index + 1}. [${thread.status}] ${ctx.sanitizeTerminalText(thread.id)} · ` +
|
|
264
|
+
`${ctx.sanitizeTerminalText(authorName)} · ${ctx.sanitizeTerminalText(thread.createdAt)}`
|
|
265
|
+
)
|
|
266
|
+
if (thread.editedAt) {
|
|
267
|
+
ctx.printLine(` Edited: ${ctx.sanitizeTerminalText(thread.editedAt)}`)
|
|
268
|
+
}
|
|
269
|
+
if (anchor) {
|
|
270
|
+
ctx.printLine(` Selection: ${anchor}`)
|
|
271
|
+
}
|
|
272
|
+
printIndentedBody(ctx, thread.body, ' ')
|
|
273
|
+
|
|
274
|
+
if (thread.replies.length > 0) {
|
|
275
|
+
ctx.printLine(` Replies (${thread.replies.length})`)
|
|
276
|
+
for (const reply of thread.replies) {
|
|
277
|
+
const replyAuthor = reportCommentAuthorName(reply.author)
|
|
278
|
+
ctx.printLine(
|
|
279
|
+
` - ${ctx.sanitizeTerminalText(reply.id)} · ` +
|
|
280
|
+
`${ctx.sanitizeTerminalText(replyAuthor)} · ${ctx.sanitizeTerminalText(reply.createdAt)}`
|
|
281
|
+
)
|
|
282
|
+
if (reply.editedAt) {
|
|
283
|
+
ctx.printLine(` Edited: ${ctx.sanitizeTerminalText(reply.editedAt)}`)
|
|
284
|
+
}
|
|
285
|
+
printIndentedBody(ctx, reply.body, ' ')
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function reportCommentsTargetParams(
|
|
292
|
+
ctx: ReportCommentsCliContext,
|
|
293
|
+
action: string
|
|
294
|
+
): Promise<URLSearchParams> {
|
|
295
|
+
const promptRef = ctx.getArg('--prompt')
|
|
296
|
+
const runId = ctx.getArg('--run') || ctx.getArg('--run-id')
|
|
297
|
+
const taskId = ctx.getArg('--task')
|
|
298
|
+
|
|
299
|
+
ctx.rejectDashPrefixedOptionValue('--prompt', promptRef)
|
|
300
|
+
ctx.rejectDashPrefixedOptionValue('--run', runId)
|
|
301
|
+
ctx.rejectDashPrefixedOptionValue('--task', taskId)
|
|
302
|
+
|
|
303
|
+
const present = [
|
|
304
|
+
promptRef ? 'prompt' : null,
|
|
305
|
+
runId ? 'run' : null,
|
|
306
|
+
taskId ? 'task' : null,
|
|
307
|
+
].filter(Boolean)
|
|
308
|
+
|
|
309
|
+
if (present.length !== 1) {
|
|
310
|
+
throw new Error(`${reportCommentsUsage(action)}\n${COMMENTS_TARGET_USAGE}`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const params = new URLSearchParams()
|
|
314
|
+
if (promptRef) {
|
|
315
|
+
const project = ctx.getArg('--project') || await ctx.resolveProjectSlug(null)
|
|
316
|
+
const label = ctx.getArg('--label')
|
|
317
|
+
const version = ctx.getArg('--version')
|
|
318
|
+
ctx.rejectDashPrefixedOptionValue('--project', project)
|
|
319
|
+
ctx.rejectDashPrefixedOptionValue('--label', label)
|
|
320
|
+
ctx.rejectDashPrefixedOptionValue('--version', version)
|
|
321
|
+
if (label && version) {
|
|
322
|
+
throw new Error('Use either --label or --version, not both')
|
|
323
|
+
}
|
|
324
|
+
params.set('prompt', promptRef)
|
|
325
|
+
params.set('project', project)
|
|
326
|
+
if (label) params.set('label', label)
|
|
327
|
+
if (version) params.set('version', version)
|
|
328
|
+
return params
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (runId) {
|
|
332
|
+
params.set('run', runId)
|
|
333
|
+
return params
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
params.set('task', taskId as string)
|
|
337
|
+
return params
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function readReportCommentBodyInput(ctx: ReportCommentsCliContext, action: string): string {
|
|
341
|
+
const body = ctx.getArg('--body')
|
|
342
|
+
ctx.rejectDashPrefixedOptionValue('--body', body)
|
|
343
|
+
if (!body) {
|
|
344
|
+
throw new Error(reportCommentsUsage(action))
|
|
345
|
+
}
|
|
346
|
+
if (body === '@') {
|
|
347
|
+
throw new Error('Usage: --body <text|@file>')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const value = body.startsWith('@') ? ctx.readSourceFile(body.slice(1)) : body
|
|
351
|
+
if (!value.trim()) {
|
|
352
|
+
throw new Error('Comment body must not be blank')
|
|
353
|
+
}
|
|
354
|
+
return value
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function readReportCommentAnchorInput(ctx: ReportCommentsCliContext): ReportCommentAnchor | null {
|
|
358
|
+
const anchor = ctx.getArg('--anchor')
|
|
359
|
+
const lines = ctx.getArg('--lines')
|
|
360
|
+
ctx.rejectDashPrefixedOptionValue('--anchor', anchor)
|
|
361
|
+
ctx.rejectDashPrefixedOptionValue('--lines', lines)
|
|
362
|
+
|
|
363
|
+
let startLine: number | null = null
|
|
364
|
+
let endLine: number | null = null
|
|
365
|
+
if (lines) {
|
|
366
|
+
const match = lines.match(/^(\d+):(\d+)$/)
|
|
367
|
+
if (!match) {
|
|
368
|
+
throw new Error('--lines must use format <start:end>')
|
|
369
|
+
}
|
|
370
|
+
startLine = Number(match[1])
|
|
371
|
+
endLine = Number(match[2])
|
|
372
|
+
if (!Number.isInteger(startLine) || !Number.isInteger(endLine) || startLine < 1 || endLine < startLine) {
|
|
373
|
+
throw new Error('--lines must contain positive integers where end is greater than or equal to start')
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!anchor && startLine === null) {
|
|
378
|
+
return null
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
text: anchor ? anchor.trim() : null,
|
|
383
|
+
startLine,
|
|
384
|
+
endLine,
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function readReportCommentViaInput(ctx: ReportCommentsCliContext): string | undefined {
|
|
389
|
+
const via = ctx.getArg('--via')
|
|
390
|
+
ctx.rejectDashPrefixedOptionValue('--via', via)
|
|
391
|
+
return via?.trim() || undefined
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function listReportComments(ctx: ReportCommentsCliContext) {
|
|
395
|
+
const params = await reportCommentsTargetParams(ctx, 'list')
|
|
396
|
+
const response = await ctx.authedFetch(`/api/cli/report-comments?${params.toString()}`)
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
throw new Error(`Failed to fetch report comments: ${await readReportCommentError(ctx, response)}`)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const data = await ctx.parseJsonResponse<ReportCommentsPayload>(response, 'Report comments')
|
|
402
|
+
if (ctx.hasJsonFlag()) {
|
|
403
|
+
ctx.printJson(data as unknown as Record<string, unknown>)
|
|
404
|
+
return
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
printReportComments(ctx, data)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function addReportComment(ctx: ReportCommentsCliContext) {
|
|
411
|
+
const params = await reportCommentsTargetParams(ctx, 'add')
|
|
412
|
+
const body = readReportCommentBodyInput(ctx, 'add')
|
|
413
|
+
const response = await ctx.authedFetch(`/api/cli/report-comments?${params.toString()}`, {
|
|
414
|
+
method: 'POST',
|
|
415
|
+
headers: { 'Content-Type': 'application/json' },
|
|
416
|
+
body: JSON.stringify({
|
|
417
|
+
body,
|
|
418
|
+
via: readReportCommentViaInput(ctx),
|
|
419
|
+
anchor: readReportCommentAnchorInput(ctx),
|
|
420
|
+
}),
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
throw new Error(`Failed to add report comment: ${await readReportCommentError(ctx, response)}`)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const data = await ctx.parseJsonResponse<ReportCommentMutationPayload>(response, 'Report comment add')
|
|
428
|
+
if (ctx.hasJsonFlag()) {
|
|
429
|
+
ctx.printJson(data as unknown as Record<string, unknown>)
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
ctx.printLine(`Added comment ${ctx.sanitizeTerminalText(data.comment.id)}`)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function replyReportComment(ctx: ReportCommentsCliContext) {
|
|
436
|
+
const commentId = ctx.getPositionalArg(2)
|
|
437
|
+
if (!commentId) {
|
|
438
|
+
throw new Error(reportCommentsUsage('reply'))
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const response = await ctx.authedFetch(`/api/cli/report-comments/${encodeURIComponent(commentId)}`, {
|
|
442
|
+
method: 'POST',
|
|
443
|
+
headers: { 'Content-Type': 'application/json' },
|
|
444
|
+
body: JSON.stringify({
|
|
445
|
+
body: readReportCommentBodyInput(ctx, 'reply'),
|
|
446
|
+
via: readReportCommentViaInput(ctx),
|
|
447
|
+
}),
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
if (!response.ok) {
|
|
451
|
+
throw new Error(`Failed to reply to report comment: ${await readReportCommentError(ctx, response)}`)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const data = await ctx.parseJsonResponse<ReportCommentMutationPayload>(response, 'Report comment reply')
|
|
455
|
+
if (ctx.hasJsonFlag()) {
|
|
456
|
+
ctx.printJson(data as unknown as Record<string, unknown>)
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
ctx.printLine(`Added reply ${ctx.sanitizeTerminalText(data.comment.id)} to ${ctx.sanitizeTerminalText(commentId)}`)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async function updateReportComment(
|
|
463
|
+
ctx: ReportCommentsCliContext,
|
|
464
|
+
action: 'resolve' | 'unresolve' | 'edit'
|
|
465
|
+
) {
|
|
466
|
+
const commentId = ctx.getPositionalArg(2)
|
|
467
|
+
if (!commentId) {
|
|
468
|
+
throw new Error(reportCommentsUsage(action))
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const payload = action === 'edit'
|
|
472
|
+
? { body: readReportCommentBodyInput(ctx, 'edit') }
|
|
473
|
+
: { resolved: action === 'resolve' }
|
|
474
|
+
|
|
475
|
+
const response = await ctx.authedFetch(`/api/cli/report-comments/${encodeURIComponent(commentId)}`, {
|
|
476
|
+
method: 'PATCH',
|
|
477
|
+
headers: { 'Content-Type': 'application/json' },
|
|
478
|
+
body: JSON.stringify(payload),
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
if (!response.ok) {
|
|
482
|
+
throw new Error(`Failed to ${action} report comment: ${await readReportCommentError(ctx, response)}`)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const data = await ctx.parseJsonResponse<ReportCommentMutationPayload>(response, `Report comment ${action}`)
|
|
486
|
+
if (ctx.hasJsonFlag()) {
|
|
487
|
+
ctx.printJson(data as unknown as Record<string, unknown>)
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const verb = action === 'unresolve' ? 'Reopened' : action === 'edit' ? 'Edited' : 'Resolved'
|
|
492
|
+
ctx.printLine(`${verb} comment ${ctx.sanitizeTerminalText(data.comment.id)}`)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export async function reportCommentsCommand(
|
|
496
|
+
action: string | undefined,
|
|
497
|
+
ctx: ReportCommentsCliContext
|
|
498
|
+
) {
|
|
499
|
+
if (action === 'list') {
|
|
500
|
+
await listReportComments(ctx)
|
|
501
|
+
return
|
|
502
|
+
}
|
|
503
|
+
if (action === 'add') {
|
|
504
|
+
await addReportComment(ctx)
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
if (action === 'reply') {
|
|
508
|
+
await replyReportComment(ctx)
|
|
509
|
+
return
|
|
510
|
+
}
|
|
511
|
+
if (action === 'resolve' || action === 'unresolve' || action === 'edit') {
|
|
512
|
+
await updateReportComment(ctx, action)
|
|
513
|
+
return
|
|
514
|
+
}
|
|
515
|
+
throw new Error(
|
|
516
|
+
'Usage: orizu comments <list|add|reply|resolve|unresolve|edit> ...'
|
|
517
|
+
)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function throwDeprecatedPromptCommentsCommand(): never {
|
|
521
|
+
throw new Error(
|
|
522
|
+
'orizu prompts comments has moved. Use `orizu comments list --prompt <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>]`.'
|
|
523
|
+
)
|
|
524
|
+
}
|
package/src/skill-installer.ts
CHANGED
|
@@ -28,6 +28,8 @@ export const SKILL_INSTALL_TARGETS = [
|
|
|
28
28
|
'codex-project',
|
|
29
29
|
'claude-user',
|
|
30
30
|
'claude-project',
|
|
31
|
+
'opencode-user',
|
|
32
|
+
'opencode-project',
|
|
31
33
|
'agents-md',
|
|
32
34
|
] as const
|
|
33
35
|
|
|
@@ -35,7 +37,7 @@ export type SkillInstallTarget = typeof SKILL_INSTALL_TARGETS[number]
|
|
|
35
37
|
|
|
36
38
|
export const SKILL_META_FILENAME = '.orizu-skill-meta.json'
|
|
37
39
|
|
|
38
|
-
export const SKILL_INSTALL_AGENTS = ['claude', 'codex'] as const
|
|
40
|
+
export const SKILL_INSTALL_AGENTS = ['claude', 'codex', 'opencode'] as const
|
|
39
41
|
|
|
40
42
|
export type SkillInstallAgent = typeof SKILL_INSTALL_AGENTS[number]
|
|
41
43
|
|
|
@@ -47,6 +49,7 @@ const PROJECT_LEVEL_TARGETS: readonly SkillInstallTarget[] = [
|
|
|
47
49
|
'agents-project',
|
|
48
50
|
'codex-project',
|
|
49
51
|
'claude-project',
|
|
52
|
+
'opencode-project',
|
|
50
53
|
]
|
|
51
54
|
|
|
52
55
|
export interface SkillInstallOptions {
|
|
@@ -218,6 +221,17 @@ export function getSkillInstallPath(
|
|
|
218
221
|
return resolve(cwd, '.claude', 'skills', SKILL_NAME)
|
|
219
222
|
}
|
|
220
223
|
|
|
224
|
+
// OpenCode discovers user skills under XDG config (~/.config/opencode/skills)
|
|
225
|
+
// and project skills under .opencode/skills (ALI-1044). The copy/link machinery
|
|
226
|
+
// is agent-neutral; only the destination path differs.
|
|
227
|
+
if (target === 'opencode-user') {
|
|
228
|
+
return resolve(home, '.config', 'opencode', 'skills', SKILL_NAME)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (target === 'opencode-project') {
|
|
232
|
+
return resolve(cwd, '.opencode', 'skills', SKILL_NAME)
|
|
233
|
+
}
|
|
234
|
+
|
|
221
235
|
return resolve(cwd, 'AGENTS.md')
|
|
222
236
|
}
|
|
223
237
|
|
|
@@ -228,6 +242,9 @@ export function getTargetForAgent(
|
|
|
228
242
|
if (agent === 'claude') {
|
|
229
243
|
return scope === 'global' ? 'claude-user' : 'claude-project'
|
|
230
244
|
}
|
|
245
|
+
if (agent === 'opencode') {
|
|
246
|
+
return scope === 'global' ? 'opencode-user' : 'opencode-project'
|
|
247
|
+
}
|
|
231
248
|
return scope === 'global' ? 'codex-user' : 'agents-project'
|
|
232
249
|
}
|
|
233
250
|
|
|
@@ -105,7 +105,7 @@ Deeper: `references/primer.md` (Step 2); `references/cli-reference.md` (apps + t
|
|
|
105
105
|
|
|
106
106
|
## 3. Judge — turn labels into automated evaluators
|
|
107
107
|
|
|
108
|
-
Use the Phase 0 prompt control plane when the user wants judges stored in Orizu as versioned prompt artifacts, runner zips, scorer definitions, and submitted score results. Read **`references/prompt-control-plane.md`** before using those commands. When the user mentions prompt feedback, prompt comments, or asks what to focus on for the next prompt version, inspect comment threads with `orizu
|
|
108
|
+
Use the Phase 0 prompt control plane when the user wants judges stored in Orizu as versioned prompt artifacts, runner zips, scorer definitions, and submitted score results. Read **`references/prompt-control-plane.md`** before using those commands. When the user mentions prompt feedback, prompt comments, or asks what to focus on for the next prompt version, inspect comment threads with `orizu comments list --prompt <prompt-id-or-name> --project <team>/<project>` before editing or pushing.
|
|
109
109
|
|
|
110
110
|
Judge construction details are still useful when authoring the evaluator itself.
|
|
111
111
|
|
|
@@ -192,4 +192,11 @@ Detailed walkthrough — GEPA mechanics, Orizu-tracked optimization, optional DS
|
|
|
192
192
|
- Prompt control-plane commands should use ids for dataset versions, split sets, prompt versions, scorer versions, runner versions, optimizer versions, and optimization runs.
|
|
193
193
|
- Optimization exports default to `<run-id>.optimization.json`; prefer the existing `logs/<run-id>` directory from `run-gepa` when it is available because it contains the full local trace without needing server rehydration.
|
|
194
194
|
- When ending an optimization run, attach a markdown report with `--report-file` when possible. Use `references/optimization-reports.md` for what to include.
|
|
195
|
-
- Use `orizu
|
|
195
|
+
- Use `orizu comments list --prompt <prompt-id-or-name> --project <team>/<project> [--label <label> | --version <id>] [--json]` to list prompt-level discussion threads with open/resolved status, selected text/line context, and replies.
|
|
196
|
+
|
|
197
|
+
# Where things live
|
|
198
|
+
|
|
199
|
+
- **Orizu is the source of truth for run metadata and pointers**: production/default prompt/scorer/runner pointers, version lineage, score runs, optimization runs, and report history all live in Orizu — query them with `orizu ...` commands (e.g. `orizu prompts list`, `orizu prompts versions`, `orizu runs ...`), never by reading repo files.
|
|
200
|
+
- **The repo is the working set (files only)**: source, configs, `App.tsx`, schemas, and local traces under `logs/<run-id>`. It is the scratch space you edit and commit; it does NOT hold the canonical pointer/run state — that is in Orizu.
|
|
201
|
+
- **Hosted sessions ship a pre-authenticated CLI**: inside a hosted agent sandbox the `orizu` CLI is already authenticated via `ORIZU_TOKEN_FILE` (with `ORIZU_BASE_URL` pointing at the API). You do NOT run `orizu login` — just call `orizu ...`. The bearer carries a **team-curator READ scope**: you can read projects, datasets (+ content/artifact bytes), prompts, scorers, runners/optimizers, runs, and reports across your own team, and execute runs / submit scores / create artifacts / create promotion manifests as the agent actor.
|
|
202
|
+
- **Human-only actions will 403**: manifest approve / reject / apply, production and default pointer flips, and team/member/billing/integration admin are deliberately outside the agent envelope. Do not attempt them from a hosted session — they return 403 by design. Prepare a promotion manifest and hand it to a human curator to approve and apply.
|
|
@@ -350,7 +350,7 @@ orizu --local prompts restore <prompt-id-or-name> --project <team>/<project>
|
|
|
350
350
|
List prompt comment threads for the latest version, or a specific label/version:
|
|
351
351
|
|
|
352
352
|
```bash
|
|
353
|
-
orizu --local
|
|
353
|
+
orizu --local comments list --prompt <prompt-id-or-name> \
|
|
354
354
|
--project <team>/<project> \
|
|
355
355
|
[--label production | --version <prompt-version-id>] \
|
|
356
356
|
[--json]
|