opencode-agent-skill 7.7.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 (206) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/LICENSE +9 -0
  3. package/README.md +581 -0
  4. package/bin/ocskill.mjs +975 -0
  5. package/docs/DETERMINISTIC-TOOLS.md +88 -0
  6. package/docs/ENGINEERING-DESIGN.md +176 -0
  7. package/docs/EVALS.md +136 -0
  8. package/docs/NPM-PUBLISH.md +102 -0
  9. package/docs/OPENCODE-COMPAT.md +109 -0
  10. package/docs/RESEARCH-SOURCES.md +37 -0
  11. package/docs/TRACE-SCHEMA.md +109 -0
  12. package/docs/V7-INTELLIGENCE-RUNTIME.md +166 -0
  13. package/evals/live/fixtures/engineering-bench/package.json +1 -0
  14. package/evals/live/fixtures/engineering-bench/src/api-errors.mjs +3 -0
  15. package/evals/live/fixtures/engineering-bench/src/authz.mjs +3 -0
  16. package/evals/live/fixtures/engineering-bench/src/cache-tags.mjs +3 -0
  17. package/evals/live/fixtures/engineering-bench/src/config.mjs +3 -0
  18. package/evals/live/fixtures/engineering-bench/src/contract-consumer.mjs +6 -0
  19. package/evals/live/fixtures/engineering-bench/src/contract-producer.mjs +3 -0
  20. package/evals/live/fixtures/engineering-bench/src/dedupe.mjs +3 -0
  21. package/evals/live/fixtures/engineering-bench/src/dependency.mjs +3 -0
  22. package/evals/live/fixtures/engineering-bench/src/discount.mjs +3 -0
  23. package/evals/live/fixtures/engineering-bench/src/inventory.mjs +3 -0
  24. package/evals/live/fixtures/engineering-bench/src/migration.mjs +3 -0
  25. package/evals/live/fixtures/engineering-bench/src/money.mjs +3 -0
  26. package/evals/live/fixtures/engineering-bench/src/pagination.mjs +5 -0
  27. package/evals/live/fixtures/engineering-bench/src/path-safe.mjs +5 -0
  28. package/evals/live/fixtures/engineering-bench/src/payment.mjs +5 -0
  29. package/evals/live/fixtures/engineering-bench/src/query-sort.mjs +3 -0
  30. package/evals/live/fixtures/engineering-bench/src/react-state.mjs +8 -0
  31. package/evals/live/fixtures/engineering-bench/src/retry.mjs +3 -0
  32. package/evals/live/fixtures/engineering-bench/src/rn-platform.mjs +3 -0
  33. package/evals/live/fixtures/engineering-bench/src/upload.mjs +3 -0
  34. package/evals/live/fixtures/engineering-bench/src/webhook.mjs +3 -0
  35. package/evals/live/graders/engineering-bench.mjs +239 -0
  36. package/evals/live/tasks.json +126 -0
  37. package/evals/long/fixtures/long-horizon/package.json +1 -0
  38. package/evals/long/fixtures/long-horizon/src/auth.mjs +5 -0
  39. package/evals/long/fixtures/long-horizon/src/checkout.mjs +9 -0
  40. package/evals/long/fixtures/long-horizon/src/inventory.mjs +5 -0
  41. package/evals/long/fixtures/long-horizon/src/money.mjs +3 -0
  42. package/evals/long/fixtures/long-horizon/src/payment.mjs +7 -0
  43. package/evals/long/fixtures/long-horizon/src/product-api.mjs +10 -0
  44. package/evals/long/fixtures/long-horizon/src/product-cache.mjs +3 -0
  45. package/evals/long/fixtures/long-horizon/src/product-service.mjs +6 -0
  46. package/evals/long/fixtures/long-horizon/src/product-state.mjs +8 -0
  47. package/evals/long/fixtures/long-horizon/src/project-api.mjs +9 -0
  48. package/evals/long/fixtures/long-horizon/src/project-service.mjs +7 -0
  49. package/evals/long/fixtures/long-horizon/src/user-consumer.mjs +3 -0
  50. package/evals/long/fixtures/long-horizon/src/user-migration.mjs +7 -0
  51. package/evals/long/fixtures/long-horizon/src/user-serializer.mjs +3 -0
  52. package/evals/long/fixtures/long-horizon/src/user-validation.mjs +3 -0
  53. package/evals/long/graders/long-horizon.mjs +209 -0
  54. package/evals/long/tasks.json +36 -0
  55. package/evals/router-triggers.json +1238 -0
  56. package/evals/routing.json +321 -0
  57. package/global-config/AGENTS.md +212 -0
  58. package/global-config/agents/architect.md +38 -0
  59. package/global-config/agents/codebase-mapper.md +41 -0
  60. package/global-config/agents/critic.md +37 -0
  61. package/global-config/agents/debugger.md +36 -0
  62. package/global-config/agents/executor.md +48 -0
  63. package/global-config/agents/integration-verifier.md +43 -0
  64. package/global-config/agents/plan-checker.md +43 -0
  65. package/global-config/agents/researcher.md +30 -0
  66. package/global-config/agents/reviewer.md +30 -0
  67. package/global-config/agents/verifier.md +33 -0
  68. package/global-config/commands/audit.md +8 -0
  69. package/global-config/commands/critique.md +8 -0
  70. package/global-config/commands/debug.md +8 -0
  71. package/global-config/commands/feature.md +8 -0
  72. package/global-config/commands/fix.md +8 -0
  73. package/global-config/commands/plan.md +8 -0
  74. package/global-config/commands/research.md +8 -0
  75. package/global-config/commands/resume.md +22 -0
  76. package/global-config/commands/review.md +8 -0
  77. package/global-config/commands/run.md +29 -0
  78. package/global-config/commands/verify.md +8 -0
  79. package/global-config/plugins/ues-router/capabilities.js +20 -0
  80. package/global-config/plugins/ues-router/index.js +277 -0
  81. package/global-config/plugins/ues-router/router.js +74 -0
  82. package/global-config/plugins/ues-router/safety.js +16 -0
  83. package/global-config/skills/accessibility/SKILL.md +10 -0
  84. package/global-config/skills/accessibility/references/workflow.md +17 -0
  85. package/global-config/skills/api-contract/SKILL.md +12 -0
  86. package/global-config/skills/api-contract/references/workflow.md +17 -0
  87. package/global-config/skills/auth-security/SKILL.md +12 -0
  88. package/global-config/skills/auth-security/references/workflow.md +15 -0
  89. package/global-config/skills/bug-diagnosis/SKILL.md +28 -0
  90. package/global-config/skills/change-impact-analysis/SKILL.md +23 -0
  91. package/global-config/skills/code-review/SKILL.md +19 -0
  92. package/global-config/skills/context-engineering/SKILL.md +18 -0
  93. package/global-config/skills/context-engineering/references/large-repo.md +16 -0
  94. package/global-config/skills/database-engineering/SKILL.md +12 -0
  95. package/global-config/skills/database-engineering/references/workflow.md +15 -0
  96. package/global-config/skills/dependency-management/SKILL.md +12 -0
  97. package/global-config/skills/dependency-management/references/workflow.md +14 -0
  98. package/global-config/skills/devops-engineering/SKILL.md +10 -0
  99. package/global-config/skills/devops-engineering/references/workflow.md +11 -0
  100. package/global-config/skills/django-engineering/SKILL.md +10 -0
  101. package/global-config/skills/django-engineering/references/workflow.md +11 -0
  102. package/global-config/skills/documentation-engineering/SKILL.md +10 -0
  103. package/global-config/skills/documentation-engineering/references/workflow.md +15 -0
  104. package/global-config/skills/dotnet-engineering/SKILL.md +10 -0
  105. package/global-config/skills/dotnet-engineering/references/workflow.md +11 -0
  106. package/global-config/skills/ecommerce-engineering/SKILL.md +10 -0
  107. package/global-config/skills/ecommerce-engineering/references/workflow.md +17 -0
  108. package/global-config/skills/engineering-orchestrator/SKILL.md +29 -0
  109. package/global-config/skills/engineering-orchestrator/references/delegation.md +22 -0
  110. package/global-config/skills/engineering-orchestrator/references/evaluator-loop.md +18 -0
  111. package/global-config/skills/engineering-orchestrator/references/long-horizon.md +57 -0
  112. package/global-config/skills/engineering-orchestrator/references/model-escalation.md +19 -0
  113. package/global-config/skills/engineering-orchestrator/references/retry-policy.md +12 -0
  114. package/global-config/skills/engineering-orchestrator/references/routing.md +39 -0
  115. package/global-config/skills/engineering-orchestrator/references/verification-matrix.md +18 -0
  116. package/global-config/skills/fastapi-engineering/SKILL.md +10 -0
  117. package/global-config/skills/fastapi-engineering/references/workflow.md +13 -0
  118. package/global-config/skills/file-upload-engineering/SKILL.md +10 -0
  119. package/global-config/skills/file-upload-engineering/references/workflow.md +17 -0
  120. package/global-config/skills/flutter-engineering/SKILL.md +10 -0
  121. package/global-config/skills/flutter-engineering/references/workflow.md +13 -0
  122. package/global-config/skills/git-safety/SKILL.md +10 -0
  123. package/global-config/skills/git-safety/references/workflow.md +15 -0
  124. package/global-config/skills/implementation-engineer/SKILL.md +10 -0
  125. package/global-config/skills/implementation-engineer/references/workflow.md +14 -0
  126. package/global-config/skills/java-spring-engineering/SKILL.md +10 -0
  127. package/global-config/skills/java-spring-engineering/references/workflow.md +13 -0
  128. package/global-config/skills/long-task-state/SKILL.md +28 -0
  129. package/global-config/skills/long-task-state/references/context-ledger.md +27 -0
  130. package/global-config/skills/long-task-state/templates/STATE.md +48 -0
  131. package/global-config/skills/nestjs-engineering/SKILL.md +10 -0
  132. package/global-config/skills/nestjs-engineering/references/workflow.md +11 -0
  133. package/global-config/skills/nextjs-engineering/SKILL.md +12 -0
  134. package/global-config/skills/nextjs-engineering/references/workflow.md +15 -0
  135. package/global-config/skills/nodejs-engineering/SKILL.md +12 -0
  136. package/global-config/skills/nodejs-engineering/references/workflow.md +11 -0
  137. package/global-config/skills/payment-engineering/SKILL.md +12 -0
  138. package/global-config/skills/payment-engineering/references/workflow.md +19 -0
  139. package/global-config/skills/performance-engineering/SKILL.md +10 -0
  140. package/global-config/skills/performance-engineering/references/workflow.md +17 -0
  141. package/global-config/skills/python-engineering/SKILL.md +10 -0
  142. package/global-config/skills/python-engineering/references/workflow.md +11 -0
  143. package/global-config/skills/react-engineering/SKILL.md +14 -0
  144. package/global-config/skills/react-engineering/references/workflow.md +16 -0
  145. package/global-config/skills/react-native-engineering/SKILL.md +14 -0
  146. package/global-config/skills/react-native-engineering/references/workflow.md +16 -0
  147. package/global-config/skills/repo-explorer/SKILL.md +17 -0
  148. package/global-config/skills/research-verification/SKILL.md +18 -0
  149. package/global-config/skills/research-verification/references/source-hierarchy.md +12 -0
  150. package/global-config/skills/rest-api-design/SKILL.md +10 -0
  151. package/global-config/skills/rest-api-design/references/workflow.md +18 -0
  152. package/global-config/skills/software-architect/SKILL.md +10 -0
  153. package/global-config/skills/software-architect/references/workflow.md +18 -0
  154. package/global-config/skills/task-planner/SKILL.md +21 -0
  155. package/global-config/skills/task-planner/references/plan-schema.md +56 -0
  156. package/global-config/skills/test-driven-development/SKILL.md +22 -0
  157. package/global-config/skills/test-driven-development/references/writing-good-tests.md +21 -0
  158. package/global-config/skills/test-verification/SKILL.md +20 -0
  159. package/global-config/skills/ui-ux-engineering/SKILL.md +10 -0
  160. package/global-config/skills/ui-ux-engineering/references/workflow.md +17 -0
  161. package/global-config/skills/web-security-review/SKILL.md +10 -0
  162. package/global-config/skills/web-security-review/references/workflow.md +22 -0
  163. package/lib/context-manifest.mjs +101 -0
  164. package/lib/control-center.mjs +148 -0
  165. package/lib/eval-auth.mjs +21 -0
  166. package/lib/eval-report.mjs +72 -0
  167. package/lib/eval-telemetry.mjs +155 -0
  168. package/lib/evidence-receipt.mjs +50 -0
  169. package/lib/hermes-bridge.mjs +28 -0
  170. package/lib/ids.mjs +21 -0
  171. package/lib/installer.mjs +636 -0
  172. package/lib/learning-engine.mjs +161 -0
  173. package/lib/model-config.mjs +88 -0
  174. package/lib/model-policy.mjs +71 -0
  175. package/lib/opencode-compat.mjs +86 -0
  176. package/lib/orchestrator-policy.mjs +35 -0
  177. package/lib/process-runner.mjs +117 -0
  178. package/lib/repo-graph.mjs +135 -0
  179. package/lib/repo-inspect.mjs +264 -0
  180. package/lib/review-scope.mjs +98 -0
  181. package/lib/router-config.mjs +40 -0
  182. package/lib/task-engine.mjs +777 -0
  183. package/lib/task-graph.mjs +262 -0
  184. package/lib/update-resolver.mjs +43 -0
  185. package/lib/verification-plan.mjs +52 -0
  186. package/lib/version.mjs +47 -0
  187. package/lib/workspace-snapshot.mjs +45 -0
  188. package/lib/worktree-sandbox.mjs +59 -0
  189. package/package.json +69 -0
  190. package/scripts/check-working-tree.mjs +2 -0
  191. package/scripts/collect-evidence.mjs +2 -0
  192. package/scripts/control-center.mjs +37 -0
  193. package/scripts/detect-stack.mjs +2 -0
  194. package/scripts/detect-test-commands.mjs +2 -0
  195. package/scripts/eval-live.mjs +437 -0
  196. package/scripts/eval-report.mjs +49 -0
  197. package/scripts/eval-router.mjs +45 -0
  198. package/scripts/eval-skills.mjs +71 -0
  199. package/scripts/impact-map.mjs +4 -0
  200. package/scripts/install.mjs +61 -0
  201. package/scripts/repo-map.mjs +2 -0
  202. package/scripts/smoke-packed-install.mjs +326 -0
  203. package/scripts/syntax-check.mjs +35 -0
  204. package/scripts/uninstall.mjs +21 -0
  205. package/scripts/validate-live-suite.mjs +65 -0
  206. package/scripts/validate.mjs +133 -0
@@ -0,0 +1,975 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from "node:fs"
3
+ import { spawnSync } from "node:child_process"
4
+ import { fileURLToPath } from "node:url"
5
+ import path from "node:path"
6
+ import os from "node:os"
7
+ import {
8
+ PACKAGE_NAME,
9
+ getConfigDir,
10
+ getPackageVersion,
11
+ getStatus,
12
+ installResources,
13
+ removeResources,
14
+ } from "../lib/installer.mjs"
15
+ import { compareVersions } from "../lib/version.mjs"
16
+ import { resolveLatestPublishedVersion } from "../lib/update-resolver.mjs"
17
+ import { readRouterConfig, writeRouterConfig } from "../lib/router-config.mjs"
18
+ import {
19
+ detectStack,
20
+ detectTestCommands,
21
+ repoMap,
22
+ impactMap,
23
+ collectEvidence,
24
+ checkWorkingTree,
25
+ } from "../lib/repo-inspect.mjs"
26
+ import { buildRepoGraph } from "../lib/repo-graph.mjs"
27
+ import { analyzePlan } from "../lib/task-graph.mjs"
28
+ import {
29
+ addBlocker,
30
+ addDecision,
31
+ approvePlan,
32
+ completeTask,
33
+ contextPack,
34
+ failTask,
35
+ finalizeWork,
36
+ importPlan,
37
+ initWork,
38
+ recordIntegrationVerification,
39
+ resolveBlocker,
40
+ resumeWork,
41
+ startTask,
42
+ workStatus,
43
+ heartbeatTask,
44
+ recoverStaleTasks,
45
+ recordVerificationReceipt,
46
+ workspaceFingerprint,
47
+ } from "../lib/task-engine.mjs"
48
+ import { reviewScope } from "../lib/review-scope.mjs"
49
+ import { buildVerificationPlan } from "../lib/verification-plan.mjs"
50
+ import { resolveAdaptiveModel, resolveModel } from "../lib/model-policy.mjs"
51
+ import { createVerificationReceipt } from "../lib/evidence-receipt.mjs"
52
+ 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"
55
+ import { hermesStatus, buildHermesDelegationPrompt, hermesOneShotArgs } from "../lib/hermes-bridge.mjs"
56
+ import { readModelPolicy, validateModelID, writeModelPolicy } from "../lib/model-config.mjs"
57
+
58
+ const args = process.argv.slice(2)
59
+ const command = args[0] || "help"
60
+ const force = args.includes("--force")
61
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
62
+
63
+ function printHelp() {
64
+ console.log(`
65
+ OpenCode Universal Engineering System
66
+
67
+ Usage:
68
+ ocskill install [--force] Install or re-sync bundled OpenCode resources
69
+ ocskill status Show package/resource synchronization status
70
+ ocskill doctor Check Node, npm, OpenCode and installed resources
71
+ ocskill eval Validate the bundled static skill-routing suite
72
+ ocskill eval-live [options] Run baseline-vs-UES live behavioral evals
73
+ ocskill eval-report [paths] Aggregate live eval pass-rate/cost/tool telemetry
74
+ ocskill inspect [dir] Deterministic repository/stack/test-command map
75
+ ocskill impact <query> [dir] Search likely impact paths and matching lines
76
+ ocskill evidence [dir] Collect stack, verification and Git evidence
77
+ ocskill working-tree [dir] Report Git branch/HEAD/dirty state
78
+ ocskill repo-graph [dir] Build a bounded source import/dependency graph
79
+ ocskill review-scope [base] [dir]
80
+ Enumerate changed files, review coverage and risk
81
+ ocskill verification-plan [dir]
82
+ Build project-native verification recommendations
83
+ ocskill task-graph <plan> Validate PLAN.json and compute dependency-safe waves
84
+ ocskill context-pack <slug> <task> [dir]
85
+ Emit bounded task context for a fresh executor
86
+ ocskill work <action> ... Manage persistent .ues-work long-task state
87
+ ocskill model-policy <role> [--attempt N]
88
+ Resolve light/standard/heavy escalation tier
89
+ 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
92
+ ocskill hermes <action> ... Optional Hermes adapter/status
93
+ ocskill dashboard [dir] [--serve] [--port N]
94
+ Generate/serve the local UES Control Center
95
+ ocskill models <status|on|off|set|role> ...
96
+ Configure role/tier model routing, then re-sync
97
+ ocskill router [status|on|off] [--max N]
98
+ Configure the OpenCode v2 automatic skill router
99
+ ocskill update Update the global npm package and re-sync resources
100
+ ocskill remove [--force] Remove managed resources and uninstall the npm package
101
+ ocskill version Show package version
102
+ ocskill help Show this help
103
+
104
+ Long-task actions:
105
+ ocskill work init <slug> [dir] --goal <text>
106
+ ocskill work plan <slug> <plan.json> [dir]
107
+ ocskill work status|resume <slug> [dir]
108
+ ocskill work approve-plan <slug> [dir] --evidence <plan-checker-evidence>
109
+ ocskill work start <slug> <task-id> [dir] [--lease-ms N]
110
+ ocskill work heartbeat <slug> <task-id> [dir] [--run-id <id>]
111
+ 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>]
115
+ ocskill work decision <slug> [dir] --text <decision>
116
+ ocskill work block|unblock <slug> [dir] --text <blocker>
117
+ ocskill work verify-integration <slug> [dir] --verdict PASS|FAIL|PARTIAL --evidence <text> [--report-file <file>]
118
+ ocskill work finalize <slug> [dir] --evidence <integration-evidence>
119
+
120
+ Model routing:
121
+ ocskill models status
122
+ ocskill models on|off
123
+ ocskill models set <light|standard|heavy> <provider/model[#variant]>
124
+ ocskill models role <role> <light|standard|heavy>
125
+
126
+ --force backs up and replaces/removes state owned by another package.
127
+ `)
128
+ }
129
+
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
+
163
+ function run(executable, commandArgs, options = {}) {
164
+ const common = { stdio: "inherit", ...options }
165
+
166
+ if (process.platform !== "win32") {
167
+ const result = spawnSync(executable, commandArgs, common)
168
+ return result.status ?? 1
169
+ }
170
+
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
183
+ }
184
+
185
+ const result = spawnSync(resolved, commandArgs, common)
186
+ return result.status ?? 1
187
+ }
188
+
189
+ function hasCommand(name) {
190
+ if (process.platform === "win32") return findWindowsCommand(name) !== null
191
+ return spawnSync("which", [name], { stdio: "ignore" }).status === 0
192
+ }
193
+
194
+ function runCapture(executable, commandArgs, options = {}) {
195
+ const common = { encoding: "utf8", ...options }
196
+
197
+ if (process.platform !== "win32") {
198
+ return spawnSync(executable, commandArgs, common)
199
+ }
200
+
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
+ )
214
+ }
215
+
216
+ return spawnSync(resolved, commandArgs, common)
217
+ }
218
+
219
+
220
+ async function install() {
221
+ const result = await installResources({ force })
222
+ if (result.stateError) {
223
+ for (const warning of result.warnings) console.warn(`[ocskill] WARNING: ${warning}`)
224
+ console.error(`[ocskill] ERROR: ${result.stateError}`)
225
+ process.exitCode = 1
226
+ return
227
+ }
228
+ console.log(`[ocskill] Installed resources for v${result.version}`)
229
+ console.log(`[ocskill] OpenCode config: ${result.configDir}`)
230
+ console.log(`[ocskill] Skills: ${result.skills.length}`)
231
+ console.log(`[ocskill] Commands: ${result.commands.length}`)
232
+ console.log(`[ocskill] Subagents: ${result.agents.length}`)
233
+ console.log(`[ocskill] OpenCode major: ${result.openCodeMajor}`)
234
+ if ((result.plugins || []).length) console.log(`[ocskill] Router plugins: ${result.plugins.length}`)
235
+ for (const warning of result.warnings) console.warn(`[ocskill] WARNING: ${warning}`)
236
+ console.log("[ocskill] Start a new OpenCode session to pick up workflow changes.")
237
+ }
238
+
239
+ async function status() {
240
+ const currentVersion = await getPackageVersion()
241
+ const result = await getStatus()
242
+
243
+ if (!result.installed) {
244
+ console.log(`[ocskill] Package version: ${currentVersion}`)
245
+ console.log("[ocskill] OpenCode resources are not installed or state is invalid.")
246
+ console.log(`[ocskill] Expected config: ${result.configDir}`)
247
+ if (result.stateError) console.log(`[ocskill] State: ${result.stateError}`)
248
+ process.exitCode = 1
249
+ return
250
+ }
251
+
252
+ const synced = result.version === currentVersion
253
+ console.log(`[ocskill] Package version: ${currentVersion}`)
254
+ console.log(`[ocskill] Resource version: ${result.version}`)
255
+ console.log(`[ocskill] Sync: ${synced ? "OK" : "OUTDATED - run ocskill install"}`)
256
+ console.log(`[ocskill] Config: ${result.configDir}`)
257
+ console.log(`[ocskill] Skills: ${result.skillsPresent}/${result.skills.length}`)
258
+ console.log(`[ocskill] Commands: ${result.commandsPresent}/${result.commands.length}`)
259
+ console.log(`[ocskill] Subagents: ${result.agentsPresent}/${(result.agents || []).length}`)
260
+ console.log(`[ocskill] OpenCode major: ${result.openCodeMajor ?? "legacy/unknown"}`)
261
+ if ((result.plugins || []).length) console.log(`[ocskill] Router plugins: ${result.pluginsPresent}/${result.plugins.length}`)
262
+ console.log(`[ocskill] Workflow: ${result.workflowPresent ? "OK" : "MISSING"}`)
263
+
264
+ if (!synced ||
265
+ result.skillsPresent !== result.skills.length ||
266
+ result.commandsPresent !== result.commands.length ||
267
+ result.agentsPresent !== (result.agents || []).length ||
268
+ result.pluginsPresent !== (result.plugins || []).length ||
269
+ !result.workflowPresent) {
270
+ process.exitCode = 1
271
+ }
272
+ }
273
+
274
+ async function doctor() {
275
+ console.log("OpenCode Universal Engineering System - doctor")
276
+ console.log(`Package: ${PACKAGE_NAME}`)
277
+ console.log(`Version: ${await getPackageVersion()}`)
278
+ console.log(`Node: ${process.version}`)
279
+ console.log(`Config: ${getConfigDir()}`)
280
+ console.log(`npm: ${hasCommand("npm") ? "OK" : "MISSING"}`)
281
+ console.log(`OpenCode: ${hasCommand("opencode") ? "OK" : "MISSING"}`)
282
+ if (hasCommand("opencode")) run("opencode", ["--version"])
283
+ await status()
284
+ }
285
+
286
+ async function evaluate() {
287
+ const code = run(process.execPath, [path.join(packageRoot, "scripts", "eval-skills.mjs")])
288
+ if (code !== 0) process.exitCode = code
289
+ }
290
+
291
+ async function evaluateLive() {
292
+ const code = run(
293
+ process.execPath,
294
+ [path.join(packageRoot, "scripts", "eval-live.mjs"), ...args.slice(1)],
295
+ )
296
+ if (code !== 0) process.exitCode = code
297
+ }
298
+
299
+ function printJson(value) {
300
+ console.log(JSON.stringify(value, null, 2))
301
+ }
302
+
303
+ async function evaluateReport() {
304
+ const code = run(
305
+ process.execPath,
306
+ [path.join(packageRoot, "scripts", "eval-report.mjs"), ...args.slice(1)],
307
+ )
308
+ if (code !== 0) process.exitCode = code
309
+ }
310
+
311
+ async function inspectRepository() {
312
+ printJson(await repoMap(args[1] || process.cwd()))
313
+ }
314
+
315
+ async function inspectImpact() {
316
+ const query = args[1]
317
+ if (!query) {
318
+ console.error("Usage: ocskill impact <query> [dir]")
319
+ process.exitCode = 2
320
+ return
321
+ }
322
+ printJson(await impactMap(args[2] || process.cwd(), query))
323
+ }
324
+
325
+ async function inspectEvidence() {
326
+ printJson(await collectEvidence(args[1] || process.cwd()))
327
+ }
328
+
329
+ async function inspectWorkingTree() {
330
+ printJson(await checkWorkingTree(args[1] || process.cwd()))
331
+ }
332
+
333
+ async function inspectStack() {
334
+ printJson(await detectStack(args[1] || process.cwd()))
335
+ }
336
+
337
+ async function inspectTests() {
338
+ printJson(await detectTestCommands(args[1] || process.cwd()))
339
+ }
340
+
341
+ function optionValue(name) {
342
+ const index = args.indexOf(name)
343
+ return index >= 0 ? args[index + 1] : null
344
+ }
345
+
346
+ async function inspectRepoGraph() {
347
+ printJson(await buildRepoGraph(args[1] || process.cwd()))
348
+ }
349
+
350
+ 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()
353
+ printJson(reviewScope(root, base))
354
+ }
355
+
356
+ async function inspectVerificationPlan() {
357
+ printJson(await buildVerificationPlan(args[1] || process.cwd(), optionValue("--base")))
358
+ }
359
+
360
+ async function inspectTaskGraph() {
361
+ const file = args[1]
362
+ if (!file) {
363
+ console.error("Usage: ocskill task-graph <plan.json>")
364
+ process.exitCode = 2
365
+ return
366
+ }
367
+ try {
368
+ const plan = JSON.parse(readFileSync(path.resolve(file), "utf8"))
369
+ const analysis = analyzePlan(plan)
370
+ printJson(analysis)
371
+ if (!analysis.valid) process.exitCode = 1
372
+ } catch (error) {
373
+ console.error(error instanceof Error ? error.message : error)
374
+ process.exitCode = 1
375
+ }
376
+ }
377
+
378
+ async function inspectContextPack() {
379
+ const slug = args[1]
380
+ const taskID = args[2]
381
+ if (!slug || !taskID) {
382
+ console.error("Usage: ocskill context-pack <slug> <task-id> [dir]")
383
+ process.exitCode = 2
384
+ return
385
+ }
386
+ printJson(await contextPack(args[3] || process.cwd(), slug, taskID))
387
+ }
388
+
389
+ async function workControl() {
390
+ const action = args[1]
391
+ const slug = args[2]
392
+ 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> ...")
394
+ process.exitCode = 2
395
+ return
396
+ }
397
+
398
+ try {
399
+ if (action === "init") {
400
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
401
+ printJson(await initWork(root, slug, optionValue("--goal")))
402
+ return
403
+ }
404
+ if (action === "plan") {
405
+ const planFile = args[3]
406
+ const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
407
+ if (!planFile) throw new Error("Usage: ocskill work plan <slug> <plan.json> [dir]")
408
+ const result = await importPlan(root, slug, planFile)
409
+ printJson({ state: result.state, analysis: result.analysis, dir: result.paths.dir })
410
+ return
411
+ }
412
+ if (action === "status") {
413
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
414
+ printJson(await workStatus(root, slug))
415
+ return
416
+ }
417
+ if (action === "resume") {
418
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
419
+ printJson(await resumeWork(root, slug))
420
+ return
421
+ }
422
+ if (action === "approve-plan") {
423
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
424
+ printJson(await approvePlan(root, slug, optionValue("--evidence")))
425
+ return
426
+ }
427
+ if (action === "start") {
428
+ const taskID = args[3]
429
+ const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
430
+ if (!taskID) throw new Error("Usage: ocskill work start <slug> <task-id> [dir]")
431
+ printJson(await startTask(root, slug, taskID, {
432
+ leaseMs: Number.parseInt(optionValue("--lease-ms") || "0", 10) || undefined,
433
+ }))
434
+ return
435
+ }
436
+ if (action === "heartbeat") {
437
+ 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,
442
+ }))
443
+ return
444
+ }
445
+ if (action === "recover") {
446
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
447
+ printJson(await recoverStaleTasks(root, slug, { force: args.includes("--force") }))
448
+ return
449
+ }
450
+ if (action === "verify-command") {
451
+ const taskID = args[3]
452
+ const root = args[4] && args[4] !== "--" && !args[4].startsWith("--") ? args[4] : process.cwd()
453
+ const separator = args.indexOf("--")
454
+ const executable = separator >= 0 ? args[separator + 1] : null
455
+ 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...]")
458
+ }
459
+ const startedAt = new Date().toISOString()
460
+ const before = workspaceFingerprint(root)
461
+ const startedMs = Date.now()
462
+ const result = runCapture(executable, commandArgs, {
463
+ cwd: path.resolve(root),
464
+ maxBuffer: 8 * 1024 * 1024,
465
+ })
466
+ const receipt = createVerificationReceipt({
467
+ task: taskID,
468
+ runId: optionValue("--run-id"),
469
+ command: executable,
470
+ args: commandArgs,
471
+ cwd: path.resolve(root),
472
+ exitCode: result.status ?? 1,
473
+ startedAt,
474
+ finishedAt: new Date().toISOString(),
475
+ durationMs: Date.now() - startedMs,
476
+ stdout: result.stdout,
477
+ stderr: result.stderr,
478
+ workspaceBefore: before,
479
+ workspaceAfter: workspaceFingerprint(root),
480
+ })
481
+ 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
+ printJson({
487
+ receipt: recorded,
488
+ output: {
489
+ stdout: clip(result.stdout),
490
+ stderr: clip(result.stderr),
491
+ },
492
+ })
493
+ if ((result.status ?? 1) !== 0) process.exitCode = result.status ?? 1
494
+ return
495
+ }
496
+ if (action === "complete") {
497
+ 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
502
+ printJson(await completeTask(root, slug, taskID, {
503
+ evidence: optionValue("--evidence"),
504
+ report,
505
+ runId: optionValue("--run-id"),
506
+ }))
507
+ return
508
+ }
509
+ if (action === "fail") {
510
+ 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") }))
514
+ return
515
+ }
516
+ if (action === "decision") {
517
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
518
+ printJson(await addDecision(root, slug, optionValue("--text")))
519
+ return
520
+ }
521
+ if (action === "block") {
522
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
523
+ printJson(await addBlocker(root, slug, optionValue("--text")))
524
+ return
525
+ }
526
+ if (action === "unblock") {
527
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
528
+ printJson(await resolveBlocker(root, slug, optionValue("--text")))
529
+ return
530
+ }
531
+ 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
535
+ printJson(await recordIntegrationVerification(
536
+ root,
537
+ slug,
538
+ optionValue("--verdict"),
539
+ optionValue("--evidence"),
540
+ report,
541
+ ))
542
+ return
543
+ }
544
+ if (action === "finalize") {
545
+ const root = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
546
+ printJson(await finalizeWork(root, slug, optionValue("--evidence")))
547
+ return
548
+ }
549
+
550
+ throw new Error("Unknown work action: " + action)
551
+ } catch (error) {
552
+ console.error(error instanceof Error ? error.message : error)
553
+ if (error?.validation) printJson(error.validation)
554
+ process.exitCode = 1
555
+ }
556
+ }
557
+
558
+ async function modelPolicy() {
559
+ const role = args[1]
560
+ if (!role) {
561
+ console.error("Usage: ocskill model-policy <role> [--attempt N]")
562
+ process.exitCode = 2
563
+ return
564
+ }
565
+ const attempt = Number.parseInt(optionValue("--attempt") || "1", 10)
566
+ const policy = await readModelPolicy(getConfigDir())
567
+ const normalizedAttempt = Number.isInteger(attempt) && attempt > 0 ? attempt : 1
568
+ const taskText = optionValue("--text")
569
+ if (taskText) {
570
+ printJson(resolveAdaptiveModel(role, normalizedAttempt, classifyEngineeringTask(taskText), policy))
571
+ return
572
+ }
573
+ printJson(resolveModel(role, normalizedAttempt, policy))
574
+ }
575
+
576
+
577
+ async function modelsControl() {
578
+ const action = args[1] || "status"
579
+ let policy = await readModelPolicy(getConfigDir())
580
+
581
+ if (action === "status") {
582
+ printJson(policy)
583
+ return
584
+ }
585
+ if (action === "on" || action === "off") {
586
+ policy = await writeModelPolicy(getConfigDir(), { enabled: action === "on" })
587
+ printJson(policy)
588
+ console.log("[ocskill] Run 'ocskill install' to rewrite managed agent frontmatter.")
589
+ return
590
+ }
591
+ if (action === "set") {
592
+ const tier = args[2]
593
+ const model = args[3]
594
+ if (!["light", "standard", "heavy"].includes(tier) || !validateModelID(model)) {
595
+ console.error("Usage: ocskill models set <light|standard|heavy> <provider/model[#variant]>")
596
+ process.exitCode = 2
597
+ return
598
+ }
599
+ policy = await writeModelPolicy(getConfigDir(), {
600
+ enabled: true,
601
+ tiers: { [tier]: model },
602
+ })
603
+ printJson(policy)
604
+ console.log("[ocskill] Run 'ocskill install' to apply model mappings to managed agents.")
605
+ return
606
+ }
607
+ if (action === "role") {
608
+ const role = args[2]
609
+ const tier = args[3]
610
+ if (!role || !["light", "standard", "heavy"].includes(tier)) {
611
+ console.error("Usage: ocskill models role <role> <light|standard|heavy>")
612
+ process.exitCode = 2
613
+ return
614
+ }
615
+ policy = await writeModelPolicy(getConfigDir(), {
616
+ roleTiers: { [role]: tier },
617
+ })
618
+ printJson(policy)
619
+ console.log("[ocskill] Run 'ocskill install' to apply role-tier changes.")
620
+ return
621
+ }
622
+
623
+ console.error("Usage: ocskill models <status|on|off|set|role> ...")
624
+ process.exitCode = 2
625
+ }
626
+
627
+ async function routerControl() {
628
+ const action = args[1] || "status"
629
+ const maxIndex = args.indexOf("--max")
630
+ const maxValue = maxIndex >= 0 ? Number.parseInt(args[maxIndex + 1] || "", 10) : null
631
+
632
+ if (!["status", "on", "off"].includes(action)) {
633
+ console.error("Usage: ocskill router [status|on|off] [--max 1..6]")
634
+ process.exitCode = 2
635
+ return
636
+ }
637
+ if (maxIndex >= 0 && (!Number.isInteger(maxValue) || maxValue < 1 || maxValue > 6)) {
638
+ console.error("--max must be an integer from 1 through 6")
639
+ process.exitCode = 2
640
+ return
641
+ }
642
+
643
+ let config = await readRouterConfig(getConfigDir())
644
+ if (action !== "status" || maxValue !== null) {
645
+ config = await writeRouterConfig(getConfigDir(), {
646
+ enabled: action === "on" ? true : action === "off" ? false : config.enabled,
647
+ ...(maxValue !== null ? { maxSkills: maxValue } : {}),
648
+ })
649
+ }
650
+
651
+ const installed = await getStatus()
652
+ const runtimeAvailable =
653
+ installed.installed &&
654
+ Number(installed.openCodeMajor) >= 2 &&
655
+ (installed.plugins || []).includes("ues-router/index.js") &&
656
+ installed.pluginsPresent === installed.plugins.length
657
+
658
+ console.log(`[ocskill] Router preference: ${config.enabled ? "ON" : "OFF"}`)
659
+ console.log(`[ocskill] Router runtime: ${runtimeAvailable ? "AVAILABLE" : "UNAVAILABLE"}`)
660
+ if (!runtimeAvailable) {
661
+ console.log("[ocskill] Runtime routing requires OpenCode 2.x followed by 'ocskill install'.")
662
+ }
663
+ console.log(`[ocskill] Max automatic skills: ${config.maxSkills}`)
664
+ console.log(`[ocskill] Config: ${config.file}`)
665
+ }
666
+
667
+
668
+ async function taskPolicyControl() {
669
+ const text = args.slice(1).join(" ").trim()
670
+ if (!text) {
671
+ console.error("Usage: ocskill task-policy <text>")
672
+ process.exitCode = 2
673
+ return
674
+ }
675
+ printJson(classifyEngineeringTask(text))
676
+ }
677
+
678
+ async function sandboxControl() {
679
+ const action = args[1] || "list"
680
+ try {
681
+ if (action === "list") {
682
+ printJson(listTaskSandboxes(args[2] || process.cwd()))
683
+ return
684
+ }
685
+ if (action === "create") {
686
+ const slug = args[2]
687
+ const taskID = args[3]
688
+ const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
689
+ if (!slug || !taskID) throw new Error("Usage: ocskill sandbox create <slug> <task-id> [dir]")
690
+ printJson(await createTaskSandbox(root, slug, taskID))
691
+ return
692
+ }
693
+ if (action === "remove") {
694
+ 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") }))
698
+ return
699
+ }
700
+ throw new Error("Usage: ocskill sandbox <list|create|remove> ...")
701
+ } catch (error) {
702
+ console.error(error instanceof Error ? error.message : error)
703
+ process.exitCode = 1
704
+ }
705
+ }
706
+
707
+ async function learningControl() {
708
+ const action = args[1] || "status"
709
+ const root = args[2] && !args[2].startsWith("--") ? args[2] : process.cwd()
710
+ try {
711
+ if (action === "status") {
712
+ printJson(await readLearningState(root))
713
+ return
714
+ }
715
+ if (action === "analyze") {
716
+ const evalDir = optionValue("--eval-dir") || path.join(path.resolve(root), ".ues-evals")
717
+ const analysis = await analyzeEvalTraces(evalDir)
718
+ const state = await saveLearningAnalysis(root, analysis)
719
+ printJson({ analysis, state })
720
+ return
721
+ }
722
+ if (action === "accept") {
723
+ const id = args[2]
724
+ const acceptRoot = args[3] && !args[3].startsWith("--") ? args[3] : process.cwd()
725
+ if (!id) throw new Error("Usage: ocskill learn accept <proposal-id> [dir]")
726
+ printJson(await acceptLearning(acceptRoot, id))
727
+ return
728
+ }
729
+ throw new Error("Usage: ocskill learn <status|analyze|accept> ...")
730
+ } catch (error) {
731
+ console.error(error instanceof Error ? error.message : error)
732
+ process.exitCode = 1
733
+ }
734
+ }
735
+
736
+ async function hermesControl() {
737
+ const action = args[1] || "status"
738
+ if (action === "status") {
739
+ printJson(hermesStatus())
740
+ return
741
+ }
742
+ if (action === "prompt" || action === "exec") {
743
+ const slug = args[2]
744
+ const taskID = args[3]
745
+ const root = args[4] && !args[4].startsWith("--") ? args[4] : process.cwd()
746
+ if (!slug || !taskID) {
747
+ console.error("Usage: ocskill hermes <prompt|exec> <slug> <task-id> [dir]")
748
+ process.exitCode = 2
749
+ return
750
+ }
751
+ const prompt = buildHermesDelegationPrompt(await contextPack(root, slug, taskID))
752
+ if (action === "prompt") {
753
+ console.log(prompt)
754
+ return
755
+ }
756
+
757
+ const status = hermesStatus()
758
+ if (!status.available) {
759
+ console.error(status.error || "Hermes CLI is unavailable")
760
+ process.exitCode = 1
761
+ return
762
+ }
763
+ const result = runCapture("hermes", hermesOneShotArgs(prompt), {
764
+ cwd: path.resolve(root),
765
+ maxBuffer: 8 * 1024 * 1024,
766
+ })
767
+ if (result.stdout) process.stdout.write(result.stdout)
768
+ if (result.stderr) process.stderr.write(result.stderr)
769
+ if ((result.status ?? 1) !== 0) process.exitCode = result.status ?? 1
770
+ return
771
+ }
772
+ console.error("Usage: ocskill hermes <status|prompt|exec> ...")
773
+ process.exitCode = 2
774
+ }
775
+
776
+ async function dashboardControl() {
777
+ const forwarded = args.slice(1)
778
+ const code = run(process.execPath, [path.join(packageRoot, "scripts", "control-center.mjs"), ...forwarded])
779
+ if (code !== 0) process.exitCode = code
780
+ }
781
+
782
+ async function update() {
783
+ if (!hasCommand("npm")) {
784
+ console.error("[ocskill] npm is required to update this global package.")
785
+ process.exitCode = 1
786
+ return
787
+ }
788
+
789
+ const currentVersion = await getPackageVersion()
790
+ let resolved
791
+ try {
792
+ resolved = resolveLatestPublishedVersion({
793
+ runCapture,
794
+ packageName: PACKAGE_NAME,
795
+ cwd: os.homedir(),
796
+ })
797
+ } catch (error) {
798
+ 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)
800
+ process.exitCode = error?.exitCode || 1
801
+ return
802
+ }
803
+
804
+ const latestVersion = resolved.version
805
+
806
+ let compared
807
+ try {
808
+ compared = compareVersions(latestVersion, currentVersion)
809
+ } catch (error) {
810
+ console.error("[ocskill] npm returned a version that could not be compared safely; refusing update.")
811
+ console.error(error instanceof Error ? error.message : error)
812
+ process.exitCode = 1
813
+ return
814
+ }
815
+
816
+ if (compared < 0) {
817
+ console.error(
818
+ `[ocskill] Registry latest is v${latestVersion}, older than installed v${currentVersion}; refusing downgrade.`,
819
+ )
820
+ process.exitCode = 1
821
+ return
822
+ }
823
+
824
+ if (compared === 0) {
825
+ console.log(`[ocskill] Already on latest npm version v${currentVersion}; re-syncing current resources.`)
826
+ await install()
827
+ return
828
+ }
829
+
830
+ console.log(`[ocskill] Updating ${PACKAGE_NAME} from v${currentVersion} to v${latestVersion}...`)
831
+ const code = run(
832
+ "npm",
833
+ ["install", "-g", `${PACKAGE_NAME}@${latestVersion}`, "--ignore-scripts"],
834
+ { cwd: os.homedir() },
835
+ )
836
+ if (code !== 0) {
837
+ process.exitCode = code
838
+ return
839
+ }
840
+
841
+ console.log("[ocskill] Re-syncing resources after update...")
842
+ const syncCode = run("ocskill", ["install"])
843
+ if (syncCode !== 0) process.exitCode = syncCode
844
+ }
845
+
846
+ async function remove() {
847
+ const removed = await removeResources({ force })
848
+ if (removed.stateError) {
849
+ for (const warning of removed.warnings || []) {
850
+ console.warn(`[ocskill] WARNING: ${warning}`)
851
+ }
852
+ console.error(`[ocskill] ERROR: ${removed.stateError}`)
853
+ process.exitCode = 1
854
+ return
855
+ }
856
+ console.log(
857
+ `[ocskill] Removed ${removed.skills} skills, ${removed.commands} commands, ${removed.agents} subagents and ${removed.plugins || 0} plugins.`,
858
+ )
859
+ for (const warning of removed.warnings || []) {
860
+ console.warn(`[ocskill] WARNING: ${warning}`)
861
+ }
862
+
863
+ if (!hasCommand("npm")) return
864
+
865
+ const code = run(
866
+ "npm",
867
+ ["uninstall", "-g", PACKAGE_NAME, "--ignore-scripts"],
868
+ { cwd: os.homedir() },
869
+ )
870
+ if (code !== 0) process.exitCode = code
871
+ }
872
+
873
+ switch (command) {
874
+ case "install":
875
+ case "sync":
876
+ await install()
877
+ break
878
+ case "status":
879
+ await status()
880
+ break
881
+ case "doctor":
882
+ await doctor()
883
+ break
884
+ case "eval":
885
+ case "evals":
886
+ await evaluate()
887
+ break
888
+ case "eval-live":
889
+ await evaluateLive()
890
+ break
891
+ case "eval-report":
892
+ await evaluateReport()
893
+ break
894
+ case "inspect":
895
+ await inspectRepository()
896
+ break
897
+ case "impact":
898
+ await inspectImpact()
899
+ break
900
+ case "evidence":
901
+ await inspectEvidence()
902
+ break
903
+ case "working-tree":
904
+ await inspectWorkingTree()
905
+ break
906
+ case "repo-graph":
907
+ await inspectRepoGraph()
908
+ break
909
+ case "review-scope":
910
+ await inspectReviewScope()
911
+ break
912
+ case "verification-plan":
913
+ await inspectVerificationPlan()
914
+ break
915
+ case "task-graph":
916
+ await inspectTaskGraph()
917
+ break
918
+ case "context-pack":
919
+ await inspectContextPack()
920
+ break
921
+ case "work":
922
+ await workControl()
923
+ break
924
+ case "model-policy":
925
+ await modelPolicy()
926
+ break
927
+ case "task-policy":
928
+ await taskPolicyControl()
929
+ break
930
+ case "sandbox":
931
+ await sandboxControl()
932
+ break
933
+ case "learn":
934
+ await learningControl()
935
+ break
936
+ case "hermes":
937
+ await hermesControl()
938
+ break
939
+ case "dashboard":
940
+ await dashboardControl()
941
+ break
942
+ case "models":
943
+ await modelsControl()
944
+ break
945
+ case "detect-stack":
946
+ await inspectStack()
947
+ break
948
+ case "detect-tests":
949
+ await inspectTests()
950
+ break
951
+ case "router":
952
+ await routerControl()
953
+ break
954
+ case "update":
955
+ await update()
956
+ break
957
+ case "remove":
958
+ case "uninstall":
959
+ await remove()
960
+ break
961
+ case "version":
962
+ case "--version":
963
+ case "-v":
964
+ console.log(await getPackageVersion())
965
+ break
966
+ case "help":
967
+ case "--help":
968
+ case "-h":
969
+ printHelp()
970
+ break
971
+ default:
972
+ console.error(`Unknown command: ${command}`)
973
+ printHelp()
974
+ process.exitCode = 1
975
+ }