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,262 @@
1
+ import path from "node:path"
2
+
3
+ const VALID_RISKS = new Set(["low", "medium", "high", "critical"])
4
+
5
+ function unsafeRepoPath(value) {
6
+ const raw = String(value || "").replaceAll("\\", "/")
7
+ if (!raw) return false
8
+ if (path.posix.isAbsolute(raw) || /^[A-Za-z]:\//.test(raw)) return true
9
+ const normalized = path.posix.normalize(raw).replace(/^\.\//, "")
10
+ return normalized === ".." || normalized.startsWith("../")
11
+ }
12
+
13
+ function normalizeFile(value) {
14
+ if (unsafeRepoPath(value)) return ""
15
+ const normalized = path.posix
16
+ .normalize(String(value || "").replaceAll("\\", "/"))
17
+ .replace(/^\.\//, "")
18
+ return normalized === "." ? "" : normalized
19
+ }
20
+
21
+ export function taskFiles(task) {
22
+ const files = task?.files
23
+ if (Array.isArray(files)) return [...new Set(files.map(normalizeFile).filter(Boolean))].sort()
24
+ if (!files || typeof files !== "object") return []
25
+
26
+ const values = []
27
+ for (const key of ["create", "modify", "test", "delete", "read"]) {
28
+ if (!Array.isArray(files[key])) continue
29
+ values.push(...files[key])
30
+ }
31
+ return [...new Set(values.map(normalizeFile).filter(Boolean))].sort()
32
+ }
33
+
34
+ export function taskWriteFiles(task) {
35
+ const files = task?.files
36
+ if (Array.isArray(files)) return taskFiles(task)
37
+ if (!files || typeof files !== "object") return []
38
+ const values = []
39
+ for (const key of ["create", "modify", "test", "delete"]) {
40
+ if (Array.isArray(files[key])) values.push(...files[key])
41
+ }
42
+ return [...new Set(values.map(normalizeFile).filter(Boolean))].sort()
43
+ }
44
+
45
+ export function taskReadFiles(task) {
46
+ const files = task?.files
47
+ if (!files || Array.isArray(files) || typeof files !== "object") return []
48
+ return [...new Set((files.read || []).map(normalizeFile).filter(Boolean))].sort()
49
+ }
50
+
51
+ function taskMap(plan) {
52
+ return new Map((plan?.tasks || []).map((task) => [task.id, task]))
53
+ }
54
+
55
+ export function validatePlan(plan) {
56
+ const errors = []
57
+ const warnings = []
58
+
59
+ if (!plan || typeof plan !== "object" || Array.isArray(plan)) {
60
+ return { valid: false, errors: ["plan must be a JSON object"], warnings }
61
+ }
62
+ if (plan.schemaVersion !== 1) errors.push("schemaVersion must be 1")
63
+ if (!String(plan.goal || "").trim()) errors.push("goal must be a non-empty string")
64
+ if (!Array.isArray(plan.tasks) || plan.tasks.length === 0) {
65
+ errors.push("tasks must be a non-empty array")
66
+ return { valid: false, errors, warnings }
67
+ }
68
+
69
+ const ids = new Set()
70
+ for (const [index, task] of plan.tasks.entries()) {
71
+ const prefix = `tasks[${index}]`
72
+ if (!task || typeof task !== "object" || Array.isArray(task)) {
73
+ errors.push(`${prefix} must be an object`)
74
+ continue
75
+ }
76
+
77
+ const id = String(task.id || "").trim()
78
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(id)) {
79
+ errors.push(`${prefix}.id must use letters, numbers, dot, underscore or dash`)
80
+ } else if (ids.has(id)) {
81
+ errors.push(`duplicate task id: ${id}`)
82
+ } else {
83
+ ids.add(id)
84
+ }
85
+
86
+ if (!String(task.title || "").trim()) errors.push(`${prefix}.title is required`)
87
+ if (!String(task.summary || "").trim()) warnings.push(`${id || prefix}: summary is empty`)
88
+
89
+ const deps = task.dependsOn ?? []
90
+ if (!Array.isArray(deps)) errors.push(`${id || prefix}.dependsOn must be an array`)
91
+ else if (deps.includes(id)) errors.push(`${id}: task cannot depend on itself`)
92
+
93
+ if (!Array.isArray(task.acceptance) || task.acceptance.length === 0) {
94
+ errors.push(`${id || prefix}: acceptance must contain at least one observable criterion`)
95
+ }
96
+ if (!Array.isArray(task.verification) || task.verification.length === 0) {
97
+ errors.push(`${id || prefix}: verification must contain at least one concrete check`)
98
+ }
99
+
100
+ const risk = task.risk ?? "medium"
101
+ if (!VALID_RISKS.has(risk)) errors.push(`${id || prefix}: invalid risk '${risk}'`)
102
+
103
+ const rawFiles = task.files
104
+ const rawValues = Array.isArray(rawFiles)
105
+ ? rawFiles
106
+ : rawFiles && typeof rawFiles === "object"
107
+ ? ["create", "modify", "test", "delete", "read"].flatMap((key) => Array.isArray(rawFiles[key]) ? rawFiles[key] : [])
108
+ : []
109
+ for (const file of rawValues) {
110
+ if (unsafeRepoPath(file)) errors.push(`${id || prefix}: file scope must stay inside the repository: ${file}`)
111
+ }
112
+
113
+ const files = taskFiles(task)
114
+ if (files.length === 0) warnings.push(`${id || prefix}: no files declared; parallel scheduling will be conservative`)
115
+ }
116
+
117
+ const map = taskMap(plan)
118
+ for (const task of plan.tasks) {
119
+ if (!task?.id || !Array.isArray(task.dependsOn)) continue
120
+ for (const dep of task.dependsOn) {
121
+ if (!map.has(dep)) errors.push(`${task.id}: unknown dependency '${dep}'`)
122
+ }
123
+ }
124
+
125
+ const cycle = findCycle(plan)
126
+ if (cycle.length) errors.push(`dependency cycle: ${cycle.join(" -> ")}`)
127
+
128
+ return { valid: errors.length === 0, errors, warnings }
129
+ }
130
+
131
+ function findCycle(plan) {
132
+ const map = taskMap(plan)
133
+ const visiting = new Set()
134
+ const visited = new Set()
135
+ const stack = []
136
+
137
+ function visit(id) {
138
+ if (visiting.has(id)) {
139
+ const start = stack.indexOf(id)
140
+ return [...stack.slice(start), id]
141
+ }
142
+ if (visited.has(id)) return []
143
+
144
+ visiting.add(id)
145
+ stack.push(id)
146
+ for (const dep of map.get(id)?.dependsOn || []) {
147
+ const cycle = visit(dep)
148
+ if (cycle.length) return cycle
149
+ }
150
+ stack.pop()
151
+ visiting.delete(id)
152
+ visited.add(id)
153
+ return []
154
+ }
155
+
156
+ for (const id of map.keys()) {
157
+ const cycle = visit(id)
158
+ if (cycle.length) return cycle
159
+ }
160
+ return []
161
+ }
162
+
163
+ function overlaps(taskA, taskB) {
164
+ const allA = taskFiles(taskA)
165
+ const allB = taskFiles(taskB)
166
+ if (allA.length === 0 || allB.length === 0) return true
167
+
168
+ const writeA = new Set(taskWriteFiles(taskA))
169
+ const writeB = new Set(taskWriteFiles(taskB))
170
+ const readA = new Set(taskReadFiles(taskA))
171
+ const readB = new Set(taskReadFiles(taskB))
172
+
173
+ if ([...writeA].some((file) => writeB.has(file) || readB.has(file))) return true
174
+ if ([...writeB].some((file) => readA.has(file))) return true
175
+ return false
176
+ }
177
+
178
+ export function computeTopologicalWaves(plan) {
179
+ const validation = validatePlan(plan)
180
+ if (!validation.valid) {
181
+ const error = new Error("invalid task plan")
182
+ error.validation = validation
183
+ throw error
184
+ }
185
+
186
+ const map = taskMap(plan)
187
+ const complete = new Set()
188
+ const remaining = new Set(map.keys())
189
+ const waves = []
190
+
191
+ while (remaining.size) {
192
+ const ready = [...remaining]
193
+ .filter((id) => (map.get(id).dependsOn || []).every((dep) => complete.has(dep)))
194
+ .sort()
195
+
196
+ if (ready.length === 0) throw new Error("task graph cannot make progress")
197
+ waves.push(ready)
198
+ for (const id of ready) {
199
+ remaining.delete(id)
200
+ complete.add(id)
201
+ }
202
+ }
203
+ return waves
204
+ }
205
+
206
+ export function computeSafeWaves(plan) {
207
+ const validation = validatePlan(plan)
208
+ if (!validation.valid) {
209
+ const error = new Error("invalid task plan")
210
+ error.validation = validation
211
+ throw error
212
+ }
213
+
214
+ const map = taskMap(plan)
215
+ const complete = new Set()
216
+ const remaining = new Set(map.keys())
217
+ const waves = []
218
+ const serialized = []
219
+
220
+ while (remaining.size) {
221
+ const ready = [...remaining]
222
+ .filter((id) => (map.get(id).dependsOn || []).every((dep) => complete.has(dep)))
223
+ .sort()
224
+
225
+ if (ready.length === 0) throw new Error("task graph cannot make progress")
226
+
227
+ const selected = []
228
+ for (const id of ready) {
229
+ const task = map.get(id)
230
+ const conflict = selected.find((otherID) => overlaps(task, map.get(otherID)))
231
+ if (conflict) {
232
+ serialized.push({ task: id, conflictsWith: conflict, reason: "declared write/read conflict or unknown file scope" })
233
+ continue
234
+ }
235
+ selected.push(id)
236
+ }
237
+
238
+ if (selected.length === 0) selected.push(ready[0])
239
+ waves.push(selected)
240
+ for (const id of selected) {
241
+ remaining.delete(id)
242
+ complete.add(id)
243
+ }
244
+ }
245
+
246
+ return { waves, serialized }
247
+ }
248
+
249
+ export function analyzePlan(plan) {
250
+ const validation = validatePlan(plan)
251
+ if (!validation.valid) return { ...validation, topologicalWaves: [], safeWaves: [], serialized: [] }
252
+
253
+ const topologicalWaves = computeTopologicalWaves(plan)
254
+ const safe = computeSafeWaves(plan)
255
+ return {
256
+ ...validation,
257
+ topologicalWaves,
258
+ safeWaves: safe.waves,
259
+ serialized: safe.serialized,
260
+ taskCount: plan.tasks.length,
261
+ }
262
+ }
@@ -0,0 +1,43 @@
1
+ function parseJsonString(stdout) {
2
+ try {
3
+ const value = JSON.parse(String(stdout || "").trim())
4
+ return typeof value === "string" && value ? value : null
5
+ } catch {
6
+ return null
7
+ }
8
+ }
9
+
10
+ function parseLatestTag(stdout) {
11
+ for (const line of String(stdout || "").split(/\r?\n/)) {
12
+ const match = line.trim().match(/^latest:\s*(\S+)$/)
13
+ if (match) return match[1]
14
+ }
15
+ return null
16
+ }
17
+
18
+ export function resolveLatestPublishedVersion({ runCapture, packageName, cwd }) {
19
+ const direct = runCapture(
20
+ "npm",
21
+ ["view", `${packageName}@latest`, "version", "--json"],
22
+ { cwd },
23
+ )
24
+ if (direct.status === 0) {
25
+ const version = parseJsonString(direct.stdout)
26
+ if (version) return { version, source: "view@latest" }
27
+ }
28
+
29
+ const tags = runCapture("npm", ["dist-tag", "ls", packageName], { cwd })
30
+ if (tags.status === 0) {
31
+ const version = parseLatestTag(tags.stdout)
32
+ if (version) return { version, source: "dist-tag" }
33
+ }
34
+
35
+ const detail = [direct.stderr, direct.stdout, tags.stderr, tags.stdout]
36
+ .map((value) => String(value || "").trim())
37
+ .filter(Boolean)
38
+ .join("\n")
39
+
40
+ const error = new Error("Could not resolve npm latest dist-tag safely." + (detail ? "\n" + detail : ""))
41
+ error.exitCode = direct.status || tags.status || 1
42
+ throw error
43
+ }
@@ -0,0 +1,52 @@
1
+ import path from "node:path"
2
+ import { detectStack, detectTestCommands, checkWorkingTree } from "./repo-inspect.mjs"
3
+ import { reviewScope } from "./review-scope.mjs"
4
+
5
+ export async function buildVerificationPlan(root = process.cwd(), base = null) {
6
+ root = path.resolve(root)
7
+ const [stack, commands, workingTree] = await Promise.all([
8
+ detectStack(root),
9
+ detectTestCommands(root),
10
+ checkWorkingTree(root),
11
+ ])
12
+ const scope = reviewScope(root, base)
13
+
14
+ const recommended = []
15
+ const add = (kind, command, reason) => {
16
+ if (!command || recommended.some((item) => item.command === command)) return
17
+ recommended.push({ kind, command, reason })
18
+ }
19
+
20
+ for (const item of commands) {
21
+ if (["test", "typecheck", "check", "lint", "build", "analyze", "ci"].includes(item.kind)) {
22
+ add(item.kind, item.command, "project-native " + item.kind + " command from " + item.source)
23
+ }
24
+ }
25
+
26
+ if (scope.git && ["high", "critical"].includes(scope.overallRisk)) {
27
+ add("diff-review", "git diff --check", "high-risk changes require whitespace/conflict-marker sanity")
28
+ }
29
+
30
+ const acceptancePrompts = []
31
+ if (scope.git) {
32
+ for (const file of scope.files) {
33
+ for (const reason of file.risk.reasons) {
34
+ if (reason === "auth/security") acceptancePrompts.push("Verify denied/unauthorized cases, not only the happy path.")
35
+ if (reason === "payment") acceptancePrompts.push("Verify idempotency/retry/state-transition behavior with negative cases.")
36
+ if (reason === "persistence/schema") acceptancePrompts.push("Verify migration compatibility, existing rows, rollback/roll-forward behavior.")
37
+ if (reason === "public/interface") acceptancePrompts.push("Verify producers and consumers of changed contracts.")
38
+ if (reason === "delivery/infrastructure") acceptancePrompts.push("Verify configuration/deployment behavior in a representative environment.")
39
+ }
40
+ }
41
+ }
42
+
43
+ return {
44
+ schemaVersion: 1,
45
+ root,
46
+ stack,
47
+ workingTree,
48
+ scope,
49
+ recommended,
50
+ acceptancePrompts: [...new Set(acceptancePrompts)],
51
+ }
52
+ }
@@ -0,0 +1,47 @@
1
+ function parse(value) {
2
+ const match = String(value).trim().match(
3
+ /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/,
4
+ )
5
+ if (!match) throw new Error(`Invalid semantic version: ${value}`)
6
+
7
+ return {
8
+ core: [Number(match[1]), Number(match[2]), Number(match[3])],
9
+ prerelease: match[4] ? match[4].split(".") : [],
10
+ }
11
+ }
12
+
13
+ function compareIdentifier(left, right) {
14
+ const leftNumber = /^\d+$/.test(left) ? Number(left) : null
15
+ const rightNumber = /^\d+$/.test(right) ? Number(right) : null
16
+
17
+ if (leftNumber !== null && rightNumber !== null) return Math.sign(leftNumber - rightNumber)
18
+ if (leftNumber !== null) return -1
19
+ if (rightNumber !== null) return 1
20
+ return left === right ? 0 : left < right ? -1 : 1
21
+ }
22
+
23
+ export function compareVersions(left, right) {
24
+ const a = parse(left)
25
+ const b = parse(right)
26
+
27
+ for (let index = 0; index < 3; index += 1) {
28
+ if (a.core[index] !== b.core[index]) {
29
+ return Math.sign(a.core[index] - b.core[index])
30
+ }
31
+ }
32
+
33
+ if (a.prerelease.length === 0 && b.prerelease.length === 0) return 0
34
+ if (a.prerelease.length === 0) return 1
35
+ if (b.prerelease.length === 0) return -1
36
+
37
+ const length = Math.max(a.prerelease.length, b.prerelease.length)
38
+ for (let index = 0; index < length; index += 1) {
39
+ if (a.prerelease[index] === undefined) return -1
40
+ if (b.prerelease[index] === undefined) return 1
41
+
42
+ const compared = compareIdentifier(a.prerelease[index], b.prerelease[index])
43
+ if (compared !== 0) return compared
44
+ }
45
+
46
+ return 0
47
+ }
@@ -0,0 +1,45 @@
1
+ import { createHash } from "node:crypto"
2
+ import { readFile, readdir, stat } from "node:fs/promises"
3
+ import path from "node:path"
4
+
5
+ const SKIP = new Set([".git", "node_modules", ".next", "dist", "build", "coverage"])
6
+
7
+ async function walk(root) {
8
+ const files = []
9
+ async function visit(dir) {
10
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => [])
11
+ for (const entry of entries) {
12
+ if (SKIP.has(entry.name)) continue
13
+ const full = path.join(dir, entry.name)
14
+ if (entry.isDirectory()) await visit(full)
15
+ else if (entry.isFile()) files.push(full)
16
+ }
17
+ }
18
+ await visit(root)
19
+ return files
20
+ }
21
+
22
+ export async function snapshotWorkspace(root) {
23
+ const snapshot = {}
24
+ for (const file of await walk(root)) {
25
+ const info = await stat(file).catch(() => null)
26
+ if (!info || info.size > 2 * 1024 * 1024) continue
27
+ const bytes = await readFile(file)
28
+ snapshot[path.relative(root, file)] = {
29
+ size: bytes.length,
30
+ sha256: createHash("sha256").update(bytes).digest("hex"),
31
+ }
32
+ }
33
+ return snapshot
34
+ }
35
+
36
+ export function diffWorkspaceSnapshots(before, after) {
37
+ const paths = new Set([...Object.keys(before), ...Object.keys(after)])
38
+ const changed = []
39
+ for (const file of [...paths].sort()) {
40
+ if (!before[file]) changed.push({ path: file, change: "added" })
41
+ else if (!after[file]) changed.push({ path: file, change: "removed" })
42
+ else if (before[file].sha256 !== after[file].sha256) changed.push({ path: file, change: "modified" })
43
+ }
44
+ return changed
45
+ }
@@ -0,0 +1,59 @@
1
+ import { existsSync } from "node:fs"
2
+ import { mkdir, rm } from "node:fs/promises"
3
+ import { spawnSync } from "node:child_process"
4
+ import path from "node:path"
5
+
6
+ function git(root, args) {
7
+ return spawnSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 })
8
+ }
9
+
10
+ function safeName(value) {
11
+ return String(value || "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "")
12
+ }
13
+
14
+ export async function createTaskSandbox(root, slug, taskID, options = {}) {
15
+ root = path.resolve(root)
16
+ const base = options.baseDir
17
+ ? path.resolve(options.baseDir)
18
+ : path.join(path.dirname(root), "." + path.basename(root) + ".ues-sandboxes")
19
+ const name = safeName(slug + "-" + taskID)
20
+ if (!name) throw new Error("sandbox name is empty")
21
+ const dir = path.join(base, name)
22
+ if (existsSync(dir)) throw new Error("sandbox already exists: " + dir)
23
+
24
+ await mkdir(base, { recursive: true })
25
+ const branch = options.branch || "ues/" + name
26
+ const startPoint = options.startPoint || "HEAD"
27
+ const result = git(root, ["worktree", "add", "-b", branch, dir, startPoint])
28
+ if (result.status !== 0) throw new Error((result.stderr || result.stdout || "git worktree add failed").trim())
29
+
30
+ return { schemaVersion: 1, root, dir, branch, slug, taskID, startPoint }
31
+ }
32
+
33
+ export async function removeTaskSandbox(root, dir, options = {}) {
34
+ root = path.resolve(root)
35
+ dir = path.resolve(dir)
36
+ const result = git(root, ["worktree", "remove", ...(options.force ? ["--force"] : []), dir])
37
+ if (result.status !== 0 && existsSync(dir)) {
38
+ if (options.force) await rm(dir, { recursive: true, force: true })
39
+ else throw new Error((result.stderr || result.stdout || "git worktree remove failed").trim())
40
+ }
41
+ git(root, ["worktree", "prune"])
42
+ return { removed: !existsSync(dir), dir }
43
+ }
44
+
45
+ export function listTaskSandboxes(root) {
46
+ const result = git(path.resolve(root), ["worktree", "list", "--porcelain"])
47
+ if (result.status !== 0) throw new Error((result.stderr || result.stdout || "git worktree list failed").trim())
48
+ const items = []
49
+ let current = null
50
+ for (const line of result.stdout.split(/\r?\n/)) {
51
+ if (line.startsWith("worktree ")) {
52
+ if (current) items.push(current)
53
+ current = { path: line.slice(9) }
54
+ } else if (current && line.startsWith("HEAD ")) current.head = line.slice(5)
55
+ else if (current && line.startsWith("branch ")) current.branch = line.slice(7)
56
+ }
57
+ if (current) items.push(current)
58
+ return items
59
+ }
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "opencode-agent-skill",
3
+ "version": "7.7.0",
4
+ "description": "Adaptive crash-safe engineering runtime, durable orchestration, structured evidence, learning loop and control center for OpenCode",
5
+ "type": "module",
6
+ "bin": {
7
+ "ocskill": "bin/ocskill.mjs"
8
+ },
9
+ "files": [
10
+ "bin/ocskill.mjs",
11
+ "lib/",
12
+ "scripts/",
13
+ "global-config/",
14
+ "evals/",
15
+ "docs/",
16
+ "README.md",
17
+ "CHANGELOG.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "syntax": "node scripts/syntax-check.mjs",
22
+ "validate": "node scripts/validate.mjs",
23
+ "evals": "node scripts/eval-skills.mjs",
24
+ "evals:live:validate": "node scripts/validate-live-suite.mjs",
25
+ "test": "node --test test/*.test.mjs",
26
+ "ci": "npm run syntax && npm run validate && npm run evals && npm run evals:router && npm run evals:live:validate && npm run evals:long:validate && npm test && npm pack --dry-run && npm run smoke:pack",
27
+ "postinstall": "node scripts/install.mjs",
28
+ "preuninstall": "node scripts/uninstall.mjs",
29
+ "prepublishOnly": "npm run ci",
30
+ "evals:live": "node scripts/eval-live.mjs",
31
+ "smoke:pack": "node scripts/smoke-packed-install.mjs",
32
+ "inspect:stack": "node scripts/detect-stack.mjs",
33
+ "inspect:tests": "node scripts/detect-test-commands.mjs",
34
+ "inspect:repo": "node scripts/repo-map.mjs",
35
+ "inspect:impact": "node scripts/impact-map.mjs",
36
+ "inspect:evidence": "node scripts/collect-evidence.mjs",
37
+ "inspect:git": "node scripts/check-working-tree.mjs",
38
+ "evals:report": "node scripts/eval-report.mjs",
39
+ "evals:router": "node scripts/eval-router.mjs",
40
+ "evals:long:validate": "node scripts/validate-live-suite.mjs --suite long",
41
+ "evals:long": "node scripts/eval-live.mjs --suite long",
42
+ "dashboard": "node scripts/control-center.mjs"
43
+ },
44
+ "keywords": [
45
+ "opencode",
46
+ "agent-skills",
47
+ "coding-agent",
48
+ "developer-tools",
49
+ "engineering-workflow",
50
+ "code-review",
51
+ "debugging",
52
+ "verification"
53
+ ],
54
+ "engines": {
55
+ "node": ">=20"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/laivannha0202/opencode-agent-skill-.git"
63
+ },
64
+ "homepage": "https://github.com/laivannha0202/opencode-agent-skill-#readme",
65
+ "bugs": {
66
+ "url": "https://github.com/laivannha0202/opencode-agent-skill-/issues"
67
+ },
68
+ "license": "MIT"
69
+ }
@@ -0,0 +1,2 @@
1
+ import { checkWorkingTree } from "../lib/repo-inspect.mjs"
2
+ console.log(JSON.stringify(await checkWorkingTree(process.argv[2] || process.cwd()), null, 2))
@@ -0,0 +1,2 @@
1
+ import { collectEvidence } from "../lib/repo-inspect.mjs"
2
+ console.log(JSON.stringify(await collectEvidence(process.argv[2] || process.cwd()), null, 2))
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { createServer } from "node:http"
3
+ import { readFile } from "node:fs/promises"
4
+ import path from "node:path"
5
+ import { collectControlCenterData, writeControlCenter } from "../lib/control-center.mjs"
6
+
7
+ const args = process.argv.slice(2)
8
+ const root = path.resolve(args[0] && !args[0].startsWith("--") ? args[0] : process.cwd())
9
+ const outputIndex = args.indexOf("--output")
10
+ const output = outputIndex >= 0 ? args[outputIndex + 1] : null
11
+ const portIndex = args.indexOf("--port")
12
+ const port = portIndex >= 0 ? Number.parseInt(args[portIndex + 1] || "4177", 10) : 4177
13
+ const serve = args.includes("--serve")
14
+
15
+ const result = await writeControlCenter(root, output)
16
+ console.log("[ues] Control Center: " + result.file)
17
+
18
+ if (serve) {
19
+ const server = createServer(async (req, res) => {
20
+ if (req.url === "/data.json") {
21
+ const current = await collectControlCenterData(root)
22
+ const body = JSON.stringify(current, null, 2) + "\n"
23
+ res.writeHead(200, {
24
+ "content-type": "application/json; charset=utf-8",
25
+ "cache-control": "no-store",
26
+ })
27
+ res.end(body)
28
+ return
29
+ }
30
+ const body = await readFile(result.file)
31
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" })
32
+ res.end(body)
33
+ })
34
+ server.listen(port, "127.0.0.1", () => {
35
+ console.log("[ues] Serving http://127.0.0.1:" + port)
36
+ })
37
+ }
@@ -0,0 +1,2 @@
1
+ import { detectStack } from "../lib/repo-inspect.mjs"
2
+ console.log(JSON.stringify(await detectStack(process.argv[2] || process.cwd()), null, 2))
@@ -0,0 +1,2 @@
1
+ import { detectTestCommands } from "../lib/repo-inspect.mjs"
2
+ console.log(JSON.stringify(await detectTestCommands(process.argv[2] || process.cwd()), null, 2))