@b00dea/cockpit 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (426) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/LICENSE +21 -0
  3. package/README.md +25 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2644 -0
  6. package/dist/autonomy.d.ts +45 -0
  7. package/dist/autonomy.js +173 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +117 -0
  11. package/dist/browser/commands.js +453 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +118 -0
  17. package/dist/browser/runtime.js +186 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +561 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/canonical-json.d.ts +2 -0
  23. package/dist/canonical-json.js +5 -0
  24. package/dist/cli-guard.d.ts +5 -0
  25. package/dist/cli-guard.js +71 -0
  26. package/dist/cli-main.d.ts +176 -0
  27. package/dist/cli-main.js +1584 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +16 -0
  30. package/dist/command-table.d.ts +121 -0
  31. package/dist/command-table.js +664 -0
  32. package/dist/config/load.d.ts +183 -0
  33. package/dist/config/load.js +367 -0
  34. package/dist/config/sibling-toml.d.ts +48 -0
  35. package/dist/config/sibling-toml.js +160 -0
  36. package/dist/config/surface-tools-toml.d.ts +9 -0
  37. package/dist/config/surface-tools-toml.js +37 -0
  38. package/dist/config/unconfirmed-toml.d.ts +9 -0
  39. package/dist/config/unconfirmed-toml.js +29 -0
  40. package/dist/config/verified-write.d.ts +43 -0
  41. package/dist/config/verified-write.js +89 -0
  42. package/dist/diagnostics.d.ts +26 -0
  43. package/dist/diagnostics.js +344 -0
  44. package/dist/docs/check.d.ts +16 -0
  45. package/dist/docs/check.js +197 -0
  46. package/dist/docs/decision-record.d.ts +34 -0
  47. package/dist/docs/decision-record.js +135 -0
  48. package/dist/docs/decisions.d.ts +16 -0
  49. package/dist/docs/decisions.js +103 -0
  50. package/dist/docs/markdown.d.ts +36 -0
  51. package/dist/docs/markdown.js +88 -0
  52. package/dist/docs/references.d.ts +32 -0
  53. package/dist/docs/references.js +148 -0
  54. package/dist/emit.d.ts +22 -0
  55. package/dist/emit.js +43 -0
  56. package/dist/entrypoint-kinds.d.ts +3 -0
  57. package/dist/entrypoint-kinds.js +11 -0
  58. package/dist/environment/application/confirm.d.ts +35 -0
  59. package/dist/environment/application/confirm.js +122 -0
  60. package/dist/environment/application/destructive.d.ts +6 -0
  61. package/dist/environment/application/destructive.js +429 -0
  62. package/dist/environment/application/doctor.d.ts +94 -0
  63. package/dist/environment/application/doctor.js +247 -0
  64. package/dist/environment/application/guard.d.ts +49 -0
  65. package/dist/environment/application/guard.js +677 -0
  66. package/dist/environment/application/health.d.ts +143 -0
  67. package/dist/environment/application/health.js +385 -0
  68. package/dist/environment/application/lifecycle-context.d.ts +235 -0
  69. package/dist/environment/application/lifecycle-context.js +154 -0
  70. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  71. package/dist/environment/application/lifecycle-state.js +202 -0
  72. package/dist/environment/application/lifecycle.d.ts +18 -0
  73. package/dist/environment/application/lifecycle.js +737 -0
  74. package/dist/environment/application/logs.d.ts +45 -0
  75. package/dist/environment/application/logs.js +127 -0
  76. package/dist/environment/application/services.d.ts +60 -0
  77. package/dist/environment/application/services.js +89 -0
  78. package/dist/environment/application/setup.d.ts +129 -0
  79. package/dist/environment/application/setup.js +741 -0
  80. package/dist/environment/application/shared.d.ts +11 -0
  81. package/dist/environment/application/shared.js +61 -0
  82. package/dist/environment/application/siblings.d.ts +43 -0
  83. package/dist/environment/application/siblings.js +269 -0
  84. package/dist/environment/claims.d.ts +389 -0
  85. package/dist/environment/claims.js +1701 -0
  86. package/dist/environment/codex-hook-trust.d.ts +11 -0
  87. package/dist/environment/codex-hook-trust.js +185 -0
  88. package/dist/environment/compose-environment.d.ts +1 -0
  89. package/dist/environment/compose-environment.js +6 -0
  90. package/dist/environment/compose-ownership.d.ts +21 -0
  91. package/dist/environment/compose-ownership.js +117 -0
  92. package/dist/environment/compose-topology.d.ts +125 -0
  93. package/dist/environment/compose-topology.js +490 -0
  94. package/dist/environment/contract.d.ts +522 -0
  95. package/dist/environment/contract.js +794 -0
  96. package/dist/environment/declaration.d.ts +14 -0
  97. package/dist/environment/declaration.js +39 -0
  98. package/dist/environment/destructive-composition.d.ts +29 -0
  99. package/dist/environment/destructive-composition.js +333 -0
  100. package/dist/environment/docker-daemon.d.ts +30 -0
  101. package/dist/environment/docker-daemon.js +78 -0
  102. package/dist/environment/docker-endpoint.d.ts +10 -0
  103. package/dist/environment/docker-endpoint.js +19 -0
  104. package/dist/environment/env-schema-observation.d.ts +46 -0
  105. package/dist/environment/env-schema-observation.js +241 -0
  106. package/dist/environment/evidence.d.ts +136 -0
  107. package/dist/environment/evidence.js +437 -0
  108. package/dist/environment/executable.d.ts +38 -0
  109. package/dist/environment/executable.js +190 -0
  110. package/dist/environment/generated-application/authorities.d.ts +5 -0
  111. package/dist/environment/generated-application/authorities.js +61 -0
  112. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  113. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  114. package/dist/environment/generated-application/check.d.ts +13 -0
  115. package/dist/environment/generated-application/check.js +191 -0
  116. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  117. package/dist/environment/generated-application/custom-surface.js +289 -0
  118. package/dist/environment/generated-application/prepare.d.ts +29 -0
  119. package/dist/environment/generated-application/prepare.js +278 -0
  120. package/dist/environment/generated-application/publication.d.ts +33 -0
  121. package/dist/environment/generated-application/publication.js +547 -0
  122. package/dist/environment/generated-application/shared.d.ts +492 -0
  123. package/dist/environment/generated-application/shared.js +354 -0
  124. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  125. package/dist/environment/generated-application/surface-observation.js +69 -0
  126. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  127. package/dist/environment/generated-application/surface-outputs.js +400 -0
  128. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  129. package/dist/environment/generated-application/surfaces.js +704 -0
  130. package/dist/environment/generated-application/sync.d.ts +6 -0
  131. package/dist/environment/generated-application/sync.js +124 -0
  132. package/dist/environment/git-observation.d.ts +202 -0
  133. package/dist/environment/git-observation.js +553 -0
  134. package/dist/environment/health-composition.d.ts +12 -0
  135. package/dist/environment/health-composition.js +645 -0
  136. package/dist/environment/host-observation.d.ts +56 -0
  137. package/dist/environment/host-observation.js +52 -0
  138. package/dist/environment/identity.d.ts +48 -0
  139. package/dist/environment/identity.js +85 -0
  140. package/dist/environment/init-application.d.ts +333 -0
  141. package/dist/environment/init-application.js +726 -0
  142. package/dist/environment/install-policy-observation.d.ts +71 -0
  143. package/dist/environment/install-policy-observation.js +284 -0
  144. package/dist/environment/install-policy.d.ts +48 -0
  145. package/dist/environment/install-policy.js +96 -0
  146. package/dist/environment/instance.d.ts +33 -0
  147. package/dist/environment/instance.js +122 -0
  148. package/dist/environment/instruction-pointer.d.ts +19 -0
  149. package/dist/environment/instruction-pointer.js +144 -0
  150. package/dist/environment/lifecycle-composition.d.ts +31 -0
  151. package/dist/environment/lifecycle-composition.js +742 -0
  152. package/dist/environment/lifecycle-stage.d.ts +120 -0
  153. package/dist/environment/lifecycle-stage.js +408 -0
  154. package/dist/environment/lifecycle.d.ts +75 -0
  155. package/dist/environment/lifecycle.js +354 -0
  156. package/dist/environment/logs.d.ts +132 -0
  157. package/dist/environment/logs.js +519 -0
  158. package/dist/environment/map-limit.d.ts +2 -0
  159. package/dist/environment/map-limit.js +13 -0
  160. package/dist/environment/mcp-observation.d.ts +9 -0
  161. package/dist/environment/mcp-observation.js +46 -0
  162. package/dist/environment/observations.d.ts +46 -0
  163. package/dist/environment/observations.js +346 -0
  164. package/dist/environment/operator-application.d.ts +79 -0
  165. package/dist/environment/operator-application.js +197 -0
  166. package/dist/environment/ports.d.ts +182 -0
  167. package/dist/environment/ports.js +1205 -0
  168. package/dist/environment/posture-observation.d.ts +68 -0
  169. package/dist/environment/posture-observation.js +253 -0
  170. package/dist/environment/presence.d.ts +4 -0
  171. package/dist/environment/presence.js +26 -0
  172. package/dist/environment/read-composition.d.ts +32 -0
  173. package/dist/environment/read-composition.js +397 -0
  174. package/dist/environment/readiness-composition.d.ts +57 -0
  175. package/dist/environment/readiness-composition.js +151 -0
  176. package/dist/environment/reclaim.d.ts +370 -0
  177. package/dist/environment/reclaim.js +1940 -0
  178. package/dist/environment/repository-identity.d.ts +13 -0
  179. package/dist/environment/repository-identity.js +108 -0
  180. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  181. package/dist/environment/runtime-measurement-record.js +111 -0
  182. package/dist/environment/runtime-measurement.d.ts +27 -0
  183. package/dist/environment/runtime-measurement.js +224 -0
  184. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  185. package/dist/environment/runtime-probe-residue.js +148 -0
  186. package/dist/environment/runtime-probe.d.ts +72 -0
  187. package/dist/environment/runtime-probe.js +725 -0
  188. package/dist/environment/runtime.d.ts +551 -0
  189. package/dist/environment/runtime.js +2405 -0
  190. package/dist/environment/seed.d.ts +16 -0
  191. package/dist/environment/seed.js +43 -0
  192. package/dist/environment/selftest-application.d.ts +86 -0
  193. package/dist/environment/selftest-application.js +713 -0
  194. package/dist/environment/sensitive.d.ts +3 -0
  195. package/dist/environment/sensitive.js +48 -0
  196. package/dist/environment/served-checkout-grants.d.ts +53 -0
  197. package/dist/environment/served-checkout-grants.js +182 -0
  198. package/dist/environment/setup-observation.d.ts +62 -0
  199. package/dist/environment/setup-observation.js +1473 -0
  200. package/dist/environment/setup.d.ts +43 -0
  201. package/dist/environment/setup.js +176 -0
  202. package/dist/environment/sibling-checkouts.d.ts +119 -0
  203. package/dist/environment/sibling-checkouts.js +254 -0
  204. package/dist/environment/sibling-instructions.d.ts +33 -0
  205. package/dist/environment/sibling-instructions.js +224 -0
  206. package/dist/environment/sibling-observation.d.ts +27 -0
  207. package/dist/environment/sibling-observation.js +45 -0
  208. package/dist/environment/sibling-pull.d.ts +23 -0
  209. package/dist/environment/sibling-pull.js +95 -0
  210. package/dist/environment/sibling-registration.d.ts +45 -0
  211. package/dist/environment/sibling-registration.js +538 -0
  212. package/dist/environment/stack-inputs.d.ts +18 -0
  213. package/dist/environment/stack-inputs.js +210 -0
  214. package/dist/environment/stack-inventory-prune.d.ts +31 -0
  215. package/dist/environment/stack-inventory-prune.js +129 -0
  216. package/dist/environment/stack-inventory-report.d.ts +44 -0
  217. package/dist/environment/stack-inventory-report.js +78 -0
  218. package/dist/environment/stack-inventory.d.ts +94 -0
  219. package/dist/environment/stack-inventory.js +418 -0
  220. package/dist/environment/state.d.ts +410 -0
  221. package/dist/environment/state.js +2153 -0
  222. package/dist/environment/templates.d.ts +140 -0
  223. package/dist/environment/templates.js +685 -0
  224. package/dist/environment/tool-registration.d.ts +9 -0
  225. package/dist/environment/tool-registration.js +92 -0
  226. package/dist/environment/upgrade-application.d.ts +19 -0
  227. package/dist/environment/upgrade-application.js +175 -0
  228. package/dist/environment-exec/execute.d.ts +105 -0
  229. package/dist/environment-exec/execute.js +1179 -0
  230. package/dist/environment-exec/libexec-program.d.ts +6 -0
  231. package/dist/environment-exec/libexec-program.js +22 -0
  232. package/dist/environment-exec/verified-digest.d.ts +4 -0
  233. package/dist/environment-exec/verified-digest.js +16 -0
  234. package/dist/exact-keys.d.ts +7 -0
  235. package/dist/exact-keys.js +15 -0
  236. package/dist/fix-catalogue.d.ts +32 -0
  237. package/dist/fix-catalogue.js +60 -0
  238. package/dist/freshness.d.ts +29 -0
  239. package/dist/freshness.js +75 -0
  240. package/dist/generated-environment/adopt.d.ts +16 -0
  241. package/dist/generated-environment/adopt.js +271 -0
  242. package/dist/generated-environment/command.d.ts +47 -0
  243. package/dist/generated-environment/command.js +75 -0
  244. package/dist/generated-environment/transaction.d.ts +186 -0
  245. package/dist/generated-environment/transaction.js +1770 -0
  246. package/dist/grading/evaluate.d.ts +71 -0
  247. package/dist/grading/evaluate.js +403 -0
  248. package/dist/grading/grade.d.ts +33 -0
  249. package/dist/grading/grade.js +57 -0
  250. package/dist/guards/answer-confirmation.d.ts +41 -0
  251. package/dist/guards/answer-confirmation.js +109 -0
  252. package/dist/guards/evaluator.d.ts +130 -0
  253. package/dist/guards/evaluator.js +1258 -0
  254. package/dist/guards/harness-adapter.d.ts +17 -0
  255. package/dist/guards/harness-adapter.js +57 -0
  256. package/dist/guards/host.d.ts +8 -0
  257. package/dist/guards/host.js +10 -0
  258. package/dist/guards/invocation-parser.d.ts +66 -0
  259. package/dist/guards/invocation-parser.js +595 -0
  260. package/dist/guards/local-identity.d.ts +44 -0
  261. package/dist/guards/local-identity.js +129 -0
  262. package/dist/guards/policy-change.d.ts +43 -0
  263. package/dist/guards/policy-change.js +159 -0
  264. package/dist/guards/policy-pin.d.ts +13 -0
  265. package/dist/guards/policy-pin.js +177 -0
  266. package/dist/guards/protected-locations.d.ts +62 -0
  267. package/dist/guards/protected-locations.js +461 -0
  268. package/dist/guards/read-only.d.ts +37 -0
  269. package/dist/guards/read-only.js +315 -0
  270. package/dist/init/command.d.ts +41 -0
  271. package/dist/init/command.js +330 -0
  272. package/dist/init/derive.d.ts +53 -0
  273. package/dist/init/derive.js +819 -0
  274. package/dist/init/draft.d.ts +140 -0
  275. package/dist/init/draft.js +1228 -0
  276. package/dist/init/render.d.ts +47 -0
  277. package/dist/init/render.js +104 -0
  278. package/dist/init/scaffold.d.ts +53 -0
  279. package/dist/init/scaffold.js +121 -0
  280. package/dist/invocation.d.ts +13 -0
  281. package/dist/invocation.js +20 -0
  282. package/dist/package-metadata.d.ts +9 -0
  283. package/dist/package-metadata.js +19 -0
  284. package/dist/readiness.d.ts +53 -0
  285. package/dist/readiness.js +88 -0
  286. package/dist/references/named.d.ts +24 -0
  287. package/dist/references/named.js +27 -0
  288. package/dist/refusal.d.ts +15 -0
  289. package/dist/refusal.js +7 -0
  290. package/dist/registry-derivation.d.ts +31 -0
  291. package/dist/registry-derivation.js +32 -0
  292. package/dist/registry-resolve/command.d.ts +47 -0
  293. package/dist/registry-resolve/command.js +226 -0
  294. package/dist/registry-resolve/registry.d.ts +68 -0
  295. package/dist/registry-resolve/registry.js +292 -0
  296. package/dist/registry-resolve/render-shell.d.ts +18 -0
  297. package/dist/registry-resolve/render-shell.js +36 -0
  298. package/dist/repair.d.ts +29 -0
  299. package/dist/repair.js +8 -0
  300. package/dist/repository-path.d.ts +9 -0
  301. package/dist/repository-path.js +16 -0
  302. package/dist/schema-json.d.ts +1 -0
  303. package/dist/schema-json.js +177 -0
  304. package/dist/schema.d.ts +1289 -0
  305. package/dist/schema.js +985 -0
  306. package/dist/setup/command.d.ts +4 -0
  307. package/dist/setup/command.js +4 -0
  308. package/dist/sha256.d.ts +1 -0
  309. package/dist/sha256.js +4 -0
  310. package/dist/surfaces/adoption.d.ts +41 -0
  311. package/dist/surfaces/adoption.js +217 -0
  312. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  313. package/dist/surfaces/consumer-instructions.js +43 -0
  314. package/dist/surfaces/contract.d.ts +42 -0
  315. package/dist/surfaces/contract.js +169 -0
  316. package/dist/surfaces/core-declarations.d.ts +24 -0
  317. package/dist/surfaces/core-declarations.js +79 -0
  318. package/dist/surfaces/development-markers.d.ts +1 -0
  319. package/dist/surfaces/development-markers.js +18 -0
  320. package/dist/surfaces/generator-adapter.d.ts +71 -0
  321. package/dist/surfaces/generator-adapter.js +809 -0
  322. package/dist/surfaces/harness-registry.d.ts +260 -0
  323. package/dist/surfaces/harness-registry.js +146 -0
  324. package/dist/surfaces/mcp.d.ts +145 -0
  325. package/dist/surfaces/mcp.js +349 -0
  326. package/dist/surfaces/merge.d.ts +30 -0
  327. package/dist/surfaces/merge.js +105 -0
  328. package/dist/surfaces/ownership.d.ts +190 -0
  329. package/dist/surfaces/ownership.js +795 -0
  330. package/dist/surfaces/projection.d.ts +136 -0
  331. package/dist/surfaces/projection.js +716 -0
  332. package/dist/surfaces/render.d.ts +79 -0
  333. package/dist/surfaces/render.js +636 -0
  334. package/dist/surfaces/restrictions.d.ts +130 -0
  335. package/dist/surfaces/restrictions.js +447 -0
  336. package/dist/surfaces/selftest.d.ts +162 -0
  337. package/dist/surfaces/selftest.js +393 -0
  338. package/dist/surfaces/skill-contract.d.ts +85 -0
  339. package/dist/surfaces/skill-contract.js +449 -0
  340. package/dist/surfaces/sync.d.ts +57 -0
  341. package/dist/surfaces/sync.js +164 -0
  342. package/dist/team-metadata.d.ts +41 -0
  343. package/dist/team-metadata.js +513 -0
  344. package/dist/testing/gates.d.ts +91 -0
  345. package/dist/testing/gates.js +182 -0
  346. package/dist/testing/hooks.d.ts +59 -0
  347. package/dist/testing/hooks.js +139 -0
  348. package/dist/testing/test-protection.d.ts +34 -0
  349. package/dist/testing/test-protection.js +137 -0
  350. package/dist/testing/verdict.d.ts +29 -0
  351. package/dist/testing/verdict.js +62 -0
  352. package/dist/testing/weakening.d.ts +15 -0
  353. package/dist/testing/weakening.js +30 -0
  354. package/dist/work-orders/application-closure.d.ts +7 -0
  355. package/dist/work-orders/application-closure.js +76 -0
  356. package/dist/work-orders/application-context.d.ts +162 -0
  357. package/dist/work-orders/application-context.js +137 -0
  358. package/dist/work-orders/application-decisions.d.ts +27 -0
  359. package/dist/work-orders/application-decisions.js +121 -0
  360. package/dist/work-orders/application-grading.d.ts +11 -0
  361. package/dist/work-orders/application-grading.js +212 -0
  362. package/dist/work-orders/application.d.ts +6 -0
  363. package/dist/work-orders/application.js +223 -0
  364. package/dist/work-orders/composition.d.ts +101 -0
  365. package/dist/work-orders/composition.js +1202 -0
  366. package/dist/work-orders/contract.d.ts +77 -0
  367. package/dist/work-orders/contract.js +140 -0
  368. package/dist/work-orders/decisions.d.ts +23 -0
  369. package/dist/work-orders/decisions.js +55 -0
  370. package/dist/work-orders/store.d.ts +207 -0
  371. package/dist/work-orders/store.js +291 -0
  372. package/docs/README.md +68 -0
  373. package/docs/architecture.md +136 -0
  374. package/docs/capability-matrix.md +58 -0
  375. package/docs/cli-reference.md +807 -0
  376. package/docs/command-list.md +62 -0
  377. package/docs/compose-patterns.md +99 -0
  378. package/docs/concepts.md +173 -0
  379. package/docs/configuration-essentials.md +177 -0
  380. package/docs/configuration-reference.md +636 -0
  381. package/docs/docs-check.md +76 -0
  382. package/docs/environment.md +948 -0
  383. package/docs/extending.md +43 -0
  384. package/docs/faq.md +113 -0
  385. package/docs/getting-started.md +692 -0
  386. package/docs/harness-surfaces.md +500 -0
  387. package/docs/joining-a-team.md +76 -0
  388. package/docs/release-policy.md +49 -0
  389. package/docs/safety-and-autonomy.md +463 -0
  390. package/docs/troubleshooting.md +382 -0
  391. package/docs/tutorial.md +171 -0
  392. package/docs/work-and-browser.md +420 -0
  393. package/libexec/browser-operation.cjs +270 -0
  394. package/libexec/fence-marker.cjs +101 -0
  395. package/libexec/fence-marker.d.cts +24 -0
  396. package/libexec/fenced-group-runner.cjs +77 -0
  397. package/libexec/fenced-supervisor.cjs +137 -0
  398. package/libexec/generated-filesystem.cjs +731 -0
  399. package/libexec/install-filesystem.cjs +225 -0
  400. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  401. package/libexec/rulesync-generator.cjs +743 -0
  402. package/libexec/surface-generator-worker.cjs +266 -0
  403. package/libexec/surface-output-reader.cjs +99 -0
  404. package/libexec/surface-scratch-cleanup.cjs +14 -0
  405. package/libexec/validate-harness-evidence.d.mts +183 -0
  406. package/libexec/validate-harness-evidence.mjs +1312 -0
  407. package/package.json +74 -4
  408. package/surfaces/README.md +33 -0
  409. package/surfaces/agents/evaluator.agent.md +76 -0
  410. package/surfaces/guards/bash-guard.sh +48 -0
  411. package/surfaces/guards/claude.adapter.json +25 -0
  412. package/surfaces/guards/codex.adapter.json +24 -0
  413. package/surfaces/guards/opencode.adapter.json +25 -0
  414. package/surfaces/references/browser.md +63 -0
  415. package/surfaces/references/test-data-and-personas.md +84 -0
  416. package/surfaces/references/testing-tiers.md +11 -0
  417. package/surfaces/skills/cockpit-browser.skill.md +59 -0
  418. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  419. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  420. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  421. package/surfaces/skills/cockpit-work.skill.md +34 -0
  422. package/surfaces/skills/collection.json +5 -0
  423. package/surfaces/skills/procedures/explain.md +66 -0
  424. package/surfaces/skills/procedures/fix.md +95 -0
  425. package/surfaces/skills/procedures/setup.md +452 -0
  426. package/surfaces/skills/procedures/work.md +134 -0
@@ -0,0 +1,43 @@
1
+ import { type EnvironmentCommandOutcome, type FilesystemCheck, type Observation, type SetupActionKind } from './contract.js';
2
+ import type { Sibling } from '../schema.js';
3
+ import { type ResolvedExecutable } from './executable.js';
4
+ type ObservedPathKind = 'absent' | 'directory' | 'repository' | 'symlink';
5
+ export interface SetupPathEvidence {
6
+ kind: ObservedPathKind;
7
+ missingParent?: string;
8
+ remotes?: Readonly<Record<string, string>>;
9
+ filesystemChecks?: readonly FilesystemCheck[];
10
+ }
11
+ export type SetupObservation = Observation<SetupPathEvidence>;
12
+ export interface SetupResult {
13
+ sibling: string;
14
+ target: string;
15
+ observed: ObservedPathKind;
16
+ action: SetupActionKind;
17
+ changed: boolean;
18
+ }
19
+ interface PlanSetupBaseInput {
20
+ siblingsRoot: string;
21
+ sibling: Sibling;
22
+ env: Readonly<Record<string, string>>;
23
+ gitExecutable: ResolvedExecutable;
24
+ }
25
+ export type PlanSetupInput = PlanSetupBaseInput & ({
26
+ observation: SetupObservation;
27
+ observe?: never;
28
+ } | {
29
+ observation?: never;
30
+ observe(target: string, siblingsRoot: string, env: Readonly<Record<string, string>>, gitExecutable: ResolvedExecutable): Promise<SetupObservation>;
31
+ });
32
+ export declare function gitEnvironment(ambient: Readonly<Record<string, string | undefined>>): Readonly<Record<string, string>>;
33
+ export type LocalRemoteResolution = {
34
+ readonly kind: 'resolved';
35
+ readonly remote: string;
36
+ } | {
37
+ readonly kind: 'unresolvable';
38
+ readonly declared: string;
39
+ readonly errno: string;
40
+ };
41
+ export declare function resolveLocalRemote(remote: string, siblingsRoot: string): LocalRemoteResolution;
42
+ export declare function planSetup(input: PlanSetupInput): Promise<EnvironmentCommandOutcome<SetupResult>>;
43
+ export {};
@@ -0,0 +1,176 @@
1
+ import { realpathSync } from 'node:fs';
2
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
3
+ import { ENVIRONMENT_CODES, forwardedRefusal, } from './contract.js';
4
+ import { resolvedGitExecutable } from './executable.js';
5
+ function refusal(code, message, target) {
6
+ return forwardedRefusal([{ severity: 'error', code, message, target }]);
7
+ }
8
+ const APPROVED_GIT_ENVIRONMENT = [
9
+ 'PATH', 'HOME', 'SSH_AUTH_SOCK', 'TMPDIR', 'LANG', 'LC_ALL',
10
+ ];
11
+ const NONE_POSTURE = {
12
+ absent: { code: ENVIRONMENT_CODES.SETUP_NONE_ABSENT, found: 'nothing exists there yet' },
13
+ directory: { code: ENVIRONMENT_CODES.SETUP_NONE_DIRECTORY, found: 'it is a plain directory, not a Git checkout' },
14
+ repository: { code: ENVIRONMENT_CODES.SETUP_NONE_REPOSITORY, found: 'it is an existing Git checkout' },
15
+ };
16
+ export function gitEnvironment(ambient) {
17
+ const approved = {};
18
+ for (const name of APPROVED_GIT_ENVIRONMENT) {
19
+ const value = ambient[name];
20
+ if (value !== undefined)
21
+ approved[name] = value;
22
+ }
23
+ return {
24
+ GIT_CONFIG_GLOBAL: '/dev/null',
25
+ GIT_CONFIG_NOSYSTEM: '1',
26
+ GIT_CONFIG_SYSTEM: '/dev/null',
27
+ GIT_TERMINAL_PROMPT: '0',
28
+ ...approved,
29
+ };
30
+ }
31
+ export function resolveLocalRemote(remote, siblingsRoot) {
32
+ const resolved = (value) => ({ kind: 'resolved', remote: value });
33
+ if (remote === '' || remote.startsWith('-') || isAbsolute(remote) || remote.startsWith('~'))
34
+ return resolved(remote);
35
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(remote))
36
+ return resolved(remote);
37
+ const colon = remote.indexOf(':');
38
+ if (colon !== -1 && !remote.slice(0, colon).includes('/'))
39
+ return resolved(remote);
40
+ const path = resolve(siblingsRoot, remote);
41
+ try {
42
+ return resolved(realpathSync(path));
43
+ }
44
+ catch (error) {
45
+ const errno = error.code;
46
+ if (errno === 'ENOENT')
47
+ return resolved(path);
48
+ return { kind: 'unresolvable', declared: remote, errno: errno ?? 'unknown error' };
49
+ }
50
+ }
51
+ function unresolvableRemoteRefusal(value, which, target) {
52
+ return {
53
+ kind: 'refusal',
54
+ status: 2,
55
+ diagnostics: [{
56
+ severity: 'error',
57
+ code: ENVIRONMENT_CODES.SETUP_REMOTE_UNRESOLVABLE,
58
+ message: `cannot resolve ${which} local remote ${JSON.stringify(value.declared)} (${value.errno})`,
59
+ target,
60
+ fix: 'make the remote path readable, or declare the remote as a URL or an absolute path to an existing repository, then run npx --no cockpit setup --config <your cockpit.toml> again',
61
+ }],
62
+ result: null,
63
+ };
64
+ }
65
+ function fallbackChecks(observation, siblingsRoot, target) {
66
+ if (observation.kind === 'absent') {
67
+ return [
68
+ { path: siblingsRoot, expected: 'directory' },
69
+ { path: target, expected: 'absent' },
70
+ ];
71
+ }
72
+ if (observation.kind === 'repository')
73
+ return [{ path: target, expected: 'directory' }];
74
+ return [];
75
+ }
76
+ function step(id, argv, cwd, env, target, filesystemChecks, executable) {
77
+ return {
78
+ id, argv, argv0: executable.invocationPath, cwd, env, timeoutMs: 120_000, stdoutLimitBytes: 65_536, stderrLimitBytes: 65_536,
79
+ failureOutput: 'stderr-tail',
80
+ paths: [target], ports: [], labels: { operation: 'setup' },
81
+ expected: [{ kind: 'path', target, state: 'repository' }],
82
+ consumedFiles: [{ path: executable.canonicalPath, sha256: executable.sha256, maxBytes: 536_870_912, argvIndexes: [0], executable: true, executionMode: executable.executionMode }],
83
+ filesystemChecks, compensation: [],
84
+ };
85
+ }
86
+ function preview(result, mutation, diagnostics = []) {
87
+ const plan = { steps: [mutation] };
88
+ return { kind: 'preview', status: 0, diagnostics, result, plan };
89
+ }
90
+ export async function planSetup(input) {
91
+ const siblingsRoot = resolve(input.siblingsRoot);
92
+ const target = resolve(siblingsRoot, input.sibling.dir);
93
+ const fromRoot = relative(siblingsRoot, target);
94
+ const gitExecutable = resolvedGitExecutable(input.gitExecutable);
95
+ if (gitExecutable === undefined) {
96
+ return refusal(ENVIRONMENT_CODES.SETUP_PATH_TYPE_INVALID, 'Git executable identity is unresolved', target);
97
+ }
98
+ const resolvedGitEnvironment = gitEnvironment(input.env);
99
+ if (isAbsolute(input.sibling.dir) || fromRoot === '..' || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {
100
+ return refusal(ENVIRONMENT_CODES.SETUP_PATH_OUTSIDE_ROOT, 'sibling target is outside the declared siblings root', target);
101
+ }
102
+ const observed = input.observation ?? await input.observe(target, siblingsRoot, resolvedGitEnvironment, gitExecutable);
103
+ if (observed.state === 'failed') {
104
+ return refusal(observed.code, observed.reason, target);
105
+ }
106
+ if (observed.state === 'unavailable')
107
+ return refusal(ENVIRONMENT_CODES.SETUP_PATH_TYPE_INVALID, observed.reason, target);
108
+ const observation = observed.value;
109
+ if (observation.kind === 'symlink') {
110
+ return refusal(ENVIRONMENT_CODES.SETUP_PATH_SYMLINK, 'sibling target or one of its path components is a symlink', target);
111
+ }
112
+ const result = (action, changed) => ({
113
+ sibling: input.sibling.slug,
114
+ target,
115
+ observed: observation.kind,
116
+ action,
117
+ changed,
118
+ });
119
+ if (input.sibling.acquire === 'none') {
120
+ return {
121
+ kind: 'complete',
122
+ status: 0,
123
+ diagnostics: [{
124
+ severity: 'info',
125
+ code: NONE_POSTURE[observation.kind].code,
126
+ message: `acquire = "none", so setup leaves this path alone: ${NONE_POSTURE[observation.kind].found}`,
127
+ target,
128
+ }],
129
+ result: result('report', false),
130
+ };
131
+ }
132
+ const declared = resolveLocalRemote(input.sibling.remote, siblingsRoot);
133
+ if (declared.kind === 'unresolvable')
134
+ return unresolvableRemoteRefusal(declared, 'declared', target);
135
+ const remote = declared.remote;
136
+ if (observation.kind === 'absent') {
137
+ if (input.sibling.acquire === 'manual') {
138
+ return {
139
+ kind: 'complete',
140
+ status: 0,
141
+ diagnostics: [{
142
+ severity: 'warning',
143
+ code: ENVIRONMENT_CODES.SETUP_MANUAL_MISSING,
144
+ message: 'manual checkout is absent',
145
+ target,
146
+ fix: 'create the checkout, then run npx --no cockpit setup --config <your cockpit.toml> again',
147
+ }],
148
+ result: result('report', false),
149
+ };
150
+ }
151
+ if (observation.missingParent) {
152
+ return refusal(ENVIRONMENT_CODES.SETUP_PARENT_MISSING, 'automatic checkout parent directory is absent', observation.missingParent);
153
+ }
154
+ const branch = input.sibling.branch ? ['--branch', input.sibling.branch] : [];
155
+ const checks = observation.filesystemChecks ?? fallbackChecks(observation, siblingsRoot, target);
156
+ return preview(result('clone', true), step(`setup:${input.sibling.slug}:clone`, [gitExecutable.canonicalPath, 'clone', ...branch, '--', remote, target], siblingsRoot, resolvedGitEnvironment, target, checks, gitExecutable));
157
+ }
158
+ if (observation.kind !== 'repository') {
159
+ return refusal(ENVIRONMENT_CODES.SETUP_PATH_TYPE_INVALID, 'repository acquisition target exists but is not a repository', target);
160
+ }
161
+ const observedOrigin = observation.remotes?.origin;
162
+ const observedResolution = observedOrigin === undefined ? undefined : resolveLocalRemote(observedOrigin, siblingsRoot);
163
+ if (observedResolution?.kind === 'unresolvable')
164
+ return unresolvableRemoteRefusal(observedResolution, 'existing origin', target);
165
+ const current = observedResolution?.remote;
166
+ if (current === remote)
167
+ return { kind: 'complete', status: 0, diagnostics: [], result: result('report', false) };
168
+ if (input.sibling.acquire === 'auto') {
169
+ return refusal(ENVIRONMENT_CODES.SETUP_REMOTE_MISMATCH, current === undefined
170
+ ? 'existing automatic checkout has no origin remote'
171
+ : 'existing automatic checkout has a different origin remote', target);
172
+ }
173
+ const verb = current === undefined ? 'add' : 'set-url';
174
+ const checks = observation.filesystemChecks ?? fallbackChecks(observation, siblingsRoot, target);
175
+ return preview(result('wire-remote', true), step(`setup:${input.sibling.slug}:remote-${verb}`, [gitExecutable.canonicalPath, '-C', target, 'remote', verb, '--', 'origin', remote], siblingsRoot, resolvedGitEnvironment, target, checks, gitExecutable));
176
+ }
@@ -0,0 +1,119 @@
1
+ import { type ExecuteProbeOutcome } from '../environment-exec/execute.js';
2
+ import type { CockpitConfig, Sibling } from '../schema.js';
3
+ import { type EnvironmentDiagnostic, type ExecutionStep } from './contract.js';
4
+ import { type ResolvedExecutable } from './executable.js';
5
+ export declare const SIBLING_CHECKOUTS_PATH: string;
6
+ /** Each overridden sibling's slug and the canonical absolute directory that serves it. */
7
+ type SiblingCheckoutRecord = Readonly<Record<string, string>>;
8
+ export type SiblingCheckoutRecordRead = Readonly<{
9
+ kind: 'complete';
10
+ checkouts: SiblingCheckoutRecord;
11
+ }> | Readonly<{
12
+ kind: 'refusal';
13
+ diagnostic: EnvironmentDiagnostic;
14
+ }>;
15
+ /** The recorded overrides; none when nothing was recorded. A record cockpit did not write is refused, never ignored. */
16
+ export declare function readSiblingCheckouts(cockpitRoot: string): Promise<SiblingCheckoutRecordRead>;
17
+ export declare function writeSiblingCheckouts(cockpitRoot: string, checkouts: SiblingCheckoutRecord): Promise<{
18
+ readonly kind: 'complete';
19
+ } | {
20
+ readonly kind: 'refusal';
21
+ readonly message: string;
22
+ }>;
23
+ export interface ServedSibling {
24
+ readonly slug: string;
25
+ /** Where cockpit.toml puts the checkout. */
26
+ readonly declared: string;
27
+ /** The checkout the stack, the guard and the health commands use. */
28
+ readonly served: string;
29
+ readonly overridden: boolean;
30
+ }
31
+ /** Every declared sibling with the directory that serves it; an override for an undeclared slug serves nothing. */
32
+ export declare function servedSiblings(cockpitRoot: string, config: Readonly<{
33
+ cockpit: Readonly<{
34
+ siblings_root: string;
35
+ }>;
36
+ sibling: readonly Readonly<{
37
+ slug: string;
38
+ dir: string;
39
+ }>[];
40
+ }>, checkouts: SiblingCheckoutRecord): ReadonlyMap<string, ServedSibling>;
41
+ /** The directory serving a declared sibling; a slug the map lacks is a caller's error, named as one. */
42
+ export declare function servedPath(served: ReadonlyMap<string, ServedSibling>, slug: string): string;
43
+ type ServedSiblingsRead = Readonly<{
44
+ kind: 'complete';
45
+ siblings: ReadonlyMap<string, ServedSibling>;
46
+ }> | Readonly<{
47
+ kind: 'refusal';
48
+ diagnostic: EnvironmentDiagnostic;
49
+ }>;
50
+ export declare function loadServedSiblings(cockpitRoot: string, config: Pick<CockpitConfig, 'cockpit' | 'sibling'>): Promise<ServedSiblingsRead>;
51
+ /** The overridden siblings whose recorded directory is gone, such as a removed worktree. */
52
+ export declare function vanishedServedCheckouts(served: ReadonlyMap<string, ServedSibling>): Promise<readonly ServedSibling[]>;
53
+ export declare function vanishedCheckoutFix(slug: string): string;
54
+ /**
55
+ * A refusal when an override names a directory that is gone. Docker creates a missing bind-mount
56
+ * source as an empty directory, so the stack would start on nothing. Preflight and doctor report
57
+ * the same fact as the served-checkouts observation row, so all three agree.
58
+ */
59
+ export declare function missingServedCheckout(served: ReadonlyMap<string, ServedSibling>): Promise<EnvironmentDiagnostic | undefined>;
60
+ /** The directory variables Compose interpolates: each sibling's `var` and the directory that serves it. */
61
+ export declare function servedSiblingVariables(config: Pick<CockpitConfig, 'sibling'>, served: ReadonlyMap<string, ServedSibling>): Readonly<Record<string, string>>;
62
+ type GitRun = Readonly<{
63
+ kind: 'complete';
64
+ stdout: string;
65
+ }> | Readonly<{
66
+ kind: 'failed';
67
+ exitCode: number;
68
+ code: string;
69
+ detail: string;
70
+ stderr: string;
71
+ }>;
72
+ /**
73
+ * `stdoutLimitBytes` replaces the default bound, which keeps only the tail of longer output, with a
74
+ * hard one: output past it fails with EXECUTION_STDOUT_LIMIT instead of arriving in part.
75
+ */
76
+ type GitRunOptions = Readonly<{
77
+ timeoutMs?: number;
78
+ stdoutLimitBytes?: number;
79
+ }>;
80
+ /** Runs one Git command in a checkout through the authenticated executor and returns its text. */
81
+ export interface GitRunner {
82
+ readonly executable: ResolvedExecutable;
83
+ run(cwd: string, id: string, args: readonly string[], options?: GitRunOptions): Promise<GitRun>;
84
+ }
85
+ export declare const GIT_CHILD_CONFIG: readonly ['-c', 'core.fsmonitor=false', '-c', 'core.hooksPath=/dev/null'];
86
+ export declare function createGitRunner(env: Readonly<Record<string, string | undefined>>, execute: (step: ExecutionStep) => Promise<ExecuteProbeOutcome>): Promise<GitRunner | Readonly<{
87
+ unavailable: string;
88
+ }>>;
89
+ type SiblingCheckoutInspection = Readonly<{
90
+ kind: 'same-repository';
91
+ directory: string;
92
+ evidence: 'object-store' | 'remote';
93
+ }> | Readonly<{
94
+ kind: 'refusal';
95
+ diagnostic: EnvironmentDiagnostic;
96
+ }>;
97
+ /**
98
+ * Whether `candidate` is the root of a checkout of the sibling's repository: it shares the declared
99
+ * checkout's object store (a linked worktree), or its origin names the same repository as the
100
+ * declared checkout's origin or the declared remote. Anything else is refused: serving an unrelated
101
+ * repository under a sibling's name would hand its code the sibling's mounts and grants.
102
+ */
103
+ export declare function inspectSiblingCheckout(input: Readonly<{
104
+ sibling: Pick<Sibling, 'slug' | 'remote'>;
105
+ declared: string;
106
+ candidate: string;
107
+ git: GitRunner;
108
+ }>): Promise<SiblingCheckoutInspection>;
109
+ /** The declared environment files the served checkout lacks; a fresh worktree has none of the ignored ones. */
110
+ export declare function missingEnvironmentFiles(served: ServedSibling, sibling: Pick<Sibling, 'environment_files'>): Promise<readonly Readonly<{
111
+ template: string;
112
+ instance: string;
113
+ }>[]>;
114
+ /** One warning per environment file an overriding checkout lacks, naming who must create it and from what. */
115
+ export declare function missingEnvironmentFileDiagnostics(served: ServedSibling, missing: readonly Readonly<{
116
+ template: string;
117
+ instance: string;
118
+ }>[]): readonly EnvironmentDiagnostic[];
119
+ export {};
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Owns which checkout serves each sibling: the directory cockpit.toml declares under siblings_root,
3
+ * or another checkout of the same repository (a linked worktree, usually) that `cockpit use`
4
+ * recorded for this cockpit checkout alone. Mounts, the guard's allowed roots and the health
5
+ * commands all read the served directory from here, so they describe the same code.
6
+ *
7
+ * The record is machine-local, under `.cockpit/local`, never in cockpit.toml: a worktree path is
8
+ * one person's disk, and a committed one would point every other clone at a directory it lacks.
9
+ * Without it, an agent working on a branch in a sibling's worktree had the stack, the checks and
10
+ * every grade run against the main checkout's branch while reporting green.
11
+ */
12
+ import { lstatIfPresent } from './presence.js';
13
+ import { lstat, realpath } from 'node:fs/promises';
14
+ import { isAbsolute, join, normalize, resolve } from 'node:path';
15
+ import { authenticatedExecutionOutcome } from '../environment-exec/execute.js';
16
+ import { readBoundedFile } from '../bounded-read.js';
17
+ import { cockpitCommand, formatShellCommand } from '../fix-catalogue.js';
18
+ import { GUARD_LOCAL_PATHS, writeCockpitLocalFiles } from '../guards/local-identity.js';
19
+ import { ENVIRONMENT_CODES, errorCause, hasErrorCode, RefusalError, tryParseJson } from './contract.js';
20
+ import { resolveGitExecutable } from './executable.js';
21
+ import { repositoryIdentity } from './repository-identity.js';
22
+ import { redactSensitiveMessage } from './sensitive.js';
23
+ import { gitEnvironment } from './setup.js';
24
+ const FORMAT = 'cockpit-sibling-checkouts/v1';
25
+ const RECORD_NAME = 'sibling-checkouts';
26
+ export const SIBLING_CHECKOUTS_PATH = `${GUARD_LOCAL_PATHS.directory}/${RECORD_NAME}`;
27
+ const RECORD_MAX_BYTES = 65_536;
28
+ const SLUG = /^[a-z0-9][a-z0-9-]*$/;
29
+ function canonicalDirectory(value) {
30
+ return typeof value === 'string' && isAbsolute(value) && normalize(value) === value && value !== '/'
31
+ && !value.endsWith('/') && !/[\x00-\x1f\x7f]/.test(value);
32
+ }
33
+ function recordRefusal(path, reason) {
34
+ return { kind: 'refusal', diagnostic: {
35
+ severity: 'error',
36
+ code: 'SIBLING_CHECKOUT_RECORD_INVALID',
37
+ message: `${path} is not a sibling checkout record cockpit wrote (${reason}), so cockpit cannot tell which checkout serves each sibling`,
38
+ target: SIBLING_CHECKOUTS_PATH,
39
+ fix: `delete ${path}, then run npx --no cockpit use <slug> <checkout> --config <your cockpit.toml> again for each sibling another checkout should serve`,
40
+ } };
41
+ }
42
+ /** The recorded overrides; none when nothing was recorded. A record cockpit did not write is refused, never ignored. */
43
+ export async function readSiblingCheckouts(cockpitRoot) {
44
+ const path = join(cockpitRoot, SIBLING_CHECKOUTS_PATH);
45
+ let bytes;
46
+ try {
47
+ const read = readBoundedFile(path, RECORD_MAX_BYTES);
48
+ if (read.kind === 'too-large')
49
+ return recordRefusal(path, `it is larger than ${RECORD_MAX_BYTES} bytes`);
50
+ bytes = read.bytes;
51
+ }
52
+ catch (error) {
53
+ if (hasErrorCode(error, 'ENOENT'))
54
+ return { kind: 'complete', checkouts: {} };
55
+ // ELOOP: a link at the last component; EISDIR: the reader's code for a file that is not regular.
56
+ if (hasErrorCode(error, 'ELOOP', 'EISDIR'))
57
+ return recordRefusal(path, 'it is not a plain file');
58
+ return recordRefusal(path, `it could not be read: ${errorCause(error)}`);
59
+ }
60
+ const value = tryParseJson(bytes.toString('utf8'));
61
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
62
+ return recordRefusal(path, 'it is not a JSON object');
63
+ const record = value;
64
+ if (Object.keys(record).sort().join(',') !== 'checkouts,format' || record.format !== FORMAT) {
65
+ return recordRefusal(path, `it does not carry the ${FORMAT} format`);
66
+ }
67
+ const checkouts = record.checkouts;
68
+ if (checkouts === null || typeof checkouts !== 'object' || Array.isArray(checkouts)
69
+ || Object.entries(checkouts).some(([slug, directory]) => !SLUG.test(slug) || !canonicalDirectory(directory))) {
70
+ return recordRefusal(path, 'a slug or directory in it is malformed');
71
+ }
72
+ return { kind: 'complete', checkouts: Object.freeze({ ...checkouts }) };
73
+ }
74
+ export async function writeSiblingCheckouts(cockpitRoot, checkouts) {
75
+ const ordered = Object.fromEntries(Object.entries(checkouts).sort(([left], [right]) => left < right ? -1 : 1));
76
+ return writeCockpitLocalFiles(cockpitRoot, [{ name: RECORD_NAME, bytes: `${JSON.stringify({ format: FORMAT, checkouts: ordered })}\n` }]);
77
+ }
78
+ /** Every declared sibling with the directory that serves it; an override for an undeclared slug serves nothing. */
79
+ export function servedSiblings(cockpitRoot, config, checkouts) {
80
+ const siblingsRoot = resolve(cockpitRoot, config.cockpit.siblings_root);
81
+ return new Map(config.sibling.map(sibling => {
82
+ const declared = resolve(siblingsRoot, sibling.dir);
83
+ const override = Object.hasOwn(checkouts, sibling.slug) ? checkouts[sibling.slug] : undefined;
84
+ return [sibling.slug, Object.freeze({ slug: sibling.slug, declared, served: override ?? declared, overridden: override !== undefined })];
85
+ }));
86
+ }
87
+ /** The directory serving a declared sibling; a slug the map lacks is a caller's error, named as one. */
88
+ export function servedPath(served, slug) {
89
+ const entry = served.get(slug);
90
+ if (!entry)
91
+ throw new RefusalError(`sibling ${slug} has no served checkout; it is not declared in cockpit.toml`);
92
+ return entry.served;
93
+ }
94
+ export async function loadServedSiblings(cockpitRoot, config) {
95
+ const read = await readSiblingCheckouts(cockpitRoot);
96
+ return read.kind === 'complete' ? { kind: 'complete', siblings: servedSiblings(cockpitRoot, config, read.checkouts) } : read;
97
+ }
98
+ /** The overridden siblings whose recorded directory is gone, such as a removed worktree. */
99
+ export async function vanishedServedCheckouts(served) {
100
+ const overridden = [...served.values()].filter(sibling => sibling.overridden);
101
+ const present = await Promise.all(overridden.map(async (sibling) => (await lstatIfPresent(sibling.served))?.isDirectory() === true));
102
+ return overridden.filter((_, index) => !present[index]);
103
+ }
104
+ export function vanishedCheckoutFix(slug) {
105
+ return `serve the declared checkout again with ${cockpitCommand('use', { operand: slug, args: '--clear' })}, or name another checkout of it`;
106
+ }
107
+ /**
108
+ * A refusal when an override names a directory that is gone. Docker creates a missing bind-mount
109
+ * source as an empty directory, so the stack would start on nothing. Preflight and doctor report
110
+ * the same fact as the served-checkouts observation row, so all three agree.
111
+ */
112
+ export async function missingServedCheckout(served) {
113
+ const [sibling] = await vanishedServedCheckouts(served);
114
+ return sibling === undefined ? undefined : {
115
+ severity: 'error',
116
+ code: 'SIBLING_CHECKOUT_UNAVAILABLE',
117
+ message: `sibling ${sibling.slug} is recorded to be served from ${sibling.served}, which is no longer a directory`,
118
+ target: sibling.served,
119
+ fix: vanishedCheckoutFix(sibling.slug),
120
+ };
121
+ }
122
+ /** The directory variables Compose interpolates: each sibling's `var` and the directory that serves it. */
123
+ export function servedSiblingVariables(config, served) {
124
+ return Object.fromEntries(config.sibling.map(sibling => [sibling.var, servedPath(served, sibling.slug)]));
125
+ }
126
+ // Repository-local `core.fsmonitor` and hooks run code on nearly any Git command. Agents cannot write under
127
+ // `.git`, so this is defence in depth against a checkout whose configuration arrived some other way.
128
+ export const GIT_CHILD_CONFIG = ['-c', 'core.fsmonitor=false', '-c', 'core.hooksPath=/dev/null'];
129
+ // Global and system configuration can carry aliases, credential helpers and fsmonitor commands that
130
+ // run code; cockpit's Git children read only the repository's own configuration, as setup's clone does.
131
+ export async function createGitRunner(env, execute) {
132
+ const git = await resolveGitExecutable(env);
133
+ if (git.state !== 'available')
134
+ return { unavailable: git.reason };
135
+ const executable = git.value;
136
+ const environment = gitEnvironment(env);
137
+ return {
138
+ executable,
139
+ async run(cwd, id, args, { timeoutMs = 30_000, stdoutLimitBytes } = {}) {
140
+ const step = {
141
+ id, argv: [executable.canonicalPath, '-C', cwd, ...GIT_CHILD_CONFIG, ...args], argv0: executable.invocationPath,
142
+ cwd, env: environment, timeoutMs, stdoutLimitBytes: stdoutLimitBytes ?? 65_536, stderrLimitBytes: 65_536,
143
+ ...stdoutLimitBytes === undefined ? { outputOverflow: 'retain-tail' } : {},
144
+ paths: [], ports: [], labels: { purpose: id }, expected: [],
145
+ consumedFiles: [{ path: executable.canonicalPath, sha256: executable.sha256, maxBytes: 536_870_912,
146
+ argvIndexes: [0], executable: true, executionMode: executable.executionMode }],
147
+ filesystemChecks: [], compensation: [],
148
+ };
149
+ const outcome = authenticatedExecutionOutcome(await execute(step), step);
150
+ if (!outcome)
151
+ return { kind: 'failed', exitCode: -1, code: 'unauthenticated', detail: 'the execution outcome could not be authenticated', stderr: '' };
152
+ const text = (bytes) => new TextDecoder('utf-8', { fatal: false }).decode(bytes).trim();
153
+ if (outcome.kind === 'complete')
154
+ return { kind: 'complete', stdout: text(outcome.stdout) };
155
+ // Git's last line names the cause (an unreachable host, a file a merge would overwrite); a
156
+ // remote URL in it may carry credentials, and a line that cannot be redacted is withheld.
157
+ const line = text(outcome.stderr).replace(/(\w+:\/\/)[^/@\s]+@/g, '$1[redacted]@').split('\n').filter(Boolean).at(-1);
158
+ const cause = line === undefined ? undefined : redactSensitiveMessage(line.slice(0, 240));
159
+ return { kind: 'failed', exitCode: outcome.execution.exitCode, code: outcome.code,
160
+ detail: cause ? `${outcome.code}: ${cause}` : outcome.code, stderr: text(outcome.stderr) };
161
+ },
162
+ };
163
+ }
164
+ async function checkoutIdentity(git, directory) {
165
+ const located = await git.run(directory, 'checkout:identity', ['rev-parse', '--path-format=absolute', '--show-toplevel', '--git-common-dir']);
166
+ if (located.kind !== 'complete')
167
+ return { reason: `it is not a Git checkout (${located.detail})` };
168
+ const [toplevel, commonDirectory] = located.stdout.split('\n');
169
+ if (!toplevel || !commonDirectory)
170
+ return { reason: 'Git did not name its working tree and object store' };
171
+ const origin = await git.run(directory, 'checkout:origin', ['config', '--get', 'remote.origin.url']);
172
+ const identity = origin.kind === 'complete' ? repositoryIdentity(origin.stdout) : undefined;
173
+ try {
174
+ return {
175
+ toplevel: await realpath(toplevel), commonDirectory: await realpath(commonDirectory),
176
+ origin: identity?.kind === 'known' ? identity.identity : undefined,
177
+ };
178
+ }
179
+ catch (error) {
180
+ return { reason: `the working tree or object store Git named cannot be resolved (${errorCause(error)})` };
181
+ }
182
+ }
183
+ function sameIdentity(left, right) {
184
+ return left.host === right.host && left.owner.toLowerCase() === right.owner.toLowerCase()
185
+ && left.repository.toLowerCase() === right.repository.toLowerCase();
186
+ }
187
+ function spelled(identity) {
188
+ return identity === undefined ? 'no recognisable origin remote' : `${identity.host}/${identity.owner}/${identity.repository}`;
189
+ }
190
+ /**
191
+ * Whether `candidate` is the root of a checkout of the sibling's repository: it shares the declared
192
+ * checkout's object store (a linked worktree), or its origin names the same repository as the
193
+ * declared checkout's origin or the declared remote. Anything else is refused: serving an unrelated
194
+ * repository under a sibling's name would hand its code the sibling's mounts and grants.
195
+ */
196
+ export async function inspectSiblingCheckout(input) {
197
+ const unavailable = (message, fix) => ({ kind: 'refusal', diagnostic: {
198
+ severity: 'error', code: 'SIBLING_CHECKOUT_UNAVAILABLE',
199
+ message, target: input.candidate, fix,
200
+ } });
201
+ const listWorktrees = `list the checkouts of ${input.sibling.slug} with git -C ${input.declared} worktree list, and name one of them`;
202
+ let directory;
203
+ try {
204
+ directory = await realpath(input.candidate);
205
+ if (!(await lstat(directory)).isDirectory())
206
+ return unavailable(`${input.candidate} is not a directory`, listWorktrees);
207
+ }
208
+ catch (error) {
209
+ return unavailable(`${input.candidate} cannot be resolved (${errorCause(error)})`, listWorktrees);
210
+ }
211
+ // The guard admits an agent's `use` by the path as written; a link in it, swapped in after that
212
+ // check, would record and then grant a directory the agent was never allowed to reach.
213
+ if (directory !== input.candidate) {
214
+ return unavailable(`${input.candidate} reaches ${directory} through a symbolic link, so cockpit will not record it`, `name the directory itself: ${cockpitCommand('use', { operand: input.sibling.slug, args: formatShellCommand([directory]) })}`);
215
+ }
216
+ const [candidate, declared] = await Promise.all([checkoutIdentity(input.git, directory), checkoutIdentity(input.git, input.declared)]);
217
+ if ('reason' in candidate)
218
+ return unavailable(`${directory} cannot serve sibling ${input.sibling.slug}: ${candidate.reason}`, listWorktrees);
219
+ if (candidate.toplevel !== directory) {
220
+ return unavailable(`${directory} is inside the checkout at ${candidate.toplevel}, not its root, so the stack would mount part of a checkout`, `name the checkout's root: ${cockpitCommand('use', { operand: input.sibling.slug, args: formatShellCommand([candidate.toplevel]) })}`);
221
+ }
222
+ if (!('reason' in declared) && declared.commonDirectory === candidate.commonDirectory) {
223
+ return { kind: 'same-repository', directory, evidence: 'object-store' };
224
+ }
225
+ // The declared checkout may track a fork while cockpit.toml's remote names the upstream; either counts.
226
+ const declaredRemote = input.sibling.remote === undefined ? undefined : repositoryIdentity(input.sibling.remote);
227
+ const expected = [...!('reason' in declared) && declared.origin !== undefined ? [declared.origin] : [],
228
+ ...declaredRemote?.kind === 'known' ? [declaredRemote.identity] : []];
229
+ if (candidate.origin !== undefined && expected.some(identity => sameIdentity(identity, candidate.origin))) {
230
+ return { kind: 'same-repository', directory, evidence: 'remote' };
231
+ }
232
+ return { kind: 'refusal', diagnostic: {
233
+ severity: 'error', code: 'SIBLING_CHECKOUT_FOREIGN',
234
+ message: `${directory} is not a checkout of sibling ${input.sibling.slug}: it shares no object store with ${input.declared}, `
235
+ + `and its origin (${spelled(candidate.origin)}) is not the sibling's (${expected.length === 0 ? 'none known' : expected.map(spelled).join(' or ')})`,
236
+ target: input.candidate,
237
+ fix: `create a worktree of the sibling with git -C ${input.declared} worktree add <path> <branch> and name that path, or ${listWorktrees}`,
238
+ } };
239
+ }
240
+ /** The declared environment files the served checkout lacks; a fresh worktree has none of the ignored ones. */
241
+ export async function missingEnvironmentFiles(served, sibling) {
242
+ const present = await Promise.all((sibling.environment_files ?? []).map(async (file) => await lstatIfPresent(join(served.served, file.instance)) !== undefined));
243
+ return (sibling.environment_files ?? []).filter((_, index) => !present[index]).map(file => ({ template: file.template, instance: file.instance }));
244
+ }
245
+ /** One warning per environment file an overriding checkout lacks, naming who must create it and from what. */
246
+ export function missingEnvironmentFileDiagnostics(served, missing) {
247
+ return missing.map(file => ({
248
+ severity: 'warning',
249
+ code: 'SIBLING_ENVIRONMENT_FILE_MISSING',
250
+ message: `sibling ${served.slug} is served from ${served.served}, which has no ${file.instance}; the services that read it would start without its settings`,
251
+ target: join(served.served, file.instance),
252
+ fix: `a person creates ${join(served.served, file.instance)} from the template ${join(served.served, file.template)}, or copies the one in ${served.declared}; cockpit never copies environment files between checkouts`,
253
+ }));
254
+ }
@@ -0,0 +1,33 @@
1
+ import { type EnvironmentDiagnostic } from './contract.js';
2
+ /**
3
+ * One warning for each way a repository's root instruction files leave a listed agent tool without
4
+ * them. A team that uses only Claude Code loses nothing when a repository keeps CLAUDE.md alone.
5
+ */
6
+ export declare function inspectSiblingInstructions(repository: Readonly<{
7
+ slug: string;
8
+ root: string;
9
+ }>, tools: readonly string[]): Promise<readonly EnvironmentDiagnostic[]>;
10
+ export type SiblingInstructionFilesChange = 'claude-import' | 'move-to-agents';
11
+ export type SiblingInstructionFilesResult = Readonly<{
12
+ kind: 'none';
13
+ root: string;
14
+ }> | Readonly<{
15
+ kind: 'offered' | 'written';
16
+ root: string;
17
+ change: SiblingInstructionFilesChange;
18
+ }> | Readonly<{
19
+ kind: 'refusal';
20
+ root: string;
21
+ reason: string;
22
+ }>;
23
+ /**
24
+ * Makes AGENTS.md the file every tool loads and CLAUDE.md the import Claude Code needs, in the two
25
+ * cases that need no judgement: AGENTS.md alone gains a CLAUDE.md reading `@AGENTS.md`, and CLAUDE.md
26
+ * alone, when it imports nothing of its own, moves byte for byte to AGENTS.md and becomes that import.
27
+ * Without `write` it only reports the change. It writes only ordinary files directly in a canonical
28
+ * root, never through a link, and refuses when a file changed after it was read.
29
+ */
30
+ export declare function synchronizeSiblingInstructionFiles(input: Readonly<{
31
+ siblingRoot: string;
32
+ write: boolean;
33
+ }>): Promise<SiblingInstructionFilesResult>;