opencode-agent-skill 7.7.0 → 9.0.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/README.md +675 -581
  3. package/bin/ocskill.mjs +358 -156
  4. package/docs/DETERMINISTIC-TOOLS.md +25 -8
  5. package/docs/ENGINEERING-DESIGN.md +31 -13
  6. package/docs/EVALS.md +34 -12
  7. package/docs/NPM-PUBLISH.md +6 -6
  8. package/docs/OPENCODE-COMPAT.md +11 -8
  9. package/docs/TRACE-SCHEMA.md +15 -2
  10. package/docs/V8-INTELLIGENCE-RELIABILITY.md +206 -0
  11. package/docs/V9-SPEED-INTELLIGENCE.md +102 -0
  12. package/evals/live/tasks.json +6 -6
  13. package/evals/polyglot/fixtures/polyglot-bench/api/generated/client.ts +2 -0
  14. package/evals/polyglot/fixtures/polyglot-bench/api/openapi.json +25 -0
  15. package/evals/polyglot/fixtures/polyglot-bench/db/migrations/20260920_add_order_key.sql +1 -0
  16. package/evals/polyglot/fixtures/polyglot-bench/dotnet/OrderService.cs +8 -0
  17. package/evals/polyglot/fixtures/polyglot-bench/java/PriceService.java +5 -0
  18. package/evals/polyglot/fixtures/polyglot-bench/monorepo/package.json +6 -0
  19. package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/api/package.json +4 -0
  20. package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/web/package.json +7 -0
  21. package/evals/polyglot/fixtures/polyglot-bench/monorepo/pnpm-lock.yaml +5 -0
  22. package/evals/polyglot/fixtures/polyglot-bench/next/app/api/products/route.ts +7 -0
  23. package/evals/polyglot/fixtures/polyglot-bench/python/tenant_auth.py +4 -0
  24. package/evals/polyglot/fixtures/polyglot-bench/react-native/keyboard.ts +3 -0
  25. package/evals/polyglot/graders/polyglot-bench.mjs +101 -0
  26. package/evals/polyglot/tasks.json +54 -0
  27. package/global-config/AGENTS.md +10 -7
  28. package/global-config/agents/integration-verifier.md +1 -1
  29. package/global-config/agents/plan-checker.md +1 -1
  30. package/global-config/commands/run.md +9 -5
  31. package/global-config/plugins/ues-router/capabilities.js +4 -0
  32. package/global-config/plugins/ues-router/index.js +414 -21
  33. package/global-config/plugins/ues-router/router.js +140 -23
  34. package/global-config/skills/engineering-orchestrator/references/long-horizon.md +6 -4
  35. package/lib/aci.mjs +128 -0
  36. package/lib/benchmark-confidence.mjs +135 -0
  37. package/lib/cli-utils.mjs +41 -0
  38. package/lib/container-sandbox.mjs +102 -0
  39. package/lib/context-manifest.mjs +300 -22
  40. package/lib/control-center.mjs +36 -3
  41. package/lib/eval-order.mjs +9 -0
  42. package/lib/eval-telemetry.mjs +5 -2
  43. package/lib/gate-receipt.mjs +52 -0
  44. package/lib/installer.mjs +39 -25
  45. package/lib/learning-engine.mjs +236 -38
  46. package/lib/opencode-compat.mjs +25 -10
  47. package/lib/orchestrator-policy.mjs +101 -20
  48. package/lib/process-runner.mjs +30 -9
  49. package/lib/runtime-events.mjs +31 -0
  50. package/lib/semantic-index.mjs +318 -0
  51. package/lib/task-engine.mjs +416 -24
  52. package/lib/trajectory.mjs +89 -0
  53. package/lib/windows-shim.mjs +227 -0
  54. package/lib/worktree-sandbox.mjs +85 -3
  55. package/package.json +10 -4
  56. package/scripts/check-release-tag.mjs +22 -0
  57. package/scripts/control-center.mjs +25 -0
  58. package/scripts/eval-live.mjs +39 -58
  59. package/scripts/eval-matrix.mjs +155 -0
  60. package/scripts/smoke-packed-install.mjs +138 -4
  61. package/scripts/smoke-plain-install.mjs +91 -0
  62. package/scripts/validate-live-suite.mjs +3 -3
  63. package/scripts/validate.mjs +27 -5
package/bin/ocskill.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync } from "node:fs"
2
+ import { writeFileSync } from "node:fs"
3
3
  import { spawnSync } from "node:child_process"
4
4
  import { fileURLToPath } from "node:url"
5
5
  import path from "node:path"
@@ -15,6 +15,7 @@ import {
15
15
  import { compareVersions } from "../lib/version.mjs"
16
16
  import { resolveLatestPublishedVersion } from "../lib/update-resolver.mjs"
17
17
  import { readRouterConfig, writeRouterConfig } from "../lib/router-config.mjs"
18
+ import { resolveWindowsCommand } from "../lib/windows-shim.mjs"
18
19
  import {
19
20
  detectStack,
20
21
  detectTestCommands,
@@ -24,10 +25,15 @@ import {
24
25
  checkWorkingTree,
25
26
  } from "../lib/repo-inspect.mjs"
26
27
  import { buildRepoGraph } from "../lib/repo-graph.mjs"
28
+ import { buildSemanticIndex, semanticIndexStatus } from "../lib/semantic-index.mjs"
29
+ import { aciReferences, aciSearch, aciTextSearch, aciView } from "../lib/aci.mjs"
30
+ import { appendTrajectoryEvent, readTrajectory } from "../lib/trajectory.mjs"
31
+ import { containerSandboxCapability, runContainerSandbox } from "../lib/container-sandbox.mjs"
27
32
  import { analyzePlan } from "../lib/task-graph.mjs"
28
33
  import {
29
34
  addBlocker,
30
35
  addDecision,
36
+ attachTaskSession,
31
37
  approvePlan,
32
38
  completeTask,
33
39
  contextPack,
@@ -42,18 +48,32 @@ import {
42
48
  workStatus,
43
49
  heartbeatTask,
44
50
  recoverStaleTasks,
51
+ recoverTask,
45
52
  recordVerificationReceipt,
46
53
  workspaceFingerprint,
54
+ createPlanVerificationReceipt,
55
+ createIntegrationVerificationReceipt,
56
+ runtimeEvents,
47
57
  } from "../lib/task-engine.mjs"
48
58
  import { reviewScope } from "../lib/review-scope.mjs"
49
59
  import { buildVerificationPlan } from "../lib/verification-plan.mjs"
50
60
  import { resolveAdaptiveModel, resolveModel } from "../lib/model-policy.mjs"
51
61
  import { createVerificationReceipt } from "../lib/evidence-receipt.mjs"
52
62
  import { classifyEngineeringTask } from "../lib/orchestrator-policy.mjs"
53
- import { createTaskSandbox, listTaskSandboxes, removeTaskSandbox } from "../lib/worktree-sandbox.mjs"
54
- import { analyzeEvalTraces, saveLearningAnalysis, readLearningState, acceptLearning } from "../lib/learning-engine.mjs"
63
+ import { createTaskSandbox, integrateTaskSandbox, listTaskSandboxes, removeTaskSandbox } from "../lib/worktree-sandbox.mjs"
64
+ import { analyzeEvalTraces, saveLearningAnalysis, readLearningState, acceptLearning, promoteLearning } from "../lib/learning-engine.mjs"
55
65
  import { hermesStatus, buildHermesDelegationPrompt, hermesOneShotArgs } from "../lib/hermes-bridge.mjs"
56
66
  import { readModelPolicy, validateModelID, writeModelPolicy } from "../lib/model-config.mjs"
67
+ import {
68
+ clipOutput,
69
+ errorMessage,
70
+ optionInt,
71
+ optionIntOrUndefined,
72
+ optionValue,
73
+ positionalArg,
74
+ readJsonFile,
75
+ readTextFile,
76
+ } from "../lib/cli-utils.mjs"
57
77
 
58
78
  const args = process.argv.slice(2)
59
79
  const command = args[0] || "help"
@@ -76,6 +96,11 @@ Usage:
76
96
  ocskill evidence [dir] Collect stack, verification and Git evidence
77
97
  ocskill working-tree [dir] Report Git branch/HEAD/dirty state
78
98
  ocskill repo-graph [dir] Build a bounded source import/dependency graph
99
+ ocskill index <status|build|rebuild> [dir]
100
+ Build/reuse the persistent incremental semantic index
101
+ ocskill aci <search|refs|view|text> ...
102
+ Bounded evidence-first code search/view interface
103
+ ocskill trace <show|append> ... Inspect or append redacted operational trajectory events
79
104
  ocskill review-scope [base] [dir]
80
105
  Enumerate changed files, review coverage and risk
81
106
  ocskill verification-plan [dir]
@@ -87,8 +112,9 @@ Usage:
87
112
  ocskill model-policy <role> [--attempt N]
88
113
  Resolve light/standard/heavy escalation tier
89
114
  ocskill task-policy <text> Classify task mode/risk/context/model tier
90
- ocskill sandbox <action> ... Manage isolated Git worktree task sandboxes
91
- ocskill learn <action> ... Analyze eval traces and promote accepted lessons
115
+ ocskill sandbox <action> ... Create, integrate and clean isolated Git worktree sandboxes
116
+ Also supports capability/exec for fail-closed container verification
117
+ ocskill learn <action> ... Analyze eval traces and promote benchmark-validated lessons
92
118
  ocskill hermes <action> ... Optional Hermes adapter/status
93
119
  ocskill dashboard [dir] [--serve] [--port N]
94
120
  Generate/serve the local UES Control Center
@@ -105,16 +131,20 @@ Usage:
105
131
  ocskill work init <slug> [dir] --goal <text>
106
132
  ocskill work plan <slug> <plan.json> [dir]
107
133
  ocskill work status|resume <slug> [dir]
108
- ocskill work approve-plan <slug> [dir] --evidence <plan-checker-evidence>
134
+ ocskill work gate-receipt <slug> <plan|integration> [dir] --evidence <text> [--verdict PASS|FAIL|PARTIAL] [--verifier <role>] [--session-id <id>] [--report-file <file>] [--out <file>]
135
+ ocskill work approve-plan <slug> [dir] --evidence <plan-checker-evidence> [--receipt-file <file>]
109
136
  ocskill work start <slug> <task-id> [dir] [--lease-ms N]
110
- ocskill work heartbeat <slug> <task-id> [dir] [--run-id <id>]
137
+ ocskill work attach-session <slug> <task-id> [dir] --run-id <id> --session-id <id> [--execution-dir <dir>] [--sandbox-dir <dir>]
138
+ ocskill work heartbeat <slug> <task-id> [dir] --run-id <id>
111
139
  ocskill work recover <slug> [dir] [--force]
112
- ocskill work verify-command <slug> <task-id> [dir] [--run-id <id>] -- <command> [args...]
113
- ocskill work complete <slug> <task-id> [dir] --evidence <text> [--report-file <file>] [--run-id <id>]
114
- ocskill work fail <slug> <task-id> [dir] --reason <text> [--run-id <id>]
140
+ ocskill work recover-task <slug> <task-id> [dir] [--force] [--reason <text>]
141
+ ocskill work events <slug> [dir] [--limit N]
142
+ ocskill work verify-command <slug> <task-id> [dir] --run-id <id> -- <command> [args...]
143
+ ocskill work complete <slug> <task-id> [dir] --run-id <id> --evidence <text> [--report-file <file>]
144
+ ocskill work fail <slug> <task-id> [dir] --run-id <id> --reason <text>
115
145
  ocskill work decision <slug> [dir] --text <decision>
116
146
  ocskill work block|unblock <slug> [dir] --text <blocker>
117
- ocskill work verify-integration <slug> [dir] --verdict PASS|FAIL|PARTIAL --evidence <text> [--report-file <file>]
147
+ ocskill work verify-integration <slug> [dir] --verdict PASS|FAIL|PARTIAL --evidence <text> [--report-file <file>] [--receipt-file <file>]
118
148
  ocskill work finalize <slug> [dir] --evidence <integration-evidence>
119
149
 
120
150
  Model routing:
@@ -127,38 +157,6 @@ Usage:
127
157
  `)
128
158
  }
129
159
 
130
- function findWindowsCommand(name) {
131
- if (path.isAbsolute(name) && existsSync(name)) return name
132
-
133
- const result = spawnSync("where", [name], { encoding: "utf8" })
134
- if (result.status !== 0 || !result.stdout) return null
135
-
136
- const matches = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
137
- return matches.find((item) => /\.(cmd|bat)$/i.test(item)) || matches[0] || null
138
- }
139
-
140
- function quoteCmd(value) {
141
- if (/^[A-Za-z0-9_@%+=:,./\\-]+$/.test(value)) return value
142
- return `"${value.replaceAll('"', '""')}"`
143
- }
144
-
145
- function findNodeShimEntry(cmdPath) {
146
- const dir = path.dirname(cmdPath)
147
-
148
- if (/^npm(?:\.cmd|\.exe)?$/i.test(path.basename(cmdPath))) {
149
- const entry = path.join(dir, "node_modules", "npm", "bin", "npm-cli.js")
150
- if (existsSync(entry)) return entry
151
- }
152
-
153
- let shim = ""
154
- try {
155
- shim = readFileSync(cmdPath, "utf8")
156
- } catch {}
157
- const match = shim.match(/node_modules[\\/][^\s"]+?\.(?:js|mjs)/gi)?.at(-1)
158
- if (!match) return null
159
- const entry = path.resolve(dir, match)
160
- return existsSync(entry) ? entry : null
161
- }
162
160
 
163
161
  function run(executable, commandArgs, options = {}) {
164
162
  const common = { stdio: "inherit", ...options }
@@ -168,26 +166,22 @@ function run(executable, commandArgs, options = {}) {
168
166
  return result.status ?? 1
169
167
  }
170
168
 
171
- const resolved = findWindowsCommand(executable)
172
- if (!resolved) return 127
173
-
174
- if (/\.(cmd|bat)$/i.test(resolved)) {
175
- const entry = findNodeShimEntry(resolved)
176
- if (entry) {
177
- const result = spawnSync(process.execPath, [entry, ...commandArgs], common)
178
- return result.status ?? 1
179
- }
180
- const line = [resolved, ...commandArgs].map(quoteCmd).join(" ")
181
- const result = spawnSync(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", line], common)
182
- return result.status ?? 1
169
+ const resolved = resolveWindowsCommand(executable)
170
+ if (!resolved) {
171
+ console.error("[ocskill] No safely executable Windows command found for: " + executable)
172
+ return 127
183
173
  }
184
174
 
185
- const result = spawnSync(resolved, commandArgs, common)
175
+ const result = spawnSync(
176
+ resolved.executable,
177
+ [...resolved.argsPrefix, ...commandArgs],
178
+ common,
179
+ )
186
180
  return result.status ?? 1
187
181
  }
188
182
 
189
183
  function hasCommand(name) {
190
- if (process.platform === "win32") return findWindowsCommand(name) !== null
184
+ if (process.platform === "win32") return resolveWindowsCommand(name) !== null
191
185
  return spawnSync("which", [name], { stdio: "ignore" }).status === 0
192
186
  }
193
187
 
@@ -198,22 +192,20 @@ function runCapture(executable, commandArgs, options = {}) {
198
192
  return spawnSync(executable, commandArgs, common)
199
193
  }
200
194
 
201
- const resolved = findWindowsCommand(executable)
202
- if (!resolved) return { status: 127, stdout: "", stderr: `Command not found: ${executable}` }
203
-
204
- if (/\.(cmd|bat)$/i.test(resolved)) {
205
- const entry = findNodeShimEntry(resolved)
206
- if (entry) return spawnSync(process.execPath, [entry, ...commandArgs], common)
207
-
208
- const line = [resolved, ...commandArgs].map(quoteCmd).join(" ")
209
- return spawnSync(
210
- process.env.ComSpec || "cmd.exe",
211
- ["/d", "/s", "/c", line],
212
- common,
213
- )
195
+ const resolved = resolveWindowsCommand(executable)
196
+ if (!resolved) {
197
+ return {
198
+ status: 127,
199
+ stdout: "",
200
+ stderr: `No safely executable Windows command found for: ${executable}`,
201
+ }
214
202
  }
215
203
 
216
- return spawnSync(resolved, commandArgs, common)
204
+ return spawnSync(
205
+ resolved.executable,
206
+ [...resolved.argsPrefix, ...commandArgs],
207
+ common,
208
+ )
217
209
  }
218
210
 
219
211
 
@@ -278,8 +270,12 @@ async function doctor() {
278
270
  console.log(`Node: ${process.version}`)
279
271
  console.log(`Config: ${getConfigDir()}`)
280
272
  console.log(`npm: ${hasCommand("npm") ? "OK" : "MISSING"}`)
281
- console.log(`OpenCode: ${hasCommand("opencode") ? "OK" : "MISSING"}`)
282
- if (hasCommand("opencode")) run("opencode", ["--version"])
273
+ const openCodeAvailable = hasCommand("opencode")
274
+ console.log(`OpenCode: ${openCodeAvailable ? "OK" : "MISSING"}`)
275
+ if (openCodeAvailable) {
276
+ const code = run("opencode", ["--version"])
277
+ if (code !== 0) process.exitCode = code
278
+ }
283
279
  await status()
284
280
  }
285
281
 
@@ -309,7 +305,7 @@ async function evaluateReport() {
309
305
  }
310
306
 
311
307
  async function inspectRepository() {
312
- printJson(await repoMap(args[1] || process.cwd()))
308
+ printJson(await repoMap(positionalArg(args, 1) || process.cwd()))
313
309
  }
314
310
 
315
311
  async function inspectImpact() {
@@ -319,42 +315,129 @@ async function inspectImpact() {
319
315
  process.exitCode = 2
320
316
  return
321
317
  }
322
- printJson(await impactMap(args[2] || process.cwd(), query))
318
+ printJson(await impactMap(positionalArg(args, 2) || process.cwd(), query))
323
319
  }
324
320
 
325
321
  async function inspectEvidence() {
326
- printJson(await collectEvidence(args[1] || process.cwd()))
322
+ printJson(await collectEvidence(positionalArg(args, 1) || process.cwd()))
327
323
  }
328
324
 
329
325
  async function inspectWorkingTree() {
330
- printJson(await checkWorkingTree(args[1] || process.cwd()))
326
+ printJson(await checkWorkingTree(positionalArg(args, 1) || process.cwd()))
331
327
  }
332
328
 
333
329
  async function inspectStack() {
334
- printJson(await detectStack(args[1] || process.cwd()))
330
+ printJson(await detectStack(positionalArg(args, 1) || process.cwd()))
335
331
  }
336
332
 
337
333
  async function inspectTests() {
338
- printJson(await detectTestCommands(args[1] || process.cwd()))
334
+ printJson(await detectTestCommands(positionalArg(args, 1) || process.cwd()))
339
335
  }
340
336
 
341
- function optionValue(name) {
342
- const index = args.indexOf(name)
343
- return index >= 0 ? args[index + 1] : null
337
+ async function inspectRepoGraph() {
338
+ printJson(await buildRepoGraph(positionalArg(args, 1) || process.cwd()))
344
339
  }
345
340
 
346
- async function inspectRepoGraph() {
347
- printJson(await buildRepoGraph(args[1] || process.cwd()))
341
+ async function semanticIndexControl() {
342
+ const action = args[1] || "status"
343
+ const root = positionalArg(args, 2) || process.cwd()
344
+ try {
345
+ if (action === "status") {
346
+ printJson(await semanticIndexStatus(root))
347
+ return
348
+ }
349
+ if (action === "build" || action === "rebuild") {
350
+ const built = await buildSemanticIndex(root, { rebuild: action === "rebuild" })
351
+ printJson({ action, stats: built.stats })
352
+ return
353
+ }
354
+ throw new Error("Usage: ocskill index <status|build|rebuild> [dir]")
355
+ } catch (error) {
356
+ console.error(errorMessage(error))
357
+ process.exitCode = 1
358
+ }
359
+ }
360
+
361
+ async function aciControl() {
362
+ const action = args[1]
363
+ try {
364
+ if (action === "search") {
365
+ const query = args[2]
366
+ const root = positionalArg(args, 3) || process.cwd()
367
+ if (!query) throw new Error("Usage: ocskill aci search <query> [dir] [--limit N]")
368
+ printJson(await aciSearch(root, query, { limit: optionInt(args, "--limit", 20) }))
369
+ return
370
+ }
371
+ if (action === "refs") {
372
+ const symbol = args[2]
373
+ const root = positionalArg(args, 3) || process.cwd()
374
+ if (!symbol) throw new Error("Usage: ocskill aci refs <symbol> [dir] [--limit N]")
375
+ printJson(await aciReferences(root, symbol, { limit: optionInt(args, "--limit", 40) }))
376
+ return
377
+ }
378
+ if (action === "view") {
379
+ const file = args[2]
380
+ const root = positionalArg(args, 3) || process.cwd()
381
+ if (!file) throw new Error("Usage: ocskill aci view <file> [dir] [--line N] [--lines N]")
382
+ printJson(await aciView(root, file, {
383
+ line: optionInt(args, "--line", 1),
384
+ startLine: optionInt(args, "--start-line", 0),
385
+ lines: optionInt(args, "--lines", 120),
386
+ }))
387
+ return
388
+ }
389
+ if (action === "text") {
390
+ const query = args[2]
391
+ const root = positionalArg(args, 3) || process.cwd()
392
+ if (!query) throw new Error("Usage: ocskill aci text <query> [dir] [--limit N]")
393
+ printJson(await aciTextSearch(root, query, { limit: optionInt(args, "--limit", 80) }))
394
+ return
395
+ }
396
+ throw new Error("Usage: ocskill aci <search|refs|view|text> ...")
397
+ } catch (error) {
398
+ console.error(errorMessage(error))
399
+ process.exitCode = 1
400
+ }
401
+ }
402
+
403
+ async function traceControl() {
404
+ const action = args[1] || "show"
405
+ try {
406
+ if (action === "show") {
407
+ const traceID = args[2]
408
+ const root = positionalArg(args, 3) || process.cwd()
409
+ if (!traceID) throw new Error("Usage: ocskill trace show <trace-id> [dir] [--limit N]")
410
+ printJson(await readTrajectory(root, traceID, { limit: optionInt(args, "--limit", 200) }))
411
+ return
412
+ }
413
+ if (action === "append") {
414
+ const traceID = args[2]
415
+ const root = positionalArg(args, 3) || process.cwd()
416
+ const type = optionValue(args, "--type")
417
+ const payload64 = optionValue(args, "--payload-b64")
418
+ if (!traceID || !type) throw new Error("Usage: ocskill trace append <trace-id> [dir] --type <type> [--payload-b64 <base64-json>]")
419
+ let payload = {}
420
+ if (payload64) {
421
+ payload = JSON.parse(Buffer.from(payload64, "base64").toString("utf8"))
422
+ }
423
+ printJson(await appendTrajectoryEvent(root, traceID, type, payload))
424
+ return
425
+ }
426
+ throw new Error("Usage: ocskill trace <show|append> ...")
427
+ } catch (error) {
428
+ console.error(errorMessage(error))
429
+ process.exitCode = 1
430
+ }
348
431
  }
349
432
 
350
433
  async function inspectReviewScope() {
351
- const base = args[1] && !args[1].startsWith("--") ? args[1] : null
352
- const root = args[2] && !args[2].startsWith("--") ? args[2] : process.cwd()
434
+ const base = positionalArg(args, 1)
435
+ const root = positionalArg(args, 2) || process.cwd()
353
436
  printJson(reviewScope(root, base))
354
437
  }
355
438
 
356
439
  async function inspectVerificationPlan() {
357
- printJson(await buildVerificationPlan(args[1] || process.cwd(), optionValue("--base")))
440
+ printJson(await buildVerificationPlan(positionalArg(args, 1) || process.cwd(), optionValue(args, "--base")))
358
441
  }
359
442
 
360
443
  async function inspectTaskGraph() {
@@ -365,12 +448,12 @@ async function inspectTaskGraph() {
365
448
  return
366
449
  }
367
450
  try {
368
- const plan = JSON.parse(readFileSync(path.resolve(file), "utf8"))
451
+ const plan = readJsonFile(file)
369
452
  const analysis = analyzePlan(plan)
370
453
  printJson(analysis)
371
454
  if (!analysis.valid) process.exitCode = 1
372
455
  } catch (error) {
373
- console.error(error instanceof Error ? error.message : error)
456
+ console.error(errorMessage(error))
374
457
  process.exitCode = 1
375
458
  }
376
459
  }
@@ -383,78 +466,144 @@ async function inspectContextPack() {
383
466
  process.exitCode = 2
384
467
  return
385
468
  }
386
- printJson(await contextPack(args[3] || process.cwd(), slug, taskID))
469
+ printJson(await contextPack(positionalArg(args, 3) || process.cwd(), slug, taskID))
387
470
  }
388
471
 
389
472
  async function workControl() {
390
473
  const action = args[1]
391
474
  const slug = args[2]
392
475
  if (!action || !slug) {
393
- console.error("Usage: ocskill work <init|plan|status|resume|approve-plan|start|heartbeat|recover|verify-command|complete|fail|decision|block|unblock|verify-integration|finalize> <slug> ...")
476
+ console.error("Usage: ocskill work <init|plan|status|resume|gate-receipt|approve-plan|start|attach-session|heartbeat|recover|recover-task|events|verify-command|complete|fail|decision|block|unblock|verify-integration|finalize> <slug> ...")
394
477
  process.exitCode = 2
395
478
  return
396
479
  }
397
480
 
398
481
  try {
399
482
  if (action === "init") {
400
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
401
- printJson(await initWork(root, slug, optionValue("--goal")))
483
+ const root = positionalArg(args, 3) || process.cwd()
484
+ printJson(await initWork(root, slug, optionValue(args, "--goal")))
402
485
  return
403
486
  }
404
487
  if (action === "plan") {
405
488
  const planFile = args[3]
406
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
489
+ const root = positionalArg(args, 4) || process.cwd()
407
490
  if (!planFile) throw new Error("Usage: ocskill work plan <slug> <plan.json> [dir]")
408
491
  const result = await importPlan(root, slug, planFile)
409
492
  printJson({ state: result.state, analysis: result.analysis, dir: result.paths.dir })
410
493
  return
411
494
  }
412
495
  if (action === "status") {
413
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
496
+ const root = positionalArg(args, 3) || process.cwd()
414
497
  printJson(await workStatus(root, slug))
415
498
  return
416
499
  }
417
500
  if (action === "resume") {
418
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
501
+ const root = positionalArg(args, 3) || process.cwd()
419
502
  printJson(await resumeWork(root, slug))
420
503
  return
421
504
  }
505
+ if (action === "gate-receipt") {
506
+ const kind = args[3]
507
+ const root = positionalArg(args, 4) || process.cwd()
508
+ if (!["plan", "integration"].includes(kind)) {
509
+ throw new Error("Usage: ocskill work gate-receipt <slug> <plan|integration> [dir] --evidence <text>")
510
+ }
511
+ const reportFile = optionValue(args, "--report-file")
512
+ const report = reportFile ? readTextFile(reportFile) : null
513
+ const input = {
514
+ verdict: optionValue(args, "--verdict") || "PASS",
515
+ verifier: optionValue(args, "--verifier") || undefined,
516
+ sessionID: optionValue(args, "--session-id"),
517
+ runId: optionValue(args, "--run-id"),
518
+ evidence: optionValue(args, "--evidence"),
519
+ report,
520
+ }
521
+ const receipt = kind === "plan"
522
+ ? await createPlanVerificationReceipt(root, slug, input)
523
+ : await createIntegrationVerificationReceipt(root, slug, input)
524
+ const outputFile = optionValue(args, "--out")
525
+ if (outputFile) {
526
+ const resolved = path.resolve(outputFile)
527
+ writeFileSync(resolved, JSON.stringify(receipt, null, 2) + "\n", "utf8")
528
+ printJson({ file: resolved, receipt })
529
+ } else {
530
+ printJson(receipt)
531
+ }
532
+ return
533
+ }
422
534
  if (action === "approve-plan") {
423
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
424
- printJson(await approvePlan(root, slug, optionValue("--evidence")))
535
+ const root = positionalArg(args, 3) || process.cwd()
536
+ const receiptFile = optionValue(args, "--receipt-file")
537
+ const receipt = receiptFile ? readJsonFile(receiptFile) : null
538
+ printJson(await approvePlan(root, slug, optionValue(args, "--evidence"), { receipt }))
425
539
  return
426
540
  }
427
541
  if (action === "start") {
428
542
  const taskID = args[3]
429
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
543
+ const root = positionalArg(args, 4) || process.cwd()
430
544
  if (!taskID) throw new Error("Usage: ocskill work start <slug> <task-id> [dir]")
431
545
  printJson(await startTask(root, slug, taskID, {
432
- leaseMs: Number.parseInt(optionValue("--lease-ms") || "0", 10) || undefined,
546
+ leaseMs: optionIntOrUndefined(args, "--lease-ms"),
433
547
  }))
434
548
  return
435
549
  }
550
+ if (action === "attach-session") {
551
+ const taskID = args[3]
552
+ const root = positionalArg(args, 4) || process.cwd()
553
+ if (!taskID) throw new Error("Usage: ocskill work attach-session <slug> <task-id> [dir] --run-id <id> --session-id <id>")
554
+ printJson(await attachTaskSession(
555
+ root,
556
+ slug,
557
+ taskID,
558
+ optionValue(args, "--run-id"),
559
+ optionValue(args, "--session-id"),
560
+ {
561
+ executionDir: optionValue(args, "--execution-dir"),
562
+ sandboxDir: optionValue(args, "--sandbox-dir"),
563
+ },
564
+ ))
565
+ return
566
+ }
436
567
  if (action === "heartbeat") {
437
568
  const taskID = args[3]
438
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
439
- if (!taskID) throw new Error("Usage: ocskill work heartbeat <slug> <task-id> [dir] [--run-id <id>]")
440
- printJson(await heartbeatTask(root, slug, taskID, optionValue("--run-id"), {
441
- leaseMs: Number.parseInt(optionValue("--lease-ms") || "0", 10) || undefined,
569
+ const root = positionalArg(args, 4) || process.cwd()
570
+ const runId = optionValue(args, "--run-id")
571
+ if (!taskID || !runId) throw new Error("Usage: ocskill work heartbeat <slug> <task-id> [dir] --run-id <id>")
572
+ printJson(await heartbeatTask(root, slug, taskID, runId, {
573
+ leaseMs: optionIntOrUndefined(args, "--lease-ms"),
442
574
  }))
443
575
  return
444
576
  }
445
577
  if (action === "recover") {
446
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
578
+ const root = positionalArg(args, 3) || process.cwd()
447
579
  printJson(await recoverStaleTasks(root, slug, { force: args.includes("--force") }))
448
580
  return
449
581
  }
582
+ if (action === "recover-task") {
583
+ const taskID = args[3]
584
+ const root = positionalArg(args, 4) || process.cwd()
585
+ if (!taskID) throw new Error("Usage: ocskill work recover-task <slug> <task-id> [dir] [--force] [--reason <text>]")
586
+ printJson(await recoverTask(root, slug, taskID, {
587
+ force: args.includes("--force"),
588
+ reason: optionValue(args, "--reason"),
589
+ }))
590
+ return
591
+ }
592
+ if (action === "events") {
593
+ const root = positionalArg(args, 3) || process.cwd()
594
+ const limit = optionInt(args, "--limit", 200)
595
+ printJson(await runtimeEvents(root, slug, { limit }))
596
+ return
597
+ }
450
598
  if (action === "verify-command") {
451
599
  const taskID = args[3]
452
- const root = args[4] && args[4] !== "--" && !args[4].startsWith("--") ? args[4] : process.cwd()
600
+ const root = positionalArg(args, 4) || process.cwd()
453
601
  const separator = args.indexOf("--")
454
602
  const executable = separator >= 0 ? args[separator + 1] : null
455
603
  const commandArgs = separator >= 0 ? args.slice(separator + 2) : []
456
- if (!taskID || !executable) {
457
- throw new Error("Usage: ocskill work verify-command <slug> <task-id> [dir] [--run-id <id>] -- <command> [args...]")
604
+ const runId = optionValue(args, "--run-id")
605
+ if (!taskID || !runId || !executable) {
606
+ throw new Error("Usage: ocskill work verify-command <slug> <task-id> [dir] --run-id <id> -- <command> [args...]")
458
607
  }
459
608
  const startedAt = new Date().toISOString()
460
609
  const before = workspaceFingerprint(root)
@@ -465,7 +614,7 @@ async function workControl() {
465
614
  })
466
615
  const receipt = createVerificationReceipt({
467
616
  task: taskID,
468
- runId: optionValue("--run-id"),
617
+ runId,
469
618
  command: executable,
470
619
  args: commandArgs,
471
620
  cwd: path.resolve(root),
@@ -479,15 +628,11 @@ async function workControl() {
479
628
  workspaceAfter: workspaceFingerprint(root),
480
629
  })
481
630
  const recorded = await recordVerificationReceipt(root, slug, taskID, receipt)
482
- const clip = (value) => {
483
- const text = String(value || "")
484
- return text.length <= 8000 ? text : "...[truncated]\n" + text.slice(-8000)
485
- }
486
631
  printJson({
487
632
  receipt: recorded,
488
633
  output: {
489
- stdout: clip(result.stdout),
490
- stderr: clip(result.stderr),
634
+ stdout: clipOutput(result.stdout),
635
+ stderr: clipOutput(result.stderr),
491
636
  },
492
637
  })
493
638
  if ((result.status ?? 1) !== 0) process.exitCode = result.status ?? 1
@@ -495,61 +640,66 @@ async function workControl() {
495
640
  }
496
641
  if (action === "complete") {
497
642
  const taskID = args[3]
498
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
499
- if (!taskID) throw new Error("Usage: ocskill work complete <slug> <task-id> [dir] --evidence <text>")
500
- const reportFile = optionValue("--report-file")
501
- const report = reportFile ? readFileSync(path.resolve(reportFile), "utf8") : null
643
+ const root = positionalArg(args, 4) || process.cwd()
644
+ const runId = optionValue(args, "--run-id")
645
+ if (!taskID || !runId) throw new Error("Usage: ocskill work complete <slug> <task-id> [dir] --run-id <id> --evidence <text>")
646
+ const reportFile = optionValue(args, "--report-file")
647
+ const report = reportFile ? readTextFile(reportFile) : null
502
648
  printJson(await completeTask(root, slug, taskID, {
503
- evidence: optionValue("--evidence"),
649
+ evidence: optionValue(args, "--evidence"),
504
650
  report,
505
- runId: optionValue("--run-id"),
651
+ runId,
506
652
  }))
507
653
  return
508
654
  }
509
655
  if (action === "fail") {
510
656
  const taskID = args[3]
511
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
512
- if (!taskID) throw new Error("Usage: ocskill work fail <slug> <task-id> [dir] --reason <text>")
513
- printJson(await failTask(root, slug, taskID, optionValue("--reason"), { runId: optionValue("--run-id") }))
657
+ const root = positionalArg(args, 4) || process.cwd()
658
+ const runId = optionValue(args, "--run-id")
659
+ if (!taskID || !runId) throw new Error("Usage: ocskill work fail <slug> <task-id> [dir] --run-id <id> --reason <text>")
660
+ printJson(await failTask(root, slug, taskID, optionValue(args, "--reason"), { runId }))
514
661
  return
515
662
  }
516
663
  if (action === "decision") {
517
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
518
- printJson(await addDecision(root, slug, optionValue("--text")))
664
+ const root = positionalArg(args, 3) || process.cwd()
665
+ printJson(await addDecision(root, slug, optionValue(args, "--text")))
519
666
  return
520
667
  }
521
668
  if (action === "block") {
522
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
523
- printJson(await addBlocker(root, slug, optionValue("--text")))
669
+ const root = positionalArg(args, 3) || process.cwd()
670
+ printJson(await addBlocker(root, slug, optionValue(args, "--text")))
524
671
  return
525
672
  }
526
673
  if (action === "unblock") {
527
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
528
- printJson(await resolveBlocker(root, slug, optionValue("--text")))
674
+ const root = positionalArg(args, 3) || process.cwd()
675
+ printJson(await resolveBlocker(root, slug, optionValue(args, "--text")))
529
676
  return
530
677
  }
531
678
  if (action === "verify-integration") {
532
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
533
- const reportFile = optionValue("--report-file")
534
- const report = reportFile ? readFileSync(path.resolve(reportFile), "utf8") : null
679
+ const root = positionalArg(args, 3) || process.cwd()
680
+ const reportFile = optionValue(args, "--report-file")
681
+ const report = reportFile ? readTextFile(reportFile) : null
682
+ const receiptFile = optionValue(args, "--receipt-file")
683
+ const receipt = receiptFile ? readJsonFile(receiptFile) : null
535
684
  printJson(await recordIntegrationVerification(
536
685
  root,
537
686
  slug,
538
- optionValue("--verdict"),
539
- optionValue("--evidence"),
687
+ optionValue(args, "--verdict"),
688
+ optionValue(args, "--evidence"),
540
689
  report,
690
+ { receipt },
541
691
  ))
542
692
  return
543
693
  }
544
694
  if (action === "finalize") {
545
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
546
- printJson(await finalizeWork(root, slug, optionValue("--evidence")))
695
+ const root = positionalArg(args, 3) || process.cwd()
696
+ printJson(await finalizeWork(root, slug, optionValue(args, "--evidence")))
547
697
  return
548
698
  }
549
699
 
550
700
  throw new Error("Unknown work action: " + action)
551
701
  } catch (error) {
552
- console.error(error instanceof Error ? error.message : error)
702
+ console.error(errorMessage(error))
553
703
  if (error?.validation) printJson(error.validation)
554
704
  process.exitCode = 1
555
705
  }
@@ -562,10 +712,10 @@ async function modelPolicy() {
562
712
  process.exitCode = 2
563
713
  return
564
714
  }
565
- const attempt = Number.parseInt(optionValue("--attempt") || "1", 10)
715
+ const attempt = optionInt(args, "--attempt", 1)
566
716
  const policy = await readModelPolicy(getConfigDir())
567
717
  const normalizedAttempt = Number.isInteger(attempt) && attempt > 0 ? attempt : 1
568
- const taskText = optionValue("--text")
718
+ const taskText = optionValue(args, "--text")
569
719
  if (taskText) {
570
720
  printJson(resolveAdaptiveModel(role, normalizedAttempt, classifyEngineeringTask(taskText), policy))
571
721
  return
@@ -627,7 +777,7 @@ async function modelsControl() {
627
777
  async function routerControl() {
628
778
  const action = args[1] || "status"
629
779
  const maxIndex = args.indexOf("--max")
630
- const maxValue = maxIndex >= 0 ? Number.parseInt(args[maxIndex + 1] || "", 10) : null
780
+ const maxValue = maxIndex >= 0 ? Number.parseInt(optionValue(args, "--max") ?? "", 10) : null
631
781
 
632
782
  if (!["status", "on", "off"].includes(action)) {
633
783
  console.error("Usage: ocskill router [status|on|off] [--max 1..6]")
@@ -678,42 +828,76 @@ async function taskPolicyControl() {
678
828
  async function sandboxControl() {
679
829
  const action = args[1] || "list"
680
830
  try {
831
+ if (action === "capability") {
832
+ printJson(containerSandboxCapability(optionValue(args, "--engine")))
833
+ return
834
+ }
835
+ if (action === "exec") {
836
+ const separator = args.indexOf("--")
837
+ const root = positionalArg(args, 2) || process.cwd()
838
+ const executable = separator >= 0 ? args[separator + 1] : null
839
+ const commandArgs = separator >= 0 ? args.slice(separator + 2) : []
840
+ const image = optionValue(args, "--image")
841
+ if (!image || !executable) {
842
+ throw new Error("Usage: ocskill sandbox exec [dir] --image <image> [--engine docker|podman] -- <command> [args...]")
843
+ }
844
+ const result = runContainerSandbox(root, {
845
+ engine: optionValue(args, "--engine"),
846
+ image,
847
+ command: executable,
848
+ args: commandArgs,
849
+ timeoutMs: optionInt(args, "--timeout-ms", 10 * 60_000),
850
+ })
851
+ printJson({ ...result, stdout: clipOutput(result.stdout), stderr: clipOutput(result.stderr) })
852
+ if (result.status !== 0) process.exitCode = result.status
853
+ return
854
+ }
681
855
  if (action === "list") {
682
- printJson(listTaskSandboxes(args[2] || process.cwd()))
856
+ printJson(listTaskSandboxes(positionalArg(args, 2) || process.cwd()))
683
857
  return
684
858
  }
685
859
  if (action === "create") {
686
860
  const slug = args[2]
687
861
  const taskID = args[3]
688
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
862
+ const root = positionalArg(args, 4) || process.cwd()
689
863
  if (!slug || !taskID) throw new Error("Usage: ocskill sandbox create <slug> <task-id> [dir]")
690
864
  printJson(await createTaskSandbox(root, slug, taskID))
691
865
  return
692
866
  }
867
+ if (action === "integrate") {
868
+ const dir = args[2]
869
+ const root = positionalArg(args, 3) || process.cwd()
870
+ if (!dir) throw new Error("Usage: ocskill sandbox integrate <worktree-path> [dir] [--keep]")
871
+ printJson(await integrateTaskSandbox(root, dir, { keep: args.includes("--keep") }))
872
+ return
873
+ }
693
874
  if (action === "remove") {
694
875
  const dir = args[2]
695
- const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
696
- if (!dir) throw new Error("Usage: ocskill sandbox remove <worktree-path> [dir] [--force]")
697
- printJson(await removeTaskSandbox(root, dir, { force: args.includes("--force") }))
876
+ const root = positionalArg(args, 3) || process.cwd()
877
+ if (!dir) throw new Error("Usage: ocskill sandbox remove <worktree-path> [dir] [--force] [--delete-branch]")
878
+ printJson(await removeTaskSandbox(root, dir, {
879
+ force: args.includes("--force"),
880
+ deleteBranch: args.includes("--delete-branch"),
881
+ }))
698
882
  return
699
883
  }
700
- throw new Error("Usage: ocskill sandbox <list|create|remove> ...")
884
+ throw new Error("Usage: ocskill sandbox <capability|exec|list|create|integrate|remove> ...")
701
885
  } catch (error) {
702
- console.error(error instanceof Error ? error.message : error)
886
+ console.error(errorMessage(error))
703
887
  process.exitCode = 1
704
888
  }
705
889
  }
706
890
 
707
891
  async function learningControl() {
708
892
  const action = args[1] || "status"
709
- const root = args[2] && !args[2].startsWith("--") ? args[2] : process.cwd()
893
+ const root = positionalArg(args, 2) || process.cwd()
710
894
  try {
711
895
  if (action === "status") {
712
896
  printJson(await readLearningState(root))
713
897
  return
714
898
  }
715
899
  if (action === "analyze") {
716
- const evalDir = optionValue("--eval-dir") || path.join(path.resolve(root), ".ues-evals")
900
+ const evalDir = optionValue(args, "--eval-dir") || path.join(path.resolve(root), ".ues-evals")
717
901
  const analysis = await analyzeEvalTraces(evalDir)
718
902
  const state = await saveLearningAnalysis(root, analysis)
719
903
  printJson({ analysis, state })
@@ -721,14 +905,23 @@ async function learningControl() {
721
905
  }
722
906
  if (action === "accept") {
723
907
  const id = args[2]
724
- const acceptRoot = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
908
+ const acceptRoot = positionalArg(args, 3) || process.cwd()
725
909
  if (!id) throw new Error("Usage: ocskill learn accept <proposal-id> [dir]")
726
910
  printJson(await acceptLearning(acceptRoot, id))
727
911
  return
728
912
  }
729
- throw new Error("Usage: ocskill learn <status|analyze|accept> ...")
913
+ if (action === "promote") {
914
+ const id = args[2]
915
+ const promoteRoot = positionalArg(args, 3) || process.cwd()
916
+ if (!id) throw new Error("Usage: ocskill learn promote <proposal-id> [dir] --report <matrix-summary.json>")
917
+ printJson(await promoteLearning(promoteRoot, id, {
918
+ report: optionValue(args, "--report"),
919
+ }))
920
+ return
921
+ }
922
+ throw new Error("Usage: ocskill learn <status|analyze|accept|promote> ...")
730
923
  } catch (error) {
731
- console.error(error instanceof Error ? error.message : error)
924
+ console.error(errorMessage(error))
732
925
  process.exitCode = 1
733
926
  }
734
927
  }
@@ -742,7 +935,7 @@ async function hermesControl() {
742
935
  if (action === "prompt" || action === "exec") {
743
936
  const slug = args[2]
744
937
  const taskID = args[3]
745
- const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
938
+ const root = positionalArg(args, 4) || process.cwd()
746
939
  if (!slug || !taskID) {
747
940
  console.error("Usage: ocskill hermes <prompt|exec> <slug> <task-id> [dir]")
748
941
  process.exitCode = 2
@@ -796,7 +989,7 @@ async function update() {
796
989
  })
797
990
  } catch (error) {
798
991
  console.error("[ocskill] Could not determine the latest published npm version; refusing an unsafe self-update.")
799
- console.error(error instanceof Error ? error.message : error)
992
+ console.error(errorMessage(error))
800
993
  process.exitCode = error?.exitCode || 1
801
994
  return
802
995
  }
@@ -808,7 +1001,7 @@ async function update() {
808
1001
  compared = compareVersions(latestVersion, currentVersion)
809
1002
  } catch (error) {
810
1003
  console.error("[ocskill] npm returned a version that could not be compared safely; refusing update.")
811
- console.error(error instanceof Error ? error.message : error)
1004
+ console.error(errorMessage(error))
812
1005
  process.exitCode = 1
813
1006
  return
814
1007
  }
@@ -906,6 +1099,15 @@ switch (command) {
906
1099
  case "repo-graph":
907
1100
  await inspectRepoGraph()
908
1101
  break
1102
+ case "index":
1103
+ await semanticIndexControl()
1104
+ break
1105
+ case "aci":
1106
+ await aciControl()
1107
+ break
1108
+ case "trace":
1109
+ await traceControl()
1110
+ break
909
1111
  case "review-scope":
910
1112
  await inspectReviewScope()
911
1113
  break