gentle-pi 3.7.0 → 4.1.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 (590) hide show
  1. package/README.md +155 -10
  2. package/assets/agents/gentle-ai-explore.md +13 -6
  3. package/assets/agents/gentle-ai-verify.md +27 -6
  4. package/assets/agents/gentle-ai-worker.md +13 -15
  5. package/assets/install-wizard/index.html +47 -0
  6. package/assets/install-wizard/wizard.css +1279 -0
  7. package/assets/install-wizard/wizard.js +951 -0
  8. package/assets/orchestrator-delegation.md +75 -169
  9. package/assets/orchestrator-memory.md +12 -26
  10. package/assets/orchestrator-prompts.md +48 -0
  11. package/assets/orchestrator-skills.md +1 -1
  12. package/assets/orchestrator-tracking.md +53 -0
  13. package/assets/orchestrator-verification.md +28 -0
  14. package/assets/orchestrator-writer.md +38 -0
  15. package/assets/orchestrator.md +30 -38
  16. package/assets/sounds/LICENSE.md +52 -0
  17. package/assets/sounds/attention.wav +0 -0
  18. package/assets/sounds/error.wav +0 -0
  19. package/assets/sounds/success.wav +0 -0
  20. package/assets/support/strict-tdd-verify.md +4 -266
  21. package/assets/support/strict-tdd.md +8 -360
  22. package/bin/engram-guard +9 -0
  23. package/bin/gentle-shell-install.mjs +302 -0
  24. package/bin/gentle-shell.mjs +956 -50
  25. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
  26. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
  27. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
  28. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
  29. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
  30. package/docs/bundled-install.md +397 -0
  31. package/docs/delegated-verification.md +27 -4
  32. package/docs/gentle-agents-activity.md +537 -0
  33. package/docs/gentle-shell-uninstall.md +136 -0
  34. package/docs/gentle-shell.md +165 -51
  35. package/docs/install-wizard.md +2040 -0
  36. package/docs/native-authority-architecture.md +3 -3
  37. package/docs/permission-modes.md +82 -0
  38. package/docs/prompt-history.md +280 -0
  39. package/docs/readme-reference.md +364 -231
  40. package/docs/render-cache-performance.md +28 -0
  41. package/docs/sound-notifications-proposal.md +191 -0
  42. package/docs/sound-notifications.md +114 -0
  43. package/docs/telemetry.md +1 -1
  44. package/docs/yolo-mode.md +86 -0
  45. package/extensions/child-context.ts +35 -0
  46. package/extensions/child-safety.ts +25 -0
  47. package/extensions/codegraph-tools.ts +18 -5
  48. package/extensions/gentle-agents.ts +1093 -326
  49. package/extensions/gentle-ai.ts +1636 -890
  50. package/extensions/gentle-herdr-activity.ts +81 -0
  51. package/extensions/gentle-notifications.ts +104 -0
  52. package/extensions/gentle-shell.ts +1619 -136
  53. package/extensions/gentle-stats.ts +101 -0
  54. package/extensions/gentle-todo.ts +126 -20
  55. package/extensions/history/atomic-write.ts +38 -0
  56. package/extensions/history/hide-prompts.ts +183 -0
  57. package/extensions/history/index.ts +1424 -0
  58. package/extensions/history/load-shared-history.ts +39 -0
  59. package/extensions/history/selector-helpers.ts +538 -0
  60. package/extensions/history/session-scan.ts +233 -0
  61. package/extensions/history/store.ts +1119 -0
  62. package/extensions/nan-provider.ts +6 -0
  63. package/extensions/pi-pretty.ts +39 -3
  64. package/extensions/quiet-tools.ts +254 -92
  65. package/extensions/resume-hint.ts +60 -0
  66. package/extensions/skill-registry.ts +191 -58
  67. package/extensions/startup-banner.ts +71 -33
  68. package/lib/agent-assets.ts +604 -0
  69. package/lib/agent-home.ts +10 -1
  70. package/lib/agent-model-resolution.ts +80 -0
  71. package/lib/agent-profile-pin.ts +12 -0
  72. package/lib/agent-profiles.ts +13 -3
  73. package/lib/agents-completion-delivery.ts +11 -3
  74. package/lib/agents-message-delivery.ts +181 -0
  75. package/lib/agents-messaging.ts +1 -1
  76. package/lib/agents-protocol.ts +99 -0
  77. package/lib/agents-runner.ts +180 -107
  78. package/lib/agents-view.ts +26 -3
  79. package/lib/agents-widget.ts +16 -9
  80. package/lib/append-system-prompt.ts +21 -0
  81. package/lib/auto-permission-classifier.ts +280 -0
  82. package/lib/auto-permission-runtime.ts +268 -0
  83. package/lib/background-jobs-tools.ts +202 -0
  84. package/lib/background-jobs.ts +253 -0
  85. package/lib/background-monitor.ts +154 -0
  86. package/lib/bounded-writer-admission.ts +220 -0
  87. package/lib/builtin-codemode-optout.ts +166 -0
  88. package/lib/card-content-policy.ts +85 -0
  89. package/lib/card-style-policy.ts +60 -0
  90. package/lib/child-context-files.ts +166 -0
  91. package/lib/child-package-injection.ts +62 -0
  92. package/lib/codemode-renderer.ts +195 -0
  93. package/lib/command-palette-catalog.ts +5 -10
  94. package/lib/command-palette.ts +25 -14
  95. package/lib/context-threshold.ts +26 -0
  96. package/lib/destructive-command-guard.ts +144 -0
  97. package/lib/gentle-ai-binary.ts +109 -4
  98. package/lib/gentle-ai-elapsed-store.ts +87 -0
  99. package/lib/gentle-ai-renderer.ts +196 -39
  100. package/lib/gentle-shell-bundled.ts +517 -0
  101. package/lib/gentle-shell-launcher.ts +89 -27
  102. package/lib/gentle-shell-resume-hint.ts +176 -0
  103. package/lib/gentle-shell-startup-flow.ts +54 -0
  104. package/lib/gentle-shell-startup-update.ts +92 -0
  105. package/lib/gentle-shell-uninstall.ts +840 -0
  106. package/lib/gentle-shell-update-panel.ts +231 -0
  107. package/lib/herdr-activity.ts +183 -0
  108. package/lib/history-capture-policy.ts +95 -0
  109. package/lib/inprocess-reviewer.ts +3 -0
  110. package/lib/jobs-sidebar-state.ts +20 -0
  111. package/lib/jobs-view.ts +161 -0
  112. package/lib/model-price-ratio.ts +83 -0
  113. package/lib/model-routing-authority.ts +5 -1
  114. package/lib/nan-provider.ts +249 -0
  115. package/lib/native-review-cli.ts +242 -180
  116. package/lib/notification-audio-native.ts +162 -0
  117. package/lib/notification-audio-windows.ts +399 -0
  118. package/lib/notification-audio.ts +332 -0
  119. package/lib/notification-customize.ts +241 -0
  120. package/lib/notification-events.ts +113 -0
  121. package/lib/notification-policy.ts +148 -0
  122. package/lib/notification-pulse-client.ts +367 -0
  123. package/lib/notification-pulse-protocol.ts +305 -0
  124. package/lib/notification-pulse-stream.ts +213 -0
  125. package/lib/notification-pulse-worker.ts +66 -0
  126. package/lib/notification-scheduler.ts +173 -0
  127. package/lib/notification-service.ts +202 -0
  128. package/lib/notification-ui.ts +87 -0
  129. package/lib/odd-phase-inference.ts +231 -0
  130. package/lib/odd-phase.ts +141 -0
  131. package/lib/orchestrator-alias.ts +20 -0
  132. package/lib/orchestrator-catalog.ts +79 -0
  133. package/lib/orchestrator-consultation.ts +61 -0
  134. package/lib/orchestrator-discovery.ts +49 -0
  135. package/lib/orchestrator-helper-consent.ts +122 -0
  136. package/lib/orchestrator-helper.ts +167 -0
  137. package/lib/orchestrator-message-card.ts +105 -0
  138. package/lib/orchestrator-presence.ts +115 -14
  139. package/lib/orchestrator-scope.ts +80 -0
  140. package/lib/orchestrator-state.ts +107 -0
  141. package/lib/orchestrator-tool-card.ts +280 -0
  142. package/lib/orchestrator-work-search.ts +218 -0
  143. package/lib/orchestrator-work.ts +79 -0
  144. package/lib/overlay-repaint.ts +26 -0
  145. package/lib/permission-mode-policy.ts +59 -0
  146. package/lib/pi-tui-keys.ts +53 -0
  147. package/lib/review-candidate-view-owner.ts +73 -21
  148. package/lib/review-candidate-view.ts +115 -27
  149. package/lib/review-compact-contract.ts +2 -2
  150. package/lib/review-host-relay.ts +7 -2
  151. package/lib/review-integration-v2.ts +100 -22
  152. package/lib/review-publication-gate.ts +1 -1
  153. package/lib/review-reminder-receipt.ts +48 -8
  154. package/lib/review-risk-assessment.ts +227 -56
  155. package/lib/review-sidebar-state.ts +224 -0
  156. package/lib/review-transaction.ts +72 -3
  157. package/lib/runtime-metrics-children.ts +1 -1
  158. package/lib/runtime-metrics-native.ts +3 -2
  159. package/lib/selection-engine.ts +527 -0
  160. package/lib/session-change-capture.ts +5 -4
  161. package/lib/session-changes.ts +27 -22
  162. package/lib/session-messaging-grants.ts +135 -0
  163. package/lib/session-profile-binding.ts +83 -0
  164. package/lib/session-profile-freeze.ts +266 -0
  165. package/lib/session-worktree-registry.ts +87 -9
  166. package/lib/shell-bar.ts +215 -30
  167. package/lib/shell-card.ts +287 -18
  168. package/lib/shell-changes-view.ts +42 -14
  169. package/lib/shell-changes.ts +86 -71
  170. package/lib/shell-prompt.ts +102 -6
  171. package/lib/shell-sidebar-layout.ts +83 -15
  172. package/lib/shell-sidebar.ts +15 -1
  173. package/lib/shell-todo.ts +84 -31
  174. package/lib/shell-usage-view.ts +9 -4
  175. package/lib/shell-usage.ts +70 -13
  176. package/lib/stats-collector.ts +381 -0
  177. package/lib/stats-view.ts +431 -0
  178. package/lib/status-timing-diagnostics.ts +201 -0
  179. package/lib/telemetry-trigger.ts +3 -1
  180. package/lib/theme-customization.ts +52 -0
  181. package/lib/vim-editor-adapter.ts +383 -0
  182. package/lib/vim-normal-engine.ts +154 -0
  183. package/lib/vim-operator-engine.ts +416 -0
  184. package/lib/vim-policy.ts +49 -0
  185. package/lib/vim-visual-engine.ts +107 -0
  186. package/lib/visual-customization-policy.ts +119 -0
  187. package/lib/visual-customize-view.ts +481 -0
  188. package/lib/visual-profiles.ts +228 -0
  189. package/lib/writer-surfaces.ts +162 -0
  190. package/lib/yolo-session-policy.ts +317 -0
  191. package/package.json +20 -8
  192. package/runtime/child-package-injection.mjs +63 -0
  193. package/runtime/gentle-ai-binary.mjs +109 -4
  194. package/runtime/gentle-shell-bundled.mjs +518 -0
  195. package/runtime/gentle-shell-launcher.mjs +87 -25
  196. package/runtime/gentle-shell-resume-hint.mjs +177 -0
  197. package/runtime/gentle-shell-startup-flow.mjs +55 -0
  198. package/runtime/gentle-shell-startup-update.mjs +93 -0
  199. package/runtime/gentle-shell-uninstall.mjs +841 -0
  200. package/runtime/gentle-shell-update-panel.mjs +232 -0
  201. package/runtime/native-review-cli.mjs +239 -177
  202. package/runtime/review-integration-v2.mjs +100 -22
  203. package/runtime/review-risk-assessment.mjs +225 -54
  204. package/runtime/status-timing-diagnostics.mjs +202 -0
  205. package/runtime/telemetry-trigger.mjs +3 -1
  206. package/scripts/bootstrap.cmd +255 -0
  207. package/scripts/bootstrap.sh +168 -0
  208. package/scripts/build-distribution.mjs +157 -0
  209. package/scripts/build-installer-bundles.mjs +136 -0
  210. package/scripts/build-runtime-modules.mjs +8 -0
  211. package/scripts/bundled-install.mjs +1308 -0
  212. package/scripts/bundled-wizard.mjs +399 -0
  213. package/scripts/check-types.mjs +4 -6
  214. package/scripts/gentle-ai-installer.mjs +83 -35
  215. package/scripts/install-gentle-ai.mjs +48 -1
  216. package/scripts/install-tui-mode-setting.mjs +10 -1
  217. package/scripts/install-wizard-preview.mjs +154 -0
  218. package/scripts/installer-downloads.mjs +648 -0
  219. package/scripts/installer-preflight.mjs +387 -0
  220. package/scripts/installer-probes.mjs +564 -0
  221. package/scripts/installer-runner.mjs +1102 -0
  222. package/scripts/installer-server.mjs +881 -0
  223. package/scripts/installer-windows-artifacts.json +15 -0
  224. package/scripts/installer-windows.mjs +752 -0
  225. package/scripts/main-channel.mjs +452 -0
  226. package/scripts/mirror-odd-routing.mjs +2 -2
  227. package/scripts/npm-pack-result.mjs +31 -0
  228. package/scripts/render-cache-performance-worker.mjs +205 -0
  229. package/scripts/render-cache-performance.mjs +115 -0
  230. package/scripts/run-test-suite.mjs +90 -0
  231. package/scripts/test-installer-acceptance.sh +161 -0
  232. package/scripts/test-packed-runner.mjs +54 -19
  233. package/scripts/types-baseline.json +2 -92
  234. package/scripts/verify-package-files.mjs +91 -38
  235. package/skills/branch-pr/SKILL.md +24 -52
  236. package/skills/chained-pr/SKILL.md +31 -15
  237. package/skills/chained-pr/references/chaining-details.md +31 -20
  238. package/skills/gentle-ai/SKILL.md +16 -21
  239. package/skills/issue-creation/SKILL.md +8 -2
  240. package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
  241. package/skills/work-unit-commits/SKILL.md +4 -3
  242. package/tests/agent-home.test.ts +56 -3
  243. package/tests/agent-model-resolution.test.ts +113 -0
  244. package/tests/agent-profiles.test.ts +30 -0
  245. package/tests/agent-risk-escalation.test.ts +123 -0
  246. package/tests/agents-completion-delivery.test.ts +17 -0
  247. package/tests/agents-fake-child.ts +2 -2
  248. package/tests/agents-message-delivery.test.ts +106 -0
  249. package/tests/agents-protocol.test.ts +97 -0
  250. package/tests/agents-queries.test.ts +9 -9
  251. package/tests/agents-runner-process.test.ts +31 -0
  252. package/tests/agents-runner.test.ts +467 -93
  253. package/tests/agents-view-thread-identity.test.ts +169 -0
  254. package/tests/agents-view.test.ts +8 -2
  255. package/tests/agents-widget.test.ts +154 -15
  256. package/tests/append-system-prompt-route.test.ts +160 -0
  257. package/tests/append-system-prompt.test.ts +46 -0
  258. package/tests/artifact-language.test.ts +19 -213
  259. package/tests/ask-user-question.test.ts +44 -1
  260. package/tests/asset-installation-runtime.test.ts +5 -16
  261. package/tests/auto-mode-invariants.test.ts +258 -0
  262. package/tests/auto-mode-sdk.test.ts +57 -0
  263. package/tests/auto-mode.test.ts +49 -0
  264. package/tests/auto-permission-classifier.test.ts +283 -0
  265. package/tests/auto-permission-runtime.test.ts +196 -0
  266. package/tests/autonomous-guard.test.ts +69 -1
  267. package/tests/background-cache-warming.test.ts +2 -1
  268. package/tests/background-jobs.test.ts +227 -0
  269. package/tests/background-monitor.test.ts +195 -0
  270. package/tests/background-subagents-default-mode.test.ts +28 -5
  271. package/tests/bounded-stop-contract.test.ts +85 -0
  272. package/tests/bounded-writer-admission.test.ts +146 -0
  273. package/tests/branch-pr-skill.test.ts +43 -0
  274. package/tests/bridge-wake-lifecycle.test.ts +129 -0
  275. package/tests/bridge-wake-rendering.test.ts +56 -0
  276. package/tests/build-distribution.test.ts +269 -0
  277. package/tests/builtin-codemode-optout.test.ts +242 -0
  278. package/tests/bundled-install.test.ts +1319 -0
  279. package/tests/bundled-wizard.test.ts +505 -0
  280. package/tests/card-content-policy.test.ts +68 -0
  281. package/tests/card-style-policy.test.ts +87 -0
  282. package/tests/chained-pr-skill.test.ts +124 -0
  283. package/tests/child-codegraph-bootstrap.test.ts +74 -0
  284. package/tests/child-context-files.test.ts +255 -0
  285. package/tests/child-package-entrypoints.test.ts +65 -0
  286. package/tests/child-package-injection.test.ts +74 -0
  287. package/tests/child-safety.test.ts +94 -0
  288. package/tests/child-web-package.test.ts +224 -0
  289. package/tests/codegraph-tools.test.ts +35 -0
  290. package/tests/codemode-rendering.test.ts +596 -0
  291. package/tests/command-palette.test.ts +43 -7
  292. package/tests/context-threshold.test.ts +31 -0
  293. package/tests/delegate-for-reason-contract.test.ts +327 -0
  294. package/tests/delegated-key-learnings-contract.test.ts +3 -78
  295. package/tests/destructive-command-guard.test.ts +84 -0
  296. package/tests/devbinary/native-review-parity.devtest.ts +171 -3
  297. package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
  298. package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
  299. package/tests/engram-safety.test.ts +288 -0
  300. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
  301. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
  302. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
  303. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
  304. package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
  305. package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
  306. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
  307. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
  308. package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
  309. package/tests/generic-agent-tools.test.ts +69 -0
  310. package/tests/gentle-agents.test.ts +2804 -346
  311. package/tests/gentle-ai-binary-isolation.test.ts +135 -0
  312. package/tests/gentle-ai-binary.test.ts +21 -4
  313. package/tests/gentle-ai-child-guards.test.ts +202 -0
  314. package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
  315. package/tests/gentle-ai-elapsed-store.test.ts +68 -0
  316. package/tests/gentle-ai-installer.test.ts +566 -62
  317. package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
  318. package/tests/gentle-ai-renderer.test.ts +493 -14
  319. package/tests/gentle-ai.test.ts +1427 -236
  320. package/tests/gentle-card-text.ts +2 -1
  321. package/tests/gentle-herdr-activity.test.ts +100 -0
  322. package/tests/gentle-notifications.test.ts +427 -0
  323. package/tests/gentle-shell-bin.test.ts +896 -115
  324. package/tests/gentle-shell-bundled.test.ts +1002 -0
  325. package/tests/gentle-shell-launcher.test.ts +333 -65
  326. package/tests/gentle-shell-resume-hint.test.ts +270 -0
  327. package/tests/gentle-shell-startup-flow.test.ts +75 -0
  328. package/tests/gentle-shell-startup-update.test.ts +85 -0
  329. package/tests/gentle-shell-uninstall.test.ts +978 -0
  330. package/tests/gentle-shell-update-panel.test.ts +158 -0
  331. package/tests/gentle-shell.test.ts +4216 -119
  332. package/tests/gentle-stats.test.ts +152 -0
  333. package/tests/gentle-theme.test.ts +4 -1
  334. package/tests/gentle-todo.test.ts +333 -10
  335. package/tests/herdr-activity.test.ts +243 -0
  336. package/tests/history-agent-home.test.ts +81 -0
  337. package/tests/history-atomic-write.test.ts +57 -0
  338. package/tests/history-capture-policy.test.ts +102 -0
  339. package/tests/history-child-guard.test.ts +70 -0
  340. package/tests/history-command-registration.test.ts +164 -0
  341. package/tests/history-dedupe-entries.test.ts +123 -0
  342. package/tests/history-delete-backfill.test.ts +190 -0
  343. package/tests/history-delete-confirm.test.ts +460 -0
  344. package/tests/history-dispatch.test.ts +180 -0
  345. package/tests/history-drain-hidden.test.ts +110 -0
  346. package/tests/history-drain-order.test.ts +98 -0
  347. package/tests/history-expanded-globals.test.ts +62 -0
  348. package/tests/history-gc.test.ts +832 -0
  349. package/tests/history-header-layout.test.ts +265 -0
  350. package/tests/history-hide-prompts.test.ts +275 -0
  351. package/tests/history-lazy-windowing.test.ts +508 -0
  352. package/tests/history-legacy-migrate-v2.test.ts +297 -0
  353. package/tests/history-load-shared-history.test.ts +53 -0
  354. package/tests/history-max-results-cap.test.ts +76 -0
  355. package/tests/history-multi-reader.test.ts +203 -0
  356. package/tests/history-off-path.test.ts +170 -0
  357. package/tests/history-openflow-integration.test.ts +173 -0
  358. package/tests/history-overlay-margin.test.ts +326 -0
  359. package/tests/history-preview-layout.test.ts +93 -0
  360. package/tests/history-registry.test.ts +143 -0
  361. package/tests/history-scope-delete.test.ts +411 -0
  362. package/tests/history-search-caret-keys.test.ts +142 -0
  363. package/tests/history-seed-bootstrap.test.ts +170 -0
  364. package/tests/history-seed-regen.test.ts +129 -0
  365. package/tests/history-selector-windowing.test.ts +94 -0
  366. package/tests/history-session-scan-directory.test.ts +87 -0
  367. package/tests/history-session-scan-extract.test.ts +583 -0
  368. package/tests/history-session-writer.test.ts +351 -0
  369. package/tests/history-store-paths.test.ts +79 -0
  370. package/tests/history-tombstone-exact.test.ts +139 -0
  371. package/tests/history-wheel-mouse.test.ts +242 -0
  372. package/tests/inprocess-reviewer.test.ts +49 -19
  373. package/tests/install-gentle-ai-failure.test.ts +69 -0
  374. package/tests/install-tui-mode-setting.test.ts +15 -0
  375. package/tests/install-wizard.test.ts +1223 -0
  376. package/tests/installer-bundles.test.ts +122 -0
  377. package/tests/installer-posix-bootstrap.test.ts +1201 -0
  378. package/tests/installer-preflight.test.ts +750 -0
  379. package/tests/installer-probes.test.ts +1106 -0
  380. package/tests/installer-runner.test.ts +2158 -0
  381. package/tests/installer-server.test.ts +1567 -0
  382. package/tests/installer-windows-bootstrap.test.ts +2410 -0
  383. package/tests/issue-creation-skill.test.ts +61 -0
  384. package/tests/jobs-view.test.ts +160 -0
  385. package/tests/main-channel-upgrade.test.ts +389 -0
  386. package/tests/main-channel.test.ts +344 -0
  387. package/tests/maintainer/provider-relay.maintest.ts +13 -12
  388. package/tests/model-price-ratio.test.ts +116 -0
  389. package/tests/model-profiles-preservation.test.ts +153 -0
  390. package/tests/model-routing-authority.test.ts +16 -0
  391. package/tests/model-routing-package.test.ts +10 -0
  392. package/tests/nan-provider-runtime.test.ts +156 -0
  393. package/tests/nan-provider.test.ts +481 -0
  394. package/tests/native-binary-gate.test.ts +2 -0
  395. package/tests/native-review-capability-contract.test.ts +13 -1
  396. package/tests/native-review-cli.test.ts +239 -118
  397. package/tests/native-review-consent.test.ts +2 -2
  398. package/tests/native-review-parity-runtime.test.ts +101 -4
  399. package/tests/native-review-parity.test.ts +12 -48
  400. package/tests/native-risk-evidence-parity.test.ts +21 -0
  401. package/tests/no-self-pause-contract.test.ts +87 -0
  402. package/tests/notification-audio-native.test.ts +281 -0
  403. package/tests/notification-audio-windows.test.ts +581 -0
  404. package/tests/notification-audio.test.ts +548 -0
  405. package/tests/notification-customize.test.ts +655 -0
  406. package/tests/notification-events.test.ts +115 -0
  407. package/tests/notification-policy.test.ts +201 -0
  408. package/tests/notification-pulse-client.test.ts +356 -0
  409. package/tests/notification-pulse-protocol.test.ts +213 -0
  410. package/tests/notification-pulse-stream.test.ts +320 -0
  411. package/tests/notification-scheduler.test.ts +258 -0
  412. package/tests/notification-ui.test.ts +157 -0
  413. package/tests/npm-pack-result.test.ts +41 -0
  414. package/tests/odd-integration.test.ts +76 -0
  415. package/tests/odd-phase-inference.test.ts +213 -0
  416. package/tests/odd-phase-loader.test.ts +253 -0
  417. package/tests/odd-phase.test.ts +307 -0
  418. package/tests/odd-routing-canonical-ratchet.test.ts +60 -14
  419. package/tests/odd-routing-contract.test.ts +158 -50
  420. package/tests/orchestrator-budget.test.ts +43 -45
  421. package/tests/orchestrator-catalog.test.ts +146 -0
  422. package/tests/orchestrator-consultation-sdk.test.ts +614 -0
  423. package/tests/orchestrator-consultation.test.ts +111 -0
  424. package/tests/orchestrator-discovery.test.ts +241 -0
  425. package/tests/orchestrator-helper-consent.test.ts +171 -0
  426. package/tests/orchestrator-helper.test.ts +297 -0
  427. package/tests/orchestrator-message-card.test.ts +190 -0
  428. package/tests/orchestrator-presence.test.ts +23 -0
  429. package/tests/orchestrator-rdd-ownership.test.ts +8 -7
  430. package/tests/orchestrator-scope.test.ts +107 -0
  431. package/tests/orchestrator-state.test.ts +114 -0
  432. package/tests/orchestrator-status-publication-contract.test.ts +34 -0
  433. package/tests/orchestrator-tool-card.test.ts +398 -0
  434. package/tests/orchestrator-work-search.test.ts +318 -0
  435. package/tests/orchestrator-work.test.ts +147 -0
  436. package/tests/overlay-repaint.test.ts +74 -0
  437. package/tests/package-manifest.test.ts +344 -123
  438. package/tests/packed-runner-owned-path.test.ts +80 -0
  439. package/tests/parallel-reason-contract.test.ts +21 -0
  440. package/tests/parallel-writer-admission.test.ts +210 -0
  441. package/tests/permission-mode-policy.test.ts +194 -0
  442. package/tests/persona-single-channel.test.ts +10 -18
  443. package/tests/pi-pretty.test.ts +56 -0
  444. package/tests/provider-defect-handoff.test.ts +7 -14
  445. package/tests/quiet-bash-runtime.test.ts +76 -0
  446. package/tests/quiet-tool-rendering.test.ts +569 -187
  447. package/tests/rdd-aware-verification-contract.test.ts +103 -40
  448. package/tests/rdd-status-line.test.ts +9 -4
  449. package/tests/render-cache-performance.test.ts +48 -0
  450. package/tests/resume-hint-extension.test.ts +122 -0
  451. package/tests/review-agent-end-preflight.test.ts +237 -21
  452. package/tests/review-candidate-owner-retry.test.ts +22 -1
  453. package/tests/review-candidate-view.test.ts +399 -12
  454. package/tests/review-contract-prompt.test.ts +138 -44
  455. package/tests/review-controller-lock-status.test.ts +0 -1
  456. package/tests/review-controller-native-recovery.test.ts +327 -17
  457. package/tests/review-controller-native-routing.test.ts +866 -6
  458. package/tests/review-controller-workspace-root.test.ts +206 -6
  459. package/tests/review-controller.test.ts +2 -2
  460. package/tests/review-correction-lifecycle.test.ts +2 -1
  461. package/tests/review-gate.test.ts +7 -4
  462. package/tests/review-graph-schema.test.ts +2 -2
  463. package/tests/review-host-relay-routing.test.ts +342 -4
  464. package/tests/review-host-relay.test.ts +49 -2
  465. package/tests/review-integration-v2-forward.test.ts +221 -1
  466. package/tests/review-json-arguments.test.ts +167 -0
  467. package/tests/review-last-event-closure.test.ts +157 -8
  468. package/tests/review-ledger-contract.test.ts +14 -37
  469. package/tests/review-policy-judgment-day.test.ts +1 -1
  470. package/tests/review-policy-ordinary.test.ts +2 -2
  471. package/tests/review-relay-transport-agent.test.ts +66 -1
  472. package/tests/review-reminder-receipt.test.ts +47 -1
  473. package/tests/review-risk-assessment.test.ts +594 -26
  474. package/tests/review-session-standing-permission-controller.test.ts +16 -17
  475. package/tests/review-session-standing-permission-ipc.test.ts +6 -5
  476. package/tests/review-sidebar-state.test.ts +402 -0
  477. package/tests/review-test-fixtures.ts +7 -1
  478. package/tests/review-transaction.test.ts +18 -1
  479. package/tests/run-test-suite.test.ts +202 -0
  480. package/tests/runtime-harness.mjs +186 -800
  481. package/tests/runtime-metrics-children.test.ts +16 -23
  482. package/tests/runtime-metrics-extension.test.ts +1 -1
  483. package/tests/runtime-metrics-native.test.ts +31 -1
  484. package/tests/selection-engine.test.ts +451 -0
  485. package/tests/session-change-capture.test.ts +10 -10
  486. package/tests/session-changes-shell.test.ts +242 -48
  487. package/tests/session-changes.test.ts +17 -24
  488. package/tests/session-messaging-grants.test.ts +255 -0
  489. package/tests/session-profile-binding.test.ts +163 -0
  490. package/tests/session-profile-freeze.test.ts +381 -0
  491. package/tests/session-subject-routing-contract.test.ts +34 -0
  492. package/tests/session-worktree-registry.test.ts +134 -2
  493. package/tests/shell-bar.test.ts +422 -1
  494. package/tests/shell-card.test.ts +353 -1
  495. package/tests/shell-changes-view.test.ts +135 -32
  496. package/tests/shell-changes.test.ts +61 -62
  497. package/tests/shell-prompt.test.ts +94 -2
  498. package/tests/shell-render-cache-runtime.test.ts +69 -0
  499. package/tests/shell-render-cache.test.ts +129 -0
  500. package/tests/shell-sidebar-layout.test.ts +351 -22
  501. package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
  502. package/tests/shell-todo.test.ts +207 -3
  503. package/tests/shell-usage-view.test.ts +28 -1
  504. package/tests/shell-usage.test.ts +108 -0
  505. package/tests/short-check-routing-contract.test.ts +49 -0
  506. package/tests/skill-registry-watcher.test.ts +119 -0
  507. package/tests/skill-registry.test.ts +357 -2
  508. package/tests/startup-banner.test.ts +246 -4
  509. package/tests/startup-home-repo.test.ts +170 -0
  510. package/tests/stats-collector.test.ts +195 -0
  511. package/tests/stats-view.test.ts +202 -0
  512. package/tests/status-timing-diagnostics.test.ts +283 -0
  513. package/tests/support/orchestrator-modules.ts +19 -0
  514. package/tests/task-size-routing-contract.test.ts +241 -0
  515. package/tests/telemetry-trigger.test.ts +107 -20
  516. package/tests/test-strength-contract.test.ts +124 -0
  517. package/tests/theme-customization.test.ts +72 -0
  518. package/tests/verify-agent-contract.test.ts +90 -0
  519. package/tests/verify-first-launch-contract.test.ts +139 -0
  520. package/tests/verify-package-files.test.ts +56 -0
  521. package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
  522. package/tests/vim-editor-adapter.test.ts +804 -0
  523. package/tests/vim-normal-engine.test.ts +101 -0
  524. package/tests/vim-operator-engine.test.ts +215 -0
  525. package/tests/vim-policy.test.ts +19 -0
  526. package/tests/vim-visual-engine.test.ts +52 -0
  527. package/tests/visual-customization-policy.test.ts +139 -0
  528. package/tests/visual-customize-view.test.ts +696 -0
  529. package/tests/visual-profiles.test.ts +87 -0
  530. package/tests/writer-edit-surface-scope.test.ts +59 -2
  531. package/tests/writer-surfaces.test.ts +199 -0
  532. package/tests/yolo-customize.test.ts +271 -0
  533. package/tests/yolo-mode-runtime.test.ts +188 -0
  534. package/tests/yolo-mode.test.ts +289 -0
  535. package/tests/yolo-session-policy.test.ts +59 -0
  536. package/themes/Gentle.json +2 -1
  537. package/themes/Gentleman-Cute.json +2 -1
  538. package/themes/Gentleman-Sexy.json +2 -1
  539. package/assets/agents/sdd-apply.md +0 -159
  540. package/assets/agents/sdd-archive.md +0 -228
  541. package/assets/agents/sdd-design.md +0 -49
  542. package/assets/agents/sdd-explore.md +0 -48
  543. package/assets/agents/sdd-init.md +0 -56
  544. package/assets/agents/sdd-onboard.md +0 -52
  545. package/assets/agents/sdd-proposal.md +0 -64
  546. package/assets/agents/sdd-remediate.md +0 -37
  547. package/assets/agents/sdd-research.md +0 -49
  548. package/assets/agents/sdd-spec.md +0 -192
  549. package/assets/agents/sdd-status.md +0 -54
  550. package/assets/agents/sdd-tasks.md +0 -108
  551. package/assets/agents/sdd-verify.md +0 -124
  552. package/assets/chains/sdd-full.chain.md +0 -83
  553. package/assets/chains/sdd-plan.chain.md +0 -56
  554. package/assets/chains/sdd-verify.chain.md +0 -43
  555. package/assets/sdd-orchestrator-workflow.md +0 -319
  556. package/assets/support/sdd-status-contract.md +0 -77
  557. package/docs/assets/diagrams/sdd-cycle.svg +0 -14
  558. package/extensions/sdd-init.ts +0 -816
  559. package/lib/openspec-deltas.ts +0 -156
  560. package/lib/sdd-preflight.ts +0 -1066
  561. package/lib/sdd-research-capabilities.ts +0 -94
  562. package/lib/sdd-status.ts +0 -26
  563. package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
  564. package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
  565. package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
  566. package/tests/openspec-deltas.test.ts +0 -209
  567. package/tests/sdd-agent-tools.test.ts +0 -156
  568. package/tests/sdd-archive-replay.test.ts +0 -82
  569. package/tests/sdd-classical-continuation.test.ts +0 -74
  570. package/tests/sdd-execution-routing-contract.test.ts +0 -44
  571. package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
  572. package/tests/sdd-native-managed-uptake.test.ts +0 -243
  573. package/tests/sdd-no-attempts-contract.test.ts +0 -15
  574. package/tests/sdd-odd-integration.test.ts +0 -33
  575. package/tests/sdd-optional-research.test.ts +0 -124
  576. package/tests/sdd-planning-routing-contract.test.ts +0 -45
  577. package/tests/sdd-preflight-rpc-input.test.ts +0 -125
  578. package/tests/sdd-preflight.test.ts +0 -541
  579. package/tests/sdd-research-capabilities.test.ts +0 -114
  580. package/tests/sdd-research-live.test.ts +0 -241
  581. package/tests/sdd-selection-transport.test.ts +0 -653
  582. package/tests/sdd-status.test.ts +0 -9
  583. package/tests/sdd-task-truth.test.ts +0 -43
  584. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
  585. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
  586. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
  587. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  588. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
  589. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
  590. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -0,0 +1,168 @@
1
+ #!/bin/sh
2
+ # Run from an extracted/checkout bundle, not from a remote pipe.
3
+ # Only prerequisite tooling is acquired here; standard installation belongs to T4.
4
+ set -eu
5
+ umask 077
6
+ fail() { printf '%s\n' "Bootstrap: $1" >&2; exit 1; }
7
+ need() { command -v "$1" >/dev/null 2>&1 || fail "Required utility missing: $1"; }
8
+ for utility in dirname awk; do need "$utility"; done
9
+ bundle=$(CDPATH= cd -P "$(dirname "$0")/.." && pwd) || fail 'Cannot locate installation bundle'
10
+ entry=$bundle/bin/gentle-shell-install.mjs
11
+ [ -f "$entry" ] && [ ! -L "$entry" ] || fail 'Future wizard entry is missing; this bundle cannot start a live wizard'
12
+ [ -f "$bundle/scripts/installer-downloads.mjs" ] || fail 'Download helper missing from bundle'
13
+ for utility in uname mkdir mktemp chmod mv rm sleep wc id ls; do need "$utility"; done
14
+ case ${HOME:-} in /*) ;; *) fail 'HOME must be an absolute per-user directory';; esac
15
+ # Refuse symlink ancestors and group/world-writable acquisition parents.
16
+ check_home=$HOME
17
+ while [ "$check_home" != / ]; do
18
+ [ ! -L "$check_home" ] || fail 'Unsafe symlink in HOME path'
19
+ check_home=$(dirname "$check_home")
20
+ done
21
+ [ -d "$HOME" ] || fail 'HOME directory is missing'
22
+ uid=$(id -u)
23
+ ls -nd "$HOME" | awk -v uid="$uid" '$3 == uid && substr($1,6,1) != "w" && substr($1,9,1) != "w" {ok=1} END {exit !ok}' || fail 'Unsafe HOME ownership or permissions'
24
+ minimum=$(awk -F '"' '{for (i=2; i<=NF-2; i+=2) if ($i == "node" && $(i+2) ~ /^>=[0-9]+\.[0-9]+\.[0-9]+$/) {value=$(i+2); sub(/^>=/, "", value); count++}} END {if (count == 1) print value}' "$bundle/package.json")
25
+ [ -n "$minimum" ] || fail 'Cannot establish repository Node minimum'
26
+ version_ok() {
27
+ awk -v value="$1" -v minimum="$2" 'BEGIN {
28
+ sub(/^v/, "", value)
29
+ if (value !~ /^[0-9]+\.[0-9]+\.[0-9]+$/) exit 1
30
+ split(value, a, "."); split(minimum, b, ".")
31
+ for (i=1; i<=3; i++) if (length(a[i]) > 9 || (length(a[i]) > 1 && substr(a[i],1,1) == "0")) exit 1
32
+ for (i=1; i<=3; i++) {
33
+ if (a[i]+0 > b[i]+0) exit 0
34
+ if (a[i]+0 < b[i]+0) exit 1
35
+ }
36
+ }'
37
+ }
38
+ # Any stable version at all: version_ok against 0.0.0 accepts exactly those.
39
+ stable_version() { version_ok "$1" 0.0.0; }
40
+ # A stock POSIX watchdog avoids assuming GNU timeout on macOS.
41
+ # These fixed prerequisite probes get an unignorable deadline, not graceful TERM.
42
+ # Signal only our direct child PID; do not kill inherited process groups.
43
+ bounded() {
44
+ seconds=$1
45
+ shift
46
+ "$@" &
47
+ version_pid=$!
48
+ (
49
+ sleep "$seconds" &
50
+ sleeper=$!
51
+ trap 'kill -s KILL "$sleeper" 2>/dev/null || :; wait "$sleeper" 2>/dev/null || :; exit 0' TERM
52
+ wait "$sleeper" || :
53
+ kill -s KILL "$version_pid" 2>/dev/null || :
54
+ ) >/dev/null 2>&1 &
55
+ watchdog=$!
56
+ result=0
57
+ wait "$version_pid" || result=$?
58
+ kill "$watchdog" 2>/dev/null || :
59
+ wait "$watchdog" 2>/dev/null || :
60
+ return "$result"
61
+ }
62
+ tools=
63
+ owned=0
64
+ # Only the exact directory this attempt claimed; rm -rf never follows links in it.
65
+ owned_tools() {
66
+ [ "$owned" = 1 ] && [ -n "$tools" ] || return 1
67
+ case "$tools" in "$HOME"/.gentle-shell-bootstrap-tools.*/*) return 1;; "$HOME"/.gentle-shell-bootstrap-tools.*) ;; *) return 1;; esac
68
+ [ -d "$tools" ] && [ ! -L "$tools" ] && [ -d "$tools/.claim" ] && [ ! -L "$tools/.claim" ]
69
+ }
70
+ cleanup() {
71
+ if owned_tools; then
72
+ rm -rf "$tools"
73
+ fi
74
+ }
75
+ trap cleanup EXIT
76
+ trap 'exit 1' HUP INT TERM
77
+ make_tools() {
78
+ tools=$(mktemp -d "$HOME/.gentle-shell-bootstrap-tools.XXXXXXXX") || fail 'Cannot create private tooling directory'
79
+ case "$tools" in "$HOME"/.gentle-shell-bootstrap-tools.*) ;; *) fail 'Conflicting tooling destination';; esac
80
+ [ -d "$tools" ] && [ ! -L "$tools" ] || fail 'Unsafe tooling destination'
81
+ ls -nd "$tools" | awk -v uid="$uid" '$3 == uid && $1 == "drwx------" {ok=1} END {exit !ok}' || fail 'Unsafe tooling ownership'
82
+ # No existing destination is ever reused or replaced.
83
+ mkdir "$tools/.claim" || fail 'Conflicting tooling destination'
84
+ owned=1
85
+ printf '%s\n' 'gentle-pi prerequisite tooling only' > "$tools/.bootstrap-owned" || fail 'Cannot mark tooling ownership'
86
+ }
87
+ acquire_node=1
88
+ if command -v node >/dev/null 2>&1; then
89
+ node=$(command -v node)
90
+ version=$(bounded 10 "$node" --version 2>/dev/null) || fail 'Existing Node version probe failed'
91
+ if version_ok "$version" "$minimum"; then acquire_node=0
92
+ # An older stable Node is left as it is: the verified Node below runs the
93
+ # installer instead, exactly as when Node is absent.
94
+ elif ! stable_version "$version"; then fail 'Existing Node is incompatible or unknown; refusing replacement'; fi
95
+ fi
96
+ if [ "$acquire_node" = 1 ]; then
97
+ os=$(uname -s)
98
+ arch=$(uname -m)
99
+ case "$os" in Darwin) os=darwin;; Linux) os=linux;; *) fail 'Unsupported POSIX platform';; esac
100
+ case "$arch" in x86_64|amd64) arch=x64;; aarch64|arm64) arch=arm64;; *) fail 'Unsupported architecture';; esac
101
+ if [ "$os" = linux ]; then
102
+ need getconf
103
+ libc=$(getconf GNU_LIBC_VERSION 2>/dev/null) || fail 'Cannot establish native glibc compatibility; musl is not supported'
104
+ case "$libc" in 'glibc '*) ;; *) fail 'Unsupported libc; only native glibc acquisition is planned';; esac
105
+ version_ok "${libc#glibc }.0" '2.28.0' || fail 'Native Node requires glibc >=2.28; musl is not supported'
106
+ fi
107
+ # Primary source: https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt
108
+ # Exact release URLs are fixed, even though the provenance page is moving.
109
+ case "$os-$arch" in
110
+ darwin-arm64) hash=bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057;;
111
+ darwin-x64) hash=1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097;;
112
+ linux-arm64) hash=724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5;;
113
+ linux-x64) hash=6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff;;
114
+ esac
115
+ need curl
116
+ need tar
117
+ if command -v sha256sum >/dev/null 2>&1; then hasher=sha256sum
118
+ elif command -v shasum >/dev/null 2>&1; then hasher=shasum
119
+ else fail 'Required SHA256 utility missing: sha256sum or shasum'; fi
120
+ make_tools
121
+ stem=node-v24.21.0-$os-$arch
122
+ archive=$tools/node.tar.gz
123
+ # Also bound disk use on older curl versions lacking streamed-size enforcement.
124
+ # POSIX uses 512-byte blocks; shells using KiB still impose a 200-MiB ceiling.
125
+ (
126
+ ulimit -f 204800 || exit 1
127
+ curl --fail --silent --show-error --proto '=https' --tlsv1.2 --connect-timeout 10 --max-time 120 --max-filesize 104857600 --output "$archive" "https://nodejs.org/dist/v24.21.0/$stem.tar.gz"
128
+ ) 2>/dev/null || fail 'Node download failed, was truncated, or its file-size bound is unavailable'
129
+ size=$(wc -c < "$archive")
130
+ [ "$size" -gt 0 ] && [ "$size" -le 104857600 ] || fail 'Node download size is invalid'
131
+ if [ "$hasher" = sha256sum ]; then actual=$(bounded 30 sha256sum "$archive" 2>/dev/null) || fail 'SHA256 process failed'
132
+ else actual=$(bounded 30 shasum -a 256 "$archive" 2>/dev/null) || fail 'SHA256 process failed'; fi
133
+ actual=${actual%% *}
134
+ # Hash tools prefix escaped filenames (e.g. newline/backslash) with a backslash.
135
+ actual=${actual#\\}
136
+ [ "$actual" = "$hash" ] || fail 'Node checksum integrity mismatch'
137
+ mkdir "$tools/staging" || fail 'Conflicting staging destination'
138
+ # Extract only the trusted regular executable, not npm/Corepack or archive links.
139
+ member=$stem/bin/node
140
+ listing=$(bounded 30 tar -tvzf "$archive" "$member" 2>/dev/null) || fail 'Node archive is invalid'
141
+ case "$listing" in -*) ;; *) fail 'Node archive executable is not a regular file';; esac
142
+ bounded 30 tar -xzf "$archive" -C "$tools/staging" "$member" 2>/dev/null || fail 'Node extraction failed'
143
+ candidate=$tools/staging/$member
144
+ [ -f "$candidate" ] && [ ! -L "$candidate" ] || fail 'Node executable missing or unsafe'
145
+ version=$(bounded 10 "$candidate" --version 2>/dev/null) || fail 'Acquired Node cannot execute on this native platform'
146
+ [ "$version" = v24.21.0 ] && version_ok "$version" "$minimum" || fail 'Acquired Node version rejected'
147
+ bounded 10 chmod 700 "$candidate" 2>/dev/null || fail 'Cannot secure acquired Node executable'
148
+ [ ! -e "$tools/node" ] && [ ! -L "$tools/node" ] || fail 'Conflicting Node destination'
149
+ mv "$tools/staging/$stem" "$tools/node" || fail 'Cannot publish verified Node'
150
+ rm -f "$archive" || fail 'Cannot clean owned Node archive'
151
+ rm -rf "$tools/staging" || fail 'Cannot clean owned Node staging'
152
+ node=$tools/node/bin/node
153
+ PATH=$tools/node/bin:$PATH
154
+ export PATH
155
+ fi
156
+ # The helper creates an equally private tooling directory only if pnpm is absent.
157
+ # Do not exec: retain ownership cleanup on any helper/child failure.
158
+ "$node" "$bundle/scripts/installer-downloads.mjs" --bootstrap "$bundle" "$tools" || fail 'Prerequisite acquisition or wizard child failed'
159
+ # Exit 0 means node/npm/pnpm now persist under $PNPM_HOME or were already the
160
+ # user's. A removal problem never turns the completed installation into failure.
161
+ if [ "$owned" = 1 ]; then
162
+ mark=
163
+ if owned_tools && [ -f "$tools/.bootstrap-owned" ] && [ ! -L "$tools/.bootstrap-owned" ] &&
164
+ { IFS= read -r mark < "$tools/.bootstrap-owned"; } 2>/dev/null && [ "$mark" = 'gentle-pi prerequisite tooling only' ] &&
165
+ rm -rf "$tools" 2>/dev/null; then :
166
+ else printf '%s\n' "Bootstrap: installation finished, but temporary tools could not be removed: $tools" >&2; fi
167
+ fi
168
+ owned=0
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ // Release distribution assets (S3, T1b): gentle-pi at the release version and
3
+ // the exact Pi (PI_INSTALL_VERSION), with the pnpm-lock.yaml our pinned pnpm
4
+ // resolves for them, published as two release assets that installVersion
5
+ // installs with --frozen-lockfile. pnpm records every platform's optional
6
+ // packages (ffi-rs, fff-bin, esbuild prebuilds) in the lockfile whatever system
7
+ // resolves it, so one lockfile serves Linux, macOS and Windows.
8
+ //
9
+ // node scripts/build-distribution.mjs build --shell <version> [--pi <version>] --out <dir>
10
+ // node scripts/build-distribution.mjs verify --assets <dir>
11
+ //
12
+ // Both commands work in a fresh temporary prefix with our own pinned Node and
13
+ // pnpm (and Go on Windows); the user's tools and home are never used.
14
+ import { spawnSync } from "node:child_process";
15
+ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { pathToFileURL } from "node:url";
19
+ import { parseArgs } from "node:util";
20
+ import { DISTRIBUTION_ASSETS, activateVersion, claimPrefix, distributionAsset, distributionFiles, ensureLauncher, ensureRuntime, installVersion,
21
+ pnpmEnvironment, readDistribution, runCommand } from "./bundled-install.mjs";
22
+ import { PI_INSTALL_VERSION } from "./installer-preflight.mjs";
23
+
24
+ const RESOLVE_TIMEOUT = 10 * 60 * 1000;
25
+ // Right after `npm publish` the registry may not list gentle-pi@<version> yet:
26
+ // 10 attempts 30 seconds apart wait about five minutes for it.
27
+ const RESOLVE_ATTEMPTS = 10;
28
+ const RESOLVE_DELAY = 30 * 1000;
29
+ const output = (result) => `${result.stdout ?? ""}${result.stderr ?? ""}`.slice(-4000);
30
+
31
+ /** Resolves the lockfile for gentle-pi `shell` and Pi `pi` with our pnpm
32
+ * (`install --lockfile-only`) in a fresh folder under the prefix, from the
33
+ * unmodified distributionFiles() output and the same environment installVersion
34
+ * uses. Only "no matching version" for this gentle-pi is retried (pnpm prints it
35
+ * on stdout); any other failure stops at once. Returns { manifest, assets }
36
+ * keyed by the release asset names.
37
+ */
38
+ export async function buildDistribution({ layout, shell, pi = PI_INSTALL_VERSION, env = {}, adapters = {} }) {
39
+ const manifest = { shell, pi };
40
+ const files = distributionFiles(manifest);
41
+ const sleep = adapters.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
42
+ // `shell` is an exact SemVer here, so only its dots need escaping.
43
+ const unpublished = new RegExp(`ERR_PNPM_NO_(?:MATURE_)?MATCHING_VERSION\\] No matching version found for gentle-pi@${shell.replaceAll(".", "\\.")}(?![\\w.+-])`);
44
+ for (let attempt = 1; ; attempt += 1) {
45
+ const folder = mkdtempSync(join(layout.tmp, "lockfile-"));
46
+ try {
47
+ for (const [name, text] of Object.entries(files)) writeFileSync(join(folder, name), text, { flag: "wx", mode: 0o600 });
48
+ const result = await (adapters.run ?? runCommand)(layout.node, [layout.pnpm, "install", "--lockfile-only"],
49
+ { cwd: folder, env: pnpmEnvironment(layout, { env }), timeout: RESOLVE_TIMEOUT });
50
+ if (result.status === 0) {
51
+ // Only the lockfile is read back: pnpm may rewrite pnpm-workspace.yaml with placeholders.
52
+ const lockfile = readFileSync(join(folder, "pnpm-lock.yaml"), "utf8");
53
+ return { manifest, assets: { [DISTRIBUTION_ASSETS.distribution]: distributionAsset(manifest, lockfile), [DISTRIBUTION_ASSETS.lockfile]: lockfile } };
54
+ }
55
+ if (!unpublished.test(output(result))) throw Object.assign(new Error("pnpm lockfile resolution failed"), { output: output(result) });
56
+ if (attempt === RESOLVE_ATTEMPTS) {
57
+ throw Object.assign(new Error(`gentle-pi@${shell} is not on the registry after ${RESOLVE_ATTEMPTS} attempts; check its npm publication`), { output: output(result) });
58
+ }
59
+ } finally {
60
+ rmSync(folder, { recursive: true, force: true });
61
+ }
62
+ await sleep(RESOLVE_DELAY);
63
+ }
64
+ }
65
+
66
+ /** Runs the launcher with `--version`; the .cmd one through cmd.exe. */
67
+ function launch(launcher, { env, platform }) {
68
+ const options = { env, encoding: "utf8", timeout: 120000, windowsHide: true };
69
+ if (platform !== "win32") return spawnSync(launcher, ["--version"], options);
70
+ const cmd = join(env.SystemRoot ?? env.SYSTEMROOT ?? "C:\\Windows", "System32", "cmd.exe");
71
+ return spawnSync(cmd, ["/d", "/s", "/c", `""${launcher}" --version"`], { ...options, windowsVerbatimArguments: true });
72
+ }
73
+
74
+ /** Proves a release asset pair: our runtimes (Go too on Windows, as
75
+ * installVersion requires), a frozen install of the pair, `current` switched to
76
+ * it, the launcher written, and `gentle-shell --version` reporting exactly the
77
+ * pair's gentle-pi and Pi. Returns { id, version }.
78
+ */
79
+ export async function verifyDistribution({ layout, distribution, lockfile, platform, arch, env = {}, adapters = {} }) {
80
+ const { manifest } = readDistribution(distribution, lockfile);
81
+ const runtime = await ensureRuntime({ layout, platform, arch, go: platform === "win32", env, adapters });
82
+ const { id } = await installVersion({ layout, manifest, lockfile, platform, env, go: runtime.go, adapters });
83
+ activateVersion(layout, id);
84
+ ensureLauncher(layout);
85
+ // The environment our pnpm ran in (no NODE_OPTIONS, NODE_PATH or npm/pnpm
86
+ // settings from the host), without any Pi or Gentle override that could point
87
+ // the launcher at another Pi or agent home.
88
+ const launchEnv = Object.fromEntries(Object.entries(pnpmEnvironment(layout, { platform, env })).filter(([key]) => !/^(?:gentle_|pi_)/i.test(key)));
89
+ const result = await (adapters.launch ?? launch)(layout.launcher, { env: launchEnv, platform });
90
+ const version = String(result.stdout ?? "").trim();
91
+ const [shellLine, piLine] = version.split(/\r?\n/);
92
+ if (result.status !== 0 || shellLine !== `gentle-shell ${manifest.shell}` || piLine !== `pi ${manifest.pi}`) {
93
+ throw Object.assign(new Error(`The launcher reported ${JSON.stringify(version)}, not gentle-shell ${manifest.shell} with pi ${manifest.pi}`),
94
+ { output: output(result) });
95
+ }
96
+ return { id, version };
97
+ }
98
+
99
+ /** A fresh prefix in a new temporary folder. On Windows the private-folder claim
100
+ * (owner, ACL, marker) has its own native tests; here the new folder stands in
101
+ * for it, as in those tests.
102
+ */
103
+ function temporaryPrefix(root, platform, base) {
104
+ if (platform !== "win32") {
105
+ const home = join(root, "home");
106
+ mkdirSync(home, { mode: 0o700 });
107
+ const env = { ...base, HOME: home };
108
+ return { env, layout: claimPrefix({ platform, env, home }) };
109
+ }
110
+ const [local, roaming, profile] = ["local", "roaming", "profile"].map((name) => join(root, name));
111
+ for (const folder of [local, roaming, profile]) mkdirSync(folder);
112
+ const kept = Object.entries(base).filter(([key]) => !/^(?:localappdata|appdata|userprofile|home)$/i.test(key));
113
+ const env = { ...Object.fromEntries(kept), LOCALAPPDATA: local, APPDATA: roaming, USERPROFILE: profile, HOME: profile };
114
+ const claim = (folder) => { mkdirSync(folder); return "claimed"; };
115
+ return { env, layout: claimPrefix({ platform, env, home: profile, adapters: { storage: () => {}, claim } }) };
116
+ }
117
+
118
+ /** The CLI. Its temporary prefix (`gsd-*`) is always removed, whether the
119
+ * command succeeded or failed; invalid input is refused before it exists.
120
+ */
121
+ export async function main(argv, { platform = process.platform, arch = process.arch, env = process.env, temporaryRoot = tmpdir(), adapters = {} } = {}) {
122
+ const { positionals: [command], values } = parseArgs({ args: argv, allowPositionals: true,
123
+ options: { shell: { type: "string" }, pi: { type: "string" }, out: { type: "string" }, assets: { type: "string" } } });
124
+ const build = command === "build" && values.shell && values.out;
125
+ if (!build && !(command === "verify" && values.assets)) {
126
+ throw new Error("Usage: build-distribution.mjs build --shell <version> [--pi <version>] --out <dir> | verify --assets <dir>");
127
+ }
128
+ // A non-exact version is refused before anything is downloaded.
129
+ if (build) distributionFiles({ shell: values.shell, pi: values.pi ?? PI_INSTALL_VERSION });
130
+ const root = realpathSync(mkdtempSync(join(temporaryRoot, "gsd-")));
131
+ try {
132
+ const prefix = temporaryPrefix(root, platform, env);
133
+ if (build) {
134
+ await ensureRuntime({ layout: prefix.layout, platform, arch, env: prefix.env, adapters });
135
+ const { manifest, assets } = await buildDistribution({ layout: prefix.layout, shell: values.shell, pi: values.pi, env: prefix.env, adapters });
136
+ mkdirSync(values.out, { recursive: true });
137
+ for (const [name, text] of Object.entries(assets)) writeFileSync(join(values.out, name), text, { flag: "wx" });
138
+ console.log(`Built ${Object.keys(assets).join(" and ")} for gentle-pi ${manifest.shell} with pi ${manifest.pi} in ${values.out}`);
139
+ } else {
140
+ const read = (name) => readFileSync(join(values.assets, name), "utf8");
141
+ const result = await verifyDistribution({ layout: prefix.layout, distribution: read(DISTRIBUTION_ASSETS.distribution), lockfile: read(DISTRIBUTION_ASSETS.lockfile),
142
+ platform, arch, env: prefix.env, adapters });
143
+ console.log(`Verified ${result.id}:\n${result.version}`);
144
+ }
145
+ } finally {
146
+ // Windows may hold a file a moment after its process exits.
147
+ rmSync(root, { recursive: true, force: true, maxRetries: 5 });
148
+ }
149
+ }
150
+
151
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
152
+ main(process.argv.slice(2)).catch((error) => {
153
+ const detail = error.output ?? error.stderr;
154
+ console.error(detail ? `${error.message}\n${detail}` : error.message);
155
+ process.exitCode = 1;
156
+ });
157
+ }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ // Double-click installers: one unsigned download per system that opens the
3
+ // browser installation wizard with no git, clone or typed command.
4
+ //
5
+ // Each archive holds a launcher next to an `installer/` folder with the
6
+ // wizard's own files (they import only `node:` modules, so no dependencies).
7
+ // The launcher runs the bundled bootstrap, which fetches Node.js and pnpm when
8
+ // they are missing and opens the wizard in the browser. Real files only: the
9
+ // bootstrap refuses symlinked bundle files.
10
+ //
11
+ // node scripts/build-installer-bundles.mjs --out <folder>
12
+ import { spawnSync } from "node:child_process";
13
+ import { createHash } from "node:crypto";
14
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import { dirname, join, posix, resolve } from "node:path";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
18
+ import { installerPaths } from "./verify-package-files.mjs";
19
+
20
+ /** Stable names, so `releases/latest/download/<name>` always points at the newest release. */
21
+ const MACOS_ASSET = "gentle-shell-installer-macos.zip";
22
+ const WINDOWS_ASSET = "gentle-shell-installer-windows.zip";
23
+ const LINUX_ASSET = "gentle-shell-installer-linux.tar.gz";
24
+ export const INSTALLER_ASSETS = Object.freeze([MACOS_ASSET, WINDOWS_ASSET, LINUX_ASSET]);
25
+ export const INSTALLER_CHECKSUMS = "gentle-shell-installers-SHA256SUMS.txt";
26
+
27
+ // Static and dynamic imports, and files a module reads next to itself through
28
+ // `new URL("./file.ext", import.meta.url)`. Folder URLs (a trailing slash) are not
29
+ // followed: their files are listed in installerPaths.
30
+ const RELATIVE_REFERENCE = /(?:from\s+|import\s*\(\s*|new URL\(\s*)["'](\.{1,2}\/[^"']*[^/"'])["']/g;
31
+
32
+ /** Relative module and file references in a module's source. */
33
+ export function relativeReferences(text) {
34
+ return [...text.matchAll(RELATIVE_REFERENCE)].map((match) => match[1]);
35
+ }
36
+
37
+ /** Wizard files, package.json and everything their modules import, sorted. */
38
+ export function bundleFiles(root) {
39
+ const files = new Set(["package.json", ...installerPaths]);
40
+ const pending = [...files].filter((path) => path.endsWith(".mjs"));
41
+ while (pending.length > 0) {
42
+ const file = pending.pop();
43
+ for (const specifier of relativeReferences(readFileSync(join(root, file), "utf8"))) {
44
+ const target = posix.normalize(posix.join(posix.dirname(file), specifier));
45
+ if (files.has(target)) continue;
46
+ if (!existsSync(join(root, target))) throw new Error(`${file} imports ${specifier}, which does not exist`);
47
+ files.add(target);
48
+ if (target.endsWith(".mjs")) pending.push(target);
49
+ }
50
+ }
51
+ return [...files].sort();
52
+ }
53
+
54
+ const unixLauncher = (title) => [
55
+ "#!/bin/sh",
56
+ `# ${title}: double-click to install or update Gentle Shell.`,
57
+ "# It runs the bundled bootstrap, which opens the installation wizard in your browser.",
58
+ 'cd "$(dirname "$0")" || exit 1',
59
+ 'sh "./installer/scripts/bootstrap.sh"',
60
+ "status=$?",
61
+ 'if [ "$status" -eq 0 ]; then',
62
+ ' printf "\\n%s\\n" "You can close this window."',
63
+ "else",
64
+ ' printf "\\n%s\\n" "The installer stopped. Read the messages above, then close this window."',
65
+ "fi",
66
+ 'exit "$status"',
67
+ "",
68
+ ].join("\n");
69
+
70
+ const windowsLauncher = [
71
+ "@echo off",
72
+ "rem Double-click to install or update Gentle Shell.",
73
+ "rem It runs the bundled bootstrap, which opens the installation wizard in your browser.",
74
+ 'call "%~dp0installer\\scripts\\bootstrap.cmd"',
75
+ "echo.",
76
+ "if errorlevel 1 (",
77
+ " echo The installer stopped. Read the messages above, then close this window.",
78
+ ") else (",
79
+ " echo You can close this window.",
80
+ ")",
81
+ "pause",
82
+ "",
83
+ ].join("\r\n");
84
+
85
+ function run(command, args, cwd) {
86
+ const result = spawnSync(command, args, { cwd, encoding: "utf8" });
87
+ if (result.error || result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed: ${result.error?.message ?? result.stderr}`);
88
+ }
89
+
90
+ function stage(root, folder, files, launcher) {
91
+ mkdirSync(folder, { recursive: true });
92
+ for (const file of files) {
93
+ const target = join(folder, "installer", file);
94
+ mkdirSync(dirname(target), { recursive: true });
95
+ copyFileSync(join(root, file), target);
96
+ if (file.endsWith(".sh")) chmodSync(target, 0o755);
97
+ }
98
+ const path = join(folder, launcher.name);
99
+ writeFileSync(path, launcher.content);
100
+ chmodSync(path, 0o755);
101
+ }
102
+
103
+ /** Builds the three downloads and their checksums into `outDir`; returns their paths. */
104
+ export function buildInstallerBundles({ root, outDir }) {
105
+ const files = bundleFiles(root);
106
+ const work = mkdtempSync(join(tmpdir(), "gentle-shell-installers-"));
107
+ try {
108
+ mkdirSync(outDir, { recursive: true });
109
+ const zipped = (asset, folder, launcher) => {
110
+ stage(root, join(work, asset, folder), files, launcher);
111
+ rmSync(join(outDir, asset), { force: true });
112
+ run("zip", ["-q", "-r", join(outDir, asset), folder], join(work, asset));
113
+ };
114
+ zipped(MACOS_ASSET, "Gentle Shell Installer", { name: "Install Gentle Shell.command", content: unixLauncher("macOS") });
115
+ zipped(WINDOWS_ASSET, "Gentle Shell Installer", { name: "Install Gentle Shell.cmd", content: windowsLauncher });
116
+ const linux = join(work, LINUX_ASSET);
117
+ stage(root, join(linux, "gentle-shell-installer"), files, { name: "install-gentle-shell.sh", content: unixLauncher("Linux") });
118
+ run("tar", ["-czf", join(outDir, LINUX_ASSET), "gentle-shell-installer"], linux);
119
+ const sums = INSTALLER_ASSETS.map((asset) => `${createHash("sha256").update(readFileSync(join(outDir, asset))).digest("hex")} ${asset}\n`);
120
+ writeFileSync(join(outDir, INSTALLER_CHECKSUMS), sums.join(""));
121
+ return [...INSTALLER_ASSETS, INSTALLER_CHECKSUMS].map((asset) => join(outDir, asset));
122
+ } finally {
123
+ rmSync(work, { recursive: true, force: true });
124
+ }
125
+ }
126
+
127
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
128
+ const at = process.argv.indexOf("--out");
129
+ const out = at === -1 ? undefined : process.argv[at + 1];
130
+ if (!out || process.argv.length !== 4) {
131
+ console.error("usage: node scripts/build-installer-bundles.mjs --out <folder>");
132
+ process.exit(2);
133
+ }
134
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
135
+ for (const path of buildInstallerBundles({ root, outDir: resolve(out) })) console.log(path);
136
+ }
@@ -12,8 +12,16 @@ const sources = [
12
12
  "review-integration-v2",
13
13
  "review-risk-assessment",
14
14
  "native-review-cli",
15
+ "status-timing-diagnostics",
15
16
  "telemetry-trigger",
17
+ "child-package-injection",
16
18
  "gentle-shell-launcher",
19
+ "gentle-shell-bundled",
20
+ "gentle-shell-startup-update",
21
+ "gentle-shell-update-panel",
22
+ "gentle-shell-startup-flow",
23
+ "gentle-shell-resume-hint",
24
+ "gentle-shell-uninstall",
17
25
  ];
18
26
  const header = "// Generated by scripts/build-runtime-modules.mjs. Do not edit.\n";
19
27