gsd-pi 2.28.0 → 2.29.0-dev.7612840

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 (1179) hide show
  1. package/README.md +23 -0
  2. package/dist/cli.js +20 -21
  3. package/dist/extension-discovery.d.ts +16 -0
  4. package/dist/extension-discovery.js +66 -0
  5. package/dist/headless-answers.d.ts +51 -0
  6. package/dist/headless-answers.js +224 -0
  7. package/dist/headless-context.d.ts +18 -0
  8. package/dist/headless-context.js +44 -0
  9. package/dist/headless-events.d.ts +28 -0
  10. package/dist/headless-events.js +59 -0
  11. package/dist/headless-query.d.ts +4 -0
  12. package/dist/headless-query.js +22 -4
  13. package/dist/headless-ui.d.ts +23 -0
  14. package/dist/headless-ui.js +103 -0
  15. package/dist/headless.d.ts +2 -0
  16. package/dist/headless.js +61 -183
  17. package/dist/help-text.js +4 -0
  18. package/dist/loader.js +18 -59
  19. package/dist/onboarding.js +8 -7
  20. package/dist/remote-questions-config.js +8 -3
  21. package/dist/resource-loader.d.ts +1 -6
  22. package/dist/resource-loader.js +125 -96
  23. package/dist/resources/extensions/ask-user-questions.ts +3 -2
  24. package/dist/resources/extensions/bg-shell/bg-shell-command.ts +219 -0
  25. package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.ts +400 -0
  26. package/dist/resources/extensions/bg-shell/bg-shell-tool.ts +985 -0
  27. package/dist/resources/extensions/bg-shell/index.ts +17 -1561
  28. package/dist/resources/extensions/bg-shell/overlay.ts +4 -0
  29. package/dist/resources/extensions/bg-shell/utilities.ts +4 -16
  30. package/dist/resources/extensions/browser-tools/capture.ts +34 -2
  31. package/dist/resources/extensions/browser-tools/lifecycle.ts +5 -5
  32. package/dist/resources/extensions/browser-tools/settle.ts +1 -1
  33. package/dist/resources/extensions/browser-tools/state.ts +5 -5
  34. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +1 -1
  35. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +3 -3
  36. package/dist/resources/extensions/browser-tools/tools/assertions.ts +1 -1
  37. package/dist/resources/extensions/browser-tools/tools/device.ts +1 -1
  38. package/dist/resources/extensions/browser-tools/tools/extract.ts +1 -1
  39. package/dist/resources/extensions/browser-tools/tools/navigation.ts +6 -6
  40. package/dist/resources/extensions/browser-tools/tools/network-mock.ts +1 -1
  41. package/dist/resources/extensions/browser-tools/tools/pages.ts +1 -1
  42. package/dist/resources/extensions/browser-tools/tools/screenshot.ts +28 -10
  43. package/dist/resources/extensions/browser-tools/tools/state-persistence.ts +1 -1
  44. package/dist/resources/extensions/browser-tools/tools/visual-diff.ts +1 -1
  45. package/dist/resources/extensions/browser-tools/utils.ts +5 -5
  46. package/dist/resources/extensions/get-secrets-from-user.ts +1 -1
  47. package/dist/resources/extensions/google-search/index.ts +21 -8
  48. package/dist/resources/extensions/gsd/activity-log.ts +2 -1
  49. package/dist/resources/extensions/gsd/atomic-write.ts +35 -0
  50. package/dist/resources/extensions/gsd/auto/session.ts +12 -0
  51. package/dist/resources/extensions/gsd/auto-dashboard.ts +84 -3
  52. package/dist/resources/extensions/gsd/auto-idempotency.ts +150 -0
  53. package/dist/resources/extensions/gsd/auto-post-unit.ts +591 -0
  54. package/dist/resources/extensions/gsd/auto-prompts.ts +116 -22
  55. package/dist/resources/extensions/gsd/auto-recovery.ts +21 -10
  56. package/dist/resources/extensions/gsd/auto-start.ts +500 -0
  57. package/dist/resources/extensions/gsd/auto-stuck-detection.ts +220 -0
  58. package/dist/resources/extensions/gsd/auto-timers.ts +223 -0
  59. package/dist/resources/extensions/gsd/auto-unit-closeout.ts +3 -1
  60. package/dist/resources/extensions/gsd/auto-verification.ts +229 -0
  61. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +22 -31
  62. package/dist/resources/extensions/gsd/auto-worktree.ts +83 -67
  63. package/dist/resources/extensions/gsd/auto.ts +375 -1890
  64. package/dist/resources/extensions/gsd/commands-config.ts +102 -0
  65. package/dist/resources/extensions/gsd/commands-handlers.ts +375 -0
  66. package/dist/resources/extensions/gsd/commands-inspect.ts +90 -0
  67. package/dist/resources/extensions/gsd/commands-logs.ts +537 -0
  68. package/dist/resources/extensions/gsd/commands-maintenance.ts +206 -0
  69. package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +760 -0
  70. package/dist/resources/extensions/gsd/commands.ts +367 -1477
  71. package/dist/resources/extensions/gsd/constants.ts +21 -0
  72. package/dist/resources/extensions/gsd/context-budget.ts +25 -2
  73. package/dist/resources/extensions/gsd/crash-recovery.ts +3 -4
  74. package/dist/resources/extensions/gsd/dashboard-overlay.ts +15 -5
  75. package/dist/resources/extensions/gsd/db-writer.ts +21 -2
  76. package/dist/resources/extensions/gsd/detection.ts +469 -0
  77. package/dist/resources/extensions/gsd/diff-context.ts +2 -1
  78. package/dist/resources/extensions/gsd/dispatch-guard.ts +4 -0
  79. package/dist/resources/extensions/gsd/doctor-checks.ts +564 -0
  80. package/dist/resources/extensions/gsd/doctor-format.ts +78 -0
  81. package/dist/resources/extensions/gsd/doctor-types.ts +72 -0
  82. package/dist/resources/extensions/gsd/doctor.ts +64 -701
  83. package/dist/resources/extensions/gsd/errors.ts +0 -2
  84. package/dist/resources/extensions/gsd/export-html.ts +367 -11
  85. package/dist/resources/extensions/gsd/export.ts +31 -5
  86. package/dist/resources/extensions/gsd/files.ts +8 -126
  87. package/dist/resources/extensions/gsd/forensics.ts +2 -12
  88. package/dist/resources/extensions/gsd/git-constants.ts +11 -0
  89. package/dist/resources/extensions/gsd/git-service.ts +13 -9
  90. package/dist/resources/extensions/gsd/gsd-db.ts +26 -6
  91. package/dist/resources/extensions/gsd/guided-flow-queue.ts +451 -0
  92. package/dist/resources/extensions/gsd/guided-flow.ts +231 -514
  93. package/dist/resources/extensions/gsd/history.ts +2 -20
  94. package/dist/resources/extensions/gsd/index.ts +208 -46
  95. package/dist/resources/extensions/gsd/init-wizard.ts +615 -0
  96. package/dist/resources/extensions/gsd/json-persistence.ts +52 -0
  97. package/dist/resources/extensions/gsd/jsonl-utils.ts +21 -0
  98. package/dist/resources/extensions/gsd/key-manager.ts +995 -0
  99. package/dist/resources/extensions/gsd/metrics.ts +49 -36
  100. package/dist/resources/extensions/gsd/migrate/command.ts +1 -1
  101. package/dist/resources/extensions/gsd/migrate/parsers.ts +10 -95
  102. package/dist/resources/extensions/gsd/milestone-actions.ts +126 -0
  103. package/dist/resources/extensions/gsd/milestone-ids.ts +95 -0
  104. package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -10
  105. package/dist/resources/extensions/gsd/parallel-eligibility.ts +3 -3
  106. package/dist/resources/extensions/gsd/paths.ts +1 -11
  107. package/dist/resources/extensions/gsd/plugin-importer.ts +3 -2
  108. package/dist/resources/extensions/gsd/preferences-models.ts +323 -0
  109. package/dist/resources/extensions/gsd/preferences-skills.ts +169 -0
  110. package/dist/resources/extensions/gsd/preferences-types.ts +223 -0
  111. package/dist/resources/extensions/gsd/preferences-validation.ts +597 -0
  112. package/dist/resources/extensions/gsd/preferences.ts +219 -1305
  113. package/dist/resources/extensions/gsd/prompt-cache-optimizer.ts +213 -0
  114. package/dist/resources/extensions/gsd/prompt-compressor.ts +508 -0
  115. package/dist/resources/extensions/gsd/prompt-loader.ts +4 -2
  116. package/dist/resources/extensions/gsd/prompt-ordering.ts +200 -0
  117. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -2
  118. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  119. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -4
  120. package/dist/resources/extensions/gsd/prompts/discuss.md +13 -5
  121. package/dist/resources/extensions/gsd/prompts/execute-task.md +0 -1
  122. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  123. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +0 -1
  124. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +0 -1
  125. package/dist/resources/extensions/gsd/prompts/plan-slice.md +0 -1
  126. package/dist/resources/extensions/gsd/prompts/queue.md +30 -0
  127. package/dist/resources/extensions/gsd/prompts/quick-task.md +0 -6
  128. package/dist/resources/extensions/gsd/prompts/replan-slice.md +0 -1
  129. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +0 -1
  130. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  131. package/dist/resources/extensions/gsd/provider-error-pause.ts +59 -10
  132. package/dist/resources/extensions/gsd/queue-order.ts +1 -1
  133. package/dist/resources/extensions/gsd/queue-reorder-ui.ts +15 -2
  134. package/dist/resources/extensions/gsd/quick.ts +18 -15
  135. package/dist/resources/extensions/gsd/reports.ts +1 -7
  136. package/dist/resources/extensions/gsd/routing-history.ts +13 -17
  137. package/dist/resources/extensions/gsd/safe-fs.ts +47 -0
  138. package/dist/resources/extensions/gsd/semantic-chunker.ts +336 -0
  139. package/dist/resources/extensions/gsd/session-forensics.ts +8 -23
  140. package/dist/resources/extensions/gsd/session-lock.ts +284 -0
  141. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +38 -1
  142. package/dist/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +35 -6
  143. package/dist/resources/extensions/gsd/state.ts +54 -2
  144. package/dist/resources/extensions/gsd/structured-data-formatter.ts +144 -0
  145. package/dist/resources/extensions/gsd/summary-distiller.ts +258 -0
  146. package/dist/resources/extensions/gsd/tests/activity-log.test.ts +213 -0
  147. package/dist/resources/extensions/gsd/tests/agent-end-retry.test.ts +107 -0
  148. package/dist/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +197 -0
  149. package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
  150. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +33 -39
  151. package/dist/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +108 -2
  152. package/dist/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +257 -0
  153. package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
  154. package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +3 -0
  155. package/dist/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
  156. package/dist/resources/extensions/gsd/tests/context-budget.test.ts +69 -0
  157. package/dist/resources/extensions/gsd/tests/detection.test.ts +398 -0
  158. package/dist/resources/extensions/gsd/tests/discuss-prompt.test.ts +12 -24
  159. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +118 -94
  160. package/dist/resources/extensions/gsd/tests/dispatch-stall-guard.test.ts +126 -0
  161. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +7 -3
  162. package/dist/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +75 -0
  163. package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +17 -55
  164. package/dist/resources/extensions/gsd/tests/export-html-enhancements.test.ts +375 -0
  165. package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +122 -0
  166. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +3 -0
  167. package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
  168. package/dist/resources/extensions/gsd/tests/headless-answers.test.ts +340 -0
  169. package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +24 -82
  170. package/dist/resources/extensions/gsd/tests/init-wizard.test.ts +197 -0
  171. package/dist/resources/extensions/gsd/tests/key-manager.test.ts +414 -0
  172. package/dist/resources/extensions/gsd/tests/metrics.test.ts +173 -305
  173. package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +3 -0
  174. package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +59 -1
  175. package/dist/resources/extensions/gsd/tests/next-milestone-id.test.ts +18 -61
  176. package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +17 -8
  177. package/dist/resources/extensions/gsd/tests/parallel-merge.test.ts +3 -0
  178. package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
  179. package/dist/resources/extensions/gsd/tests/park-edge-cases.test.ts +276 -0
  180. package/dist/resources/extensions/gsd/tests/park-milestone.test.ts +401 -0
  181. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +23 -47
  182. package/dist/resources/extensions/gsd/tests/preferences.test.ts +284 -0
  183. package/dist/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +314 -0
  184. package/dist/resources/extensions/gsd/tests/prompt-compressor.test.ts +529 -0
  185. package/dist/resources/extensions/gsd/tests/prompt-ordering.test.ts +296 -0
  186. package/dist/resources/extensions/gsd/tests/provider-errors.test.ts +338 -0
  187. package/dist/resources/extensions/gsd/tests/reassess-detection.test.ts +154 -0
  188. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +1 -1
  189. package/dist/resources/extensions/gsd/tests/remote-status.test.ts +2 -2
  190. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -60
  191. package/dist/resources/extensions/gsd/tests/semantic-chunker.test.ts +426 -0
  192. package/dist/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
  193. package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -0
  194. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +8 -5
  195. package/dist/resources/extensions/gsd/tests/structured-data-formatter.test.ts +365 -0
  196. package/dist/resources/extensions/gsd/tests/summary-distiller.test.ts +323 -0
  197. package/dist/resources/extensions/gsd/tests/token-counter.test.ts +129 -0
  198. package/dist/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +1272 -0
  199. package/dist/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +164 -0
  200. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  201. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +69 -73
  202. package/dist/resources/extensions/gsd/tests/validate-directory.test.ts +222 -0
  203. package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
  204. package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
  205. package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +251 -8
  206. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +2 -2
  207. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +2 -1
  208. package/dist/resources/extensions/gsd/tests/workspace-index.test.ts +24 -61
  209. package/dist/resources/extensions/gsd/tests/worktree-e2e.test.ts +5 -2
  210. package/dist/resources/extensions/gsd/tests/write-gate.test.ts +132 -43
  211. package/dist/resources/extensions/gsd/token-counter.ts +20 -0
  212. package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
  213. package/dist/resources/extensions/gsd/types.ts +5 -1
  214. package/dist/resources/extensions/gsd/unit-runtime.ts +16 -13
  215. package/dist/resources/extensions/gsd/validate-directory.ts +164 -0
  216. package/dist/resources/extensions/gsd/verification-evidence.ts +9 -4
  217. package/dist/resources/extensions/gsd/verification-gate.ts +83 -7
  218. package/dist/resources/extensions/gsd/visualizer-data.ts +1 -1
  219. package/dist/resources/extensions/gsd/visualizer-overlay.ts +5 -2
  220. package/dist/resources/extensions/gsd/visualizer-views.ts +2 -3
  221. package/dist/resources/extensions/gsd/worktree-command.ts +4 -51
  222. package/dist/resources/extensions/gsd/worktree-manager.ts +7 -9
  223. package/dist/resources/extensions/gsd/worktree.ts +41 -1
  224. package/dist/resources/extensions/mcporter/index.ts +27 -14
  225. package/dist/resources/extensions/remote-questions/discord-adapter.ts +9 -20
  226. package/dist/resources/extensions/remote-questions/http-client.ts +76 -0
  227. package/dist/resources/extensions/remote-questions/manager.ts +6 -24
  228. package/dist/resources/extensions/remote-questions/mod.ts +16 -0
  229. package/dist/resources/extensions/remote-questions/notify.ts +90 -0
  230. package/dist/resources/extensions/remote-questions/remote-command.ts +1 -1
  231. package/dist/resources/extensions/remote-questions/slack-adapter.ts +11 -18
  232. package/dist/resources/extensions/remote-questions/store.ts +5 -1
  233. package/dist/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
  234. package/dist/resources/extensions/remote-questions/types.ts +29 -3
  235. package/dist/resources/extensions/search-the-web/native-search.ts +7 -0
  236. package/dist/resources/extensions/search-the-web/provider.ts +15 -3
  237. package/dist/resources/extensions/search-the-web/tool-llm-context.ts +1 -13
  238. package/dist/resources/extensions/search-the-web/tool-search.ts +1 -13
  239. package/dist/resources/extensions/shared/format-utils.ts +53 -0
  240. package/dist/resources/extensions/shared/frontmatter.ts +117 -0
  241. package/dist/resources/extensions/shared/mod.ts +33 -0
  242. package/dist/resources/extensions/shared/sanitize.ts +19 -0
  243. package/dist/resources/extensions/slash-commands/create-extension.ts +1 -1
  244. package/dist/resources/extensions/slash-commands/create-slash-command.ts +1 -1
  245. package/dist/resources/extensions/subagent/index.ts +1 -2
  246. package/dist/resources/extensions/ttsr/index.ts +5 -0
  247. package/dist/resources/extensions/ttsr/rule-loader.ts +4 -51
  248. package/dist/resources/extensions/universal-config/discovery.ts +37 -15
  249. package/dist/resources/extensions/voice/index.ts +1 -1
  250. package/dist/resources/skills/accessibility/SKILL.md +522 -0
  251. package/dist/resources/skills/accessibility/references/WCAG.md +162 -0
  252. package/dist/resources/skills/agent-browser/SKILL.md +517 -0
  253. package/dist/resources/skills/agent-browser/references/authentication.md +202 -0
  254. package/dist/resources/skills/agent-browser/references/commands.md +263 -0
  255. package/dist/resources/skills/agent-browser/references/profiling.md +120 -0
  256. package/dist/resources/skills/agent-browser/references/proxy-support.md +194 -0
  257. package/dist/resources/skills/agent-browser/references/session-management.md +193 -0
  258. package/dist/resources/skills/agent-browser/references/snapshot-refs.md +194 -0
  259. package/dist/resources/skills/agent-browser/references/video-recording.md +173 -0
  260. package/dist/resources/skills/agent-browser/templates/authenticated-session.sh +105 -0
  261. package/dist/resources/skills/agent-browser/templates/capture-workflow.sh +69 -0
  262. package/dist/resources/skills/agent-browser/templates/form-automation.sh +62 -0
  263. package/dist/resources/skills/best-practices/SKILL.md +583 -0
  264. package/dist/resources/skills/code-optimizer/SKILL.md +160 -0
  265. package/dist/resources/skills/code-optimizer/references/algorithmic-complexity.md +66 -0
  266. package/dist/resources/skills/code-optimizer/references/build-compilation.md +90 -0
  267. package/dist/resources/skills/code-optimizer/references/bundle-dependencies.md +82 -0
  268. package/dist/resources/skills/code-optimizer/references/caching-memoization.md +76 -0
  269. package/dist/resources/skills/code-optimizer/references/concurrency-async.md +80 -0
  270. package/dist/resources/skills/code-optimizer/references/config-infra.md +71 -0
  271. package/dist/resources/skills/code-optimizer/references/data-structures.md +80 -0
  272. package/dist/resources/skills/code-optimizer/references/database-queries.md +76 -0
  273. package/dist/resources/skills/code-optimizer/references/dead-code-redundancy.md +84 -0
  274. package/dist/resources/skills/code-optimizer/references/error-resilience.md +80 -0
  275. package/dist/resources/skills/code-optimizer/references/io-network.md +89 -0
  276. package/dist/resources/skills/code-optimizer/references/logging-observability.md +64 -0
  277. package/dist/resources/skills/code-optimizer/references/memory-resources.md +66 -0
  278. package/dist/resources/skills/code-optimizer/references/rendering-ui.md +90 -0
  279. package/dist/resources/skills/code-optimizer/references/security-performance.md +68 -0
  280. package/dist/resources/skills/core-web-vitals/SKILL.md +441 -0
  281. package/dist/resources/skills/core-web-vitals/references/LCP.md +208 -0
  282. package/dist/resources/skills/make-interfaces-feel-better/SKILL.md +122 -0
  283. package/dist/resources/skills/make-interfaces-feel-better/animations.md +379 -0
  284. package/dist/resources/skills/make-interfaces-feel-better/performance.md +88 -0
  285. package/dist/resources/skills/make-interfaces-feel-better/surfaces.md +247 -0
  286. package/dist/resources/skills/make-interfaces-feel-better/typography.md +123 -0
  287. package/dist/resources/skills/react-best-practices/README.md +123 -0
  288. package/dist/resources/skills/react-best-practices/SKILL.md +136 -0
  289. package/dist/resources/skills/react-best-practices/metadata.json +15 -0
  290. package/dist/resources/skills/react-best-practices/rules/_sections.md +46 -0
  291. package/dist/resources/skills/react-best-practices/rules/_template.md +28 -0
  292. package/dist/resources/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  293. package/dist/resources/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  294. package/dist/resources/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  295. package/dist/resources/skills/react-best-practices/rules/async-api-routes.md +38 -0
  296. package/dist/resources/skills/react-best-practices/rules/async-defer-await.md +80 -0
  297. package/dist/resources/skills/react-best-practices/rules/async-dependencies.md +51 -0
  298. package/dist/resources/skills/react-best-practices/rules/async-parallel.md +28 -0
  299. package/dist/resources/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  300. package/dist/resources/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  301. package/dist/resources/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  302. package/dist/resources/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  303. package/dist/resources/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  304. package/dist/resources/skills/react-best-practices/rules/bundle-preload.md +50 -0
  305. package/dist/resources/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  306. package/dist/resources/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  307. package/dist/resources/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  308. package/dist/resources/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  309. package/dist/resources/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
  310. package/dist/resources/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  311. package/dist/resources/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  312. package/dist/resources/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  313. package/dist/resources/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  314. package/dist/resources/skills/react-best-practices/rules/js-early-exit.md +50 -0
  315. package/dist/resources/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  316. package/dist/resources/skills/react-best-practices/rules/js-index-maps.md +37 -0
  317. package/dist/resources/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  318. package/dist/resources/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  319. package/dist/resources/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  320. package/dist/resources/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  321. package/dist/resources/skills/react-best-practices/rules/rendering-activity.md +26 -0
  322. package/dist/resources/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  323. package/dist/resources/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  324. package/dist/resources/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  325. package/dist/resources/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  326. package/dist/resources/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  327. package/dist/resources/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  328. package/dist/resources/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  329. package/dist/resources/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  330. package/dist/resources/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  331. package/dist/resources/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  332. package/dist/resources/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  333. package/dist/resources/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  334. package/dist/resources/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  335. package/dist/resources/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  336. package/dist/resources/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  337. package/dist/resources/skills/react-best-practices/rules/rerender-memo.md +44 -0
  338. package/dist/resources/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  339. package/dist/resources/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  340. package/dist/resources/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  341. package/dist/resources/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  342. package/dist/resources/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  343. package/dist/resources/skills/react-best-practices/rules/server-auth-actions.md +96 -0
  344. package/dist/resources/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  345. package/dist/resources/skills/react-best-practices/rules/server-cache-react.md +76 -0
  346. package/dist/resources/skills/react-best-practices/rules/server-dedup-props.md +65 -0
  347. package/dist/resources/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  348. package/dist/resources/skills/react-best-practices/rules/server-serialization.md +38 -0
  349. package/dist/resources/skills/userinterface-wiki/SKILL.md +253 -0
  350. package/dist/resources/skills/userinterface-wiki/rules/_sections.md +66 -0
  351. package/dist/resources/skills/userinterface-wiki/rules/_template.md +24 -0
  352. package/dist/resources/skills/userinterface-wiki/rules/a11y-reduced-motion-check.md +30 -0
  353. package/dist/resources/skills/userinterface-wiki/rules/a11y-toggle-setting.md +30 -0
  354. package/dist/resources/skills/userinterface-wiki/rules/a11y-visual-equivalent.md +36 -0
  355. package/dist/resources/skills/userinterface-wiki/rules/a11y-volume-control.md +28 -0
  356. package/dist/resources/skills/userinterface-wiki/rules/appropriate-confirmations-only.md +19 -0
  357. package/dist/resources/skills/userinterface-wiki/rules/appropriate-errors-warnings.md +18 -0
  358. package/dist/resources/skills/userinterface-wiki/rules/appropriate-no-decorative.md +21 -0
  359. package/dist/resources/skills/userinterface-wiki/rules/appropriate-no-high-frequency.md +28 -0
  360. package/dist/resources/skills/userinterface-wiki/rules/appropriate-no-punishing.md +27 -0
  361. package/dist/resources/skills/userinterface-wiki/rules/container-callback-ref.md +31 -0
  362. package/dist/resources/skills/userinterface-wiki/rules/container-guard-initial-zero.md +25 -0
  363. package/dist/resources/skills/userinterface-wiki/rules/container-no-excessive-use.md +13 -0
  364. package/dist/resources/skills/userinterface-wiki/rules/container-overflow-hidden.md +25 -0
  365. package/dist/resources/skills/userinterface-wiki/rules/container-transition-delay.md +21 -0
  366. package/dist/resources/skills/userinterface-wiki/rules/container-two-div-pattern.md +35 -0
  367. package/dist/resources/skills/userinterface-wiki/rules/container-use-resize-observer.md +48 -0
  368. package/dist/resources/skills/userinterface-wiki/rules/context-cleanup-nodes.md +25 -0
  369. package/dist/resources/skills/userinterface-wiki/rules/context-resume-suspended.md +28 -0
  370. package/dist/resources/skills/userinterface-wiki/rules/context-reuse-single.md +30 -0
  371. package/dist/resources/skills/userinterface-wiki/rules/design-filter-for-character.md +25 -0
  372. package/dist/resources/skills/userinterface-wiki/rules/design-noise-for-percussion.md +26 -0
  373. package/dist/resources/skills/userinterface-wiki/rules/design-oscillator-for-tonal.md +22 -0
  374. package/dist/resources/skills/userinterface-wiki/rules/duration-max-300ms.md +21 -0
  375. package/dist/resources/skills/userinterface-wiki/rules/duration-press-hover.md +21 -0
  376. package/dist/resources/skills/userinterface-wiki/rules/duration-shorten-before-curve.md +21 -0
  377. package/dist/resources/skills/userinterface-wiki/rules/duration-small-state.md +15 -0
  378. package/dist/resources/skills/userinterface-wiki/rules/easing-entrance-ease-out.md +21 -0
  379. package/dist/resources/skills/userinterface-wiki/rules/easing-exit-ease-in.md +21 -0
  380. package/dist/resources/skills/userinterface-wiki/rules/easing-for-state-change.md +27 -0
  381. package/dist/resources/skills/userinterface-wiki/rules/easing-linear-only-progress.md +21 -0
  382. package/dist/resources/skills/userinterface-wiki/rules/easing-natural-decay.md +22 -0
  383. package/dist/resources/skills/userinterface-wiki/rules/easing-no-linear-motion.md +22 -0
  384. package/dist/resources/skills/userinterface-wiki/rules/easing-transition-ease-in-out.md +15 -0
  385. package/dist/resources/skills/userinterface-wiki/rules/envelope-exponential-decay.md +21 -0
  386. package/dist/resources/skills/userinterface-wiki/rules/envelope-no-zero-target.md +21 -0
  387. package/dist/resources/skills/userinterface-wiki/rules/envelope-set-initial-value.md +22 -0
  388. package/dist/resources/skills/userinterface-wiki/rules/exit-key-required.md +29 -0
  389. package/dist/resources/skills/userinterface-wiki/rules/exit-matches-initial.md +29 -0
  390. package/dist/resources/skills/userinterface-wiki/rules/exit-prop-required.md +33 -0
  391. package/dist/resources/skills/userinterface-wiki/rules/exit-requires-wrapper.md +27 -0
  392. package/dist/resources/skills/userinterface-wiki/rules/impl-default-subtle.md +21 -0
  393. package/dist/resources/skills/userinterface-wiki/rules/impl-preload-audio.md +34 -0
  394. package/dist/resources/skills/userinterface-wiki/rules/impl-reset-current-time.md +26 -0
  395. package/dist/resources/skills/userinterface-wiki/rules/mode-pop-layout-for-lists.md +25 -0
  396. package/dist/resources/skills/userinterface-wiki/rules/mode-sync-layout-conflict.md +29 -0
  397. package/dist/resources/skills/userinterface-wiki/rules/mode-wait-doubles-duration.md +25 -0
  398. package/dist/resources/skills/userinterface-wiki/rules/morphing-aria-hidden.md +21 -0
  399. package/dist/resources/skills/userinterface-wiki/rules/morphing-consistent-viewbox.md +23 -0
  400. package/dist/resources/skills/userinterface-wiki/rules/morphing-group-variants.md +33 -0
  401. package/dist/resources/skills/userinterface-wiki/rules/morphing-jump-non-grouped.md +29 -0
  402. package/dist/resources/skills/userinterface-wiki/rules/morphing-reduced-motion.md +28 -0
  403. package/dist/resources/skills/userinterface-wiki/rules/morphing-spring-rotation.md +23 -0
  404. package/dist/resources/skills/userinterface-wiki/rules/morphing-strokelinecap-round.md +21 -0
  405. package/dist/resources/skills/userinterface-wiki/rules/morphing-three-lines.md +32 -0
  406. package/dist/resources/skills/userinterface-wiki/rules/morphing-use-collapsed.md +33 -0
  407. package/dist/resources/skills/userinterface-wiki/rules/native-backdrop-styling.md +27 -0
  408. package/dist/resources/skills/userinterface-wiki/rules/native-placeholder-styling.md +27 -0
  409. package/dist/resources/skills/userinterface-wiki/rules/native-selection-styling.md +18 -0
  410. package/dist/resources/skills/userinterface-wiki/rules/nested-consistent-timing.md +25 -0
  411. package/dist/resources/skills/userinterface-wiki/rules/nested-propagate-required.md +41 -0
  412. package/dist/resources/skills/userinterface-wiki/rules/none-context-menu-entrance.md +25 -0
  413. package/dist/resources/skills/userinterface-wiki/rules/none-high-frequency.md +29 -0
  414. package/dist/resources/skills/userinterface-wiki/rules/none-keyboard-navigation.md +32 -0
  415. package/dist/resources/skills/userinterface-wiki/rules/param-click-duration.md +21 -0
  416. package/dist/resources/skills/userinterface-wiki/rules/param-filter-frequency-range.md +21 -0
  417. package/dist/resources/skills/userinterface-wiki/rules/param-q-value-range.md +21 -0
  418. package/dist/resources/skills/userinterface-wiki/rules/param-reasonable-gain.md +21 -0
  419. package/dist/resources/skills/userinterface-wiki/rules/physics-active-state.md +23 -0
  420. package/dist/resources/skills/userinterface-wiki/rules/physics-no-excessive-stagger.md +22 -0
  421. package/dist/resources/skills/userinterface-wiki/rules/physics-spring-for-overshoot.md +23 -0
  422. package/dist/resources/skills/userinterface-wiki/rules/physics-subtle-deformation.md +22 -0
  423. package/dist/resources/skills/userinterface-wiki/rules/prefetch-hit-slop.md +27 -0
  424. package/dist/resources/skills/userinterface-wiki/rules/prefetch-keyboard-tab.md +19 -0
  425. package/dist/resources/skills/userinterface-wiki/rules/prefetch-not-everything.md +22 -0
  426. package/dist/resources/skills/userinterface-wiki/rules/prefetch-touch-fallback.md +34 -0
  427. package/dist/resources/skills/userinterface-wiki/rules/prefetch-trajectory-over-hover.md +32 -0
  428. package/dist/resources/skills/userinterface-wiki/rules/prefetch-use-selectively.md +13 -0
  429. package/dist/resources/skills/userinterface-wiki/rules/presence-disable-interactions.md +31 -0
  430. package/dist/resources/skills/userinterface-wiki/rules/presence-hook-in-child.md +31 -0
  431. package/dist/resources/skills/userinterface-wiki/rules/presence-safe-to-remove.md +37 -0
  432. package/dist/resources/skills/userinterface-wiki/rules/pseudo-content-required.md +28 -0
  433. package/dist/resources/skills/userinterface-wiki/rules/pseudo-first-line-styling.md +27 -0
  434. package/dist/resources/skills/userinterface-wiki/rules/pseudo-hit-target-expansion.md +31 -0
  435. package/dist/resources/skills/userinterface-wiki/rules/pseudo-marker-styling.md +28 -0
  436. package/dist/resources/skills/userinterface-wiki/rules/pseudo-over-dom-node.md +32 -0
  437. package/dist/resources/skills/userinterface-wiki/rules/pseudo-position-relative-parent.md +33 -0
  438. package/dist/resources/skills/userinterface-wiki/rules/pseudo-z-index-layering.md +37 -0
  439. package/dist/resources/skills/userinterface-wiki/rules/spring-for-gestures.md +27 -0
  440. package/dist/resources/skills/userinterface-wiki/rules/spring-for-interruptible.md +27 -0
  441. package/dist/resources/skills/userinterface-wiki/rules/spring-params-balanced.md +29 -0
  442. package/dist/resources/skills/userinterface-wiki/rules/spring-preserves-velocity.md +28 -0
  443. package/dist/resources/skills/userinterface-wiki/rules/staging-dim-background.md +22 -0
  444. package/dist/resources/skills/userinterface-wiki/rules/staging-one-focal-point.md +24 -0
  445. package/dist/resources/skills/userinterface-wiki/rules/staging-z-index-hierarchy.md +22 -0
  446. package/dist/resources/skills/userinterface-wiki/rules/timing-consistent.md +24 -0
  447. package/dist/resources/skills/userinterface-wiki/rules/timing-no-entrance-context-menu.md +22 -0
  448. package/dist/resources/skills/userinterface-wiki/rules/timing-under-300ms.md +22 -0
  449. package/dist/resources/skills/userinterface-wiki/rules/transition-name-cleanup.md +28 -0
  450. package/dist/resources/skills/userinterface-wiki/rules/transition-name-required.md +27 -0
  451. package/dist/resources/skills/userinterface-wiki/rules/transition-name-unique.md +24 -0
  452. package/dist/resources/skills/userinterface-wiki/rules/transition-over-js-library.md +32 -0
  453. package/dist/resources/skills/userinterface-wiki/rules/transition-style-pseudo-elements.md +24 -0
  454. package/dist/resources/skills/userinterface-wiki/rules/type-antialiased-on-retina.md +18 -0
  455. package/dist/resources/skills/userinterface-wiki/rules/type-disambiguation-stylistic-set.md +15 -0
  456. package/dist/resources/skills/userinterface-wiki/rules/type-font-display-swap.md +28 -0
  457. package/dist/resources/skills/userinterface-wiki/rules/type-justify-with-hyphens.md +24 -0
  458. package/dist/resources/skills/userinterface-wiki/rules/type-letter-spacing-uppercase.md +28 -0
  459. package/dist/resources/skills/userinterface-wiki/rules/type-no-font-synthesis.md +18 -0
  460. package/dist/resources/skills/userinterface-wiki/rules/type-oldstyle-nums-for-prose.md +21 -0
  461. package/dist/resources/skills/userinterface-wiki/rules/type-opentype-contextual-alternates.md +15 -0
  462. package/dist/resources/skills/userinterface-wiki/rules/type-optical-sizing-auto.md +25 -0
  463. package/dist/resources/skills/userinterface-wiki/rules/type-proper-fractions.md +15 -0
  464. package/dist/resources/skills/userinterface-wiki/rules/type-slashed-zero.md +17 -0
  465. package/dist/resources/skills/userinterface-wiki/rules/type-tabular-nums-for-data.md +21 -0
  466. package/dist/resources/skills/userinterface-wiki/rules/type-text-wrap-balance-headings.md +21 -0
  467. package/dist/resources/skills/userinterface-wiki/rules/type-text-wrap-pretty.md +16 -0
  468. package/dist/resources/skills/userinterface-wiki/rules/type-underline-offset.md +25 -0
  469. package/dist/resources/skills/userinterface-wiki/rules/type-variable-weight-continuous.md +23 -0
  470. package/dist/resources/skills/userinterface-wiki/rules/ux-aesthetic-usability.md +32 -0
  471. package/dist/resources/skills/userinterface-wiki/rules/ux-cognitive-load-reduce.md +49 -0
  472. package/dist/resources/skills/userinterface-wiki/rules/ux-common-region-boundaries.md +50 -0
  473. package/dist/resources/skills/userinterface-wiki/rules/ux-doherty-perceived-speed.md +29 -0
  474. package/dist/resources/skills/userinterface-wiki/rules/ux-doherty-under-400ms.md +30 -0
  475. package/dist/resources/skills/userinterface-wiki/rules/ux-fitts-hit-area.md +32 -0
  476. package/dist/resources/skills/userinterface-wiki/rules/ux-fitts-target-size.md +31 -0
  477. package/dist/resources/skills/userinterface-wiki/rules/ux-goal-gradient-progress.md +33 -0
  478. package/dist/resources/skills/userinterface-wiki/rules/ux-hicks-minimize-choices.md +45 -0
  479. package/dist/resources/skills/userinterface-wiki/rules/ux-jakobs-familiar-patterns.md +37 -0
  480. package/dist/resources/skills/userinterface-wiki/rules/ux-millers-chunking.md +23 -0
  481. package/dist/resources/skills/userinterface-wiki/rules/ux-pareto-prioritize-features.md +36 -0
  482. package/dist/resources/skills/userinterface-wiki/rules/ux-peak-end-finish-strong.md +35 -0
  483. package/dist/resources/skills/userinterface-wiki/rules/ux-postels-accept-messy-input.md +45 -0
  484. package/dist/resources/skills/userinterface-wiki/rules/ux-pragnanz-simplify.md +33 -0
  485. package/dist/resources/skills/userinterface-wiki/rules/ux-progressive-disclosure.md +41 -0
  486. package/dist/resources/skills/userinterface-wiki/rules/ux-proximity-grouping.md +38 -0
  487. package/dist/resources/skills/userinterface-wiki/rules/ux-serial-position.md +31 -0
  488. package/dist/resources/skills/userinterface-wiki/rules/ux-similarity-consistency.md +35 -0
  489. package/dist/resources/skills/userinterface-wiki/rules/ux-teslers-complexity.md +28 -0
  490. package/dist/resources/skills/userinterface-wiki/rules/ux-uniform-connectedness.md +43 -0
  491. package/dist/resources/skills/userinterface-wiki/rules/ux-von-restorff-emphasis.md +29 -0
  492. package/dist/resources/skills/userinterface-wiki/rules/ux-zeigarnik-show-incomplete.md +36 -0
  493. package/dist/resources/skills/userinterface-wiki/rules/visual-animate-shadow-pseudo.md +49 -0
  494. package/dist/resources/skills/userinterface-wiki/rules/visual-border-alpha-colors.md +25 -0
  495. package/dist/resources/skills/userinterface-wiki/rules/visual-button-shadow-anatomy.md +49 -0
  496. package/dist/resources/skills/userinterface-wiki/rules/visual-concentric-radius.md +40 -0
  497. package/dist/resources/skills/userinterface-wiki/rules/visual-consistent-spacing-scale.md +35 -0
  498. package/dist/resources/skills/userinterface-wiki/rules/visual-layered-shadows.md +30 -0
  499. package/dist/resources/skills/userinterface-wiki/rules/visual-no-pure-black-shadow.md +25 -0
  500. package/dist/resources/skills/userinterface-wiki/rules/visual-shadow-direction.md +25 -0
  501. package/dist/resources/skills/userinterface-wiki/rules/visual-shadow-matches-elevation.md +23 -0
  502. package/dist/resources/skills/userinterface-wiki/rules/weight-duration-matches-action.md +29 -0
  503. package/dist/resources/skills/userinterface-wiki/rules/weight-match-action.md +32 -0
  504. package/dist/resources/skills/web-design-guidelines/SKILL.md +39 -0
  505. package/dist/resources/skills/web-quality-audit/SKILL.md +170 -0
  506. package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +91 -0
  507. package/package.json +22 -8
  508. package/packages/native/package.json +28 -0
  509. package/packages/pi-agent-core/package.json +6 -0
  510. package/packages/pi-ai/dist/models.generated.d.ts +43 -11
  511. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  512. package/packages/pi-ai/dist/models.generated.js +34 -26
  513. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  514. package/packages/pi-ai/dist/providers/anthropic.js +3 -2
  515. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  516. package/packages/pi-ai/dist/providers/openai-codex-responses.js +14 -4
  517. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  518. package/packages/pi-ai/oauth.d.ts +1 -0
  519. package/packages/pi-ai/oauth.js +1 -0
  520. package/packages/pi-ai/package.json +2 -2
  521. package/packages/pi-ai/src/models.generated.ts +42 -34
  522. package/packages/pi-ai/src/providers/anthropic.ts +3 -2
  523. package/packages/pi-ai/src/providers/openai-codex-responses.ts +15 -4
  524. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  525. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  526. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  527. package/packages/pi-coding-agent/dist/core/auth-storage.js +2 -1
  528. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  529. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  530. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +2 -1
  531. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  532. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  533. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +3 -2
  534. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  535. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  536. package/packages/pi-coding-agent/dist/core/compaction/utils.js +2 -2
  537. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  538. package/packages/pi-coding-agent/dist/core/constants.d.ts +29 -0
  539. package/packages/pi-coding-agent/dist/core/constants.d.ts.map +1 -0
  540. package/packages/pi-coding-agent/dist/core/constants.js +44 -0
  541. package/packages/pi-coding-agent/dist/core/constants.js.map +1 -0
  542. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  543. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  544. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  545. package/packages/pi-coding-agent/dist/core/lsp/index.js +30 -4
  546. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  547. package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
  548. package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +13 -5
  549. package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
  550. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  551. package/packages/pi-coding-agent/dist/core/model-resolver.js +14 -0
  552. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  553. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
  554. package/packages/pi-coding-agent/dist/core/resolve-config-value.js +12 -4
  555. package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
  556. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +49 -0
  557. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  558. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  559. package/packages/pi-coding-agent/dist/core/session-manager.js +25 -2
  560. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  561. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  562. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  563. package/packages/pi-coding-agent/dist/core/settings-manager.js +22 -5
  564. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  565. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  566. package/packages/pi-coding-agent/dist/core/system-prompt.js +10 -0
  567. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  568. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +2 -2
  569. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  570. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  571. package/packages/pi-coding-agent/dist/core/tools/find.js +2 -1
  572. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  573. package/packages/pi-coding-agent/dist/core/tools/hashline-edit.js +4 -4
  574. package/packages/pi-coding-agent/dist/core/tools/hashline-edit.js.map +1 -1
  575. package/packages/pi-coding-agent/dist/core/tools/hashline.d.ts +1 -1
  576. package/packages/pi-coding-agent/dist/core/tools/hashline.js +1 -1
  577. package/packages/pi-coding-agent/dist/core/tools/hashline.js.map +1 -1
  578. package/packages/pi-coding-agent/dist/core/tools/hashline.test.js +8 -8
  579. package/packages/pi-coding-agent/dist/core/tools/hashline.test.js.map +1 -1
  580. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +1 -1
  581. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -1
  582. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  583. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts +1 -0
  584. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  585. package/packages/pi-coding-agent/dist/core/tools/path-utils.js +1 -1
  586. package/packages/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  587. package/packages/pi-coding-agent/dist/core/tools/truncate.d.ts.map +1 -1
  588. package/packages/pi-coding-agent/dist/core/tools/truncate.js +2 -1
  589. package/packages/pi-coding-agent/dist/core/tools/truncate.js.map +1 -1
  590. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts +9 -0
  591. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  592. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js +47 -5
  593. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js.map +1 -1
  594. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -1
  595. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
  596. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -1
  597. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
  598. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +4 -4
  599. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  600. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  601. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  602. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  603. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  604. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  605. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -1
  606. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  607. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  608. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +12 -2
  609. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  610. package/packages/pi-coding-agent/dist/resources/extensions/memory/pipeline.d.ts.map +1 -1
  611. package/packages/pi-coding-agent/dist/resources/extensions/memory/pipeline.js +25 -3
  612. package/packages/pi-coding-agent/dist/resources/extensions/memory/pipeline.js.map +1 -1
  613. package/packages/pi-coding-agent/package.json +1 -5
  614. package/packages/pi-coding-agent/scripts/copy-assets.cjs +39 -8
  615. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  616. package/packages/pi-coding-agent/src/core/auth-storage.ts +2 -1
  617. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -1
  618. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -2
  619. package/packages/pi-coding-agent/src/core/compaction/utils.ts +2 -2
  620. package/packages/pi-coding-agent/src/core/constants.ts +59 -0
  621. package/packages/pi-coding-agent/src/core/extensions/loader.ts +1 -1
  622. package/packages/pi-coding-agent/src/core/lsp/index.ts +31 -5
  623. package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +13 -5
  624. package/packages/pi-coding-agent/src/core/model-resolver.ts +14 -0
  625. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +58 -0
  626. package/packages/pi-coding-agent/src/core/resolve-config-value.ts +14 -4
  627. package/packages/pi-coding-agent/src/core/session-manager.ts +29 -6
  628. package/packages/pi-coding-agent/src/core/settings-manager.ts +33 -5
  629. package/packages/pi-coding-agent/src/core/system-prompt.ts +11 -0
  630. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +2 -2
  631. package/packages/pi-coding-agent/src/core/tools/find.ts +2 -1
  632. package/packages/pi-coding-agent/src/core/tools/hashline-edit.ts +4 -4
  633. package/packages/pi-coding-agent/src/core/tools/hashline.test.ts +8 -8
  634. package/packages/pi-coding-agent/src/core/tools/hashline.ts +1 -1
  635. package/packages/pi-coding-agent/src/core/tools/index.ts +1 -1
  636. package/packages/pi-coding-agent/src/core/tools/path-utils.ts +1 -1
  637. package/packages/pi-coding-agent/src/core/tools/truncate.ts +3 -1
  638. package/packages/pi-coding-agent/src/modes/interactive/components/extension-selector.ts +49 -5
  639. package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -1
  640. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +4 -4
  641. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  642. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +26 -0
  643. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +11 -2
  644. package/packages/pi-coding-agent/src/resources/extensions/memory/pipeline.ts +23 -3
  645. package/packages/pi-tui/dist/__tests__/autocomplete.test.d.ts +2 -0
  646. package/packages/pi-tui/dist/__tests__/autocomplete.test.d.ts.map +1 -0
  647. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +149 -0
  648. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -0
  649. package/packages/pi-tui/dist/__tests__/fuzzy.test.d.ts +2 -0
  650. package/packages/pi-tui/dist/__tests__/fuzzy.test.d.ts.map +1 -0
  651. package/packages/pi-tui/dist/__tests__/fuzzy.test.js +94 -0
  652. package/packages/pi-tui/dist/__tests__/fuzzy.test.js.map +1 -0
  653. package/packages/pi-tui/dist/autocomplete.d.ts +8 -1
  654. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  655. package/packages/pi-tui/dist/autocomplete.js +20 -2
  656. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  657. package/packages/pi-tui/package.json +8 -2
  658. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +186 -0
  659. package/packages/pi-tui/src/__tests__/fuzzy.test.ts +112 -0
  660. package/packages/pi-tui/src/autocomplete.ts +26 -1
  661. package/pkg/package.json +1 -1
  662. package/src/resources/extensions/ask-user-questions.ts +3 -2
  663. package/src/resources/extensions/bg-shell/bg-shell-command.ts +219 -0
  664. package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +400 -0
  665. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +985 -0
  666. package/src/resources/extensions/bg-shell/index.ts +17 -1561
  667. package/src/resources/extensions/bg-shell/overlay.ts +4 -0
  668. package/src/resources/extensions/bg-shell/utilities.ts +4 -16
  669. package/src/resources/extensions/browser-tools/capture.ts +34 -2
  670. package/src/resources/extensions/browser-tools/lifecycle.ts +5 -5
  671. package/src/resources/extensions/browser-tools/settle.ts +1 -1
  672. package/src/resources/extensions/browser-tools/state.ts +5 -5
  673. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +1 -1
  674. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +3 -3
  675. package/src/resources/extensions/browser-tools/tools/assertions.ts +1 -1
  676. package/src/resources/extensions/browser-tools/tools/device.ts +1 -1
  677. package/src/resources/extensions/browser-tools/tools/extract.ts +1 -1
  678. package/src/resources/extensions/browser-tools/tools/navigation.ts +6 -6
  679. package/src/resources/extensions/browser-tools/tools/network-mock.ts +1 -1
  680. package/src/resources/extensions/browser-tools/tools/pages.ts +1 -1
  681. package/src/resources/extensions/browser-tools/tools/screenshot.ts +28 -10
  682. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +1 -1
  683. package/src/resources/extensions/browser-tools/tools/visual-diff.ts +1 -1
  684. package/src/resources/extensions/browser-tools/utils.ts +5 -5
  685. package/src/resources/extensions/get-secrets-from-user.ts +1 -1
  686. package/src/resources/extensions/google-search/index.ts +21 -8
  687. package/src/resources/extensions/gsd/activity-log.ts +2 -1
  688. package/src/resources/extensions/gsd/atomic-write.ts +35 -0
  689. package/src/resources/extensions/gsd/auto/session.ts +12 -0
  690. package/src/resources/extensions/gsd/auto-dashboard.ts +84 -3
  691. package/src/resources/extensions/gsd/auto-idempotency.ts +150 -0
  692. package/src/resources/extensions/gsd/auto-post-unit.ts +591 -0
  693. package/src/resources/extensions/gsd/auto-prompts.ts +116 -22
  694. package/src/resources/extensions/gsd/auto-recovery.ts +21 -10
  695. package/src/resources/extensions/gsd/auto-start.ts +500 -0
  696. package/src/resources/extensions/gsd/auto-stuck-detection.ts +220 -0
  697. package/src/resources/extensions/gsd/auto-timers.ts +223 -0
  698. package/src/resources/extensions/gsd/auto-unit-closeout.ts +3 -1
  699. package/src/resources/extensions/gsd/auto-verification.ts +229 -0
  700. package/src/resources/extensions/gsd/auto-worktree-sync.ts +22 -31
  701. package/src/resources/extensions/gsd/auto-worktree.ts +83 -67
  702. package/src/resources/extensions/gsd/auto.ts +375 -1890
  703. package/src/resources/extensions/gsd/commands-config.ts +102 -0
  704. package/src/resources/extensions/gsd/commands-handlers.ts +375 -0
  705. package/src/resources/extensions/gsd/commands-inspect.ts +90 -0
  706. package/src/resources/extensions/gsd/commands-logs.ts +537 -0
  707. package/src/resources/extensions/gsd/commands-maintenance.ts +206 -0
  708. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +760 -0
  709. package/src/resources/extensions/gsd/commands.ts +367 -1477
  710. package/src/resources/extensions/gsd/constants.ts +21 -0
  711. package/src/resources/extensions/gsd/context-budget.ts +25 -2
  712. package/src/resources/extensions/gsd/crash-recovery.ts +3 -4
  713. package/src/resources/extensions/gsd/dashboard-overlay.ts +15 -5
  714. package/src/resources/extensions/gsd/db-writer.ts +21 -2
  715. package/src/resources/extensions/gsd/detection.ts +469 -0
  716. package/src/resources/extensions/gsd/diff-context.ts +2 -1
  717. package/src/resources/extensions/gsd/dispatch-guard.ts +4 -0
  718. package/src/resources/extensions/gsd/doctor-checks.ts +564 -0
  719. package/src/resources/extensions/gsd/doctor-format.ts +78 -0
  720. package/src/resources/extensions/gsd/doctor-types.ts +72 -0
  721. package/src/resources/extensions/gsd/doctor.ts +64 -701
  722. package/src/resources/extensions/gsd/errors.ts +0 -2
  723. package/src/resources/extensions/gsd/export-html.ts +367 -11
  724. package/src/resources/extensions/gsd/export.ts +31 -5
  725. package/src/resources/extensions/gsd/files.ts +8 -126
  726. package/src/resources/extensions/gsd/forensics.ts +2 -12
  727. package/src/resources/extensions/gsd/git-constants.ts +11 -0
  728. package/src/resources/extensions/gsd/git-service.ts +13 -9
  729. package/src/resources/extensions/gsd/gsd-db.ts +26 -6
  730. package/src/resources/extensions/gsd/guided-flow-queue.ts +451 -0
  731. package/src/resources/extensions/gsd/guided-flow.ts +231 -514
  732. package/src/resources/extensions/gsd/history.ts +2 -20
  733. package/src/resources/extensions/gsd/index.ts +208 -46
  734. package/src/resources/extensions/gsd/init-wizard.ts +615 -0
  735. package/src/resources/extensions/gsd/json-persistence.ts +52 -0
  736. package/src/resources/extensions/gsd/jsonl-utils.ts +21 -0
  737. package/src/resources/extensions/gsd/key-manager.ts +995 -0
  738. package/src/resources/extensions/gsd/metrics.ts +49 -36
  739. package/src/resources/extensions/gsd/migrate/command.ts +1 -1
  740. package/src/resources/extensions/gsd/migrate/parsers.ts +10 -95
  741. package/src/resources/extensions/gsd/milestone-actions.ts +126 -0
  742. package/src/resources/extensions/gsd/milestone-ids.ts +95 -0
  743. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -10
  744. package/src/resources/extensions/gsd/parallel-eligibility.ts +3 -3
  745. package/src/resources/extensions/gsd/paths.ts +1 -11
  746. package/src/resources/extensions/gsd/plugin-importer.ts +3 -2
  747. package/src/resources/extensions/gsd/preferences-models.ts +323 -0
  748. package/src/resources/extensions/gsd/preferences-skills.ts +169 -0
  749. package/src/resources/extensions/gsd/preferences-types.ts +223 -0
  750. package/src/resources/extensions/gsd/preferences-validation.ts +597 -0
  751. package/src/resources/extensions/gsd/preferences.ts +219 -1305
  752. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +213 -0
  753. package/src/resources/extensions/gsd/prompt-compressor.ts +508 -0
  754. package/src/resources/extensions/gsd/prompt-loader.ts +4 -2
  755. package/src/resources/extensions/gsd/prompt-ordering.ts +200 -0
  756. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -2
  757. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  758. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -4
  759. package/src/resources/extensions/gsd/prompts/discuss.md +13 -5
  760. package/src/resources/extensions/gsd/prompts/execute-task.md +0 -1
  761. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  762. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +0 -1
  763. package/src/resources/extensions/gsd/prompts/plan-milestone.md +0 -1
  764. package/src/resources/extensions/gsd/prompts/plan-slice.md +0 -1
  765. package/src/resources/extensions/gsd/prompts/queue.md +30 -0
  766. package/src/resources/extensions/gsd/prompts/quick-task.md +0 -6
  767. package/src/resources/extensions/gsd/prompts/replan-slice.md +0 -1
  768. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +0 -1
  769. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  770. package/src/resources/extensions/gsd/provider-error-pause.ts +59 -10
  771. package/src/resources/extensions/gsd/queue-order.ts +1 -1
  772. package/src/resources/extensions/gsd/queue-reorder-ui.ts +15 -2
  773. package/src/resources/extensions/gsd/quick.ts +18 -15
  774. package/src/resources/extensions/gsd/reports.ts +1 -7
  775. package/src/resources/extensions/gsd/routing-history.ts +13 -17
  776. package/src/resources/extensions/gsd/safe-fs.ts +47 -0
  777. package/src/resources/extensions/gsd/semantic-chunker.ts +336 -0
  778. package/src/resources/extensions/gsd/session-forensics.ts +8 -23
  779. package/src/resources/extensions/gsd/session-lock.ts +284 -0
  780. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +38 -1
  781. package/src/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +35 -6
  782. package/src/resources/extensions/gsd/state.ts +54 -2
  783. package/src/resources/extensions/gsd/structured-data-formatter.ts +144 -0
  784. package/src/resources/extensions/gsd/summary-distiller.ts +258 -0
  785. package/src/resources/extensions/gsd/tests/activity-log.test.ts +213 -0
  786. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +107 -0
  787. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +197 -0
  788. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
  789. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +33 -39
  790. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +108 -2
  791. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +257 -0
  792. package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
  793. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +3 -0
  794. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
  795. package/src/resources/extensions/gsd/tests/context-budget.test.ts +69 -0
  796. package/src/resources/extensions/gsd/tests/detection.test.ts +398 -0
  797. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +12 -24
  798. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +118 -94
  799. package/src/resources/extensions/gsd/tests/dispatch-stall-guard.test.ts +126 -0
  800. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +7 -3
  801. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +75 -0
  802. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +17 -55
  803. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +375 -0
  804. package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +122 -0
  805. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +3 -0
  806. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
  807. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +340 -0
  808. package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +24 -82
  809. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +197 -0
  810. package/src/resources/extensions/gsd/tests/key-manager.test.ts +414 -0
  811. package/src/resources/extensions/gsd/tests/metrics.test.ts +173 -305
  812. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +3 -0
  813. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +59 -1
  814. package/src/resources/extensions/gsd/tests/next-milestone-id.test.ts +18 -61
  815. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +17 -8
  816. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +3 -0
  817. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
  818. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +276 -0
  819. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +401 -0
  820. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +23 -47
  821. package/src/resources/extensions/gsd/tests/preferences.test.ts +284 -0
  822. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +314 -0
  823. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +529 -0
  824. package/src/resources/extensions/gsd/tests/prompt-ordering.test.ts +296 -0
  825. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +338 -0
  826. package/src/resources/extensions/gsd/tests/reassess-detection.test.ts +154 -0
  827. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +1 -1
  828. package/src/resources/extensions/gsd/tests/remote-status.test.ts +2 -2
  829. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -60
  830. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +426 -0
  831. package/src/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
  832. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -0
  833. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +8 -5
  834. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +365 -0
  835. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +323 -0
  836. package/src/resources/extensions/gsd/tests/token-counter.test.ts +129 -0
  837. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +1272 -0
  838. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +164 -0
  839. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  840. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +69 -73
  841. package/src/resources/extensions/gsd/tests/validate-directory.test.ts +222 -0
  842. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
  843. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
  844. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +251 -8
  845. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +2 -2
  846. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +2 -1
  847. package/src/resources/extensions/gsd/tests/workspace-index.test.ts +24 -61
  848. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +5 -2
  849. package/src/resources/extensions/gsd/tests/write-gate.test.ts +132 -43
  850. package/src/resources/extensions/gsd/token-counter.ts +20 -0
  851. package/src/resources/extensions/gsd/triage-ui.ts +1 -1
  852. package/src/resources/extensions/gsd/types.ts +5 -1
  853. package/src/resources/extensions/gsd/unit-runtime.ts +16 -13
  854. package/src/resources/extensions/gsd/validate-directory.ts +164 -0
  855. package/src/resources/extensions/gsd/verification-evidence.ts +9 -4
  856. package/src/resources/extensions/gsd/verification-gate.ts +83 -7
  857. package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
  858. package/src/resources/extensions/gsd/visualizer-overlay.ts +5 -2
  859. package/src/resources/extensions/gsd/visualizer-views.ts +2 -3
  860. package/src/resources/extensions/gsd/worktree-command.ts +4 -51
  861. package/src/resources/extensions/gsd/worktree-manager.ts +7 -9
  862. package/src/resources/extensions/gsd/worktree.ts +41 -1
  863. package/src/resources/extensions/mcporter/index.ts +27 -14
  864. package/src/resources/extensions/remote-questions/discord-adapter.ts +9 -20
  865. package/src/resources/extensions/remote-questions/http-client.ts +76 -0
  866. package/src/resources/extensions/remote-questions/manager.ts +6 -24
  867. package/src/resources/extensions/remote-questions/mod.ts +16 -0
  868. package/src/resources/extensions/remote-questions/notify.ts +90 -0
  869. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  870. package/src/resources/extensions/remote-questions/slack-adapter.ts +11 -18
  871. package/src/resources/extensions/remote-questions/store.ts +5 -1
  872. package/src/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
  873. package/src/resources/extensions/remote-questions/types.ts +29 -3
  874. package/src/resources/extensions/search-the-web/native-search.ts +7 -0
  875. package/src/resources/extensions/search-the-web/provider.ts +15 -3
  876. package/src/resources/extensions/search-the-web/tool-llm-context.ts +1 -13
  877. package/src/resources/extensions/search-the-web/tool-search.ts +1 -13
  878. package/src/resources/extensions/shared/format-utils.ts +53 -0
  879. package/src/resources/extensions/shared/frontmatter.ts +117 -0
  880. package/src/resources/extensions/shared/mod.ts +33 -0
  881. package/src/resources/extensions/shared/sanitize.ts +19 -0
  882. package/src/resources/extensions/slash-commands/create-extension.ts +1 -1
  883. package/src/resources/extensions/slash-commands/create-slash-command.ts +1 -1
  884. package/src/resources/extensions/subagent/index.ts +1 -2
  885. package/src/resources/extensions/ttsr/index.ts +5 -0
  886. package/src/resources/extensions/ttsr/rule-loader.ts +4 -51
  887. package/src/resources/extensions/universal-config/discovery.ts +37 -15
  888. package/src/resources/extensions/voice/index.ts +1 -1
  889. package/src/resources/skills/accessibility/SKILL.md +522 -0
  890. package/src/resources/skills/accessibility/references/WCAG.md +162 -0
  891. package/src/resources/skills/agent-browser/SKILL.md +517 -0
  892. package/src/resources/skills/agent-browser/references/authentication.md +202 -0
  893. package/src/resources/skills/agent-browser/references/commands.md +263 -0
  894. package/src/resources/skills/agent-browser/references/profiling.md +120 -0
  895. package/src/resources/skills/agent-browser/references/proxy-support.md +194 -0
  896. package/src/resources/skills/agent-browser/references/session-management.md +193 -0
  897. package/src/resources/skills/agent-browser/references/snapshot-refs.md +194 -0
  898. package/src/resources/skills/agent-browser/references/video-recording.md +173 -0
  899. package/src/resources/skills/agent-browser/templates/authenticated-session.sh +105 -0
  900. package/src/resources/skills/agent-browser/templates/capture-workflow.sh +69 -0
  901. package/src/resources/skills/agent-browser/templates/form-automation.sh +62 -0
  902. package/src/resources/skills/best-practices/SKILL.md +583 -0
  903. package/src/resources/skills/code-optimizer/SKILL.md +160 -0
  904. package/src/resources/skills/code-optimizer/references/algorithmic-complexity.md +66 -0
  905. package/src/resources/skills/code-optimizer/references/build-compilation.md +90 -0
  906. package/src/resources/skills/code-optimizer/references/bundle-dependencies.md +82 -0
  907. package/src/resources/skills/code-optimizer/references/caching-memoization.md +76 -0
  908. package/src/resources/skills/code-optimizer/references/concurrency-async.md +80 -0
  909. package/src/resources/skills/code-optimizer/references/config-infra.md +71 -0
  910. package/src/resources/skills/code-optimizer/references/data-structures.md +80 -0
  911. package/src/resources/skills/code-optimizer/references/database-queries.md +76 -0
  912. package/src/resources/skills/code-optimizer/references/dead-code-redundancy.md +84 -0
  913. package/src/resources/skills/code-optimizer/references/error-resilience.md +80 -0
  914. package/src/resources/skills/code-optimizer/references/io-network.md +89 -0
  915. package/src/resources/skills/code-optimizer/references/logging-observability.md +64 -0
  916. package/src/resources/skills/code-optimizer/references/memory-resources.md +66 -0
  917. package/src/resources/skills/code-optimizer/references/rendering-ui.md +90 -0
  918. package/src/resources/skills/code-optimizer/references/security-performance.md +68 -0
  919. package/src/resources/skills/core-web-vitals/SKILL.md +441 -0
  920. package/src/resources/skills/core-web-vitals/references/LCP.md +208 -0
  921. package/src/resources/skills/make-interfaces-feel-better/SKILL.md +122 -0
  922. package/src/resources/skills/make-interfaces-feel-better/animations.md +379 -0
  923. package/src/resources/skills/make-interfaces-feel-better/performance.md +88 -0
  924. package/src/resources/skills/make-interfaces-feel-better/surfaces.md +247 -0
  925. package/src/resources/skills/make-interfaces-feel-better/typography.md +123 -0
  926. package/src/resources/skills/react-best-practices/README.md +123 -0
  927. package/src/resources/skills/react-best-practices/SKILL.md +136 -0
  928. package/src/resources/skills/react-best-practices/metadata.json +15 -0
  929. package/src/resources/skills/react-best-practices/rules/_sections.md +46 -0
  930. package/src/resources/skills/react-best-practices/rules/_template.md +28 -0
  931. package/src/resources/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  932. package/src/resources/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  933. package/src/resources/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  934. package/src/resources/skills/react-best-practices/rules/async-api-routes.md +38 -0
  935. package/src/resources/skills/react-best-practices/rules/async-defer-await.md +80 -0
  936. package/src/resources/skills/react-best-practices/rules/async-dependencies.md +51 -0
  937. package/src/resources/skills/react-best-practices/rules/async-parallel.md +28 -0
  938. package/src/resources/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  939. package/src/resources/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  940. package/src/resources/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  941. package/src/resources/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  942. package/src/resources/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  943. package/src/resources/skills/react-best-practices/rules/bundle-preload.md +50 -0
  944. package/src/resources/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  945. package/src/resources/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  946. package/src/resources/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  947. package/src/resources/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  948. package/src/resources/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
  949. package/src/resources/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  950. package/src/resources/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  951. package/src/resources/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  952. package/src/resources/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  953. package/src/resources/skills/react-best-practices/rules/js-early-exit.md +50 -0
  954. package/src/resources/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  955. package/src/resources/skills/react-best-practices/rules/js-index-maps.md +37 -0
  956. package/src/resources/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  957. package/src/resources/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  958. package/src/resources/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  959. package/src/resources/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  960. package/src/resources/skills/react-best-practices/rules/rendering-activity.md +26 -0
  961. package/src/resources/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  962. package/src/resources/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  963. package/src/resources/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  964. package/src/resources/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  965. package/src/resources/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  966. package/src/resources/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  967. package/src/resources/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  968. package/src/resources/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  969. package/src/resources/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  970. package/src/resources/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  971. package/src/resources/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  972. package/src/resources/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  973. package/src/resources/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  974. package/src/resources/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  975. package/src/resources/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  976. package/src/resources/skills/react-best-practices/rules/rerender-memo.md +44 -0
  977. package/src/resources/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  978. package/src/resources/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  979. package/src/resources/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  980. package/src/resources/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  981. package/src/resources/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  982. package/src/resources/skills/react-best-practices/rules/server-auth-actions.md +96 -0
  983. package/src/resources/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  984. package/src/resources/skills/react-best-practices/rules/server-cache-react.md +76 -0
  985. package/src/resources/skills/react-best-practices/rules/server-dedup-props.md +65 -0
  986. package/src/resources/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  987. package/src/resources/skills/react-best-practices/rules/server-serialization.md +38 -0
  988. package/src/resources/skills/userinterface-wiki/SKILL.md +253 -0
  989. package/src/resources/skills/userinterface-wiki/rules/_sections.md +66 -0
  990. package/src/resources/skills/userinterface-wiki/rules/_template.md +24 -0
  991. package/src/resources/skills/userinterface-wiki/rules/a11y-reduced-motion-check.md +30 -0
  992. package/src/resources/skills/userinterface-wiki/rules/a11y-toggle-setting.md +30 -0
  993. package/src/resources/skills/userinterface-wiki/rules/a11y-visual-equivalent.md +36 -0
  994. package/src/resources/skills/userinterface-wiki/rules/a11y-volume-control.md +28 -0
  995. package/src/resources/skills/userinterface-wiki/rules/appropriate-confirmations-only.md +19 -0
  996. package/src/resources/skills/userinterface-wiki/rules/appropriate-errors-warnings.md +18 -0
  997. package/src/resources/skills/userinterface-wiki/rules/appropriate-no-decorative.md +21 -0
  998. package/src/resources/skills/userinterface-wiki/rules/appropriate-no-high-frequency.md +28 -0
  999. package/src/resources/skills/userinterface-wiki/rules/appropriate-no-punishing.md +27 -0
  1000. package/src/resources/skills/userinterface-wiki/rules/container-callback-ref.md +31 -0
  1001. package/src/resources/skills/userinterface-wiki/rules/container-guard-initial-zero.md +25 -0
  1002. package/src/resources/skills/userinterface-wiki/rules/container-no-excessive-use.md +13 -0
  1003. package/src/resources/skills/userinterface-wiki/rules/container-overflow-hidden.md +25 -0
  1004. package/src/resources/skills/userinterface-wiki/rules/container-transition-delay.md +21 -0
  1005. package/src/resources/skills/userinterface-wiki/rules/container-two-div-pattern.md +35 -0
  1006. package/src/resources/skills/userinterface-wiki/rules/container-use-resize-observer.md +48 -0
  1007. package/src/resources/skills/userinterface-wiki/rules/context-cleanup-nodes.md +25 -0
  1008. package/src/resources/skills/userinterface-wiki/rules/context-resume-suspended.md +28 -0
  1009. package/src/resources/skills/userinterface-wiki/rules/context-reuse-single.md +30 -0
  1010. package/src/resources/skills/userinterface-wiki/rules/design-filter-for-character.md +25 -0
  1011. package/src/resources/skills/userinterface-wiki/rules/design-noise-for-percussion.md +26 -0
  1012. package/src/resources/skills/userinterface-wiki/rules/design-oscillator-for-tonal.md +22 -0
  1013. package/src/resources/skills/userinterface-wiki/rules/duration-max-300ms.md +21 -0
  1014. package/src/resources/skills/userinterface-wiki/rules/duration-press-hover.md +21 -0
  1015. package/src/resources/skills/userinterface-wiki/rules/duration-shorten-before-curve.md +21 -0
  1016. package/src/resources/skills/userinterface-wiki/rules/duration-small-state.md +15 -0
  1017. package/src/resources/skills/userinterface-wiki/rules/easing-entrance-ease-out.md +21 -0
  1018. package/src/resources/skills/userinterface-wiki/rules/easing-exit-ease-in.md +21 -0
  1019. package/src/resources/skills/userinterface-wiki/rules/easing-for-state-change.md +27 -0
  1020. package/src/resources/skills/userinterface-wiki/rules/easing-linear-only-progress.md +21 -0
  1021. package/src/resources/skills/userinterface-wiki/rules/easing-natural-decay.md +22 -0
  1022. package/src/resources/skills/userinterface-wiki/rules/easing-no-linear-motion.md +22 -0
  1023. package/src/resources/skills/userinterface-wiki/rules/easing-transition-ease-in-out.md +15 -0
  1024. package/src/resources/skills/userinterface-wiki/rules/envelope-exponential-decay.md +21 -0
  1025. package/src/resources/skills/userinterface-wiki/rules/envelope-no-zero-target.md +21 -0
  1026. package/src/resources/skills/userinterface-wiki/rules/envelope-set-initial-value.md +22 -0
  1027. package/src/resources/skills/userinterface-wiki/rules/exit-key-required.md +29 -0
  1028. package/src/resources/skills/userinterface-wiki/rules/exit-matches-initial.md +29 -0
  1029. package/src/resources/skills/userinterface-wiki/rules/exit-prop-required.md +33 -0
  1030. package/src/resources/skills/userinterface-wiki/rules/exit-requires-wrapper.md +27 -0
  1031. package/src/resources/skills/userinterface-wiki/rules/impl-default-subtle.md +21 -0
  1032. package/src/resources/skills/userinterface-wiki/rules/impl-preload-audio.md +34 -0
  1033. package/src/resources/skills/userinterface-wiki/rules/impl-reset-current-time.md +26 -0
  1034. package/src/resources/skills/userinterface-wiki/rules/mode-pop-layout-for-lists.md +25 -0
  1035. package/src/resources/skills/userinterface-wiki/rules/mode-sync-layout-conflict.md +29 -0
  1036. package/src/resources/skills/userinterface-wiki/rules/mode-wait-doubles-duration.md +25 -0
  1037. package/src/resources/skills/userinterface-wiki/rules/morphing-aria-hidden.md +21 -0
  1038. package/src/resources/skills/userinterface-wiki/rules/morphing-consistent-viewbox.md +23 -0
  1039. package/src/resources/skills/userinterface-wiki/rules/morphing-group-variants.md +33 -0
  1040. package/src/resources/skills/userinterface-wiki/rules/morphing-jump-non-grouped.md +29 -0
  1041. package/src/resources/skills/userinterface-wiki/rules/morphing-reduced-motion.md +28 -0
  1042. package/src/resources/skills/userinterface-wiki/rules/morphing-spring-rotation.md +23 -0
  1043. package/src/resources/skills/userinterface-wiki/rules/morphing-strokelinecap-round.md +21 -0
  1044. package/src/resources/skills/userinterface-wiki/rules/morphing-three-lines.md +32 -0
  1045. package/src/resources/skills/userinterface-wiki/rules/morphing-use-collapsed.md +33 -0
  1046. package/src/resources/skills/userinterface-wiki/rules/native-backdrop-styling.md +27 -0
  1047. package/src/resources/skills/userinterface-wiki/rules/native-placeholder-styling.md +27 -0
  1048. package/src/resources/skills/userinterface-wiki/rules/native-selection-styling.md +18 -0
  1049. package/src/resources/skills/userinterface-wiki/rules/nested-consistent-timing.md +25 -0
  1050. package/src/resources/skills/userinterface-wiki/rules/nested-propagate-required.md +41 -0
  1051. package/src/resources/skills/userinterface-wiki/rules/none-context-menu-entrance.md +25 -0
  1052. package/src/resources/skills/userinterface-wiki/rules/none-high-frequency.md +29 -0
  1053. package/src/resources/skills/userinterface-wiki/rules/none-keyboard-navigation.md +32 -0
  1054. package/src/resources/skills/userinterface-wiki/rules/param-click-duration.md +21 -0
  1055. package/src/resources/skills/userinterface-wiki/rules/param-filter-frequency-range.md +21 -0
  1056. package/src/resources/skills/userinterface-wiki/rules/param-q-value-range.md +21 -0
  1057. package/src/resources/skills/userinterface-wiki/rules/param-reasonable-gain.md +21 -0
  1058. package/src/resources/skills/userinterface-wiki/rules/physics-active-state.md +23 -0
  1059. package/src/resources/skills/userinterface-wiki/rules/physics-no-excessive-stagger.md +22 -0
  1060. package/src/resources/skills/userinterface-wiki/rules/physics-spring-for-overshoot.md +23 -0
  1061. package/src/resources/skills/userinterface-wiki/rules/physics-subtle-deformation.md +22 -0
  1062. package/src/resources/skills/userinterface-wiki/rules/prefetch-hit-slop.md +27 -0
  1063. package/src/resources/skills/userinterface-wiki/rules/prefetch-keyboard-tab.md +19 -0
  1064. package/src/resources/skills/userinterface-wiki/rules/prefetch-not-everything.md +22 -0
  1065. package/src/resources/skills/userinterface-wiki/rules/prefetch-touch-fallback.md +34 -0
  1066. package/src/resources/skills/userinterface-wiki/rules/prefetch-trajectory-over-hover.md +32 -0
  1067. package/src/resources/skills/userinterface-wiki/rules/prefetch-use-selectively.md +13 -0
  1068. package/src/resources/skills/userinterface-wiki/rules/presence-disable-interactions.md +31 -0
  1069. package/src/resources/skills/userinterface-wiki/rules/presence-hook-in-child.md +31 -0
  1070. package/src/resources/skills/userinterface-wiki/rules/presence-safe-to-remove.md +37 -0
  1071. package/src/resources/skills/userinterface-wiki/rules/pseudo-content-required.md +28 -0
  1072. package/src/resources/skills/userinterface-wiki/rules/pseudo-first-line-styling.md +27 -0
  1073. package/src/resources/skills/userinterface-wiki/rules/pseudo-hit-target-expansion.md +31 -0
  1074. package/src/resources/skills/userinterface-wiki/rules/pseudo-marker-styling.md +28 -0
  1075. package/src/resources/skills/userinterface-wiki/rules/pseudo-over-dom-node.md +32 -0
  1076. package/src/resources/skills/userinterface-wiki/rules/pseudo-position-relative-parent.md +33 -0
  1077. package/src/resources/skills/userinterface-wiki/rules/pseudo-z-index-layering.md +37 -0
  1078. package/src/resources/skills/userinterface-wiki/rules/spring-for-gestures.md +27 -0
  1079. package/src/resources/skills/userinterface-wiki/rules/spring-for-interruptible.md +27 -0
  1080. package/src/resources/skills/userinterface-wiki/rules/spring-params-balanced.md +29 -0
  1081. package/src/resources/skills/userinterface-wiki/rules/spring-preserves-velocity.md +28 -0
  1082. package/src/resources/skills/userinterface-wiki/rules/staging-dim-background.md +22 -0
  1083. package/src/resources/skills/userinterface-wiki/rules/staging-one-focal-point.md +24 -0
  1084. package/src/resources/skills/userinterface-wiki/rules/staging-z-index-hierarchy.md +22 -0
  1085. package/src/resources/skills/userinterface-wiki/rules/timing-consistent.md +24 -0
  1086. package/src/resources/skills/userinterface-wiki/rules/timing-no-entrance-context-menu.md +22 -0
  1087. package/src/resources/skills/userinterface-wiki/rules/timing-under-300ms.md +22 -0
  1088. package/src/resources/skills/userinterface-wiki/rules/transition-name-cleanup.md +28 -0
  1089. package/src/resources/skills/userinterface-wiki/rules/transition-name-required.md +27 -0
  1090. package/src/resources/skills/userinterface-wiki/rules/transition-name-unique.md +24 -0
  1091. package/src/resources/skills/userinterface-wiki/rules/transition-over-js-library.md +32 -0
  1092. package/src/resources/skills/userinterface-wiki/rules/transition-style-pseudo-elements.md +24 -0
  1093. package/src/resources/skills/userinterface-wiki/rules/type-antialiased-on-retina.md +18 -0
  1094. package/src/resources/skills/userinterface-wiki/rules/type-disambiguation-stylistic-set.md +15 -0
  1095. package/src/resources/skills/userinterface-wiki/rules/type-font-display-swap.md +28 -0
  1096. package/src/resources/skills/userinterface-wiki/rules/type-justify-with-hyphens.md +24 -0
  1097. package/src/resources/skills/userinterface-wiki/rules/type-letter-spacing-uppercase.md +28 -0
  1098. package/src/resources/skills/userinterface-wiki/rules/type-no-font-synthesis.md +18 -0
  1099. package/src/resources/skills/userinterface-wiki/rules/type-oldstyle-nums-for-prose.md +21 -0
  1100. package/src/resources/skills/userinterface-wiki/rules/type-opentype-contextual-alternates.md +15 -0
  1101. package/src/resources/skills/userinterface-wiki/rules/type-optical-sizing-auto.md +25 -0
  1102. package/src/resources/skills/userinterface-wiki/rules/type-proper-fractions.md +15 -0
  1103. package/src/resources/skills/userinterface-wiki/rules/type-slashed-zero.md +17 -0
  1104. package/src/resources/skills/userinterface-wiki/rules/type-tabular-nums-for-data.md +21 -0
  1105. package/src/resources/skills/userinterface-wiki/rules/type-text-wrap-balance-headings.md +21 -0
  1106. package/src/resources/skills/userinterface-wiki/rules/type-text-wrap-pretty.md +16 -0
  1107. package/src/resources/skills/userinterface-wiki/rules/type-underline-offset.md +25 -0
  1108. package/src/resources/skills/userinterface-wiki/rules/type-variable-weight-continuous.md +23 -0
  1109. package/src/resources/skills/userinterface-wiki/rules/ux-aesthetic-usability.md +32 -0
  1110. package/src/resources/skills/userinterface-wiki/rules/ux-cognitive-load-reduce.md +49 -0
  1111. package/src/resources/skills/userinterface-wiki/rules/ux-common-region-boundaries.md +50 -0
  1112. package/src/resources/skills/userinterface-wiki/rules/ux-doherty-perceived-speed.md +29 -0
  1113. package/src/resources/skills/userinterface-wiki/rules/ux-doherty-under-400ms.md +30 -0
  1114. package/src/resources/skills/userinterface-wiki/rules/ux-fitts-hit-area.md +32 -0
  1115. package/src/resources/skills/userinterface-wiki/rules/ux-fitts-target-size.md +31 -0
  1116. package/src/resources/skills/userinterface-wiki/rules/ux-goal-gradient-progress.md +33 -0
  1117. package/src/resources/skills/userinterface-wiki/rules/ux-hicks-minimize-choices.md +45 -0
  1118. package/src/resources/skills/userinterface-wiki/rules/ux-jakobs-familiar-patterns.md +37 -0
  1119. package/src/resources/skills/userinterface-wiki/rules/ux-millers-chunking.md +23 -0
  1120. package/src/resources/skills/userinterface-wiki/rules/ux-pareto-prioritize-features.md +36 -0
  1121. package/src/resources/skills/userinterface-wiki/rules/ux-peak-end-finish-strong.md +35 -0
  1122. package/src/resources/skills/userinterface-wiki/rules/ux-postels-accept-messy-input.md +45 -0
  1123. package/src/resources/skills/userinterface-wiki/rules/ux-pragnanz-simplify.md +33 -0
  1124. package/src/resources/skills/userinterface-wiki/rules/ux-progressive-disclosure.md +41 -0
  1125. package/src/resources/skills/userinterface-wiki/rules/ux-proximity-grouping.md +38 -0
  1126. package/src/resources/skills/userinterface-wiki/rules/ux-serial-position.md +31 -0
  1127. package/src/resources/skills/userinterface-wiki/rules/ux-similarity-consistency.md +35 -0
  1128. package/src/resources/skills/userinterface-wiki/rules/ux-teslers-complexity.md +28 -0
  1129. package/src/resources/skills/userinterface-wiki/rules/ux-uniform-connectedness.md +43 -0
  1130. package/src/resources/skills/userinterface-wiki/rules/ux-von-restorff-emphasis.md +29 -0
  1131. package/src/resources/skills/userinterface-wiki/rules/ux-zeigarnik-show-incomplete.md +36 -0
  1132. package/src/resources/skills/userinterface-wiki/rules/visual-animate-shadow-pseudo.md +49 -0
  1133. package/src/resources/skills/userinterface-wiki/rules/visual-border-alpha-colors.md +25 -0
  1134. package/src/resources/skills/userinterface-wiki/rules/visual-button-shadow-anatomy.md +49 -0
  1135. package/src/resources/skills/userinterface-wiki/rules/visual-concentric-radius.md +40 -0
  1136. package/src/resources/skills/userinterface-wiki/rules/visual-consistent-spacing-scale.md +35 -0
  1137. package/src/resources/skills/userinterface-wiki/rules/visual-layered-shadows.md +30 -0
  1138. package/src/resources/skills/userinterface-wiki/rules/visual-no-pure-black-shadow.md +25 -0
  1139. package/src/resources/skills/userinterface-wiki/rules/visual-shadow-direction.md +25 -0
  1140. package/src/resources/skills/userinterface-wiki/rules/visual-shadow-matches-elevation.md +23 -0
  1141. package/src/resources/skills/userinterface-wiki/rules/weight-duration-matches-action.md +29 -0
  1142. package/src/resources/skills/userinterface-wiki/rules/weight-match-action.md +32 -0
  1143. package/src/resources/skills/web-design-guidelines/SKILL.md +39 -0
  1144. package/src/resources/skills/web-quality-audit/SKILL.md +170 -0
  1145. package/src/resources/skills/web-quality-audit/scripts/analyze.sh +91 -0
  1146. package/dist/resources/extensions/gsd/complexity.ts +0 -237
  1147. package/dist/resources/extensions/gsd/github-client.ts +0 -235
  1148. package/dist/resources/extensions/gsd/tests/activity-log-prune.test.ts +0 -297
  1149. package/dist/resources/extensions/gsd/tests/activity-log-save.test.ts +0 -127
  1150. package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +0 -110
  1151. package/dist/resources/extensions/gsd/tests/auto-draft-pause.test.ts +0 -115
  1152. package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +0 -294
  1153. package/dist/resources/extensions/gsd/tests/metrics-io.test.ts +0 -176
  1154. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +0 -104
  1155. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -120
  1156. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -226
  1157. package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +0 -110
  1158. package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -207
  1159. package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +0 -183
  1160. package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +0 -168
  1161. package/dist/resources/extensions/shared/progress-widget.ts +0 -282
  1162. package/dist/resources/extensions/shared/thinking-widget.ts +0 -107
  1163. package/src/resources/extensions/gsd/complexity.ts +0 -237
  1164. package/src/resources/extensions/gsd/github-client.ts +0 -235
  1165. package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +0 -297
  1166. package/src/resources/extensions/gsd/tests/activity-log-save.test.ts +0 -127
  1167. package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +0 -110
  1168. package/src/resources/extensions/gsd/tests/auto-draft-pause.test.ts +0 -115
  1169. package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +0 -294
  1170. package/src/resources/extensions/gsd/tests/metrics-io.test.ts +0 -176
  1171. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +0 -104
  1172. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -120
  1173. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -226
  1174. package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +0 -110
  1175. package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -207
  1176. package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +0 -183
  1177. package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +0 -168
  1178. package/src/resources/extensions/shared/progress-widget.ts +0 -282
  1179. package/src/resources/extensions/shared/thinking-widget.ts +0 -107
@@ -1,13 +1,83 @@
1
- import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
1
+ /**
2
+ * GSD Preferences -- loading, merging, and rendering.
3
+ *
4
+ * This module is the primary entry point for preference operations.
5
+ * Type definitions live in ./preferences-types.js, validation in
6
+ * ./preferences-validation.js, skill logic in ./preferences-skills.js,
7
+ * and model logic in ./preferences-models.js.
8
+ *
9
+ * All symbols are re-exported here so that existing `import { ... } from "./preferences.js"`
10
+ * statements continue to work without modification.
11
+ */
12
+
13
+ import { existsSync, readFileSync } from "node:fs";
2
14
  import { homedir } from "node:os";
3
- import { isAbsolute, join } from "node:path";
4
- import { getAgentDir } from "@gsd/pi-coding-agent";
15
+ import { join } from "node:path";
5
16
  import { parse as parseYaml } from "yaml";
6
- import type { GitPreferences } from "./git-service.js";
7
- import type { PostUnitHookConfig, PreDispatchHookConfig, BudgetEnforcementMode, NotificationPreferences, TokenProfile, InlineLevel, PhaseSkipPreferences } from "./types.js";
17
+ import type { PostUnitHookConfig, PreDispatchHookConfig } from "./types.js";
8
18
  import type { DynamicRoutingConfig } from "./model-router.js";
9
- import { defaultRoutingConfig } from "./model-router.js";
10
- import { VALID_BRANCH_NAME } from "./git-service.js";
19
+ import { normalizeStringArray } from "../shared/mod.js";
20
+
21
+ import {
22
+ MODE_DEFAULTS,
23
+ type WorkflowMode,
24
+ type GSDPreferences,
25
+ type LoadedGSDPreferences,
26
+ type SkillResolution,
27
+ } from "./preferences-types.js";
28
+ import { validatePreferences } from "./preferences-validation.js";
29
+ import { formatSkillRef } from "./preferences-skills.js";
30
+
31
+ // ─── Re-exports: types ──────────────────────────────────────────────────────
32
+ // Every type/interface that was previously exported from this file is
33
+ // re-exported so that downstream `import { Foo } from "./preferences.js"`
34
+ // statements keep compiling.
35
+
36
+ export type {
37
+ WorkflowMode,
38
+ GSDSkillRule,
39
+ GSDPhaseModelConfig,
40
+ GSDModelConfig,
41
+ GSDModelConfigV2,
42
+ ResolvedModelConfig,
43
+ SkillDiscoveryMode,
44
+ AutoSupervisorConfig,
45
+ RemoteQuestionsConfig,
46
+ GSDPreferences,
47
+ LoadedGSDPreferences,
48
+ SkillResolution,
49
+ SkillResolutionReport,
50
+ } from "./preferences-types.js";
51
+
52
+ // ─── Re-exports: validation ─────────────────────────────────────────────────
53
+ export { validatePreferences } from "./preferences-validation.js";
54
+
55
+ // ─── Re-exports: skills ─────────────────────────────────────────────────────
56
+ export {
57
+ resolveAllSkillReferences,
58
+ resolveSkillDiscoveryMode,
59
+ resolveSkillStalenessDays,
60
+ } from "./preferences-skills.js";
61
+
62
+ // ─── Re-exports: models ─────────────────────────────────────────────────────
63
+ export {
64
+ resolveModelForUnit,
65
+ resolveModelWithFallbacksForUnit,
66
+ getNextFallbackModel,
67
+ isTransientNetworkError,
68
+ validateModelId,
69
+ updatePreferencesModels,
70
+ resolveDynamicRoutingConfig,
71
+ resolveAutoSupervisorConfig,
72
+ resolveProfileDefaults,
73
+ resolveEffectiveProfile,
74
+ resolveInlineLevel,
75
+ resolveCompressionStrategy,
76
+ resolveContextSelection,
77
+ resolveSearchProviderFromPreferences,
78
+ } from "./preferences-models.js";
79
+
80
+ // ─── Path Constants & Getters ───────────────────────────────────────────────
11
81
 
12
82
  const GLOBAL_PREFERENCES_PATH = join(homedir(), ".gsd", "preferences.md");
13
83
  const LEGACY_GLOBAL_PREFERENCES_PATH = join(homedir(), ".pi", "agent", "gsd-preferences.md");
@@ -16,181 +86,6 @@ const PROJECT_PREFERENCES_PATH = join(process.cwd(), ".gsd", "preferences.md");
16
86
  // Check uppercase as a fallback so those files aren't silently ignored.
17
87
  const GLOBAL_PREFERENCES_PATH_UPPERCASE = join(homedir(), ".gsd", "PREFERENCES.md");
18
88
  const PROJECT_PREFERENCES_PATH_UPPERCASE = join(process.cwd(), ".gsd", "PREFERENCES.md");
19
- const SKILL_ACTIONS = new Set(["use", "prefer", "avoid"]);
20
-
21
- // ─── Workflow Modes ──────────────────────────────────────────────────────────
22
-
23
- export type WorkflowMode = "solo" | "team";
24
-
25
- /** Default preference values for each workflow mode. */
26
- const MODE_DEFAULTS: Record<WorkflowMode, Partial<GSDPreferences>> = {
27
- solo: {
28
- git: {
29
- auto_push: true,
30
- push_branches: false,
31
- pre_merge_check: false,
32
- merge_strategy: "squash",
33
- isolation: "worktree",
34
- commit_docs: true,
35
- },
36
- unique_milestone_ids: false,
37
- },
38
- team: {
39
- git: {
40
- auto_push: false,
41
- push_branches: true,
42
- pre_merge_check: true,
43
- merge_strategy: "squash",
44
- isolation: "worktree",
45
- commit_docs: true,
46
- },
47
- unique_milestone_ids: true,
48
- },
49
- };
50
-
51
- /** All recognized top-level keys in GSDPreferences. Used to detect typos / stale config. */
52
- const KNOWN_PREFERENCE_KEYS = new Set<string>([
53
- "version",
54
- "mode",
55
- "always_use_skills",
56
- "prefer_skills",
57
- "avoid_skills",
58
- "skill_rules",
59
- "custom_instructions",
60
- "models",
61
- "skill_discovery",
62
- "skill_staleness_days",
63
- "auto_supervisor",
64
- "uat_dispatch",
65
- "unique_milestone_ids",
66
- "budget_ceiling",
67
- "budget_enforcement",
68
- "context_pause_threshold",
69
- "notifications",
70
- "remote_questions",
71
- "git",
72
- "post_unit_hooks",
73
- "pre_dispatch_hooks",
74
- "dynamic_routing",
75
- "token_profile",
76
- "phases",
77
- "auto_visualize",
78
- "auto_report",
79
- "parallel",
80
- "verification_commands",
81
- "verification_auto_fix",
82
- "verification_max_retries",
83
- ]);
84
-
85
- export interface GSDSkillRule {
86
- when: string;
87
- use?: string[];
88
- prefer?: string[];
89
- avoid?: string[];
90
- }
91
-
92
- /**
93
- * Model configuration for a single phase.
94
- * Supports primary model with optional fallbacks for resilience.
95
- */
96
- export interface GSDPhaseModelConfig {
97
- /** Primary model ID (e.g., "claude-opus-4-6") */
98
- model: string;
99
- /** Provider name to disambiguate when the same model ID exists across providers (e.g., "bedrock", "anthropic") */
100
- provider?: string;
101
- /** Fallback models to try in order if primary fails (e.g., rate limits, credits exhausted) */
102
- fallbacks?: string[];
103
- }
104
-
105
- /**
106
- * Legacy model config — simple string per phase.
107
- * Kept for backward compatibility; will be migrated to GSDModelConfigV2 on load.
108
- */
109
- export interface GSDModelConfig {
110
- research?: string;
111
- planning?: string;
112
- execution?: string;
113
- execution_simple?: string;
114
- completion?: string;
115
- subagent?: string;
116
- }
117
-
118
- /**
119
- * Extended model config with per-phase fallback support.
120
- * Each phase can specify a primary model and ordered fallbacks.
121
- */
122
- export interface GSDModelConfigV2 {
123
- research?: string | GSDPhaseModelConfig;
124
- planning?: string | GSDPhaseModelConfig;
125
- execution?: string | GSDPhaseModelConfig;
126
- execution_simple?: string | GSDPhaseModelConfig;
127
- completion?: string | GSDPhaseModelConfig;
128
- subagent?: string | GSDPhaseModelConfig;
129
- }
130
-
131
- /** Normalized model selection with resolved fallbacks */
132
- export interface ResolvedModelConfig {
133
- primary: string;
134
- fallbacks: string[];
135
- }
136
-
137
- export type SkillDiscoveryMode = "auto" | "suggest" | "off";
138
-
139
- export interface AutoSupervisorConfig {
140
- model?: string;
141
- soft_timeout_minutes?: number;
142
- idle_timeout_minutes?: number;
143
- hard_timeout_minutes?: number;
144
- }
145
-
146
- export interface RemoteQuestionsConfig {
147
- channel: "slack" | "discord" | "telegram";
148
- channel_id: string | number;
149
- timeout_minutes?: number; // clamped to 1-30
150
- poll_interval_seconds?: number; // clamped to 2-30
151
- }
152
-
153
- export interface GSDPreferences {
154
- version?: number;
155
- mode?: WorkflowMode;
156
- always_use_skills?: string[];
157
- prefer_skills?: string[];
158
- avoid_skills?: string[];
159
- skill_rules?: GSDSkillRule[];
160
- custom_instructions?: string[];
161
- models?: GSDModelConfig | GSDModelConfigV2;
162
- skill_discovery?: SkillDiscoveryMode;
163
- skill_staleness_days?: number; // Skills unused for N days get deprioritized (#599). 0 = disabled. Default: 60.
164
- auto_supervisor?: AutoSupervisorConfig;
165
- uat_dispatch?: boolean;
166
- unique_milestone_ids?: boolean;
167
- budget_ceiling?: number;
168
- budget_enforcement?: BudgetEnforcementMode;
169
- context_pause_threshold?: number;
170
- notifications?: NotificationPreferences;
171
- remote_questions?: RemoteQuestionsConfig;
172
- git?: GitPreferences;
173
- post_unit_hooks?: PostUnitHookConfig[];
174
- pre_dispatch_hooks?: PreDispatchHookConfig[];
175
- dynamic_routing?: DynamicRoutingConfig;
176
- token_profile?: TokenProfile;
177
- phases?: PhaseSkipPreferences;
178
- auto_visualize?: boolean;
179
- /** Generate HTML report snapshot after each milestone completion. Default: true. Set false to disable. */
180
- auto_report?: boolean;
181
- parallel?: import("./types.js").ParallelConfig;
182
- verification_commands?: string[];
183
- verification_auto_fix?: boolean;
184
- verification_max_retries?: number;
185
- }
186
-
187
- export interface LoadedGSDPreferences {
188
- path: string;
189
- scope: "global" | "project";
190
- preferences: GSDPreferences;
191
- /** Validation warnings (unknown keys, type mismatches, deprecations). Empty when preferences are clean. */
192
- warnings?: string[];
193
- }
194
89
 
195
90
  export function getGlobalGSDPreferencesPath(): string {
196
91
  return GLOBAL_PREFERENCES_PATH;
@@ -204,6 +99,8 @@ export function getProjectGSDPreferencesPath(): string {
204
99
  return PROJECT_PREFERENCES_PATH;
205
100
  }
206
101
 
102
+ // ─── Loading ────────────────────────────────────────────────────────────────
103
+
207
104
  export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null {
208
105
  return loadPreferencesFile(GLOBAL_PREFERENCES_PATH, "global")
209
106
  ?? loadPreferencesFile(GLOBAL_PREFERENCES_PATH_UPPERCASE, "global")
@@ -215,16 +112,6 @@ export function loadProjectGSDPreferences(): LoadedGSDPreferences | null {
215
112
  ?? loadPreferencesFile(PROJECT_PREFERENCES_PATH_UPPERCASE, "project");
216
113
  }
217
114
 
218
- /**
219
- * Apply mode defaults as the lowest-priority layer.
220
- * Mode defaults fill in undefined fields; any explicit user value wins.
221
- */
222
- export function applyModeDefaults(mode: WorkflowMode, prefs: GSDPreferences): GSDPreferences {
223
- const defaults = MODE_DEFAULTS[mode];
224
- if (!defaults) return prefs;
225
- return mergePreferences(defaults, prefs);
226
- }
227
-
228
115
  export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null {
229
116
  const globalPreferences = loadGlobalGSDPreferences();
230
117
  const projectPreferences = loadProjectGSDPreferences();
@@ -260,149 +147,152 @@ export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null {
260
147
  return result;
261
148
  }
262
149
 
263
- // ─── Skill Reference Resolution ───────────────────────────────────────────────
150
+ function loadPreferencesFile(path: string, scope: "global" | "project"): LoadedGSDPreferences | null {
151
+ if (!existsSync(path)) return null;
264
152
 
265
- export interface SkillResolution {
266
- /** The original reference from preferences (bare name or path). */
267
- original: string;
268
- /** The resolved absolute path to the SKILL.md file, or null if unresolved. */
269
- resolvedPath: string | null;
270
- /** How it was resolved. */
271
- method: "absolute-path" | "absolute-dir" | "user-skill" | "project-skill" | "unresolved";
272
- }
153
+ const raw = readFileSync(path, "utf-8");
154
+ const preferences = parsePreferencesMarkdown(raw);
155
+ if (!preferences) return null;
273
156
 
274
- export interface SkillResolutionReport {
275
- /** All resolution results, keyed by original reference. */
276
- resolutions: Map<string, SkillResolution>;
277
- /** References that could not be resolved. */
278
- warnings: string[];
279
- }
157
+ const validation = validatePreferences(preferences);
158
+ const allWarnings = [...validation.warnings, ...validation.errors];
280
159
 
281
- /**
282
- * Known skill directories, in priority order.
283
- * User skills (~/.gsd/agent/skills/) take precedence over project skills.
284
- */
285
- function getSkillSearchDirs(cwd: string): Array<{ dir: string; method: SkillResolution["method"] }> {
286
- return [
287
- { dir: join(getAgentDir(), "skills"), method: "user-skill" },
288
- { dir: join(cwd, ".pi", "agent", "skills"), method: "project-skill" },
289
- ];
160
+ return {
161
+ path,
162
+ scope,
163
+ preferences: validation.preferences,
164
+ ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}),
165
+ };
290
166
  }
291
167
 
292
- /**
293
- * Resolve a single skill reference to an absolute path.
294
- *
295
- * Resolution order:
296
- * 1. Absolute path to a file → check existsSync
297
- * 2. Absolute path to a directory → check for SKILL.md inside
298
- * 3. Bare name → scan known skill directories for <name>/SKILL.md
299
- */
300
- function resolveSkillReference(ref: string, cwd: string): SkillResolution {
301
- const trimmed = ref.trim();
302
-
303
- // Expand tilde
304
- const expanded = trimmed.startsWith("~/")
305
- ? join(homedir(), trimmed.slice(2))
306
- : trimmed;
168
+ /** @internal Exported for testing only */
169
+ export function parsePreferencesMarkdown(content: string): GSDPreferences | null {
170
+ // Use indexOf instead of [\s\S]*? regex to avoid backtracking (#468)
171
+ const startMarker = content.startsWith('---\r\n') ? '---\r\n' : '---\n';
172
+ if (!content.startsWith(startMarker)) return null;
173
+ const searchStart = startMarker.length;
174
+ const endIdx = content.indexOf('\n---', searchStart);
175
+ if (endIdx === -1) return null;
176
+ const block = content.slice(searchStart, endIdx);
177
+ return parseFrontmatterBlock(block.replace(/\r/g, ''));
178
+ }
307
179
 
308
- // Absolute path
309
- if (isAbsolute(expanded)) {
310
- // Direct file reference
311
- if (existsSync(expanded)) {
312
- // Check if it's a directory — look for SKILL.md inside
313
- try {
314
- const stat = statSync(expanded);
315
- if (stat.isDirectory()) {
316
- const skillFile = join(expanded, "SKILL.md");
317
- if (existsSync(skillFile)) {
318
- return { original: ref, resolvedPath: skillFile, method: "absolute-dir" };
319
- }
320
- return { original: ref, resolvedPath: null, method: "unresolved" };
321
- }
322
- } catch { /* fall through */ }
323
- return { original: ref, resolvedPath: expanded, method: "absolute-path" };
324
- }
325
- // Maybe it's a directory path without SKILL.md suffix
326
- const withSkillMd = join(expanded, "SKILL.md");
327
- if (existsSync(withSkillMd)) {
328
- return { original: ref, resolvedPath: withSkillMd, method: "absolute-dir" };
180
+ function parseFrontmatterBlock(frontmatter: string): GSDPreferences {
181
+ try {
182
+ const parsed = parseYaml(frontmatter);
183
+ if (typeof parsed !== 'object' || parsed === null) {
184
+ return {} as GSDPreferences;
329
185
  }
330
- return { original: ref, resolvedPath: null, method: "unresolved" };
331
- }
332
-
333
- // Bare name — scan known skill directories
334
- for (const { dir, method } of getSkillSearchDirs(cwd)) {
335
- if (!existsSync(dir)) continue;
336
- try {
337
- const entries = readdirSync(dir, { withFileTypes: true });
338
- for (const entry of entries) {
339
- if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
340
- if (entry.name === expanded) {
341
- const skillFile = join(dir, entry.name, "SKILL.md");
342
- if (existsSync(skillFile)) {
343
- return { original: ref, resolvedPath: skillFile, method };
344
- }
345
- }
346
- }
347
- } catch { /* directory not readable — skip */ }
186
+ return parsed as GSDPreferences;
187
+ } catch (e) {
188
+ console.error("[parseFrontmatterBlock] YAML parse error:", e);
189
+ return {} as GSDPreferences;
348
190
  }
349
-
350
- return { original: ref, resolvedPath: null, method: "unresolved" };
351
191
  }
352
192
 
193
+ // ─── Merging ────────────────────────────────────────────────────────────────
194
+
353
195
  /**
354
- * Resolve all skill references in a preferences object.
355
- * Caches resolution per reference string to avoid redundant filesystem scans.
196
+ * Apply mode defaults as the lowest-priority layer.
197
+ * Mode defaults fill in undefined fields; any explicit user value wins.
356
198
  */
357
- export function resolveAllSkillReferences(preferences: GSDPreferences, cwd: string): SkillResolutionReport {
358
- const validated = validatePreferences(preferences).preferences;
359
- preferences = validated;
360
-
361
- const resolutions = new Map<string, SkillResolution>();
362
- const warnings: string[] = [];
199
+ export function applyModeDefaults(mode: WorkflowMode, prefs: GSDPreferences): GSDPreferences {
200
+ const defaults = MODE_DEFAULTS[mode];
201
+ if (!defaults) return prefs;
202
+ return mergePreferences(defaults, prefs);
203
+ }
363
204
 
364
- function resolve(ref: string): SkillResolution {
365
- const existing = resolutions.get(ref);
366
- if (existing) return existing;
367
- const result = resolveSkillReference(ref, cwd);
368
- resolutions.set(ref, result);
369
- if (result.method === "unresolved") {
370
- warnings.push(ref);
371
- }
372
- return result;
373
- }
205
+ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPreferences {
206
+ return {
207
+ version: override.version ?? base.version,
208
+ mode: override.mode ?? base.mode,
209
+ always_use_skills: mergeStringLists(base.always_use_skills, override.always_use_skills),
210
+ prefer_skills: mergeStringLists(base.prefer_skills, override.prefer_skills),
211
+ avoid_skills: mergeStringLists(base.avoid_skills, override.avoid_skills),
212
+ skill_rules: [...(base.skill_rules ?? []), ...(override.skill_rules ?? [])],
213
+ custom_instructions: mergeStringLists(base.custom_instructions, override.custom_instructions),
214
+ models: { ...(base.models ?? {}), ...(override.models ?? {}) },
215
+ skill_discovery: override.skill_discovery ?? base.skill_discovery,
216
+ skill_staleness_days: override.skill_staleness_days ?? base.skill_staleness_days,
217
+ auto_supervisor: { ...(base.auto_supervisor ?? {}), ...(override.auto_supervisor ?? {}) },
218
+ uat_dispatch: override.uat_dispatch ?? base.uat_dispatch,
219
+ unique_milestone_ids: override.unique_milestone_ids ?? base.unique_milestone_ids,
220
+ budget_ceiling: override.budget_ceiling ?? base.budget_ceiling,
221
+ budget_enforcement: override.budget_enforcement ?? base.budget_enforcement,
222
+ context_pause_threshold: override.context_pause_threshold ?? base.context_pause_threshold,
223
+ notifications: (base.notifications || override.notifications)
224
+ ? { ...(base.notifications ?? {}), ...(override.notifications ?? {}) }
225
+ : undefined,
226
+ remote_questions: override.remote_questions
227
+ ? { ...(base.remote_questions ?? {}), ...override.remote_questions }
228
+ : base.remote_questions,
229
+ git: (base.git || override.git)
230
+ ? { ...(base.git ?? {}), ...(override.git ?? {}) }
231
+ : undefined,
232
+ post_unit_hooks: mergePostUnitHooks(base.post_unit_hooks, override.post_unit_hooks),
233
+ pre_dispatch_hooks: mergePreDispatchHooks(base.pre_dispatch_hooks, override.pre_dispatch_hooks),
234
+ dynamic_routing: (base.dynamic_routing || override.dynamic_routing)
235
+ ? { ...(base.dynamic_routing ?? {}), ...(override.dynamic_routing ?? {}) } as DynamicRoutingConfig
236
+ : undefined,
237
+ token_profile: override.token_profile ?? base.token_profile,
238
+ phases: (base.phases || override.phases)
239
+ ? { ...(base.phases ?? {}), ...(override.phases ?? {}) }
240
+ : undefined,
241
+ parallel: (base.parallel || override.parallel)
242
+ ? { ...(base.parallel ?? {}), ...(override.parallel ?? {}) } as import("./types.js").ParallelConfig
243
+ : undefined,
244
+ verification_commands: mergeStringLists(base.verification_commands, override.verification_commands),
245
+ verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
246
+ verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
247
+ search_provider: override.search_provider ?? base.search_provider,
248
+ compression_strategy: override.compression_strategy ?? base.compression_strategy,
249
+ context_selection: override.context_selection ?? base.context_selection,
250
+ };
251
+ }
374
252
 
375
- // Resolve all skill lists
376
- for (const skill of preferences.always_use_skills ?? []) resolve(skill);
377
- for (const skill of preferences.prefer_skills ?? []) resolve(skill);
378
- for (const skill of preferences.avoid_skills ?? []) resolve(skill);
253
+ function mergeStringLists(base?: unknown, override?: unknown): string[] | undefined {
254
+ const merged = [
255
+ ...normalizeStringArray(base),
256
+ ...normalizeStringArray(override),
257
+ ]
258
+ .map((item) => item.trim())
259
+ .filter(Boolean);
260
+ return merged.length > 0 ? Array.from(new Set(merged)) : undefined;
261
+ }
379
262
 
380
- // Resolve skill rules
381
- for (const rule of preferences.skill_rules ?? []) {
382
- for (const skill of rule.use ?? []) resolve(skill);
383
- for (const skill of rule.prefer ?? []) resolve(skill);
384
- for (const skill of rule.avoid ?? []) resolve(skill);
263
+ function mergePostUnitHooks(
264
+ base?: PostUnitHookConfig[],
265
+ override?: PostUnitHookConfig[],
266
+ ): PostUnitHookConfig[] | undefined {
267
+ if (!base?.length && !override?.length) return undefined;
268
+ const merged = [...(base ?? [])];
269
+ for (const hook of override ?? []) {
270
+ // Override hooks with same name replace base hooks
271
+ const idx = merged.findIndex(h => h.name === hook.name);
272
+ if (idx >= 0) {
273
+ merged[idx] = hook;
274
+ } else {
275
+ merged.push(hook);
276
+ }
385
277
  }
386
-
387
- return { resolutions, warnings };
278
+ return merged.length > 0 ? merged : undefined;
388
279
  }
389
280
 
390
- /**
391
- * Format a skill reference for the system prompt.
392
- * If resolved, shows the path so the agent knows exactly where to read.
393
- * If unresolved, marks it clearly.
394
- */
395
- function formatSkillRef(ref: string, resolutions: Map<string, SkillResolution>): string {
396
- const resolution = resolutions.get(ref);
397
- if (!resolution || resolution.method === "unresolved") {
398
- return `${ref} ( not found — check skill name or path)`;
399
- }
400
- // For absolute paths where SKILL.md is just appended, don't clutter the output
401
- if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
402
- return ref;
281
+ function mergePreDispatchHooks(
282
+ base?: PreDispatchHookConfig[],
283
+ override?: PreDispatchHookConfig[],
284
+ ): PreDispatchHookConfig[] | undefined {
285
+ if (!base?.length && !override?.length) return undefined;
286
+ const merged = [...(base ?? [])];
287
+ for (const hook of override ?? []) {
288
+ const idx = merged.findIndex(h => h.name === hook.name);
289
+ if (idx >= 0) {
290
+ merged[idx] = hook;
291
+ } else {
292
+ merged.push(hook);
293
+ }
403
294
  }
404
- // For bare names resolved from skill directories, show the resolved path
405
- return `${ref} → \`${resolution.resolvedPath}\``;
295
+ return merged.length > 0 ? merged : undefined;
406
296
  }
407
297
 
408
298
  // ─── System Prompt Rendering ──────────────────────────────────────────────────
@@ -475,67 +365,30 @@ export function renderPreferencesForSystemPrompt(preferences: GSDPreferences, re
475
365
  return lines.join("\n");
476
366
  }
477
367
 
478
- function loadPreferencesFile(path: string, scope: "global" | "project"): LoadedGSDPreferences | null {
479
- if (!existsSync(path)) return null;
480
-
481
- const raw = readFileSync(path, "utf-8");
482
- const preferences = parsePreferencesMarkdown(raw);
483
- if (!preferences) return null;
484
-
485
- const validation = validatePreferences(preferences);
486
- const allWarnings = [...validation.warnings, ...validation.errors];
487
-
488
- return {
489
- path,
490
- scope,
491
- preferences: validation.preferences,
492
- ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}),
493
- };
494
- }
495
-
496
- /** @internal Exported for testing only */
497
- export function parsePreferencesMarkdown(content: string): GSDPreferences | null {
498
- // Use indexOf instead of [\s\S]*? regex to avoid backtracking (#468)
499
- const startMarker = content.startsWith('---\r\n') ? '---\r\n' : '---\n';
500
- if (!content.startsWith(startMarker)) return null;
501
- const searchStart = startMarker.length;
502
- const endIdx = content.indexOf('\n---', searchStart);
503
- if (endIdx === -1) return null;
504
- const block = content.slice(searchStart, endIdx);
505
- return parseFrontmatterBlock(block.replace(/\r/g, ''));
506
- }
507
-
508
- function parseFrontmatterBlock(frontmatter: string): GSDPreferences {
509
- try {
510
- const parsed = parseYaml(frontmatter);
511
- if (typeof parsed !== 'object' || parsed === null) {
512
- return {} as GSDPreferences;
513
- }
514
- return parsed as GSDPreferences;
515
- } catch (e) {
516
- console.error("[parseFrontmatterBlock] YAML parse error:", e);
517
- return {} as GSDPreferences;
518
- }
519
- }
368
+ // ─── Hook Resolution ──────────────────────────────────────────────────────────
520
369
 
521
370
  /**
522
- * Resolve the skill discovery mode from effective preferences.
523
- * Defaults to "suggest" skills are identified during research but not installed automatically.
371
+ * Resolve enabled post-unit hooks from effective preferences.
372
+ * Returns an empty array when no hooks are configured.
524
373
  */
525
- export function resolveSkillDiscoveryMode(): SkillDiscoveryMode {
374
+ export function resolvePostUnitHooks(): PostUnitHookConfig[] {
526
375
  const prefs = loadEffectiveGSDPreferences();
527
- return prefs?.preferences.skill_discovery ?? "suggest";
376
+ return (prefs?.preferences.post_unit_hooks ?? [])
377
+ .filter(h => h.enabled !== false);
528
378
  }
529
379
 
530
380
  /**
531
- * Resolve the skill staleness threshold in days.
532
- * Returns 0 if disabled, default 60 if not configured.
381
+ * Resolve enabled pre-dispatch hooks from effective preferences.
382
+ * Returns an empty array when no hooks are configured.
533
383
  */
534
- export function resolveSkillStalenessDays(): number {
384
+ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
535
385
  const prefs = loadEffectiveGSDPreferences();
536
- return prefs?.preferences.skill_staleness_days ?? 60;
386
+ return (prefs?.preferences.pre_dispatch_hooks ?? [])
387
+ .filter(h => h.enabled !== false);
537
388
  }
538
389
 
390
+ // ─── Isolation & Parallel ─────────────────────────────────────────────────────
391
+
539
392
  /**
540
393
  * Resolve the effective git isolation mode from preferences.
541
394
  * Returns "worktree" (default), "branch", or "none".
@@ -547,945 +400,6 @@ export function getIsolationMode(): "none" | "worktree" | "branch" {
547
400
  return "worktree"; // default
548
401
  }
549
402
 
550
- /**
551
- * Resolve which model ID to use for a given auto-mode unit type.
552
- * Returns undefined if no model preference is set for this unit type.
553
- */
554
- export function resolveModelForUnit(unitType: string): string | undefined {
555
- const resolved = resolveModelWithFallbacksForUnit(unitType);
556
- return resolved?.primary;
557
- }
558
-
559
- /**
560
- * Resolve model and fallbacks for a given auto-mode unit type.
561
- * Returns the primary model and ordered fallbacks, or undefined if not configured.
562
- *
563
- * Supports both legacy string format and extended object format:
564
- * - Legacy: `planning: claude-opus-4-6`
565
- * - Extended: `planning: { model: claude-opus-4-6, fallbacks: [glm-5, minimax-m2.5] }`
566
- */
567
- /**
568
- * Determines the next fallback model to try when the current model fails.
569
- * If the current model is not in the configured list, returns the primary model.
570
- * If the current model is the last in the list, returns undefined (exhausted).
571
- */
572
- export function getNextFallbackModel(
573
- currentModelId: string | undefined,
574
- modelConfig: ResolvedModelConfig,
575
- ): string | undefined {
576
- const modelsToTry = [modelConfig.primary, ...modelConfig.fallbacks];
577
-
578
- if (!currentModelId) {
579
- return modelsToTry[0];
580
- }
581
-
582
- let foundCurrent = false;
583
- for (let i = 0; i < modelsToTry.length; i++) {
584
- const mId = modelsToTry[i];
585
- // Check for exact match or provider/model suffix match
586
- if (mId === currentModelId || (mId.includes("/") && mId.endsWith(`/${currentModelId}`))) {
587
- foundCurrent = true;
588
- return modelsToTry[i + 1]; // Return the next one, or undefined if at the end
589
- }
590
- }
591
-
592
- // If the current model wasn't in our preference list, default to starting the sequence
593
- if (!foundCurrent) {
594
- return modelsToTry[0];
595
- }
596
- }
597
-
598
- /**
599
- * Detect whether an error message indicates a transient network error
600
- * (worth retrying the same model) vs a permanent provider error
601
- * (auth failure, quota exceeded, etc. — should fall back immediately).
602
- */
603
- export function isTransientNetworkError(errorMsg: string): boolean {
604
- if (!errorMsg) return false;
605
- const hasNetworkSignal = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i.test(errorMsg);
606
- const hasPermanentSignal = /auth|unauthorized|forbidden|invalid.*key|quota|billing/i.test(errorMsg);
607
- return hasNetworkSignal && !hasPermanentSignal;
608
- }
609
-
610
- export function resolveModelWithFallbacksForUnit(unitType: string): ResolvedModelConfig | undefined {
611
- const prefs = loadEffectiveGSDPreferences();
612
- if (!prefs?.preferences.models) return undefined;
613
- const m = prefs.preferences.models as GSDModelConfigV2;
614
-
615
- let phaseConfig: string | GSDPhaseModelConfig | undefined;
616
- switch (unitType) {
617
- case "research-milestone":
618
- case "research-slice":
619
- phaseConfig = m.research;
620
- break;
621
- case "plan-milestone":
622
- case "plan-slice":
623
- case "replan-slice":
624
- phaseConfig = m.planning;
625
- break;
626
- case "execute-task":
627
- phaseConfig = m.execution;
628
- break;
629
- case "execute-task-simple":
630
- phaseConfig = m.execution_simple ?? m.execution;
631
- break;
632
- case "complete-slice":
633
- case "run-uat":
634
- phaseConfig = m.completion;
635
- break;
636
- default:
637
- // Subagent unit types (e.g., "subagent", "subagent/scout")
638
- if (unitType === "subagent" || unitType.startsWith("subagent/")) {
639
- phaseConfig = m.subagent;
640
- break;
641
- }
642
- return undefined;
643
- }
644
-
645
- if (!phaseConfig) return undefined;
646
-
647
- // Normalize: string -> { model, fallbacks: [] }
648
- if (typeof phaseConfig === "string") {
649
- return { primary: phaseConfig, fallbacks: [] };
650
- }
651
-
652
- // When provider is explicitly set, prepend it to the model ID so the
653
- // resolution code in auto.ts can do an explicit provider match.
654
- const primary = phaseConfig.provider && !phaseConfig.model.includes("/")
655
- ? `${phaseConfig.provider}/${phaseConfig.model}`
656
- : phaseConfig.model;
657
-
658
- return {
659
- primary,
660
- fallbacks: phaseConfig.fallbacks ?? [],
661
- };
662
- }
663
-
664
- /**
665
- * Resolve the dynamic routing configuration from effective preferences.
666
- * Returns the merged config with defaults applied.
667
- */
668
- export function resolveDynamicRoutingConfig(): DynamicRoutingConfig {
669
- const prefs = loadEffectiveGSDPreferences();
670
- const configured = prefs?.preferences.dynamic_routing;
671
- if (!configured) return defaultRoutingConfig();
672
- return {
673
- ...defaultRoutingConfig(),
674
- ...configured,
675
- };
676
- }
677
-
678
- export function resolveAutoSupervisorConfig(): AutoSupervisorConfig {
679
- const prefs = loadEffectiveGSDPreferences();
680
- const configured = prefs?.preferences.auto_supervisor ?? {};
681
-
682
- return {
683
- soft_timeout_minutes: configured.soft_timeout_minutes ?? 20,
684
- idle_timeout_minutes: configured.idle_timeout_minutes ?? 10,
685
- hard_timeout_minutes: configured.hard_timeout_minutes ?? 30,
686
- ...(configured.model ? { model: configured.model } : {}),
687
- };
688
- }
689
-
690
- // ─── Token Profile Resolution ─────────────────────────────────────────────
691
-
692
- const VALID_TOKEN_PROFILES = new Set<TokenProfile>(["budget", "balanced", "quality"]);
693
-
694
- /**
695
- * Resolve profile defaults for a given token profile tier.
696
- * Returns a partial GSDPreferences that is used as the base layer —
697
- * explicit user preferences always override these defaults.
698
- */
699
- export function resolveProfileDefaults(profile: TokenProfile): Partial<GSDPreferences> {
700
- switch (profile) {
701
- case "budget":
702
- return {
703
- models: {
704
- planning: "claude-sonnet-4-5-20250514",
705
- execution: "claude-sonnet-4-5-20250514",
706
- execution_simple: "claude-haiku-4-5-20250414",
707
- completion: "claude-haiku-4-5-20250414",
708
- subagent: "claude-haiku-4-5-20250414",
709
- },
710
- phases: {
711
- skip_research: true,
712
- skip_reassess: true,
713
- skip_slice_research: true,
714
- skip_milestone_validation: true,
715
- },
716
- };
717
- case "balanced":
718
- return {
719
- models: {
720
- subagent: "claude-sonnet-4-5-20250514",
721
- },
722
- phases: {
723
- skip_slice_research: true,
724
- },
725
- };
726
- case "quality":
727
- return {
728
- models: {},
729
- phases: {},
730
- };
731
- }
732
- }
733
-
734
- /**
735
- * Resolve the effective token profile from preferences.
736
- * Returns "balanced" when no profile is set (D046).
737
- */
738
- export function resolveEffectiveProfile(): TokenProfile {
739
- const prefs = loadEffectiveGSDPreferences();
740
- const profile = prefs?.preferences.token_profile;
741
- if (profile && VALID_TOKEN_PROFILES.has(profile)) return profile;
742
- return "balanced";
743
- }
744
-
745
- /**
746
- * Resolve the inline level from the active token profile.
747
- * budget → minimal, balanced → standard, quality → full.
748
- */
749
- export function resolveInlineLevel(): InlineLevel {
750
- const profile = resolveEffectiveProfile();
751
- switch (profile) {
752
- case "budget": return "minimal";
753
- case "balanced": return "standard";
754
- case "quality": return "full";
755
- }
756
- }
757
-
758
- function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPreferences {
759
- return {
760
- version: override.version ?? base.version,
761
- mode: override.mode ?? base.mode,
762
- always_use_skills: mergeStringLists(base.always_use_skills, override.always_use_skills),
763
- prefer_skills: mergeStringLists(base.prefer_skills, override.prefer_skills),
764
- avoid_skills: mergeStringLists(base.avoid_skills, override.avoid_skills),
765
- skill_rules: [...(base.skill_rules ?? []), ...(override.skill_rules ?? [])],
766
- custom_instructions: mergeStringLists(base.custom_instructions, override.custom_instructions),
767
- models: { ...(base.models ?? {}), ...(override.models ?? {}) },
768
- skill_discovery: override.skill_discovery ?? base.skill_discovery,
769
- skill_staleness_days: override.skill_staleness_days ?? base.skill_staleness_days,
770
- auto_supervisor: { ...(base.auto_supervisor ?? {}), ...(override.auto_supervisor ?? {}) },
771
- uat_dispatch: override.uat_dispatch ?? base.uat_dispatch,
772
- unique_milestone_ids: override.unique_milestone_ids ?? base.unique_milestone_ids,
773
- budget_ceiling: override.budget_ceiling ?? base.budget_ceiling,
774
- budget_enforcement: override.budget_enforcement ?? base.budget_enforcement,
775
- context_pause_threshold: override.context_pause_threshold ?? base.context_pause_threshold,
776
- notifications: (base.notifications || override.notifications)
777
- ? { ...(base.notifications ?? {}), ...(override.notifications ?? {}) }
778
- : undefined,
779
- remote_questions: override.remote_questions
780
- ? { ...(base.remote_questions ?? {}), ...override.remote_questions }
781
- : base.remote_questions,
782
- git: (base.git || override.git)
783
- ? { ...(base.git ?? {}), ...(override.git ?? {}) }
784
- : undefined,
785
- post_unit_hooks: mergePostUnitHooks(base.post_unit_hooks, override.post_unit_hooks),
786
- pre_dispatch_hooks: mergePreDispatchHooks(base.pre_dispatch_hooks, override.pre_dispatch_hooks),
787
- dynamic_routing: (base.dynamic_routing || override.dynamic_routing)
788
- ? { ...(base.dynamic_routing ?? {}), ...(override.dynamic_routing ?? {}) } as DynamicRoutingConfig
789
- : undefined,
790
- token_profile: override.token_profile ?? base.token_profile,
791
- phases: (base.phases || override.phases)
792
- ? { ...(base.phases ?? {}), ...(override.phases ?? {}) }
793
- : undefined,
794
- parallel: (base.parallel || override.parallel)
795
- ? { ...(base.parallel ?? {}), ...(override.parallel ?? {}) } as import("./types.js").ParallelConfig
796
- : undefined,
797
- verification_commands: mergeStringLists(base.verification_commands, override.verification_commands),
798
- verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
799
- verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
800
- };
801
- }
802
-
803
- export function validatePreferences(preferences: GSDPreferences): {
804
- preferences: GSDPreferences;
805
- errors: string[];
806
- warnings: string[];
807
- } {
808
- const errors: string[] = [];
809
- const warnings: string[] = [];
810
- const validated: GSDPreferences = {};
811
-
812
- // ─── Unknown Key Detection ──────────────────────────────────────────
813
- for (const key of Object.keys(preferences)) {
814
- if (!KNOWN_PREFERENCE_KEYS.has(key)) {
815
- warnings.push(`unknown preference key "${key}" — ignored`);
816
- }
817
- }
818
-
819
- if (preferences.version !== undefined) {
820
- if (preferences.version === 1) {
821
- validated.version = 1;
822
- } else {
823
- errors.push(`unsupported version ${preferences.version}`);
824
- }
825
- }
826
-
827
- // ─── Workflow Mode ──────────────────────────────────────────────────
828
- if (preferences.mode !== undefined) {
829
- const validModes = new Set<string>(["solo", "team"]);
830
- if (typeof preferences.mode === "string" && validModes.has(preferences.mode)) {
831
- validated.mode = preferences.mode as WorkflowMode;
832
- } else {
833
- errors.push(`invalid mode "${preferences.mode}" — must be one of: solo, team`);
834
- }
835
- }
836
-
837
- const validDiscoveryModes = new Set(["auto", "suggest", "off"]);
838
- if (preferences.skill_discovery) {
839
- if (validDiscoveryModes.has(preferences.skill_discovery)) {
840
- validated.skill_discovery = preferences.skill_discovery;
841
- } else {
842
- errors.push(`invalid skill_discovery value: ${preferences.skill_discovery}`);
843
- }
844
- }
845
-
846
- if (preferences.skill_staleness_days !== undefined) {
847
- const days = Number(preferences.skill_staleness_days);
848
- if (Number.isFinite(days) && days >= 0) {
849
- validated.skill_staleness_days = Math.floor(days);
850
- } else {
851
- errors.push(`invalid skill_staleness_days: must be a non-negative number`);
852
- }
853
- }
854
-
855
- validated.always_use_skills = normalizeStringList(preferences.always_use_skills);
856
- validated.prefer_skills = normalizeStringList(preferences.prefer_skills);
857
- validated.avoid_skills = normalizeStringList(preferences.avoid_skills);
858
- validated.custom_instructions = normalizeStringList(preferences.custom_instructions);
859
-
860
- if (preferences.skill_rules) {
861
- const validRules: GSDSkillRule[] = [];
862
- for (const rule of preferences.skill_rules) {
863
- if (!rule || typeof rule !== "object") {
864
- errors.push("invalid skill_rules entry");
865
- continue;
866
- }
867
- const when = typeof rule.when === "string" ? rule.when.trim() : "";
868
- if (!when) {
869
- errors.push("skill_rules entry missing when");
870
- continue;
871
- }
872
- const validatedRule: GSDSkillRule = { when };
873
- for (const action of SKILL_ACTIONS) {
874
- const values = normalizeStringList((rule as unknown as Record<string, unknown>)[action]);
875
- if (values.length > 0) {
876
- validatedRule[action as keyof GSDSkillRule] = values as never;
877
- }
878
- }
879
- if (!validatedRule.use && !validatedRule.prefer && !validatedRule.avoid) {
880
- errors.push(`skill rule has no actions: ${when}`);
881
- continue;
882
- }
883
- validRules.push(validatedRule);
884
- }
885
- if (validRules.length > 0) {
886
- validated.skill_rules = validRules;
887
- }
888
- }
889
-
890
- for (const key of ["always_use_skills", "prefer_skills", "avoid_skills", "custom_instructions"] as const) {
891
- if (validated[key] && validated[key]!.length === 0) {
892
- delete validated[key];
893
- }
894
- }
895
-
896
- if (preferences.uat_dispatch !== undefined) {
897
- validated.uat_dispatch = !!preferences.uat_dispatch;
898
- }
899
-
900
- if (preferences.unique_milestone_ids !== undefined) {
901
- validated.unique_milestone_ids = !!preferences.unique_milestone_ids;
902
- }
903
-
904
- if (preferences.budget_ceiling !== undefined) {
905
- const raw = preferences.budget_ceiling;
906
- if (typeof raw === "number" && Number.isFinite(raw)) {
907
- validated.budget_ceiling = raw;
908
- } else if (typeof raw === "string" && Number.isFinite(Number(raw))) {
909
- validated.budget_ceiling = Number(raw);
910
- } else {
911
- errors.push("budget_ceiling must be a finite number");
912
- }
913
- }
914
-
915
- // ─── Budget Enforcement ──────────────────────────────────────────────
916
- if (preferences.budget_enforcement !== undefined) {
917
- const validModes = new Set(["warn", "pause", "halt"]);
918
- if (typeof preferences.budget_enforcement === "string" && validModes.has(preferences.budget_enforcement)) {
919
- validated.budget_enforcement = preferences.budget_enforcement;
920
- } else {
921
- errors.push(`budget_enforcement must be one of: warn, pause, halt`);
922
- }
923
- }
924
-
925
- // ─── Token Profile ─────────────────────────────────────────────────
926
- if (preferences.token_profile !== undefined) {
927
- if (typeof preferences.token_profile === "string" && VALID_TOKEN_PROFILES.has(preferences.token_profile as TokenProfile)) {
928
- validated.token_profile = preferences.token_profile as TokenProfile;
929
- } else {
930
- errors.push(`token_profile must be one of: budget, balanced, quality`);
931
- }
932
- }
933
-
934
- // ─── Phase Skip Preferences ─────────────────────────────────────────
935
- if (preferences.phases !== undefined) {
936
- if (typeof preferences.phases === "object" && preferences.phases !== null) {
937
- const validatedPhases: PhaseSkipPreferences = {};
938
- const p = preferences.phases as Record<string, unknown>;
939
- if (p.skip_research !== undefined) validatedPhases.skip_research = !!p.skip_research;
940
- if (p.skip_reassess !== undefined) validatedPhases.skip_reassess = !!p.skip_reassess;
941
- if (p.skip_slice_research !== undefined) validatedPhases.skip_slice_research = !!p.skip_slice_research;
942
- if (p.skip_milestone_validation !== undefined) validatedPhases.skip_milestone_validation = !!p.skip_milestone_validation;
943
- if ((p as any).require_slice_discussion !== undefined) (validatedPhases as any).require_slice_discussion = !!(p as any).require_slice_discussion;
944
- // Warn on unknown phase keys
945
- const knownPhaseKeys = new Set(["skip_research", "skip_reassess", "skip_slice_research", "skip_milestone_validation", "require_slice_discussion"]);
946
- for (const key of Object.keys(p)) {
947
- if (!knownPhaseKeys.has(key)) {
948
- warnings.push(`unknown phases key "${key}" — ignored`);
949
- }
950
- }
951
- validated.phases = validatedPhases;
952
- } else {
953
- errors.push(`phases must be an object`);
954
- }
955
- }
956
-
957
- // ─── Context Pause Threshold ────────────────────────────────────────
958
- if (preferences.context_pause_threshold !== undefined) {
959
- const raw = preferences.context_pause_threshold;
960
- if (typeof raw === "number" && Number.isFinite(raw)) {
961
- validated.context_pause_threshold = raw;
962
- } else if (typeof raw === "string" && Number.isFinite(Number(raw))) {
963
- validated.context_pause_threshold = Number(raw);
964
- } else {
965
- errors.push("context_pause_threshold must be a finite number");
966
- }
967
- }
968
-
969
- // ─── Models ─────────────────────────────────────────────────────────
970
- if (preferences.models !== undefined) {
971
- if (preferences.models && typeof preferences.models === "object") {
972
- validated.models = preferences.models;
973
- } else {
974
- errors.push("models must be an object");
975
- }
976
- }
977
-
978
- // ─── Auto Supervisor ────────────────────────────────────────────────
979
- if (preferences.auto_supervisor !== undefined) {
980
- if (preferences.auto_supervisor && typeof preferences.auto_supervisor === "object") {
981
- validated.auto_supervisor = preferences.auto_supervisor;
982
- } else {
983
- errors.push("auto_supervisor must be an object");
984
- }
985
- }
986
-
987
- // ─── Notifications ──────────────────────────────────────────────────
988
- if (preferences.notifications !== undefined) {
989
- if (preferences.notifications && typeof preferences.notifications === "object") {
990
- validated.notifications = preferences.notifications;
991
- } else {
992
- errors.push("notifications must be an object");
993
- }
994
- }
995
-
996
- // ─── Remote Questions ───────────────────────────────────────────────
997
- if (preferences.remote_questions !== undefined) {
998
- if (preferences.remote_questions && typeof preferences.remote_questions === "object") {
999
- validated.remote_questions = preferences.remote_questions;
1000
- } else {
1001
- errors.push("remote_questions must be an object");
1002
- }
1003
- }
1004
-
1005
- // ─── Post-Unit Hooks ─────────────────────────────────────────────────
1006
- if (preferences.post_unit_hooks && Array.isArray(preferences.post_unit_hooks)) {
1007
- const validHooks: PostUnitHookConfig[] = [];
1008
- const seenNames = new Set<string>();
1009
- const knownUnitTypes = new Set([
1010
- "research-milestone", "plan-milestone", "research-slice", "plan-slice",
1011
- "execute-task", "complete-slice", "replan-slice", "reassess-roadmap",
1012
- "run-uat", "complete-milestone",
1013
- ]);
1014
- for (const hook of preferences.post_unit_hooks) {
1015
- if (!hook || typeof hook !== "object") {
1016
- errors.push("post_unit_hooks entry must be an object");
1017
- continue;
1018
- }
1019
- const name = typeof hook.name === "string" ? hook.name.trim() : "";
1020
- if (!name) {
1021
- errors.push("post_unit_hooks entry missing name");
1022
- continue;
1023
- }
1024
- if (seenNames.has(name)) {
1025
- errors.push(`duplicate post_unit_hooks name: ${name}`);
1026
- continue;
1027
- }
1028
- const after = normalizeStringList(hook.after);
1029
- if (after.length === 0) {
1030
- errors.push(`post_unit_hooks "${name}" missing after`);
1031
- continue;
1032
- }
1033
- for (const ut of after) {
1034
- if (!knownUnitTypes.has(ut)) {
1035
- errors.push(`post_unit_hooks "${name}" unknown unit type in after: ${ut}`);
1036
- }
1037
- }
1038
- const prompt = typeof hook.prompt === "string" ? hook.prompt.trim() : "";
1039
- if (!prompt) {
1040
- errors.push(`post_unit_hooks "${name}" missing prompt`);
1041
- continue;
1042
- }
1043
- const validHook: PostUnitHookConfig = { name, after, prompt };
1044
- if (hook.max_cycles !== undefined) {
1045
- const mc = typeof hook.max_cycles === "number" ? hook.max_cycles : Number(hook.max_cycles);
1046
- validHook.max_cycles = Number.isFinite(mc) ? Math.max(1, Math.min(10, Math.round(mc))) : 1;
1047
- }
1048
- if (typeof hook.model === "string" && hook.model.trim()) {
1049
- validHook.model = hook.model.trim();
1050
- }
1051
- if (typeof hook.artifact === "string" && hook.artifact.trim()) {
1052
- validHook.artifact = hook.artifact.trim();
1053
- }
1054
- if (typeof hook.retry_on === "string" && hook.retry_on.trim()) {
1055
- validHook.retry_on = hook.retry_on.trim();
1056
- }
1057
- if (typeof hook.agent === "string" && hook.agent.trim()) {
1058
- validHook.agent = hook.agent.trim();
1059
- }
1060
- if (hook.enabled !== undefined) {
1061
- validHook.enabled = !!hook.enabled;
1062
- }
1063
- seenNames.add(name);
1064
- validHooks.push(validHook);
1065
- }
1066
- if (validHooks.length > 0) {
1067
- validated.post_unit_hooks = validHooks;
1068
- }
1069
- }
1070
-
1071
- // ─── Pre-Dispatch Hooks ─────────────────────────────────────────────────
1072
- if (preferences.pre_dispatch_hooks && Array.isArray(preferences.pre_dispatch_hooks)) {
1073
- const validPreHooks: PreDispatchHookConfig[] = [];
1074
- const seenPreNames = new Set<string>();
1075
- const knownUnitTypes = new Set([
1076
- "research-milestone", "plan-milestone", "research-slice", "plan-slice",
1077
- "execute-task", "complete-slice", "replan-slice", "reassess-roadmap",
1078
- "run-uat", "complete-milestone",
1079
- ]);
1080
- const validActions = new Set(["modify", "skip", "replace"]);
1081
- for (const hook of preferences.pre_dispatch_hooks) {
1082
- if (!hook || typeof hook !== "object") {
1083
- errors.push("pre_dispatch_hooks entry must be an object");
1084
- continue;
1085
- }
1086
- const name = typeof hook.name === "string" ? hook.name.trim() : "";
1087
- if (!name) {
1088
- errors.push("pre_dispatch_hooks entry missing name");
1089
- continue;
1090
- }
1091
- if (seenPreNames.has(name)) {
1092
- errors.push(`duplicate pre_dispatch_hooks name: ${name}`);
1093
- continue;
1094
- }
1095
- const before = normalizeStringList(hook.before);
1096
- if (before.length === 0) {
1097
- errors.push(`pre_dispatch_hooks "${name}" missing before`);
1098
- continue;
1099
- }
1100
- for (const ut of before) {
1101
- if (!knownUnitTypes.has(ut)) {
1102
- errors.push(`pre_dispatch_hooks "${name}" unknown unit type in before: ${ut}`);
1103
- }
1104
- }
1105
- const action = typeof hook.action === "string" ? hook.action.trim() : "";
1106
- if (!validActions.has(action)) {
1107
- errors.push(`pre_dispatch_hooks "${name}" invalid action: ${action} (must be modify, skip, or replace)`);
1108
- continue;
1109
- }
1110
- const validHook: PreDispatchHookConfig = { name, before, action: action as PreDispatchHookConfig["action"] };
1111
- if (typeof hook.prepend === "string" && hook.prepend.trim()) validHook.prepend = hook.prepend.trim();
1112
- if (typeof hook.append === "string" && hook.append.trim()) validHook.append = hook.append.trim();
1113
- if (typeof hook.prompt === "string" && hook.prompt.trim()) validHook.prompt = hook.prompt.trim();
1114
- if (typeof hook.unit_type === "string" && hook.unit_type.trim()) validHook.unit_type = hook.unit_type.trim();
1115
- if (typeof hook.skip_if === "string" && hook.skip_if.trim()) validHook.skip_if = hook.skip_if.trim();
1116
- if (typeof hook.model === "string" && hook.model.trim()) validHook.model = hook.model.trim();
1117
- if (hook.enabled !== undefined) validHook.enabled = !!hook.enabled;
1118
-
1119
- // Validation: action-specific required fields
1120
- if (action === "replace" && !validHook.prompt) {
1121
- errors.push(`pre_dispatch_hooks "${name}" action "replace" requires prompt`);
1122
- continue;
1123
- }
1124
- if (action === "modify" && !validHook.prepend && !validHook.append) {
1125
- errors.push(`pre_dispatch_hooks "${name}" action "modify" requires prepend or append`);
1126
- continue;
1127
- }
1128
-
1129
- seenPreNames.add(name);
1130
- validPreHooks.push(validHook);
1131
- }
1132
- if (validPreHooks.length > 0) {
1133
- validated.pre_dispatch_hooks = validPreHooks;
1134
- }
1135
- }
1136
-
1137
- // ─── Dynamic Routing ─────────────────────────────────────────────────
1138
- if (preferences.dynamic_routing !== undefined) {
1139
- if (typeof preferences.dynamic_routing === "object" && preferences.dynamic_routing !== null) {
1140
- const dr = preferences.dynamic_routing as unknown as Record<string, unknown>;
1141
- const validDr: Partial<DynamicRoutingConfig> = {};
1142
-
1143
- if (dr.enabled !== undefined) {
1144
- if (typeof dr.enabled === "boolean") validDr.enabled = dr.enabled;
1145
- else errors.push("dynamic_routing.enabled must be a boolean");
1146
- }
1147
- if (dr.escalate_on_failure !== undefined) {
1148
- if (typeof dr.escalate_on_failure === "boolean") validDr.escalate_on_failure = dr.escalate_on_failure;
1149
- else errors.push("dynamic_routing.escalate_on_failure must be a boolean");
1150
- }
1151
- if (dr.budget_pressure !== undefined) {
1152
- if (typeof dr.budget_pressure === "boolean") validDr.budget_pressure = dr.budget_pressure;
1153
- else errors.push("dynamic_routing.budget_pressure must be a boolean");
1154
- }
1155
- if (dr.cross_provider !== undefined) {
1156
- if (typeof dr.cross_provider === "boolean") validDr.cross_provider = dr.cross_provider;
1157
- else errors.push("dynamic_routing.cross_provider must be a boolean");
1158
- }
1159
- if (dr.hooks !== undefined) {
1160
- if (typeof dr.hooks === "boolean") validDr.hooks = dr.hooks;
1161
- else errors.push("dynamic_routing.hooks must be a boolean");
1162
- }
1163
- if (dr.tier_models !== undefined) {
1164
- if (typeof dr.tier_models === "object" && dr.tier_models !== null) {
1165
- const tm = dr.tier_models as Record<string, unknown>;
1166
- const validTm: Record<string, string> = {};
1167
- for (const tier of ["light", "standard", "heavy"]) {
1168
- if (tm[tier] !== undefined) {
1169
- if (typeof tm[tier] === "string") validTm[tier] = tm[tier] as string;
1170
- else errors.push(`dynamic_routing.tier_models.${tier} must be a string`);
1171
- }
1172
- }
1173
- if (Object.keys(validTm).length > 0) validDr.tier_models = validTm as DynamicRoutingConfig["tier_models"];
1174
- } else {
1175
- errors.push("dynamic_routing.tier_models must be an object");
1176
- }
1177
- }
1178
-
1179
- if (Object.keys(validDr).length > 0) {
1180
- validated.dynamic_routing = validDr as unknown as DynamicRoutingConfig;
1181
- }
1182
- } else {
1183
- errors.push("dynamic_routing must be an object");
1184
- }
1185
- }
1186
-
1187
- // ─── Parallel Config ────────────────────────────────────────────────────
1188
- if (preferences.parallel && typeof preferences.parallel === "object") {
1189
- const p = preferences.parallel as unknown as Record<string, unknown>;
1190
- const parallel: Record<string, unknown> = {};
1191
-
1192
- if (p.enabled !== undefined) {
1193
- if (typeof p.enabled === "boolean") parallel.enabled = p.enabled;
1194
- else errors.push("parallel.enabled must be a boolean");
1195
- }
1196
- if (p.max_workers !== undefined) {
1197
- if (typeof p.max_workers === "number" && p.max_workers >= 1 && p.max_workers <= 4) {
1198
- parallel.max_workers = Math.floor(p.max_workers);
1199
- } else {
1200
- errors.push("parallel.max_workers must be a number between 1 and 4");
1201
- }
1202
- }
1203
- if (p.budget_ceiling !== undefined) {
1204
- if (typeof p.budget_ceiling === "number" && p.budget_ceiling > 0) {
1205
- parallel.budget_ceiling = p.budget_ceiling;
1206
- } else {
1207
- errors.push("parallel.budget_ceiling must be a positive number");
1208
- }
1209
- }
1210
- if (p.merge_strategy !== undefined) {
1211
- const validStrategies = new Set(["per-slice", "per-milestone"]);
1212
- if (typeof p.merge_strategy === "string" && validStrategies.has(p.merge_strategy)) {
1213
- parallel.merge_strategy = p.merge_strategy;
1214
- } else {
1215
- errors.push("parallel.merge_strategy must be one of: per-slice, per-milestone");
1216
- }
1217
- }
1218
- if (p.auto_merge !== undefined) {
1219
- const validModes = new Set(["auto", "confirm", "manual"]);
1220
- if (typeof p.auto_merge === "string" && validModes.has(p.auto_merge)) {
1221
- parallel.auto_merge = p.auto_merge;
1222
- } else {
1223
- errors.push("parallel.auto_merge must be one of: auto, confirm, manual");
1224
- }
1225
- }
1226
-
1227
- if (Object.keys(parallel).length > 0) {
1228
- validated.parallel = parallel as unknown as import("./types.js").ParallelConfig;
1229
- }
1230
- }
1231
-
1232
- // ─── Verification Preferences ───────────────────────────────────────────
1233
- if (preferences.verification_commands !== undefined) {
1234
- if (Array.isArray(preferences.verification_commands)) {
1235
- const allStrings = preferences.verification_commands.every(
1236
- (item: unknown) => typeof item === "string",
1237
- );
1238
- if (allStrings) {
1239
- validated.verification_commands = preferences.verification_commands;
1240
- } else {
1241
- errors.push("verification_commands must be an array of strings");
1242
- }
1243
- } else {
1244
- errors.push("verification_commands must be an array of strings");
1245
- }
1246
- }
1247
-
1248
- if (preferences.verification_auto_fix !== undefined) {
1249
- if (typeof preferences.verification_auto_fix === "boolean") {
1250
- validated.verification_auto_fix = preferences.verification_auto_fix;
1251
- } else {
1252
- errors.push("verification_auto_fix must be a boolean");
1253
- }
1254
- }
1255
-
1256
- if (preferences.verification_max_retries !== undefined) {
1257
- const raw = preferences.verification_max_retries;
1258
- if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) {
1259
- validated.verification_max_retries = Math.floor(raw);
1260
- } else {
1261
- errors.push("verification_max_retries must be a non-negative number");
1262
- }
1263
- }
1264
-
1265
- // ─── Git Preferences ───────────────────────────────────────────────────
1266
- if (preferences.git && typeof preferences.git === "object") {
1267
- const git: Record<string, unknown> = {};
1268
- const g = preferences.git as Record<string, unknown>;
1269
-
1270
- if (g.auto_push !== undefined) {
1271
- if (typeof g.auto_push === "boolean") git.auto_push = g.auto_push;
1272
- else errors.push("git.auto_push must be a boolean");
1273
- }
1274
- if (g.push_branches !== undefined) {
1275
- if (typeof g.push_branches === "boolean") git.push_branches = g.push_branches;
1276
- else errors.push("git.push_branches must be a boolean");
1277
- }
1278
- if (g.remote !== undefined) {
1279
- if (typeof g.remote === "string" && g.remote.trim() !== "") git.remote = g.remote.trim();
1280
- else errors.push("git.remote must be a non-empty string");
1281
- }
1282
- if (g.snapshots !== undefined) {
1283
- if (typeof g.snapshots === "boolean") git.snapshots = g.snapshots;
1284
- else errors.push("git.snapshots must be a boolean");
1285
- }
1286
- if (g.pre_merge_check !== undefined) {
1287
- if (typeof g.pre_merge_check === "boolean") {
1288
- git.pre_merge_check = g.pre_merge_check;
1289
- } else if (typeof g.pre_merge_check === "string" && g.pre_merge_check.trim() !== "") {
1290
- git.pre_merge_check = g.pre_merge_check.trim();
1291
- } else {
1292
- errors.push("git.pre_merge_check must be a boolean or a non-empty string command");
1293
- }
1294
- }
1295
- if (g.commit_type !== undefined) {
1296
- const validCommitTypes = new Set([
1297
- "feat", "fix", "refactor", "docs", "test", "chore", "perf", "ci", "build", "style",
1298
- ]);
1299
- if (typeof g.commit_type === "string" && validCommitTypes.has(g.commit_type)) {
1300
- git.commit_type = g.commit_type;
1301
- } else {
1302
- errors.push(`git.commit_type must be one of: feat, fix, refactor, docs, test, chore, perf, ci, build, style`);
1303
- }
1304
- }
1305
- if (g.merge_strategy !== undefined) {
1306
- const validStrategies = new Set(["squash", "merge"]);
1307
- if (typeof g.merge_strategy === "string" && validStrategies.has(g.merge_strategy)) {
1308
- git.merge_strategy = g.merge_strategy as "squash" | "merge";
1309
- } else {
1310
- errors.push("git.merge_strategy must be one of: squash, merge");
1311
- }
1312
- }
1313
- if (g.main_branch !== undefined) {
1314
- if (typeof g.main_branch === "string" && g.main_branch.trim() !== "" && VALID_BRANCH_NAME.test(g.main_branch)) {
1315
- git.main_branch = g.main_branch;
1316
- } else {
1317
- errors.push("git.main_branch must be a valid branch name (alphanumeric, _, -, /, .)");
1318
- }
1319
- }
1320
- if (g.isolation !== undefined) {
1321
- const validIsolation = new Set(["worktree", "branch", "none"]);
1322
- if (typeof g.isolation === "string" && validIsolation.has(g.isolation)) {
1323
- git.isolation = g.isolation as "worktree" | "branch" | "none";
1324
- } else {
1325
- errors.push("git.isolation must be one of: worktree, branch, none");
1326
- }
1327
- }
1328
- if (g.commit_docs !== undefined) {
1329
- if (typeof g.commit_docs === "boolean") git.commit_docs = g.commit_docs;
1330
- else errors.push("git.commit_docs must be a boolean");
1331
- }
1332
- if (g.manage_gitignore !== undefined) {
1333
- if (typeof g.manage_gitignore === "boolean") git.manage_gitignore = g.manage_gitignore;
1334
- else errors.push("git.manage_gitignore must be a boolean");
1335
- }
1336
- if (g.worktree_post_create !== undefined) {
1337
- if (typeof g.worktree_post_create === "string" && g.worktree_post_create.trim()) {
1338
- git.worktree_post_create = g.worktree_post_create.trim();
1339
- } else {
1340
- errors.push("git.worktree_post_create must be a non-empty string (path to script)");
1341
- }
1342
- }
1343
- // Deprecated: merge_to_main is ignored (branchless architecture).
1344
- if (g.merge_to_main !== undefined) {
1345
- warnings.push("git.merge_to_main is deprecated — milestone-level merge is now always used. Remove this setting.");
1346
- }
1347
-
1348
- if (Object.keys(git).length > 0) {
1349
- validated.git = git as GitPreferences;
1350
- }
1351
- }
1352
-
1353
- return { preferences: validated, errors, warnings };
1354
- }
1355
-
1356
- function mergeStringLists(base?: unknown, override?: unknown): string[] | undefined {
1357
- const merged = [
1358
- ...normalizeStringList(base),
1359
- ...normalizeStringList(override),
1360
- ]
1361
- .map((item) => item.trim())
1362
- .filter(Boolean);
1363
- return merged.length > 0 ? Array.from(new Set(merged)) : undefined;
1364
- }
1365
-
1366
- function normalizeStringList(value: unknown): string[] {
1367
- if (!Array.isArray(value)) return [];
1368
- return value
1369
- .filter((item): item is string => typeof item === "string")
1370
- .map((item) => item.trim())
1371
- .filter(Boolean);
1372
- }
1373
-
1374
- function mergePostUnitHooks(
1375
- base?: PostUnitHookConfig[],
1376
- override?: PostUnitHookConfig[],
1377
- ): PostUnitHookConfig[] | undefined {
1378
- if (!base?.length && !override?.length) return undefined;
1379
- const merged = [...(base ?? [])];
1380
- for (const hook of override ?? []) {
1381
- // Override hooks with same name replace base hooks
1382
- const idx = merged.findIndex(h => h.name === hook.name);
1383
- if (idx >= 0) {
1384
- merged[idx] = hook;
1385
- } else {
1386
- merged.push(hook);
1387
- }
1388
- }
1389
- return merged.length > 0 ? merged : undefined;
1390
- }
1391
-
1392
- /**
1393
- * Resolve enabled post-unit hooks from effective preferences.
1394
- * Returns an empty array when no hooks are configured.
1395
- */
1396
- export function resolvePostUnitHooks(): PostUnitHookConfig[] {
1397
- const prefs = loadEffectiveGSDPreferences();
1398
- return (prefs?.preferences.post_unit_hooks ?? [])
1399
- .filter(h => h.enabled !== false);
1400
- }
1401
-
1402
- function mergePreDispatchHooks(
1403
- base?: PreDispatchHookConfig[],
1404
- override?: PreDispatchHookConfig[],
1405
- ): PreDispatchHookConfig[] | undefined {
1406
- if (!base?.length && !override?.length) return undefined;
1407
- const merged = [...(base ?? [])];
1408
- for (const hook of override ?? []) {
1409
- const idx = merged.findIndex(h => h.name === hook.name);
1410
- if (idx >= 0) {
1411
- merged[idx] = hook;
1412
- } else {
1413
- merged.push(hook);
1414
- }
1415
- }
1416
- return merged.length > 0 ? merged : undefined;
1417
- }
1418
-
1419
- /**
1420
- * Resolve enabled pre-dispatch hooks from effective preferences.
1421
- * Returns an empty array when no hooks are configured.
1422
- */
1423
- export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
1424
- const prefs = loadEffectiveGSDPreferences();
1425
- return (prefs?.preferences.pre_dispatch_hooks ?? [])
1426
- .filter(h => h.enabled !== false);
1427
- }
1428
-
1429
- /**
1430
- * Validate a model ID string.
1431
- * Returns true if the ID looks like a valid model identifier.
1432
- */
1433
- export function validateModelId(modelId: string): boolean {
1434
- if (!modelId || typeof modelId !== "string") return false;
1435
- const trimmed = modelId.trim();
1436
- if (trimmed.length === 0 || trimmed.length > 256) return false;
1437
- // Allow alphanumeric, hyphens, underscores, dots, slashes, colons
1438
- return /^[a-zA-Z0-9\-_./:]+$/.test(trimmed);
1439
- }
1440
-
1441
- /**
1442
- * Update the models section of the global GSD preferences file.
1443
- * Performs a safe read-modify-write: reads current content, updates the models
1444
- * YAML block, and writes back. Creates the file if it doesn't exist.
1445
- */
1446
- export function updatePreferencesModels(models: GSDModelConfigV2): void {
1447
- const prefsPath = getGlobalGSDPreferencesPath();
1448
-
1449
- let content = "";
1450
- if (existsSync(prefsPath)) {
1451
- content = readFileSync(prefsPath, "utf-8");
1452
- }
1453
-
1454
- // Build the new models block
1455
- const lines: string[] = ["models:"];
1456
- for (const [phase, value] of Object.entries(models)) {
1457
- if (typeof value === "string") {
1458
- lines.push(` ${phase}: ${value}`);
1459
- } else if (value && typeof value === "object") {
1460
- const config = value as GSDPhaseModelConfig;
1461
- lines.push(` ${phase}:`);
1462
- lines.push(` model: ${config.model}`);
1463
- if (config.provider) {
1464
- lines.push(` provider: ${config.provider}`);
1465
- }
1466
- if (config.fallbacks && config.fallbacks.length > 0) {
1467
- lines.push(` fallbacks:`);
1468
- for (const fb of config.fallbacks) {
1469
- lines.push(` - ${fb}`);
1470
- }
1471
- }
1472
- }
1473
- }
1474
- const modelsBlock = lines.join("\n");
1475
-
1476
- // Replace existing models block or append
1477
- const modelsRegex = /^models:[\s\S]*?(?=\n[a-z_]|\n*$)/m;
1478
- if (modelsRegex.test(content)) {
1479
- content = content.replace(modelsRegex, modelsBlock);
1480
- } else {
1481
- content = content.trimEnd() + "\n\n" + modelsBlock + "\n";
1482
- }
1483
-
1484
- writeFileSync(prefsPath, content, "utf-8");
1485
- }
1486
-
1487
- // ─── Parallel Config Resolver ──────────────────────────────────────────────
1488
-
1489
403
  export function resolveParallelConfig(prefs: GSDPreferences | undefined): import("./types.js").ParallelConfig {
1490
404
  return {
1491
405
  enabled: prefs?.parallel?.enabled ?? false,